レイヤーの概要

この概要では、Direct2D レイヤーの使用の基本について説明します。 以下のセクションが含まれます。

レイヤーとは

ID2D1Layer オブジェクトで表されるレイヤーを使用すると、アプリケーションで描画操作のグループを操作できます。 レイヤーを使用する場合は、レイヤーをレンダー ターゲットに "プッシュ" します。 レンダー ターゲットによる後続の描画操作は、レイヤーに送られます。 レイヤーが完成したら、レンダー ターゲットからレイヤーを "ポップ" します。これにより、レイヤーのコンテンツがレンダー ターゲットに合成されます。

ブラシと同様に、レイヤーはレンダー ターゲットによって作成されるデバイスに依存するリソースです。 レイヤーは、それを作成したレンダー ターゲットを含むのと同じリソース ドメイン内の任意のレンダー ターゲットで使用できます。 ただし、レイヤー リソースは、一度に 1 つのレンダー ターゲットでのみ使用できます。 リソースの詳細については、「 リソースの概要」を参照してください。

レイヤーは興味深い効果を生成するための強力なレンダリング手法を提供しますが、レイヤーとレイヤー リソースの管理に関連するさまざまなコストが原因で、アプリケーション内のレイヤーの数が多すぎるとパフォーマンスに悪影響を与える可能性があります。 たとえば、特にハイエンドハードウェアでは、レイヤーを充填またはクリアしてからブレンドバックするコストが発生します。 その後、レイヤー リソースを管理するコストが発生します。 これらを頻繁に再割り当てする場合、GPU に対する結果の停止が最も大きな問題になります。 アプリケーションを設計するときは、レイヤー リソースの再利用を最大化してみてください。

Windows 8 以降のレイヤー

Windows 8、レイヤーの簡略化、パフォーマンスの向上、レイヤーへのフィーチャの追加を行う新しいレイヤー関連 API が導入されました。

ID2D1DeviceContext と PushLayer

ID2D1DeviceContext インターフェイスは ID2D1RenderTarget インターフェイスから派生し、Windows 8で Direct2D コンテンツを表示するための重要な要素です。このインターフェイスの詳細については、「デバイスとデバイス コンテキスト」を参照してください。 デバイス コンテキスト インターフェイスを使用すると、 CreateLayer メソッドの呼び出しをスキップし、 ID2D1DeviceContext::P ushLayer メソッドに NULL を渡すことができます。 Direct2D はレイヤー リソースを自動的に管理し、レイヤーと効果グラフの間でリソースを共有できます。

D2D1_LAYER_PARAMETERS1とD2D1_LAYER_OPTIONS1

D2D1_LAYER_PARAMETERS1構造体はD2D1_LAYER_PARAMETERSと同じですが、構造体の最終的なメンバーがD2D1_LAYER_OPTIONS1列挙体になりました。

D2D1_LAYER_OPTIONS1 には ClearType オプションがなく、パフォーマンスを向上させるために使用できる 2 つの異なるオプションがあります。

  • D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND: Direct2D は、透明な黒でクリアせずにプリミティブをレイヤーにレンダリングします。 これは既定ではありませんが、ほとんどの場合、パフォーマンスが向上します。

  • D2D1_LAYER_OPTIONS1_IGNORE_ALPHA: 基になるサーフェスが D2D1_ALPHA_MODE_IGNOREに設定されている場合、Direct2D はレイヤーのアルファ チャネルの変更を回避できます。 それ以外の場合は、これを使用しないでください。

ブレンド モード

Windows 8以降、デバイス コンテキストには、各プリミティブとターゲット サーフェスのブレンド方法を決定するプリミティブ ブレンド モードがあります。 このモードは、 PushLayer メソッドを呼び出すときにレイヤーにも適用されます。

たとえば、レイヤーを使用して透明度のプリミティブをクリップする場合は、適切な結果を得るためにデバイス コンテキストで D2D1_PRIMITIVE_BLEND_COPY モードを設定します。 コピー モードでは、レイヤーのジオメトリ マスクに従って、各ピクセルのアルファ チャネルを含む 4 つのカラー チャネルすべてを、ターゲットサーフェスのコンテンツと共に線形補間します。

相互運用

Windows 8以降、Direct2D では、レイヤーまたはクリップがプッシュされている間、Direct3D と GDI との相互運用がサポートされています。 レイヤーが GDI と相互運用するようにプッシュされている間、 ID2D1GdiInteropRenderTarget::GetDC を呼び出します。 ID2D1DeviceContext::Flush を呼び出し、基になるサーフェスにレンダリングして Direct3D と相互運用します。 Direct3D または GDI を使用してレイヤーまたはクリップ内でレンダリングするのはユーザーの責任です。 レイヤーの外側にレンダリングしようとしたり、クリップを実行しようとすると、結果は未定義になります。

レイヤーの作成

レイヤーを操作するには、 CreateLayerPushLayerPopLayer の各メソッドと、レイヤーの使用方法を定義するパラメトリック データのセットを含む D2D1_LAYER_PARAMETERS 構造に精通している必要があります。 次の一覧では、メソッドと構造体について説明します。

  • CreateLayer メソッドを呼び出して、レイヤー リソースを作成します。

    Note

    Windows 8以降、CreateLayer メソッドの呼び出しをスキップし、ID2D1DeviceContext インターフェイスの PushLayer メソッドに NULL を渡すことができます。 これは簡単で、Direct2D でレイヤー リソースを自動的に管理し、レイヤーと効果グラフ間でリソースを共有できます。

     

  • レンダー ターゲットの描画が開始された後 ( BeginDraw メソッドが呼び出された後)、 PushLayer メソッドを使用できます。 PushLayer メソッドは、指定したレイヤーをレンダー ターゲットに追加し、PopLayer が呼び出されるまでターゲットが後続のすべての描画操作を受け取るようにします。 このメソッドは、CreateLayer を呼び出して返される ID2D1Layer オブジェクトと、D2D1_LAYER_PARAMETERS構造体の layerParameters を受け取ります。 次の表では、 構造体のフィールドについて説明します。

    フィールド 説明
    contentBounds レイヤーのコンテンツ境界。 コンテンツは、これらの境界外ではレンダリングされません。 このパラメーターの既定値は InfiniteRect です。 既定値を使用すると、コンテンツ境界がレンダー ターゲットの境界として効果的に取得されます。
    geometricMask (省略可能)レイヤーをクリップする必要がある ID2D1Geometry によって定義された領域。 レイヤーをジオメトリにクリップしない場合は NULL に設定します。
    maskAntialiasMode geometricMask フィールドで指定されたジオメトリ マスクのアンチエイリアシング モードを指定する 値です。
    maskTransform レイヤーの作成時にジオメトリ マスクに適用される変換を指定する 値です。 これは、ワールド変換に対する相対値です。
    不透明 度 レイヤーの不透明度の値。 レイヤー内の各リソースの不透明度は、ターゲットに合成するときにこの値に乗算されます。
    opacityBrush (省略可能)レイヤーの不透明度を変更するために使用されるブラシ。 ブラシがレイヤーにマップされ、マップされた各ブラシ ピクセルのアルファ チャネルが対応するレイヤー ピクセルに乗算されます。 レイヤーに不透明度マスクを設定しない場合は 、NULL に設定します。
    layerOptions レイヤーが ClearType アンチエイリアシングを使用してテキストをレンダリングするかどうかを示す 値です。 このパラメーターの既定値は off です。 これをオンにすると、ClearType が正しく動作しますが、レンダリング速度が若干遅くなります。

     

    Note

    Windows 8以降、レイヤーで ClearType を使用してレンダリングすることはできないため、layerOptions パラメーターは常に D2D1_LAYER_OPTIONS_NONE に設定する必要があります。

     

    便宜上、Direct2D には、D2D1_LAYER_PARAMETERS構造体の作成に役立つ D2D1::LayerParameters メソッドが用意されています。

  • レイヤーのコンテンツをレンダー ターゲットに合成するには、 PopLayer メソッドを呼び出します。 EndDraw メソッドを呼び出す前に、PopLayer メソッドを呼び出す必要があります。

次の例は、CreateLayer、PushLayerおよび PopLayer の使用方法を示しています。 id2D1RadialGradientBrush に設定されている opacityBrush を除き、D2D1_LAYER_PARAMETERS構造体内のすべてのフィールドは既定値に設定されます。

// Create a layer.
ID2D1Layer *pLayer = NULL;
hr = pRT->CreateLayer(NULL, &pLayer);

if (SUCCEEDED(hr))
{
    pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 250));

    // Push the layer with the content bounds.
    pRT->PushLayer(
        D2D1::LayerParameters(
            D2D1::InfiniteRect(),
            NULL,
            D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
            D2D1::IdentityMatrix(),
            1.0,
            m_pRadialGradientBrush,
            D2D1_LAYER_OPTIONS_NONE),
        pLayer
        );

    pRT->DrawBitmap(m_pBambooBitmap, D2D1::RectF(0, 0, 190, 127));

    pRT->FillRectangle(
        D2D1::RectF(25.f, 25.f, 50.f, 50.f), 
        m_pSolidColorBrush
        );
    pRT->FillRectangle(
        D2D1::RectF(50.f, 50.f, 75.f, 75.f),
        m_pSolidColorBrush
        ); 
    pRT->FillRectangle(
        D2D1::RectF(75.f, 75.f, 100.f, 100.f),
        m_pSolidColorBrush
        );    
 
    pRT->PopLayer();
}
SafeRelease(&pLayer);

この例では、コードは省略されています。

PushLayer と PopLayer を呼び出すときは、各 PushLayer に対応する PopLayer 呼び出しがあることを確認してください。 PushLayer 呼び出しよりも多くの PopLayer 呼び出しがある場合、レンダー ターゲットはエラー状態になります。 すべての未処理のレイヤーがポップされる前に Flush が呼び出されると、レンダー ターゲットはエラー状態になり、エラーが返されます。 エラー状態をクリアするには、 EndDraw を使用します。

コンテンツ境界

contentBounds は、レイヤーに描画される内容の制限を設定します。 コンテンツ境界内のものだけが、レンダー ターゲットに合成されます。

次の例は、元のイメージが左上隅 (10,108) と右下隅 (121, 177) のコンテンツ境界にクリップされるように contentBounds を指定する方法を示しています。 次の図は、元の画像と、画像をコンテンツ境界にクリッピングした結果を示しています。

元の画像と結果のクリップされた画像のコンテンツ境界の図

HRESULT DemoApp::RenderWithLayerWithContentBounds(ID2D1RenderTarget *pRT)
{
    
    HRESULT hr = S_OK;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 0));

        // Push the layer with the content bounds.
        pRT->PushLayer(
            D2D1::LayerParameters(D2D1::RectF(10, 108, 121, 177)),
            pLayer
            );

        pRT->DrawBitmap(m_pWaterBitmap, D2D1::RectF(0, 0, 128, 192));
        pRT->PopLayer();
    }

    SafeRelease(&pLayer);

    return hr;
    
}

この例では、コードは省略されています。

Note

geometricMask を指定すると、結果として得られるクリップされた画像はさらに影響を受けます。 詳細については、「 ジオメトリック マスク」 セクションを参照してください。

 

ジオメトリック マスク

ジオメトリック マスクは、 ID2D1Geometry オブジェクトによって定義されるクリップまたはカットアウトであり、レンダー ターゲットによって描画されるときにレイヤーをマスクします。 D2D1_LAYER_PARAMETERS構造の geometricMask フィールドを使用して、結果をジオメトリにマスクできます。 たとえば、ブロック文字 "A" でマスクされた画像を表示する場合は、まずブロック文字 "A" を表すジオメトリを作成し、そのジオメトリをレイヤーのジオメトリック マスクとして使用できます。 その後、レイヤーをプッシュした後、画像を描画できます。 レイヤーをポップすると、イメージがブロック文字 "A" 図形にクリップされます。

次の例は、山の形状を含む ID2D1PathGeometry を作成し、パス ジオメトリを PushLayer に渡す方法を示しています。 次に、ビットマップと四角形を描画します。 レンダリングするビットマップがレイヤー内にある場合は、効率を高めるために、クランプされたビットマップ ブラシで FillGeometry を使用します。 この例からの出力を次の図に示します。

山の幾何学的なマスクを適用した後の葉の絵と結果の画像のイラスト

最初の例では、マスクとして使用するジオメトリを定義します。

hr = m_pD2DFactory->CreatePathGeometry(&m_pPathGeometry);
    
if(SUCCEEDED(hr))
{
    ID2D1GeometrySink *pSink = NULL;
    // Write to the path geometry using the geometry sink.
    hr = m_pPathGeometry->Open(&pSink);

    if (SUCCEEDED(hr))
    {
        pSink->SetFillMode(D2D1_FILL_MODE_WINDING);
        pSink->BeginFigure(
            D2D1::Point2F(0, 90),
            D2D1_FIGURE_BEGIN_FILLED
            );

        D2D1_POINT_2F points[7] = {
           D2D1::Point2F(35, 30),
           D2D1::Point2F(50, 50),
           D2D1::Point2F(70, 45),
           D2D1::Point2F(105, 90),
           D2D1::Point2F(130, 90),
           D2D1::Point2F(150, 60),
           D2D1::Point2F(170, 90)
           };

        pSink->AddLines(points, 7);
        pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
        hr = pSink->Close();
    }
    SafeRelease(&pSink);
       }

次の例では、ジオメトリをレイヤーのマスクとして使用します。

HRESULT DemoApp::RenderWithLayerWithGeometricMask(ID2D1RenderTarget *pRT)
{
    
    HRESULT hr;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 450));

        pRT->PushLayer(
            D2D1::LayerParameters(D2D1::InfiniteRect(), m_pPathGeometry),
            pLayer
            );

        pRT->DrawBitmap(m_pLeafBitmap, D2D1::RectF(0, 0, 198, 132));

        pRT->FillRectangle(
            D2D1::RectF(50.f, 50.f, 75.f, 75.f), 
            m_pSolidColorBrush
            ); 
        pRT->FillRectangle(
            D2D1::RectF(75.f, 75.f, 100.f, 100.f),
            m_pSolidColorBrush
            );        

        pRT->PopLayer();
    }

    SafeRelease(&pLayer);

    return hr;
    
}

この例では、コードは省略されています。

Note

一般に、geometricMask を指定する場合は、contentBounds に既定値 InfiniteRect を使用できます。

contentBounds が NULL で、geometricMask が NULL 以外の場合、コンテンツ境界はマスク変換が適用された後のジオメトリック マスクの境界になります。

contentBounds が NULL 以外で、geometricMask が NULL 以外の場合、変換されたジオメトリック マスクは実質的にコンテンツ境界に対してクリップされ、コンテンツ境界は無限であると見なされます。

 

不透明度マスク

不透明度マスクは、ブラシまたはビットマップによって記述されるマスクであり、そのオブジェクトを部分的または完全に透明にするために別のオブジェクトに適用されます。 これにより、ブラシのアルファ チャネルをコンテンツ マスクとして使用できます。 たとえば、不透明から透明に変化する放射状グラデーション ブラシを定義して、ビネット効果を作成できます。

次の例では、 ID2D1RadialGradientBrush (m_pRadialGradientBrush) を不透明度マスクとして使用します。 次に、ビットマップと四角形を描画します。 レンダリングするビットマップがレイヤー内にある場合は、効率を高めるために、クランプされたビットマップ ブラシで FillGeometry を使用します。 次の図は、この例の出力を示しています。

不透明度マスクを適用した後のツリーの図と結果の図の図

HRESULT DemoApp::RenderWithLayerWithOpacityMask(ID2D1RenderTarget *pRT)
{   

    HRESULT hr = S_OK;

    // Create a layer.
    ID2D1Layer *pLayer = NULL;
    hr = pRT->CreateLayer(NULL, &pLayer);

    if (SUCCEEDED(hr))
    {
        pRT->SetTransform(D2D1::Matrix3x2F::Translation(300, 250));

        // Push the layer with the content bounds.
        pRT->PushLayer(
            D2D1::LayerParameters(
                D2D1::InfiniteRect(),
                NULL,
                D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
                D2D1::IdentityMatrix(),
                1.0,
                m_pRadialGradientBrush,
                D2D1_LAYER_OPTIONS_NONE),
            pLayer
            );

        pRT->DrawBitmap(m_pBambooBitmap, D2D1::RectF(0, 0, 190, 127));

        pRT->FillRectangle(
            D2D1::RectF(25.f, 25.f, 50.f, 50.f), 
            m_pSolidColorBrush
            );
        pRT->FillRectangle(
            D2D1::RectF(50.f, 50.f, 75.f, 75.f),
            m_pSolidColorBrush
            ); 
        pRT->FillRectangle(
            D2D1::RectF(75.f, 75.f, 100.f, 100.f),
            m_pSolidColorBrush
            );    
 
        pRT->PopLayer();
    }
    SafeRelease(&pLayer);
   
    return hr;
    
}

この例では、コードは省略されています。

Note

この例では、レイヤーを使用して不透明度マスクを 1 つのオブジェクトに適用し、例をできるだけ単純に保ちます。 不透明度マスクを 1 つのオブジェクトに適用する場合、レイヤーではなく FillOpacityMask メソッドまたは FillGeometry メソッドを使用する方が効率的です。

 

レイヤーを使用せずに不透明度マスクを適用する方法については、「 不透明度マスクの概要」を参照してください。

レイヤーの代替

前述のように、レイヤーの数が多すぎると、アプリケーションのパフォーマンスに悪影響を及ぼす可能性があります。 パフォーマンスを向上させるには、可能な限りレイヤーを使用しないでください。代わりに、代替手段を使用します。 次のコード例は、 PushAxisAlignedClipPopAxisAlignedClip を使用して領域をクリップする方法を、コンテンツ境界を持つレイヤーを使用する代わりに示しています。

pRT->PushAxisAlignedClip(
    D2D1::RectF(20, 20, 100, 100),
    D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
    );

pRT->FillRectangle(D2D1::RectF(0, 0, 200, 133), m_pOriginalBitmapBrush);
pRT->PopAxisAlignedClip();

同様に、次の例に示すように、レイヤー内にレンダリングするコンテンツが 1 つしかない場合は、不透明度マスクを持つレイヤーを使用する代わりに、クランプされたビットマップ ブラシで FillGeometry を使用します。

        m_pRenderTarget->FillGeometry(
            m_pRectGeo, 
            m_pLinearFadeFlowersBitmapBrush, 
            m_pLinearGradientBrush
            );

次の例に示すように、ジオメトリック マスクでレイヤーを使用する代わりに、ビットマップ マスクを使用して領域をクリップすることを検討してください。

// D2D1_ANTIALIAS_MODE_ALIASED must be set for FillOpacityMask
// to function properly.
m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

m_pRenderTarget->FillOpacityMask(
    m_pBitmapMask,
    m_pOriginalBitmapBrush,
    D2D1_OPACITY_MASK_CONTENT_GRAPHICS,
    &rcBrushRect,
    NULL
    );

m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);

最後に、1 つのプリミティブに不透明度を適用する場合は、不透明度を に乗算してブラシの色にし、プリミティブをレンダリングする必要があります。 レイヤーまたは不透明度マスク ビットマップは必要ありません。

float opacity = 0.9f;

ID2D1SolidColorBrush *pBrush = NULL;
hr = pCompatibleRenderTarget->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f * opacity)),
    &pBrush
    );

m_pRenderTarget->FillRectangle(
    D2D1::RectF(50.0f, 50.0f, 75.0f, 75.0f), 
    pBrush
    ); 

任意の図形をクリッピングする

次の図は、画像にクリップを適用した結果を示しています。

クリップの前後の画像の例を示す画像。

この結果を得るには、ジオメトリ マスクを含むレイヤー、または不透明度ブラシを使用した FillGeometry メソッドを使用します。

レイヤーを使用する例を次に示します。

// Call PushLayer() and pass in the clipping geometry.
m_d2dContext->PushLayer(
    D2D1::LayerParameters(
        boundsRect,
        geometricMask));

FillGeometry メソッドを使用する例を次に示します。

// Create an opacity bitmap and render content.
m_d2dContext->CreateBitmap(size, nullptr, 0,
    D2D1::BitmapProperties(
        D2D1_BITMAP_OPTIONS_TARGET,
        D2D1::PixelFormat(
            DXGI_FORMAT_A8_UNORM,
            D2D1_ALPHA_MODE_PREMULTIPLIED),
        dpiX, dpiY),
    &opacityBitmap);

m_d2dContext->SetTarget(opacityBitmap.Get());
m_d2dContext->BeginDraw();
…
m_d2dContext->EndDraw();

// Create an opacity brush from the opacity bitmap.
m_d2dContext->CreateBitmapBrush(opacityBitmap.Get(),
    D2D1::BitmapBrushProperties(),
    D2D1::BrushProperties(),
    &bitmapBrush);

// Call the FillGeometry method and pass in the clip geometry and the opacity brush
m_d2dContext->FillGeometry( 
    clipGeometry.Get(),
    brush.Get(),
    opacityBrush.Get()); 

このコード例では、PushLayer メソッドを呼び出すときに、アプリで作成されたレイヤーを渡しません。 Direct2D によってレイヤーが自動的に作成されます。 Direct2D は、アプリからの関与なしに、このリソースの割り当てと破棄を管理できます。 これにより、Direct2D は内部的にレイヤーを再利用し、リソース管理の最適化を適用できます。

Note

Windows 8では、レイヤーの使用に対して多くの最適化が行われています。可能な限り FillGeometry ではなくレイヤー API を使用することをお勧めします。

 

軸揃えクリップ

クリップする領域が任意ではなく、描画サーフェスの軸に揃えられる場合。 この場合は、レイヤーの代わりにクリップ四角形を使用する場合に適しています。 パフォーマンスの向上は、アンチエイリアシングされたジオメトリよりもエイリアス化されたジオメトリの方が高くなります。 軸揃えクリップの詳細については、「 PushAxisAlignedClip 」トピックを参照してください。

Direct2D リファレンス