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

    Direct2D is a 2D 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 the code, Bitmap is converted to a Direct2D bitmap. Direct2D is then used to manipulate the image by applying the built-in effect, shadow, using interoperation with Direct3D API. After all the manipulations, the image is loaded back from Direct2D bitmap to C1Bitmap.

    To apply the shadow effect on an image, you can use the properties of Shadow, AffineTransform2D, 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 the following namespaces.
      Imports D2D = C1.Util.DX.Direct2D
      Imports D3D = C1.Util.DX.Direct3D11
      Imports DXGI = C1.Util.DX.DXGI
      Imports DW = C1.Util.DX.DirectWrite
      Imports C1.Util.DX
      
      using D2D = C1.Util.DX.Direct2D;
      using D3D = C1.Util.DX.Direct3D11;
      using DXGI = C1.Util.DX.DXGI;
      using DW = C1.Util.DX.DirectWrite;
      using C1.Util.DX;
      
    2. Create the following class objects.
      Private imgSource As SurfaceImageSource
      Private sisNative As DXGI.ISurfaceImageSourceNative
      Private btmp As C1Bitmap
      
      ' device-independent resources
      Private d2dF As D2D.Factory2
      Private dwF As DW.Factory
      
      ' device resources
      Private dxgiD As DXGI.Device
      Private d2dC As D2D.DeviceContext1
      
      ' Direct2D built-in effects
      Private shadow As D2D.Effects.Shadow
      Private affineT As D2D.Effects.AffineTransform2D
      Private compst As D2D.Effects.Composite
      
      SurfaceImageSource imgSource;
      DXGI.ISurfaceImageSourceNative sisNative;
      C1Bitmap btmp;
      
      // device-independent resources
      D2D.Factory2 d2dF;
      DW.Factory dwF;
      
      // device resources
      DXGI.Device dxgiD;
      D2D.DeviceContext1 d2dC;
      
      // Direct2D built-in effects
      D2D.Effects.Shadow shadow;
      D2D.Effects.AffineTransform2D affineT;
      D2D.Effects.Composite compst;
      
    3. Declare the following constant integers and enumeration.
      Const marginLT As Integer = 20
      Const marginRB As Integer = 36
      
      Private Enum ImageEffect
          Original
          Shadow
      End Enum
      
      const int marginLT = 20;
      const int marginRB = 36;
      
      enum ImageEffect
      {
          Original,
          Shadow
      }
      
    4. Load the image in C1Bitmap using stream. For details, see Quick Start.
    5. Add the following code to create resources, image source, and associate the image source with the image.
      ' create Direct2D and DirectWrite factories
      d2dF = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded)
      dwF = DW.Factory.Create(DW.FactoryType.[Shared])
      
      ' create GPU resources
      CreateDeviceResources()
      
      ' create the image source
      imgSource = New SurfaceImageSource(marginLT + btmp.PixelWidth + marginRB,
                  marginLT + btmp.PixelHeight + marginRB, False)
      
      ' obtain the native interface for the image source
      sisNative = ComObject.QueryInterface _
                  (Of DXGI.ISurfaceImageSourceNative)(imgSource)
      sisNative.SetDevice(dxgiD)
      
      ' draw the image to SurfaceImageSource
      UpdateImageSource(ImageEffect.Original)
      
      ' associate the image source with the Image
      img.Source = imgSource
      
      // create Direct2D and DirectWrite factories
      d2dF = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded);
      dwF = DW.Factory.Create(DW.FactoryType.Shared);
      
      // create GPU resources
      CreateDeviceResources();
      
      Unloaded += MainPage_Unloaded;
      
      // create the image source
      imgSource = new SurfaceImageSource(marginLT + btmp.PixelWidth + marginRB,
                  marginLT + btmp.PixelHeight + marginRB, false);
      
      // obtain the native interface for the image source
      sisNative = ComObject.QueryInterface<DXGI.ISurfaceImageSourceNative>(imgSource);
      sisNative.SetDevice(dxgiD);
      
      // draw the image to SurfaceImageSource
      UpdateImageSource(ImageEffect.Original);
      
      // associate the image source with the Image
      img.Source = imgSource;
      
    6. Add the following code to apply the 2D shadow effect.
      Private Sub btnShadow_Click(sender As Object, e As RoutedEventArgs) _
      Handles btnShadow.Click
          UpdateImageSource(ImageEffect.Shadow)
      End Sub
      
      Private Sub CreateDeviceResources()
          ' create the Direct3D device
          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)
          Next
          result.CheckError()
          d3dContext.Dispose()
      
          'store the DXGI device (for trimming 
          'when the Application Is being suspended)
          dxgiD = d3dDevice.QueryInterface(Of DXGI.Device)()
          d3dDevice.Dispose()
      
          ' create a RenderTarget (DeviceContext for Direct2D drawing)
          Dim d2dDevice = D2D.Device1.Create(d2dF, dxgiD)
          Dim rt = D2D.DeviceContext1.Create(d2dDevice,
                   D2D.DeviceContextOptions.None)
          d2dDevice.Dispose()
          rt.SetUnitMode(D2D.UnitMode.Pixels)
          d2dC = rt
      
          ' create built-in effects
          shadow = D2D.Effects.Shadow.Create(rt)
          affineT = D2D.Effects.AffineTransform2D.Create(rt)
          compst = D2D.Effects.Composite.Create(rt)
      End Sub
      
      Private Sub DiscardDeviceResources()
          shadow.Dispose()
          affineT.Dispose()
          compst.Dispose()
      
          dxgiD.Dispose()
          d2dC.Dispose()
      End Sub
      
      Private Sub UpdateImageSource(imageEffect__1 As ImageEffect)
          Dim w As Integer = btmp.PixelWidth + marginLT + marginRB
          Dim h As Integer = btmp.PixelHeight + marginLT + marginRB
          Dim surfaceOffset As Point2L = Point2L.Empty
          Dim dxgiSurface As DXGI.Surface = Nothing
          Dim hr = HResult.Ok
      
          ' receive the target DXGI.Surface and offset for drawing
          For i As Integer = 0 To 1
              hr = sisNative.BeginDraw(New RectL(w, h),
                   surfaceOffset, dxgiSurface)
              If (hr <> DXGI.ResultCode.DeviceRemoved _
                  AndAlso hr <> DXGI.ResultCode.DeviceReset) _
                  OrElse i > 0 Then
                  Exit For
              End If
      
              ' try to recreate the device resources 
              ' if the old GPU device was removed
              DiscardDeviceResources()
              CreateDeviceResources()
              sisNative.SetDevice(dxgiD)
          Next
          hr.CheckError()
      
          ' the render target object
          Dim rt = d2dC
      
          ' create the target Direct2D bitmap for the given DXGI.Surface
          Dim bpTarget = New D2D.BitmapProperties1(New D2D.PixelFormat(
                         DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied),
                         CSng(btmp.DpiX), CSng(btmp.DpiY),
                         D2D.BitmapOptions.Target Or D2D.BitmapOptions.CannotDraw)
      
          Dim targetBmp = D2D.Bitmap1.Create(rt, dxgiSurface, bpTarget)
          dxgiSurface.Dispose()
      
          ' associate the target bitmap with render target
          rt.SetTarget(targetBmp)
          targetBmp.Dispose()
      
          ' start drawing
          rt.BeginDraw()
      
          ' clear the target bitmap
          rt.Clear(Nothing)
      
          ' convert C1Bitmap image to Direct2D image
          Dim d2dBitmap = btmp.ToD2DBitmap1(rt, D2D.BitmapOptions.None)
          surfaceOffset.Offset(marginLT, marginLT)
      
          ' apply the effect
          Select Case imageEffect__1
              Case ImageEffect.Original
                  rt.DrawImage(d2dBitmap, surfaceOffset.ToPoint2F())
                  Exit Select
              Case ImageEffect.Shadow
                  rt.DrawImage(ApplyShadow(d2dBitmap), surfaceOffset.ToPoint2F())
                  Exit Select
          End Select
      
          d2dBitmap.Dispose()
      
          ' finish drawing (all drawing commands are executed at that moment)
          rt.EndDraw()
      
          ' detach and actually dispose the target bitmap
          rt.SetTarget(Nothing)
      
          ' complete drawing on SurfaceImageSource
          sisNative.EndDraw()
      End Sub
      
      Private Function ApplyShadow(bitmap As D2D.Bitmap1) As D2D.Effect
          shadow.SetInput(0, bitmap)
          shadow.BlurStandardDeviation = 5.0F
          affineT.SetInputEffect(0, shadow)
          affineT.TransformMatrix = Matrix3x2.Translation(20.0F, 20.0F)
          compst.SetInputEffect(0, affineT)
          compst.SetInput(1, bitmap)
          Return compst
      End Function
      
      Private Sub MainPage_Unloaded(sender As Object, e As RoutedEventArgs)
          DiscardDeviceResources()
      
          btmp.Dispose()
          d2dF.Dispose()
          dwF.Dispose()
      
          img.Source = Nothing
          sisNative.Dispose()
          sisNative = Nothing
      End Sub
      
      private void btnShadow_Click(object sender, RoutedEventArgs e)
      {
          UpdateImageSource(ImageEffect.Shadow);
      }
              
      void CreateDeviceResources()
      {
          // create the Direct3D device
          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)
          dxgiD = d3dDevice.QueryInterface<DXGI.Device>();
          d3dDevice.Dispose();
      
          // create a RenderTarget (DeviceContext for Direct2D drawing)
          var d2dDevice = D2D.Device1.Create(d2dF, dxgiD);
          var rt = D2D.DeviceContext1.Create(d2dDevice, D2D.DeviceContextOptions.None);
          d2dDevice.Dispose();
          rt.SetUnitMode(D2D.UnitMode.Pixels);
          d2dC = rt;
      
          // create built-in effects
          shadow = D2D.Effects.Shadow.Create(rt);
          affineT = D2D.Effects.AffineTransform2D.Create(rt);
          compst = D2D.Effects.Composite.Create(rt);
      }
      
      void DiscardDeviceResources()
      {
          shadow.Dispose();
          affineT.Dispose();
          compst.Dispose();
      
          dxgiD.Dispose();
          d2dC.Dispose();
      }
      
      void UpdateImageSource(ImageEffect imageEffect)
      {
          int w = btmp.PixelWidth + marginLT + marginRB;
          int h = btmp.PixelHeight + marginLT + marginRB;
          Point2L surfaceOffset = Point2L.Empty;
          DXGI.Surface dxgiSurface = null;
          var hr = HResult.Ok;
      
          // receive the target DXGI.Surface and offset for drawing
          for (int i = 0; i <= 1; i++)
          {
              hr = sisNative.BeginDraw(new RectL(w, h), 
                   out surfaceOffset, out dxgiSurface);
              if ((hr != DXGI.ResultCode.DeviceRemoved && 
                   hr != DXGI.ResultCode.DeviceReset) || i > 0)
              {
                  break;
              }
      
              // try to recreate the device resources 
              // if the old GPU device was removed
              DiscardDeviceResources();
              CreateDeviceResources();
              sisNative.SetDevice(dxgiD);
          }
          hr.CheckError();
      
          // the render target object
          var rt = d2dC;
      
          // create the target Direct2D bitmap for the given DXGI.Surface
          var bpTarget = new D2D.BitmapProperties1( new D2D.PixelFormat(
                         DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied),
                         (float)btmp.DpiX, (float)btmp.DpiY,
                         D2D.BitmapOptions.Target | D2D.BitmapOptions.CannotDraw);
      
          var targetBmp = D2D.Bitmap1.Create(rt, dxgiSurface, bpTarget);
          dxgiSurface.Dispose();
      
          // associate the target bitmap with render target
          rt.SetTarget(targetBmp);
          targetBmp.Dispose();
      
          // start drawing
          rt.BeginDraw();
      
          // clear the target bitmap
          rt.Clear(null);
      
          // convert C1Bitmap image to Direct2D image
          var d2dBitmap = btmp.ToD2DBitmap1(rt, D2D.BitmapOptions.None);
          surfaceOffset.Offset(marginLT, marginLT);
      
          // apply the effect
          switch (imageEffect)
          {
              case ImageEffect.Original:
                  rt.DrawImage(d2dBitmap, surfaceOffset.ToPoint2F());
                  break;
              case ImageEffect.Shadow:
                  rt.DrawImage(ApplyShadow(d2dBitmap), surfaceOffset.ToPoint2F());
                  break;
          }
          
          d2dBitmap.Dispose();
      
          // finish drawing (all drawing commands are executed at that moment)
          rt.EndDraw();
      
          // detach and actually dispose the target bitmap
          rt.SetTarget(null);
      
          // complete drawing on SurfaceImageSource
          sisNative.EndDraw();
      }
      
      D2D.Effect ApplyShadow(D2D.Bitmap1 bitmap)
      {
          shadow.SetInput(0, bitmap);
          shadow.BlurStandardDeviation = 5f;
          affineT.SetInputEffect(0, shadow);
          affineT.TransformMatrix = Matrix3x2.Translation(20f, 20f);
          compst.SetInputEffect(0, affineT);
          compst.SetInput(1, bitmap);
          return compst;
      }
      
      private void MainPage_Unloaded(object sender, RoutedEventArgs e)
      {
          DiscardDeviceResources();
      
          btmp.Dispose();
          d2dF.Dispose();
          dwF.Dispose();
      
          img.Source = null;
          sisNative.Dispose();
          sisNative = null;
      }
      
    See Also