Bitmap for UWP
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 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. 次のクラスオブジェクトを作成します。
    Private imgSource As SurfaceImageSource
    Private sisNative As DXGI.ISurfaceImageSourceNative
    Private btmp As C1Bitmap
    
    ' 装置独立リソース
    Private d2dF As D2D.Factory2
    Private dwF As DW.Factory
    
    ' 装置リソース
    Private dxgiD As DXGI.Device
    Private d2dC As D2D.DeviceContext1
    
    ' Direct2Dの組み込み効果
    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;
    
    // 装置独立リソース
    D2D.Factory2 d2dF;
    DW.Factory dwF;
    
    // 装置リソース
    DXGI.Device dxgiD;
    D2D.DeviceContext1 d2dC;
    
    // Direct2Dの組み込み効果
    D2D.Effects.Shadow shadow;
    D2D.Effects.AffineTransform2D affineT;
    D2D.Effects.Composite compst;
    
  3. 次の整数定数と列挙を宣言します。
    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. ストリームを使用して画像を C1Bitmap にロードします。詳細については、「クイックスタート」を参照してください。
  5. 次のコードを追加して、リソースと画像ソースを作成し、画像ソースを画像と関連付けます。
    '  Direct2DおよびDirectWriteファクトリを作成します
    d2dF = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded)
    dwF = DW.Factory.Create(DW.FactoryType.[Shared])
    
    ' GPUリソースを作成します
    CreateDeviceResources()
    
    ' イメージソースを作成します
    imgSource = New SurfaceImageSource(marginLT + btmp.PixelWidth + marginRB,
                marginLT + btmp.PixelHeight + marginRB, False)
    
    ' イメージソースのネイティブインタフェースを取得します
    sisNative = ComObject.QueryInterface _
                (Of DXGI.ISurfaceImageSourceNative)(imgSource)
    sisNative.SetDevice(dxgiD)
    
    ' SurfaceImageSourceに画像を描画します
    UpdateImageSource(ImageEffect.Original)
    
    ' イメージソースをイメージに関連付けます
    img.Source = imgSource
    
    //  Direct2DおよびDirectWriteファクトリを作成します
    d2dF = D2D.Factory2.Create(D2D.FactoryType.SingleThreaded);
    dwF = DW.Factory.Create(DW.FactoryType.Shared);
    
    // GPUリソースを作成します
    CreateDeviceResources();
    
    Unloaded += MainPage_Unloaded;
    
    // イメージソースを作成します
    imgSource = new SurfaceImageSource(marginLT + btmp.PixelWidth + marginRB,
                marginLT + btmp.PixelHeight + marginRB, false);
    
    // イメージソースのネイティブインタフェースを取得します
    sisNative = ComObject.QueryInterface<DXGI.ISurfaceImageSourceNative>(imgSource);
    sisNative.SetDevice(dxgiD);
    
    // SurfaceImageSourceに画像を描画します
    UpdateImageSource(ImageEffect.Original);
    
    // イメージソースをイメージに関連付けます
    img.Source = imgSource;
    
  6. 次のコードを追加して、2D シャドウエフェクトを適用します。
    Private Sub btnShadow_Click(sender As Object, e As RoutedEventArgs) _
    Handles btnShadow.Click
        UpdateImageSource(ImageEffect.Shadow)
    End Sub
    
    Private Sub CreateDeviceResources()
        ' Direct3Dデバイスを作成します
        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)
        Next
        result.CheckError()
        d3dContext.Dispose()
    
        ' DXGI装置を格納します(アプリケーションが
        ' 中断されているときにトリミングするため)
        dxgiD = d3dDevice.QueryInterface(Of DXGI.Device)()
        d3dDevice.Dispose()
    
        ' RenderTargetを作成します(Direct2D描画用のDeviceContext)
        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
    
        ' 組み込みの効果を作成します
        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
    
        ' 描画のためにDXGI.Surfaceとオフセットを受け取ります
        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
    
            ' 古いGPUデバイスが削除された場合は、
            ' 装置リソースを再作成してみてください
            DiscardDeviceResources()
            CreateDeviceResources()
            sisNative.SetDevice(dxgiD)
        Next
        hr.CheckError()
    
        ' レンダー対象オブジェクト
        Dim rt = d2dC
    
        ' 対象Direct2Dビットマップを作成します
        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()
    
        ' 対象ビットマップをレンダー対象に関連付けます
        rt.SetTarget(targetBmp)
        targetBmp.Dispose()
    
        ' 描画を開始します
        rt.BeginDraw()
    
        ' 対象ビットマップをクリアします
        rt.Clear(Nothing)
    
        ' C1Bitmap画像をDirect2D画像に変換します
        Dim d2dBitmap = btmp.ToD2DBitmap1(rt, D2D.BitmapOptions.None)
        surfaceOffset.Offset(marginLT, marginLT)
    
        ' 効果を適用します
        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()
    
        ' 描画を終了します(すべての描画コマンドはその時点で実行されます)
        rt.EndDraw()
    
        ' 対象ビットマップをデタッチして破棄します
        rt.SetTarget(Nothing)
    
        ' 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()
    {
        // Direct3Dデバイスを作成します
        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装置を格納します(アプリケーションが
        // 中断されているときにトリミングするため)
        dxgiD = d3dDevice.QueryInterface<DXGI.Device>();
        d3dDevice.Dispose();
    
        // RenderTargetを作成します(Direct2D描画用のDeviceContext)
        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;
    
        // 組み込みの効果を作成します
        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;
    
        // 描画のためにDXGI.Surfaceとオフセットを受け取ります
        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;
            }
    
            // 古いGPUデバイスが削除された場合は、
            // 装置リソースを再作成してみてください
            DiscardDeviceResources();
            CreateDeviceResources();
            sisNative.SetDevice(dxgiD);
        }
        hr.CheckError();
    
        // レンダー対象オブジェクト
        var rt = d2dC;
    
        // 対象Direct2Dビットマップを作成します
        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();
    
        // 対象ビットマップをレンダー対象に関連付けます
        rt.SetTarget(targetBmp);
        targetBmp.Dispose();
    
        // 描画を開始します
        rt.BeginDraw();
    
        // 対象ビットマップをクリアします
        rt.Clear(null);
    
        // C1Bitmap画像をDirect2D画像に変換します
        var d2dBitmap = btmp.ToD2DBitmap1(rt, D2D.BitmapOptions.None);
        surfaceOffset.Offset(marginLT, marginLT);
    
        // 効果を適用します
        switch (imageEffect)
        {
            case ImageEffect.Original:
                rt.DrawImage(d2dBitmap, surfaceOffset.ToPoint2F());
                break;
            case ImageEffect.Shadow:
                rt.DrawImage(ApplyShadow(d2dBitmap), surfaceOffset.ToPoint2F());
                break;
        }
        
        d2dBitmap.Dispose();
    
        // 描画を終了します(すべての描画コマンドはその時点で実行されます)
        rt.EndDraw();
    
        // 対象ビットマップをデタッチして破棄します
        rt.SetTarget(null);
    
        // 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;
    }
    
関連トピック