Поделиться через


Общие сведения о слоях

В этом обзоре описываются основы использования уровней Direct2D. В него входят следующие разделы.

Что такое слои?

Слои, представленные объектами ID2D1Layer , позволяют приложению управлять группой операций рисования. Слой используется путем "отправки" его в целевой объект отрисовки. Последующие операции рисования целевым объектом отрисовки направляются на слой. Завершив работу со слоем, вы "извлекаете" слой из целевого объекта отрисовки, который возвращает содержимое слоя в целевой объект отрисовки.

Как и кисти, слои — это зависимые от устройства ресурсы, созданные целевыми объектами отрисовки. Слои можно использовать в любом целевом объекте отрисовки в том же домене ресурсов, который содержит созданный целевой объект отрисовки. Однако ресурс слоя может использоваться только одним целевым объектом отрисовки одновременно. Дополнительные сведения о ресурсах см. в разделе Общие сведения о ресурсах.

Хотя слои предлагают мощный метод отрисовки для создания интересных эффектов, чрезмерное количество слоев в приложении может негативно повлиять на его производительность из-за различных затрат, связанных с управлением слоями и ресурсами слоев. Например, существует стоимость заполнения или очистки слоя, а затем его смешивания, особенно на оборудовании более высокого уровня. Кроме того, необходимо стоить управлять ресурсами слоя. Если вы будете часто перераспределять их, результирующий застой для GPU будет наиболее серьезной проблемой. При проектировании приложения постарайтесь максимально увеличить повторное использование ресурсов слоя.

Слои в Windows 8 и более поздних версиях

Windows 8 появились новые API, связанные со слоями, которые упрощают, повышают производительность и добавляют в слои функции.

ID2D1DeviceContext и PushLayer

Интерфейс ID2D1DeviceContext является производным от интерфейса ID2D1RenderTarget и является ключом к отображению содержимого Direct2D в Windows 8. Дополнительные сведения об этом интерфейсе см. в разделе Устройства и контексты устройств. С помощью интерфейса контекста устройства можно пропустить вызов метода CreateLayer , а затем передать значение NULL в метод ID2D1DeviceContext::P ushLayer . Direct2D автоматически управляет ресурсом слоя и может совместно использовать ресурсы между слоями и графами эффектов.

D2D1_LAYER_PARAMETERS1 и D2D1_LAYER_OPTIONS1

Структура D2D1_LAYER_PARAMETERS1 совпадает с D2D1_LAYER_PARAMETERS, за исключением того, что окончательный элемент структуры теперь является перечислением D2D1_LAYER_OPTIONS1 .

D2D1_LAYER_OPTIONS1 не имеет параметра ClearType и имеет два разных параметра, которые можно использовать для повышения производительности:

  • 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 при отправке слоя или клипа. Вы вызываете ID2D1GdiInteropRenderTarget::GetDC , когда слой передается для взаимодействия с GDI. Вы вызываете ID2D1DeviceContext::Flush , а затем выполняете отрисовку в базовой поверхности для взаимодействия с Direct3D. Вы несете ответственность за отрисовку внутри слоя или клипа с помощью Direct3D или GDI. При попытке отрисовки за пределами слоя или обрезки результаты будут неопределенными.

Создание слоев

Для работы со слоями требуется знание методов CreateLayer, PushLayer и PopLayer , а также D2D1_LAYER_PARAMETERS структуры, содержащей набор параметрических данных, определяющих способ использования слоя. В следующем списке описаны методы и структура.

  • Вызовите метод CreateLayer , чтобы создать ресурс слоя.

    Примечание

    Начиная с Windows 8, можно пропустить вызов метода CreateLayer, а затем передать значение NULL в метод PushLayer в интерфейсе ID2D1DeviceContext. Это проще и позволяет Direct2D автоматически управлять ресурсом слоя и совместно использовать ресурсы между слоями и графами эффектов.

     

  • После начала рисования целевого объекта отрисовки (после вызова метода BeginDraw ) можно использовать метод PushLayer . Метод PushLayer добавляет указанный слой к целевому объекту отрисовки, чтобы целевой объект получал все последующие операции рисования до вызова PopLayer . Этот метод принимает объект ID2D1Layer, возвращаемый путем вызова CreateLayer , и объект layerParameters в структуре D2D1_LAYER_PARAMETERS . В следующей таблице описаны поля структуры .

    Поле Описание
    contentBounds Границы содержимого слоя. Содержимое не будет отображаться за пределами этих границ. Этот параметр по умолчанию — InfiniteRect. Если используется значение по умолчанию, границы содержимого фактически считаются границами целевого объекта отрисовки.
    геометрическая маска (Необязательно) Область, определяемая id2D1Geometry, в которую необходимо обрезать слой. Установите значение NULL , если слой не должен быть обрезан до геометрии.
    maskAntialiasMode Значение типа , указывающее режим сглаживания для геометрической маски, заданной полем geometricMask .
    maskTransform Значение типа , указывающее преобразование, применяемое к геометрической маске при создании слоя. Это относится к преобразованию мира.
    Непрозрачность Значение непрозрачности слоя. Непрозрачность каждого ресурса в слое умножается на это значение при составлении к целевому объекту.
    opacityBrush (Необязательно) Кисть, используемая для изменения непрозрачности слоя. Кисть сопоставляется со слоем, а альфа-канал каждого пикселя сопоставленной кисти умножается на соответствующий пиксель слоя. Установите значение NULL , если слой не должен иметь маску непрозрачности.
    layerOptions Значение типа , указывающее, намерен ли слой отрисовывать текст с помощью сглаживания ClearType. Этот параметр по умолчанию отключен. Включение этого параметра позволяет ClearType работать правильно, но при этом скорость отрисовки будет немного медленнее.

     

    Примечание

    Начиная с Windows 8, вы не можете выполнить отрисовку с clearType в слое, поэтому параметр layerOptions всегда должен иметь значение D2D1_LAYER_OPTIONS_NONE

     

    Для удобства Direct2D предоставляет метод D2D1::LayerParameters для создания D2D1_LAYER_PARAMETERS структур.

  • Чтобы объединить содержимое слоя в целевой объект отрисовки, вызовите метод PopLayer . Перед вызовом метода EndDraw необходимо вызвать метод PopLayer.

В следующем примере показано, как использовать CreateLayer, PushLayer и PopLayer. Для всех полей в структуре D2D1_LAYER_PARAMETERS заданы значения по умолчанию, за исключением opacityBrush, для которого задано значение ID2D1RadialGradientBrush.

// 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 . Если вызовов PopLayer больше, чем вызовов PushLayer , целевой объект отрисовки помещается в состояние ошибки. Если метод Flush вызывается до того, как будут выброшены все выдающиеся слои, целевой объект отрисовки помещается в состояние ошибки и возвращает ошибку. Чтобы очистить состояние ошибки, используйте EndDraw.

Границы содержимого

ContentBounds задает предел того, что должно быть отрисовано на слое. Только те элементы, которые находятся в пределах границ содержимого, композитируются обратно в целевой объект отрисовки.

В следующем примере показано, как указать contentBounds , чтобы исходное изображение было обрезано границами содержимого с левым верхним углом в (10, 108) и правым нижним углом в (121, 177). На следующем рисунке показано исходное изображение и результат обрезки изображения до границ содержимого.

Иллюстрация границ содержимого исходного рисунка и результирующего рисунка

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

Код в этом примере опущен.

Примечание

Результирующее изображение будет дополнительно затронуто, если указать геометрическую маску. Дополнительные сведения см. в разделе Геометрические маски .

 

Геометрические маски

Геометрическая маска — это клип или вырез, определенный объектом ID2D1Geometry , который маскирует слой, когда он рисуется целевым объектом отрисовки. Поле geometryMask структуры D2D1_LAYER_PARAMETERS можно использовать для маскирования результатов под геометрию. Например, если вы хотите отобразить изображение, замаскированное блоком буквы "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;
    
}

Код в этом примере опущен.

Примечание

Как правило, при указании геометрической маски можно использовать значение по умолчанию InfiniteRect для contentBounds.

Если 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;
    
}

Код в этом примере опущен.

Примечание

В этом примере используется слой для применения маски непрозрачности к одному объекту, чтобы сделать пример максимально простым. При применении маски непрозрачности к одному объекту эффективнее использовать методы FillOpacityMask или FillGeometry , а не слой.

 

Инструкции по применению маски непрозрачности без использования слоя см. в разделе Общие сведения о масках непрозрачности.

Альтернативы слоям

Как упоминалось ранее, чрезмерное количество слоев может негативно повлиять на производительность приложения. Чтобы повысить производительность, избегайте использования слоев, когда это возможно; вместо этого используйте их альтернативы. В следующем примере кода показано, как использовать PushAxisAlignedClip и PopAxisAlignedClip для обрезки области в качестве альтернативы использованию слоя с границами содержимого.

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();

Аналогичным образом используйте 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);

Наконец, если вы хотите применить непрозрачность к одному примитиву, следует умножить непрозрачность на цвет кисти, а затем отрисовать примитив. Не требуется слой или растровое изображение маски непрозрачности.

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 повторно использовать слои внутри системы и применять оптимизацию управления ресурсами.

Примечание

В Windows 8 было сделано много оптимизаций для использования слоев, и мы рекомендуем по возможности использовать API слоев вместо FillGeometry.

 

Выровненные по оси клипы

Если обрезаемая область выравнивается по оси поверхности рисования, а не произвольно. Этот вариант подходит для использования прямоугольника клипа вместо слоя. Увеличение производительности больше для геометрии с псевдонимом, чем для сглаживания геометрии. Дополнительные сведения о выровненных по оси клипах см. в разделе PushAxisAlignedClip .

Справочник по Direct2D