Bitmap for WPF
Direct2D エフェクトの適用
Bitmap の操作 > Direct2D エフェクトの適用

Direct2D は、Microsoft によって設計された 2D グラフィック API で、画像を操作するための広範な組み込みおよびカスタムのエフェクトが提供されています。この API を使用すると、ビットマップ、2D ジオメトリ、テキストの高品質で高速なレンダリングが可能です。

Bitmap では、Direct2D のエフェクトを使用したり、画像にエフェクトを適用することができます。Bitmap を使用して適用できる画像エフェクトを次に一覧します。

これらのエフェクトから 1 つを選んで画像に適用してみましょう。次の図は、Bitmap で Direct2D を使用する例として、組み込み 2D エフェクトの 1 つ、シャドウを示しています。

コードで、Bitmap が Direct2D ビットマップに変換されます。次に Direct2D を使用して画像を操作し、Direct3D API との相互運用によって組み込みエフェクト、シャドウが適用されます。 すべての操作が完了したら、画像が Direct2D ビットマップから C1Bitmap にロードし直されます。

画像にシャドウエフェクトを適用するには、C1.Util.DX.Direct2D.Effects 名前空間のメンバクラスである ShadowAffineTransform2DComposite のプロパティを使用します。

以下の手順は、2D シャドウエフェクトを画像に適用する方法を示します。この例では、「クイックスタート」で作成したサンプルを使用します。

  1. 次の名前空間を追加します。
    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. 次のクラスオブジェクトを作成します。
    Private bitmap As C1Bitmap
    
    ' 装置独立リソース
    Private d2dFactory As D2D.Factory2
    Private dwFactory As DW.Factory
    
    ' 装置リソース
    Private dxgiDevice As DXGI.Device
    Private d2dContext As D2D.DeviceContext1
    
    ' Direct2Dの組み込み効果
    Private shadow As D2D.Effects.Shadow
    Private affineTransform As D2D.Effects.AffineTransform2D
    Private composite As D2D.Effects.Composite
    
    C1Bitmap bitmap;
    
    // 装置独立リソース
    D2D.Factory2 d2dFactory;
    DW.Factory dwFactory;
    
    // 装置リソース
    DXGI.Device dxgiDevice;
    D2D.DeviceContext1 d2dContext;
    
    // Direct2Dの組み込み効果
    D2D.Effects.Shadow shadow;
    D2D.Effects.AffineTransform2D affineTransform;
    D2D.Effects.Composite composite;
    
  3. 次の整数定数と列挙を宣言します。
    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. ストリームを使用して画像を C1Bitmap にロードします。詳細については、「クイックスタート」を参照してください。
  5. 次のコードを追加して、リソースと画像ソースを作成し、画像ソースを画像と関連付けます。
    ' Direct2DおよびDirectWriteファクトリを作成します
    d2dFactory = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded)
    dwFactory = DW.Factory.Create(DW.FactoryType.[Shared])
    
    ' GPUリソースを作成します
    CreateDeviceResources()
    
    // Direct2DおよびDirectWriteファクトリを作成します
    d2dFactory = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded);
    dwFactory = DW.Factory.Create(DW.FactoryType.Shared);
    
    // GPUリソースを作成します
    CreateDeviceResources();
    
  6. 次のコードを追加して、2D シャドウエフェクトを適用します。
    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
            ' ハードウェアが利用できない場合はWARPを使用します
            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()
    
        ' DXGI装置を格納します(アプリケーションが中断されているときにトリミングするため)
        dxgiDevice = d3dDevice.QueryInterface(Of DXGI.Device)()
        d3dDevice.Dispose()
    
        ' RenderTargetを作成します(Direct2D描画用のDeviceContext)
        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
    
        ' 組み込みの効果を作成します
        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)
        ' ソース画像の範囲外のピクセルを変更する可能性のある交換があるため、
        ' これらのピクセルを表示するためのマージンが必要です
        Dim targetOffset = New Point2F(marginLT, marginLT)
        Dim w As Integer = bitmap.PixelWidth + marginLT + marginRB
        Dim h As Integer = bitmap.PixelHeight + marginLT + marginRB
    
        ' レンダー対象オブジェクト
        Dim rt = d2dContext
    
        ' 対象Direct2Dビットマップを作成します
        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)
    
        ' 対象ビットマップをレンダー対象に関連付けます
        rt.SetTarget(targetBmp)
    
        ' 描画を開始します
        rt.BeginDraw()
    
        ' 対象ビットマップをクリアします
        rt.Clear(Nothing)
    
        ' C1Bitmap画像をDirect2D画像に変換します
        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()
    
            ' 旧GPU装置が削除された場合、装置リソースを再作成してみてください
            DiscardDeviceResources()
            CreateDeviceResources()
            Return
        End If
    
        '  対象ビットマップをデタッチします
        rt.SetTarget(Nothing)
    
        '  一時的なC1Bitmapオブジェクトを作成します
        Dim outBitmap = New C1Bitmap(bitmap.ImagingFactory)
    
        ' Direct2D対象ビットマップからC1Bitmapに画像をインポートします
        outBitmap.Import(targetBmp, rt, New RectL(w, h))
        targetBmp.Dispose()
    
        ' C1BitmapをWriteableBitmapに変換し、イメージソースとして使用します
        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++)
        {
            // ハードウェアが利用できない場合はWARPを使用します
            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();
    
        // DXGI装置を格納します(アプリケーションが中断されているときにトリミングするため)
        dxgiDevice = d3dDevice.QueryInterface<DXGI.Device>();
        d3dDevice.Dispose();
    
        // RenderTargetを作成します(Direct2D描画用のDeviceContext)
        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;
    
        // 組み込みの効果を作成します
        shadow = D2D.Effects.Shadow.Create(rt);
        affineTransform = D2D.Effects.AffineTransform2D.Create(rt);
        composite = D2D.Effects.Composite.Create(rt);
    }
    
    void UpdateImageSource(ImageEffect imageEffect)
    {
        // ソース画像の範囲外のピクセルを変更する可能性のある交換があるため、
        // これらのピクセルを表示するためのマージンが必要です
        var targetOffset = new Point2F(marginLT, marginLT);
        int w = bitmap.PixelWidth + marginLT + marginRB;
        int h = bitmap.PixelHeight + marginLT + marginRB;
    
        // レンダー対象オブジェクト
        var rt = d2dContext;
    
        // 対象Direct2Dビットマップを作成します
        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);
    
        // 対象ビットマップをレンダー対象に関連付けます
        rt.SetTarget(targetBmp);
    
        // 描画を開始します
        rt.BeginDraw();
    
        // 対象ビットマップをクリアします
        rt.Clear(null);
    
        // C1Bitmap画像をDirect2D画像に変換します
        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();
    
            // 旧GPU装置が削除された場合、装置リソースを再作成してみてください
            DiscardDeviceResources();
            CreateDeviceResources();
            return;
        }
    
        // 対象ビットマップをデタッチします
        rt.SetTarget(null);
    
        // 一時的なC1Bitmapオブジェクトを作成します
        var outBitmap = 
            new C1Bitmap(bitmap.ImagingFactory);
    
        // Direct2D対象ビットマップからC1Bitmapに画像をインポートします
        outBitmap.Import(targetBmp, rt, new RectL(w, h));
        targetBmp.Dispose();
    
        // C1BitmapをWriteableBitmapに変換し、イメージソースとして使用します
        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;
    }