ComponentOne Bitmap for WinForms
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.

    Shadow effect using ComponentOne Bitmap for WinForms

    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 C1.Win.Bitmap
      Imports D2D = C1.Util.DX.Direct2D
      Imports D3D = C1.Util.DX.Direct3D11
      Imports DW = C1.Util.DX.DirectWrite
      Imports DXGI = C1.Util.DX.DXGI
      
      using C1.Win.Bitmap;
      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
      Private lastGdiBitmap As Bitmap
      
      ' 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;
      Bitmap lastGdiBitmap;
      
      // 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 Button2_Click(sender As Object, e As EventArgs) Handles Button2.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)
              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 DiscardDeviceResources()
          shadow.Dispose()
          affineTransform.Dispose()
          composite.Dispose()
      
          dxgiDevice.Dispose()
          d2dContext.Dispose()
      End Sub
      
      Private Sub ClearGdiBitmap()
          If lastGdiBitmap IsNot Nothing Then
              PictureBox1.Image = Nothing
              lastGdiBitmap.Dispose()
              lastGdiBitmap = Nothing
          End If
      End Sub
      
      Private Sub UpdateImageSource(imageEffect_1 As ImageEffect)
          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)
      
          'apply the effect
          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()
      
          ' finish drawing (all drawing commands are executed at that moment)
          rt.EndDraw()
      
          ' detach and actually dispose 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 System.Drawing.Bitmap
          ClearGdiBitmap()
          lastGdiBitmap = outBitmap.ToGdiBitmap()
          outBitmap.Dispose()
      
          ' show the result in the PictureBox
          PictureBox1.Image = lastGdiBitmap
      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 button2_Click(object sender, EventArgs 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)
          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 DiscardDeviceResources()
       {
           shadow.Dispose();
           affineTransform.Dispose();
          composite.Dispose();
      
           dxgiDevice.Dispose();
           d2dContext.Dispose();
       }
      
      void ClearGdiBitmap()
      {
          if (lastGdiBitmap != null)
          {
              pictureBox1.Image = null;
              lastGdiBitmap.Dispose();
              lastGdiBitmap = null;
          }
      }
      
      void UpdateImageSource(ImageEffect imageEffect)
       {
           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);
      
           //apply the effect
           switch (imageEffect)
           {
               case ImageEffect.Original:
                   rt.DrawImage(d2dBitmap, targetOffset);
                   break;
               case ImageEffect.Shadow:
                   rt.DrawImage(ApplyShadow(d2dBitmap), targetOffset);
                   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);
      
          // 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 System.Drawing.Bitmap
          ClearGdiBitmap();
          lastGdiBitmap = outBitmap.ToGdiBitmap();
          outBitmap.Dispose();
      
          // show the result in the PictureBox
          pictureBox1.Image = lastGdiBitmap;
      }
      
      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;
       }