Використання графічних ефектів у додатках UWP з допомогою Win2D


Знайомтеся: Win2D це легке у використанні Windows Runtime API для більш зручного використання можливостей DirectX. Промальовування графіки здійснюється з прискоренням GPU. Win2D доступно для розробників C#, C + + і VB і в Windows 8.1 і в Windows 10.

З допомогою Win2D ви зможете малювати фігури, лінії, текст і зображення, а також додавати до всього цього різні ефекти. Крім того, можна додати якісь ефекти до відеозображення.

Пропоную розглянути на прикладах основний функціонал бібліотеки.

Встановити Win2D.uwp можна з Visual Studio з допомогою менеджера пакетів NuGet
Github сторінка цього open-source проект перебуває тут
Англомовна документація тут: Win2D documentation

Після установки в заголовок XAML сторінки можна додати:

xmlns:canvas="using Microsoft.Graphics.Canvas.UI.Xaml"

а в розмітку сторінки елемент управління CanvasControl:

<canvas:CanvasControl Width="500" Height="300" x:Name="canv" Draw="CanvasControl_Draw" ClearColor="LightGray"/>

Це не той самий елемент Canvas, який є елементом компонування сторінки і може в якості хоста містити в собі інші елементи. Якась схожість між Canvas і CanvasControl є, але продуктивність абсолютно різна.

При промальовуванні елемента CanvasControl відбувається подія Draw, якому в прикладі призначений обробник події CanvasControl_Draw. Офіційний приклад пропонує намалювати для початку фігури (еліпс) і текст:

void CanvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
args.DrawingSession.DrawEllipse(155, 115, 80, 30, Colors.Black, 3);
args.DrawingSession.DrawText("Hello, world!", 100, 100, Colors.Yellow);
}

Ось що вийде:



Для того, щоб уникнути витоків пам'яті, необхідно завжди коректно знищувати ресурси. Робиться це, як правило, при розвантаженні сторінки:

void Page_Unloaded(object sender, RoutedEventArgs e)
{
this.canv.RemoveFromVisualTree();
this.canv = null;
}

Поки що нічого незвичайного. Те ж саме можна намалювати і використовуючи фігури XAML. До речі, приклади малювання в звичайному XAML ви можете знайти тут: Draw shapes

Попрацюємо з зображеннями. Це набагато цікавіше.
Я додав у проект файл mydog.jpg і вибрав властивості дію при складанні «Зміст» (якщо бути точним, то навіть не обирав, — вона вибралася сама за замовчуванням).

За зображення в Win2D відповідає клас CanvasBitmap з простору імен Microsoft.Graphics.Canvas. Додамо в наш додаток змінну з ім'ям cbi:

CanvasBitmap cbi;

Завантаження картинки відбувається під час події елемента Canvas назвою CreateResources, тому в наш XAML код додамо цю подію:

<canvas:CanvasControl Width="500" Height="300" x:Name="canv"
Draw="CanvasControl_Draw" CreateResources="CanvasControl_CreateResources" ClearColor="LightGray"/>

Сама завантаження відбувається з допомогою наступного коду:

cbi = await CanvasBitmap.LoadAsync(sender, "mydog.jpg");

Так як вона відбувається асинхронно, то нам пропонується винести її в окремий таск:

private void CanvasControl_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{
args.TrackAsyncAction(CreateResourcesAsync(sender).AsAsyncAction());
}

async Task CreateResourcesAsync(CanvasControl sender)
{
cbi = await CanvasBitmap.LoadAsync(sender, "mydog.jpg");
}

TrackAsyncAction в даному випадку вимагає щоб таск CreateResourcesAsync завершився до закінчення CreateResources.
Залишається завантажити в Canvas:

private void CanvasControl_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
args.DrawingSession.DrawImage(cbi);
}



Завантажилося, але не особливо поки що цікаво просто відображати картинку. Додамо всього пару рядків коду і отримаємо ефект розмиття по Гауссу

var blur = new GaussianBlurEffect();
blur.BlurAmount = 1.7 f;
blur.Source = cbi;
args.DrawingSession.DrawImage(blur);



Повний списків у відн ефектів доступний на сторінці опису простору імен Microsoft.Graphics.Canvas.Effects документації
До речі, ефекти також можна створювати і за допомогою Lumia Imaging SDK
Вихідний код Lumia-imaging sdk можна теж знайти на GitHub
Крім того, якісь ефекти доступні за допомогою простору імен Windows.UI.Composition.

Іноді виникає необхідність відобразити графіку не відразу, а під час виконання програми. Зберегти її в файл, а вивести на екран або отримати масив пікселів. Такий варіант називається Offscreen drawing.
Скажімо, ось приклад, в якому зовсім не використовується XAML елемент CanvasControl, а використовується простий елемент image

<Image x:Name="img" Width="500" Height="300"></Image>

Обробити картинку і відобразити її, додавши ефект насичення, можна так:

CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget offscreen = new CanvasRenderTarget(device, 500, 300, 96);

cbi = await CanvasBitmap.LoadAsync(device, "mydog.jpg");

using (var ds = offscreen.CreateDrawingSession())
{
var satur = new SaturationEffect();
satur.Saturation = 0.2 f;
satur.Source = cbi;
ds.DrawImage(satur);
}

using (var stream = new InMemoryRandomAccessStream())
{
stream.Seek(0);
await offscreen.SaveAsync(stream, CanvasBitmapFileFormat.Png);

BitmapImage image = new BitmapImage();
image.SetSource(stream);
img.Source = image;
}

Мені так навіть більше подобається (хоча про порівняння продуктивності сказати нічого не можу)



Для того щоб скласти невелику композицію і накласти текст на картинку нам знадобляться простір імен:

using Microsoft.Graphics.Canvas.Text;

і подібний сниппет:

CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget offscreen = new CanvasRenderTarget(device, 500, 300, 96);

cbi = await CanvasBitmap.LoadAsync(device, "mydog.jpg");

using (var ds = offscreen.CreateDrawingSession())
{
ds.DrawImage(cbi);
var format = new CanvasTextFormat()
{
FontSize = 32,
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
WordWrapping = CanvasWordWrapping.Wrap,
FontFamily = "Decor",
};

using (CanvasTextLayout tl = new CanvasTextLayout(ds, "10 червня 2010", format, 250, 50)) // ширина 250 і висота 50
{
ds.DrawTextLayout(tl, 10, 10, Color.FromArgb(120, 20, 20, 20));
}

format.Dispose();
}

using (var stream = new InMemoryRandomAccessStream())
{
stream.Seek(0);
await offscreen.SaveAsync(stream, CanvasBitmapFileFormat.Png);

BitmapImage image = new BitmapImage();
image.SetSource(stream);
img.Source = image;
}



Об'єднати 2 зображення можна за допомогою ефекту blend:

CanvasBitmap cbi;
CanvasBitmap cbi2;

CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget offscreen = new CanvasRenderTarget(device, 500, 300, 96);

cbi = await CanvasBitmap.LoadAsync(device, "mydog.jpg");
cbi2 = await CanvasBitmap.LoadAsync(device, "present.png");

using (var ds = offscreen.CreateDrawingSession())
{
BlendEffect blendEffect = new BlendEffect()
{
Background = cbi,
Foreground = cbi2,
Mode = BlendEffectMode.SoftLight
};

ds.DrawImage(blendEffect);
}

using (var stream = new InMemoryRandomAccessStream())
{
stream.Seek(0);
await offscreen.SaveAsync(stream, CanvasBitmapFileFormat.Png);

BitmapImage image = new BitmapImage();
image.SetSource(stream);
img.Source = image;
}



Деякі ефекти можна комбінувати. З допомогою наступного фрагменту, комбинирующего безліч ефектів можна отримати ефект світіння тексту

var myTextBitmap = new CanvasRenderTarget(sender, 300, 100);
using (var ds = myTextBitmap.CreateDrawingSession())
{
ds.Clear(Color.FromArgb(0, 0, 0, 0));
ds.DrawText("Неоновий текст!", 0, 0, Colors.White, new CanvasTextFormat
{
FontSize = 24,
FontWeight = Windows.UI.Text.FontWeights.Bold
});
}

var effectGraph = new CompositeEffect();
effectGraph.Mode = CanvasComposite.Add;

effectGraph.Sources.Add(new ColorMatrixEffect
{
Source = new GaussianBlurEffect
{
Source = new MorphologyEffect
{
Source = myTextBitmap,
Mode = MorphologyEffectMode.Dilate,
Width = 7,
Height = 4
},
BlurAmount = 3f
},
ColorMatrix = new Matrix5x4
{
M11 = 0f, M12 = 0f, M13 = 0f, M14 = 0f,
M21 = 0f, M22 = 0f, M23 = 0f, M24 = 0f,
M31 = 0f, M32 = 0f, M33 = 0f, M34 = 0f,
M41 = 0f, M42 = 1f, M43 = 0f, M44 = 1f,
M51 = 1f, M52 = -0.5 f, M53 = 0f, M54 = 0f
}
});

effectGraph.Sources.Add(myTextBitmap);
args.DrawingSession.DrawImage(effectGraph,100,100);



Цікавий текстовий ефект, який дуже часто асоціюється з Win2D це анімаційний ефект палаючого тексту.



Прямі посилання на BurningTextExample.xaml і BurningTextExample.xaml.cs прикладу я, мабуть, залишу тут:
BurningTextExample.xaml
BurningTextExample.xaml.cs

Точно також як і у випадку з UWP Community Toolkit, є можливість завантажити додаток з прикладами використання і для Win2D. Додаток називається Win2D Example Gallery
Вихідний код програми знаходиться на github

Особисто мені найбільше сподобалася наступна картинка:



Як виявилося, вона повністю намальована за допомогою DrawLine, DrawEllipse, DrawCircle і т. п.

Бібліотека регулярно оновлюється і тому можна очікувати нові красиві ефекти.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.