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.

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