ComponentOne Bitmap for WPF
Working with Bitmap / Applying Direct2D Effects
In This Topic
    Applying Direct2D Effects
    In This Topic

    Direct2D is a two-dimensional graphics API designed by Microsoft that offers a range of built-in and custom effects for manipulating images. The API provides high quality and fast rendering for bitmaps, 2D geometries, and text.

    Bitmap allows you to use the Direct2D effects and apply them on images. Following is a list of image effects that can be applied to an image using Bitmap:

    Let us take one of these effects and apply it on an image. The following image shows one of the built-in 2D effects, shadow, presenting the use of Direct2D in Bitmap.

    In terms of implementation, Bitmap is first converted to a Direct2D bitmap. Direct2D is then used to manipulate the image by applying the built-in shadow effect using interoperation with Direct3D API. After all the manipulations, the image is loaded back from Direct2D bitmap to C1Bitmap.

    To apply shadow effect on an image, you can use the properties of ShadowAffineTransform2D, and Composite classes, members of C1.Util.DX.Direct2D.Effects namespace.

    The following steps illustrate applying the 2D shadow effect on an image. This example uses the sample created in the Quick start.

    1. Add relevant namespaces.
      Imports D2D = C1.Util.DX.Direct2D
      Imports D3D = C1.Util.DX.Direct3D11
      Imports DW = C1.Util.DX.DirectWrite
      Imports DXGI = C1.Util.DX.DXGI
      Imports C1.Util.DX
      
      using D2D = C1.Util.DX.Direct2D;
      using D3D = C1.Util.DX.Direct3D11;
      using DW = C1.Util.DX.DirectWrite;
      using DXGI = C1.Util.DX.DXGI;
      using C1.Util.DX;
      
    2. Create various class objects.
      Private bitmap As C1Bitmap
      
      ' device-independent resources
      Private d2dFactory As D2D.Factory2
      Private dwFactory As DW.Factory
      
      ' device resources
      Private dxgiDevice As DXGI.Device
      Private d2dContext As D2D.DeviceContext1
      
      ' Direct2D built-in effects
      Private shadow As D2D.Effects.Shadow
      Private affineTransform As D2D.Effects.AffineTransform2D
      Private composite As D2D.Effects.Composite
      
      C1Bitmap bitmap;
      
      // device-independent resources
      D2D.Factory2 d2dFactory;
      DW.Factory dwFactory;
      
      // device resources
      DXGI.Device dxgiDevice;
      D2D.DeviceContext1 d2dContext;
      
      // Direct2D built-in effects
      D2D.Effects.Shadow shadow;
      D2D.Effects.AffineTransform2D affineTransform;
      D2D.Effects.Composite composite;
      
    3. Declare constant integers and enumeration.
      Const marginLT As Integer = 20
      Const marginRB As Integer = 36
      
      Public Enum ImageEffect
          Original
          Shadow
      End Enum
      
      const int marginLT = 20;
      const int marginRB = 36;
      
      public enum ImageEffect
      {
          Original,
          Shadow
      }
      
    4. Load the image in C1Bitmap using stream. For details, see Quick start.
    5. Add code to create resources, image source, and associate the image source with the image.
      ' create Direct2D and DirectWrite factories
      d2dFactory = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded)
      dwFactory = DW.Factory.Create(DW.FactoryType.[Shared])
      
      ' create GPU resources
      CreateDeviceResources()
      
      // create Direct2D and DirectWrite factories
      d2dFactory = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded);
      dwFactory = DW.Factory.Create(DW.FactoryType.Shared);
      
      // create GPU resources
      CreateDeviceResources();
      
    6. Add code to apply 2D shadow effect.
      Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
      
          UpdateImageSource(ImageEffect.Shadow)
      End Sub
      
      Private Sub CreateDeviceResources()
          Dim actualLevel As D3D.FeatureLevel
          Dim d3dContext As D3D.DeviceContext = Nothing
          Dim d3dDevice = New D3D.Device(IntPtr.Zero)
          Dim result = HResult.Ok
          For i As Integer = 0 To 1
              ' use WARP if hardware is not available
              Dim dt = If(i = 0, D3D.DriverType.Hardware, D3D.DriverType.Warp)
              result = D3D.D3D11.CreateDevice(Nothing, dt, IntPtr.Zero, _
                                              D3D.DeviceCreationFlags.BgraSupport Or _
                                              D3D.DeviceCreationFlags.SingleThreaded, _
                                              Nothing, 0, _
                  D3D.D3D11.SdkVersion, d3dDevice, actualLevel, d3dContext)
              If result.Code <> CInt(&H887A0004UI) Then
                  ' DXGI_ERROR_UNSUPPORTED
                  Exit For
              End If
          Next
          result.CheckError()
          d3dContext.Dispose()
      
          ' store the DXGI device (for trimming when the application is being suspended)
          dxgiDevice = d3dDevice.QueryInterface(Of DXGI.Device)()
          d3dDevice.Dispose()
      
          ' create a RenderTarget (DeviceContext for Direct2D drawing)
          Dim d2dDevice = D2D.Device1.Create(d2dFactory, dxgiDevice)
          Dim rt = D2D.DeviceContext1.Create(d2dDevice, D2D.DeviceContextOptions.None)
          d2dDevice.Dispose()
          rt.SetUnitMode(D2D.UnitMode.Pixels)
          d2dContext = rt
      
          ' create built-in effects
          shadow = D2D.Effects.Shadow.Create(rt)
          affineTransform = D2D.Effects.AffineTransform2D.Create(rt)
          composite = D2D.Effects.Composite.Create(rt)
      End Sub
      
      Private Sub UpdateImageSource(imageEffect__1 As ImageEffect)
          ' some effects can change pixels outside the bounds of the source
          ' image, so we need a margin to make those pixels visible
          Dim targetOffset = New Point2F(marginLT, marginLT)
          Dim w As Integer = bitmap.PixelWidth + marginLT + marginRB
          Dim h As Integer = bitmap.PixelHeight + marginLT + marginRB
      
          ' the render target object
          Dim rt = d2dContext
      
          ' create the target Direct2D bitmap
          Dim bpTarget = New  _
              D2D.BitmapProperties1(New D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, _
                                      D2D.AlphaMode.Premultiplied), _
                       CSng(bitmap.DpiX), CSng(bitmap.DpiY), D2D.BitmapOptions.Target Or _
                  D2D.BitmapOptions.CannotDraw)
          Dim targetBmp = D2D.Bitmap1.Create(rt, New Size2L(w, h), bpTarget)
      
          ' associate the target bitmap with render target
          rt.SetTarget(targetBmp)
      
          ' start drawing
          rt.BeginDraw()
      
          ' clear the target bitmap
          rt.Clear(Nothing)
      
          ' convert C1Bitmap image to Direct2D image
          Dim d2dBitmap = bitmap.ToD2DBitmap1(rt, D2D.BitmapOptions.None)
      
          Select Case imageEffect__1
              Case ImageEffect.Original
                  rt.DrawImage(d2dBitmap, targetOffset)
                  Exit Select
              Case ImageEffect.Shadow
                  rt.DrawImage(ApplyShadow(d2dBitmap), targetOffset)
                  Exit Select
          End Select
          d2dBitmap.Dispose()
      
          If Not rt.EndDraw(True) Then
              targetBmp.Dispose()
      
              ' try recreate device resources if old GPU device was removed
              DiscardDeviceResources()
              CreateDeviceResources()
              Return
          End If
      
          ' detach the target bitmap
          rt.SetTarget(Nothing)
      
          ' create a temporary C1Bitmap object
          Dim outBitmap = New C1Bitmap(bitmap.ImagingFactory)
      
          ' import the image from Direct2D target bitmap to C1Bitmap
          outBitmap.Import(targetBmp, rt, New RectL(w, h))
          targetBmp.Dispose()
      
          ' convert C1Bitmap to a WriteableBitmap, then use it as an image source
          image.Source = outBitmap.ToWriteableBitmap()
          outBitmap.Dispose()
      
      End Sub
      
      Private Sub DiscardDeviceResources()
          shadow.Dispose()
          affineTransform.Dispose()
          composite.Dispose()
          dxgiDevice.Dispose()
          d2dContext.Dispose()
      End Sub
      
      Private Function ApplyShadow(bitmap As D2D.Bitmap1) As D2D.Effect
          shadow.SetInput(0, bitmap)
          shadow.BlurStandardDeviation = 5.0F
          affineTransform.SetInputEffect(0, shadow)
          affineTransform.TransformMatrix = Matrix3x2.Translation(20.0F, 20.0F)
          composite.SetInputEffect(0, affineTransform)
          composite.SetInput(1, bitmap)
          Return composite
      End Function
      
      private void Button_Click(object sender, RoutedEventArgs e)
      {
          UpdateImageSource(ImageEffect.Shadow);
      }
      
      private void CreateDeviceResources()
      {
          D3D.FeatureLevel actualLevel;
          D3D.DeviceContext d3dContext = null;
          var d3dDevice = new D3D.Device(IntPtr.Zero);
          var result = HResult.Ok;
          for (int i = 0; i <= 1; i++)
          {
              //use WARP if hardware is not available
              var dt = i == 0 ? D3D.DriverType.Hardware : D3D.DriverType.Warp;
              result = D3D.D3D11.CreateDevice(null, dt, 
                  IntPtr.Zero, D3D.DeviceCreationFlags.BgraSupport | 
                  D3D.DeviceCreationFlags.SingleThreaded,
                  null, 0, D3D.D3D11.SdkVersion, 
                  d3dDevice, out actualLevel, 
                  out d3dContext);
              if (result.Code != unchecked((int)0x887A0004)) // DXGI_ERROR_UNSUPPORTED
              {
                  break;
              }
          }
          result.CheckError();
          d3dContext.Dispose();
      
          //store the DXGI device (for trimming when the application is being suspended)
          dxgiDevice = d3dDevice.QueryInterface<DXGI.Device>();
          d3dDevice.Dispose();
      
          //create a RenderTarget (DeviceContext for Direct2D drawing)
          var d2dDevice = D2D.Device1.Create(d2dFactory, dxgiDevice);
          var rt = D2D.DeviceContext1.Create
              (d2dDevice, D2D.DeviceContextOptions.None);
          d2dDevice.Dispose();
          rt.SetUnitMode(D2D.UnitMode.Pixels);
          d2dContext = rt;
      
          //create built-in effects
          shadow = D2D.Effects.Shadow.Create(rt);
          affineTransform = D2D.Effects.AffineTransform2D.Create(rt);
          composite = D2D.Effects.Composite.Create(rt);
      }
      
      void UpdateImageSource(ImageEffect imageEffect)
      {
          //some effects can change pixels outside the bounds of the source
          //image, so we need a margin to make those pixels visible
          var targetOffset = new Point2F(marginLT, marginLT);
          int w = bitmap.PixelWidth + marginLT + marginRB;
          int h = bitmap.PixelHeight + marginLT + marginRB;
      
          // the render target object
          var rt = d2dContext;
      
          //create the target Direct2D bitmap
          var bpTarget = new D2D.BitmapProperties1(
              new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, 
              D2D.AlphaMode.Premultiplied),
              (float)bitmap.DpiX, 
              (float)bitmap.DpiY, D2D.BitmapOptions.Target | 
              D2D.BitmapOptions.CannotDraw);
          var targetBmp = 
              D2D.Bitmap1.Create(rt, new Size2L(w, h), bpTarget);
      
          //associate the target bitmap with render target
          rt.SetTarget(targetBmp);
      
          // start drawing
          rt.BeginDraw();
      
          // clear the target bitmap
          rt.Clear(null);
      
          //convert C1Bitmap image to Direct2D image
          var d2dBitmap = 
              bitmap.ToD2DBitmap1(rt, D2D.BitmapOptions.None);
      
          switch (imageEffect)
          {
              case ImageEffect.Original:
                  rt.DrawImage(d2dBitmap, 
                      targetOffset);
                  break;
              case ImageEffect.Shadow:
                  rt.DrawImage(ApplyShadow(d2dBitmap), 
                      targetOffset);
                  break;
          }
          d2dBitmap.Dispose();
      
          if (!rt.EndDraw(true))
          {
              targetBmp.Dispose();
      
              // try to recreate the device resources if old GPU device was removed
              DiscardDeviceResources();
              CreateDeviceResources();
              return;
          }
      
          //detach the target bitmap
          rt.SetTarget(null);
      
          // create a temporary C1Bitmap object
          var outBitmap = 
              new C1Bitmap(bitmap.ImagingFactory);
      
          //import the image from Direct2D target bitmap to C1Bitmap
          outBitmap.Import(targetBmp, rt, new RectL(w, h));
          targetBmp.Dispose();
      
          //convert C1Bitmap to a WriteableBitmap, then use it as an image source
          image.Source = outBitmap.ToWriteableBitmap();
          outBitmap.Dispose();
                     
      }
      
      private void DiscardDeviceResources()
      {
          shadow.Dispose();
          affineTransform.Dispose();
          composite.Dispose();
          dxgiDevice.Dispose();
          d2dContext.Dispose();
      }
      
      D2D.Effect ApplyShadow(D2D.Bitmap1 bitmap)
      {
          shadow.SetInput(0, bitmap);
          shadow.BlurStandardDeviation = 5f;
          affineTransform.SetInputEffect(0, shadow);
          affineTransform.TransformMatrix = Matrix3x2.Translation(20f, 20f);
          composite.SetInputEffect(0, affineTransform);
          composite.SetInput(1, bitmap);
          return composite;
      }