Working with Bitmap / Applying Direct2D Effects
Applying Direct2D Effects

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
    
  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
    
  3. Declare constant integers and enumeration.
    Const marginLT As Integer = 20
    Const marginRB As Integer = 36
    
    Public Enum ImageEffect
        Original
        Shadow
    End Enum
    
  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()
    
  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 <> &H887A0004 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