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


Обзор слоев

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

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

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

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

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

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

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

ID2D1DeviceContext и PushLayer

Интерфейс ID2D1DeviceContext является производным от интерфейса ID2D1RenderTarget и играет ключевую роль в отображении содержимого Direct2D в Windows 8. Для получения дополнительных сведений об этом интерфейсе см. раздел Устройства и контексты устройств. С помощью интерфейса контекста устройства можно пропустить вызов метода CreateLayer, а затем передать NULL в метод ID2D1DeviceContext::PushLayer. 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 Значение, указывающее режим сглаживания для геометрической маски, указанной в поле геометрической маски.
    maskTransform Значение, указывающее преобразование, которое применяется к геометрической маске при создании слоя. Это относительно преобразования мира.
    непрозрачность Значение непрозрачности слоя. Прозрачность каждого ресурса в слое умножается на это значение при создании целевого объекта.
    кисть непрозрачности (Необязательно) Кисть, используемая для изменения непрозрачности слоя. Кисть сопоставляется с слоем, а альфа-канал каждого сопоставленного пикселя кисти умножается на соответствующий пиксель слоя. Установите значение NULL, если слой не должен иметь маску непрозрачности.
    параметры слоя Значение, указывающее, намерен ли слой отображать текст с помощью защиты 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, а геометрическая маска не равен NULL, то границы содержимого фактически являются границами геометрической маски после преобразования маски.

Если contentBounds не имеет значения NULL и геометрическая маска не имеет значения 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