Использование графических эффектов в приложениях UWP с помощью Win2D

47c7851783ba40d28f82b7c70c61af71.PNG

Знакомьтесь: 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, который является элементом компоновки страницы и может в качестве хоста содержать в себе другие элементы. Какое-то сходство между 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);
   }

Вот что получится:

2aea98d616b7418caa3a9e900efe5599.PNG

Для того, чтобы избежать утечек памяти, необходимо всегда корректно уничтожать ресурсы. Делается это, как правило, при выгрузке страницы:

  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 код добавим это событие:
  

Сама загрузка происходит с помощью следующего кода:
  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);
     }

b3bd9a2afd9141b99e7b18c0fc56bd49.jpg

Загрузилось, но не особо пока что интересно просто отображать картинку. Добавим всего пару строк кода и получим эффект размытия по Гауссу

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

9f800077e9e747b2a0494df99412ffe5.PNG

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

Иногда возникает необходимость отобразить графику не сразу, а во время исполнения программы. Сохранить ее в файл, вывести на экран или получить массив пикселей. Такой вариант работы называется Offscreen drawing.
Скажем, вот пример, в котором совсем не используется XAML элемент CanvasControl, а используется простой элемент 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.2f;
      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;
  }

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

42ed761975534e4ba57351fa71b06b75.PNG

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

  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;
  }

363591195e084ac9b1beb0a345757538.PNG

Объединить 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;
  }

0ea64c9f84924940847320c0d0a438a6.PNG

Некоторые эффекты можно комбинировать. С помощью следующего сниппета, комбинирующего множество эффектов можно получить эффект свечения текста

  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.5f, M53 = 0f, M54 = 0f
      }
  });

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

0e60fe578d0e488187964df7c0568677.PNG

Интересный текстовый эффект, который очень часто ассоциируется с Win2D это анимационный эффект горящего текста.

dad79ce428284573bbbf90f62ca834b3.PNG

Прямые ссылки на BurningTextExample.xaml и BurningTextExample.xaml.cs примера я, пожалуй, даже оставлю здесь:
BurningTextExample.xaml
BurningTextExample.xaml.cs

Точно также как и в случае с UWP Community Toolkit, есть возможность скачать приложение с примерами использования и для Win2D. Приложение называется Win2D Example Gallery
Исходный код приложения находится на github

Лично мне больше всего понравилась следующая картинка:

9461885bc7154cfaa3a1ec3484b1d8bc.PNG

Как оказалось, она полностью нарисована с помощью DrawLine, DrawEllipse, DrawCircle и т.п.

Библиотека обновляется регулярно и поэтому можно ожидать новые красивые эффекты.

Комментарии (2)

  • 29 августа 2016 в 22:00

    0

    Всё это очень интересно, но как этим пользоваться в условиях MVVM?
  • 29 августа 2016 в 23:36

    0

    А библиотеку для графиков не находили? Чтобы 10к разных можно было рисовать.

© Habrahabr.ru