Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4451.html
 

Direct2D и слоистые окна

Опубликовано 09.01.2011 г. 20:52

Первая статья в новом году, а относится к прошлому году. В волшебном приложении про Санту Клауса я собирался использовать D2D для его рендеринга. Но вот в чем была и собственно есть проблема. Решить ее у меня как то не получилось (:

Есть два типа управления прозрачностью окна (хотя их можно сочетать):
  1. Определение альфа-прозрачности окна целиком
  2. Использование заданного цвета в качестве прозрачного
Пользуясь свойствами обычной формы TForm первый способ можно использовать задав параметры AlphaBlend & AlphaBlendValue. Второй способ - использовать параметры TransparentColor & TransparentColorValue. В принципе большинству это все известно, и в обычном программировании на самом деле редко встречается, на мой взгляд. В итоге задав прозрачность формы, мы получаем что сквозь форму просвечивает все то, что находится за ней. Этим часто пользуются, когда реализуют, например, плавное открытие окна. Второй способ позволяет сделать часть формы полностью прозрачной. Например, задав красный цвет в качестве прозрачного, мы можем нарисовать в центре формы красный квадрат, и тогда запустив приложение вместо красного квадрата мы увидим все то, что находится за формой, т.е фактически получим дырку в форме (: Если копнуть чуть далее и рассмотреть работу этих свойств мы можем увидеть, что все сводится к использованию расширенного стиля окна WS_EX_LAYERED и использования функции SetLayeredWindowAttributes(). Собственно функция имеет 4 параметра: дескриптор формы, что впрочем очевидно, значение прозрачности, значение прозрачного цвета, и флаг - какой из стилей прозрачности мы используем. Все замечательно до тех пор пока мы используем обычную GDI-канву. Если мы решили использовать TDirect2DCanvas в качестве канвы реализовав ее в форме следующим образом (создание, назначение прозрачного цвета в виде зеленого, заливка формы красным и отрисовка зеленого квадратика):
  TForm1 = class(TForm)
    FCanvas : TDirect2DCanvas;
  public
    property canvas : TDirect2DCanvas read FCanvas write FCanvas;
  end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
    FCanvas := TDirect2DCanvas.Create(handle);

    TransparentColor := true;
    TransparentColorValue := clGreen;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
    with FCanvas do begin
        BeginDraw();
        brush.Color := clred;
        FillRect(getClientRect());

        brush.Color := clGreen;
        FillRect(rect(10,10,40,40));

        EndDraw();
    end;
end;
то получим мы красную форму с зеленым квадратом, никаких дырок вместо квадрата мы не увидим. Возникает собственно вопрос: почему? В принципе вопрос интересный, если посмотреть на метод TDirect2DCanvas.CreateRenderTarget(), то мы заметим, что канва создается с настройками по умолчанию. И тут можно попробовать грешить на эти самые настройки, в частности на PixelFormat, где указывается альфа-режим в виде D2D1_ALPHA_MODE_UNKNOWN, а нам бы вроде как хотелось видеть D2D1_ALPHA_MODE_PREMULTIPLIED. Вроде как метод CreateRenderTarget мы переопределить никоим образом не можем, так что задать другие параметры у нас не получится. Давайте попробуем напрямую использовать интерфейсы Direct2D. Добавим в описание формы член FRenderTarget : ID2D1HwndRenderTarget. При создании формы инициализируем его, используя желаемые параметры, заодно назначим прозрачный цвет для "дырок":
procedure TForm1.FormCreate(Sender: TObject);
var rtProp : TD2D1RenderTargetProperties;
    rect : TRect;
begin
    rtProp := D2D1RenderTargetProperties() ;
    rtProp.pixelFormat.alphaMode := D2D1_ALPHA_MODE_PREMULTIPLIED;
    rtProp.pixelFormat.format    := DXGI_FORMAT_B8G8R8A8_UNORM;

    rect := getClientRect();
    D2DFactory.CreateHwndRenderTarget(
            rtProp,
            D2D1HwndRenderTargetProperties(handle, D2D1SizeU(Rect.Right - Rect.Left, Rect.Bottom - Rect.Top)),
            ID2D1HwndRenderTarget(FRenderTarget)
    );

    TransparentColor := true;
    TransparentColorValue := clGreen;
    color := clGreen;
end;
Следующее: рисования красной формы с зеленым квадратом, выполняется весьма просто, необходимо только создать кисть:
procedure TForm1.FormPaint(Sender: TObject);
var brush : ID2D1SolidColorBrush;
begin
    FRenderTarget.CreateSolidColorBrush(D2D1ColorF(clRed), nil, brush);

    with FRenderTarget do begin
        BeginDraw();

        FillRectangle(getClientRect(),brush);

        brush.SetColor( D2D1ColorF(clGreen));
        FillRectangle(rect(10,10,40,40), brush);

        EndDraw();
    end;
end;
Что мы видим в итоге, все равно красная форма и зеленый квадрат. Никаких дырок нет (: Прозрачность кстати (alphaBlend) работает нормально, хотя она к канве не относится. Статья Слоистые окна в Direct2D (автор Кенни Керр) разобраться мне не помогла, да и гугл что то тоже ничего не подсказывает, так что видимо останется Санта в своем убогом состоянии (:
Метки:  Direct2D 

Комментарии

Alex
10.01.2011 в 13:42
> Есть два типа управления прозрачностью окна

Есть еще один способ - UpdateLayeredWindow, позволяет рисовать форму используя изображение с альфа-каналом (например PNG). Больше инфы - здесь: http://melander.dk/articles/alphasplash/ и http://melander.dk/articles/alphasplash2/2/

Я сам использовал этот способ на практике - очень эффектно, хотя есть свои ньюансы...
ter
10.01.2011 в 21:14
за ссылки спасибо, почитаю. В статье Кенни Керра использовались UpdateLayeredWindowIndirect, что есть аналог, только параметры сведены в одну структуру.

зы: это другой технический способ реализации, а так это все тоже самое, либо смешивание либо прозрачный цвет.
Alex93
15.09.2012 в 03:40
У вас немного неправильно:
заметьте Кенни Керр создает rendertarget не на Handle, а на DC, т.е отрисовка идет не на экран, а в память.
создав таргет через CreateDCRenderTarget, у вас появляется ключевая ф-я - blindDC, которой вы указывайте совместимый с рабочим столом DC. после рендеринга обновляете через updatelayeredwindow.

Т.е. транспарентом не получиться из-за не совместимости интерфейсов. И отдельным цветом тоже - здесь все упирается в альфа-канал, что не может не радовать)))

Спасибо за статью сам долго долбался с этим. Извините, если поздно ответил)
teran
28.09.2012 в 13:29
спасибо за ответ (:
фыв
25.07.2013 в 21:50
Прошу прощения, но Вы в корне неверно работаете с TDirect2DCanvas. Посмотрите справку, и нечего на херню время тратить.
"...for each repaint, a new TDirect2DCanvas instance is created. This is necessary because the actual device context (HDC) associated with the form may change each time ..." - на каждом событии в OnPaint должен создаваться новый экземпляр TDirect2DCanvas, потому что графический контекст(HDC) может измениться в любое время.

http://docwiki.embarcadero.com/RADStudio/XE4/en/Using_the_Direct2D_Canvas
teran
06.08.2013 в 17:17
уважаемый, если потрудитесь почитать справку повнимательней, то увидите, что есть два способа использовать d2d - 1) совместсно с GDI канвой 2) экслюзивно

второй способ - тот что используется в данной статье. Канва создается для формы, используя ее дескриптор (окна).

вы же пишете (цитируете) материал к описанию первого способа, когда поверх GDI канвы в onPaint отрисовывается d2d. Т.е. динамически создается д2д канва, на ней что нужно отрисовывается, затем переносится на GDI канву с использованием ее HDC. (вернее сказать,изначально канва создается в зависимости от контекста устройства используя HDC от GDI канвы).

вы вобще как себе представляете пересоздание канвы в приложении при обычной работе? вы понимаете, что большинство объектов будут зависимыми? - кисти, шрифты, дочерние таргеты и т.п. если вы пересоздаете канву (render target), то все это необходимо пересоздавать заново, исключениями тут будудт, например, объекты геометрий, они д2д-фабрикой создаются; у дочерних связанных таргетов могут быть свои кисти и т.д. процесс рисования не кажется ли вам будет несколько подтупливать, не?
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно