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

Creating a Simple Direct2D Application

Опубликовано 10.05.2010 г. 21:52

Данный пример представляет некоторую адаптацию статьи MSDN Creating a Simple Direct2D Application с переносом использованного кода на, как не трудно догадаться, Delphi. Результатом работы кода, приведенного ниже, является форма следующего вида:
 Two rectangles on a grid background
Вообще зачем нам спрашивается использовать Direct2D? Что он нам дает?

Direct2D is a hardware-accelerated, immediate-mode, 2-D graphics API that provides high performance and high-quality rendering for 2-D geometry, bitmaps, and text.

Т.е используя D2D мы получаем более высококачественную картинку, используя аппаратное ускорение для ее генерации. Однако, возвращаясь к статье. Статья разбита на 4 части:

# Part 1: Create the DemoApp Header # Part 2: Implement the Class Infrastructure # Part 3: Create Direct2D Resources # Part 4: Render Direct2D Content

Первая и вторая части описывают как создать приложение и форму,так что мы перейдем сразу к третьей части. Предварительно включим в список uses используемых модулей "d2d1", описывающий интерфейсы для работы с Direct2D. Private секцию описание главной формы приложения расширим следующим образом:

  private
        factory : ID2D1Factory;
        renderTarget : ID2D1HwndRenderTarget;
        b1,b2 : ID2D1SolidColorBrush;

Интерфейс ID2D1Factory, представляет, так сказать, начальную точку для работы с Direct2D, и предоставляет инструментарий для работы с другими Direct2D ресурсами. объект ID2D1HwndRenderTarget представляет, скажем так, поверхность, на которой мы будем рисовать. Рекомендуется не создавать данные объекты, каждый раз при перерисовывании, а хранить их постоянно, пока они требуются, что повысит производительность. объекты b1,b2 : ID2D1SolidColorBrush представляют кисти с помощью которых будет проводится рисование (некоторый аналог TBrush). Итак, при запуске приложения создадим необходимые нам ресурсы: factory, renderTarget (заданного размера), и две кисти для рисования сетки и квадратов.

procedure TMainForm.FormCreate(Sender: TObject);
var size : TD2D1SizeU;
begin
    D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,  IID_ID2D1Factory, nil, factory);

    size := D2D1SizeU(self.ClientWidth, self.ClientHeight);

    factory.CreateHwndRenderTarget( D2D1RenderTargetProperties(),
                                   D2D1HwndRenderTargetProperties(handle, size),
                                   renderTarget
                                  );

    renderTarget.CreateSolidColorBrush( D2D1ColorF(0.7, 0.7, 0.75, 1), nil, b1);
    renderTarget.CreateSolidColorBrush( D2D1ColorF(0.5, 0.5, 0.55, 1), nil, b2);
end;

Стоит заметить что синтаксис процедуры создания фабрики factory напоминает создание COM объекта. Значения RGBA цвета при создании кистей представлены в виде значений с плавающей точкой из диапазона [0..1]. Часть четвертая (Part 4: Render Direct2D Content) описывает непосредственно рисование используя нашу поверхность и кисти. Перед тем как приступать к "рисованию", необходимо вызвать метод BeginDraw(), а по окончании EndDraw(). Далее следует определить так называемый метод трансформации (SetTransform), параметром которого является матрица (в данном примере передается никаких преобразований не совершается, однако с помощью данного метода, можно например задать угол поворота, растяжение объекта, или его параллельный перенос). Поскольку это мое первое знакомство с Direct2D (а с GDI я никогда не работал), то данный функционал еще предстоит изучить. На следующем шаге очистим поверхность (по факту зальем ее заданным цветом, например белым). Далее с помощью цикла нарисуем решетку, сначала горизонтальные линии, затем вертикальные. Для рисования линий используется метод DrawLine, параметрами которого являются координаты начальной и конечной точки отображаемого отрезка, а также кисть с помощью которой проводится рисование. Следующим этапом будет определение двух прямоугольников, и их рисование на нашей поверхности. С заданием координат я думаю все понятно, центры прямоугольников (по факту квадраты) совпадают с центром поверхности. Для рисования же квадратов используются два разных метода, FillRectangle & DrawRectangle. Как ясно из названий, первый метод заливает данную область заданным цветом (используя заданную кисть), второй рисует контур. В общем то сам алгоритм рисования понятен, теперь его непосредственная реализация, используя метод OnPaint формы.

procedure TMainForm.FormPaint(Sender: TObject);
var size : D2D1_SIZE_F;
    w,h : integer;
    x,y : integer;
    r1,r2 : TD2DRectF;
begin
    renderTarget.BeginDraw;
    renderTarget.SetTransform(TD2DMatrix3x2F.Identity);
    renderTarget.Clear(D2D1ColorF(1,1,1,1));
    renderTarget.GetSize(size);

    w := round(size.width);
    h := round(size.height);

    x :=0;
    while x  < w do begin
        renderTarget.DrawLine(D2D1PointF(x,0),D2D1PointF(x,h),b1,0.5);
        inc(x,10);
    end;
    y :=0 ;
    while y < h do begin
        renderTarget.DrawLine(D2D1PointF(0,y),D2D1PointF(w,y),b1,0.5);
        inc(y,10);
    end;

    r1 := D2D1RectF(w div 2 - 50,
                    h div 2 - 50,
                    w div 2 + 50,
                    h div 2 + 50 );

    r2 := D2D1RectF(w div 2 - 100,
                    h div 2 - 100,
                    w div 2 + 100,
                    h div 2 + 100 );

    renderTarget.FillRectangle(r1,b1);
    renderTarget.DrawRectangle(r2,b2);

    renderTarget.EndDraw();
end;

Осталось только поменять размер поверхности, при изменении размера формы.

procedure TMainForm.FormResize(Sender: TObject);
begin
    renderTarget.Resize(D2D1SizeU(ClientWidth,ClientHeight));
    self.Invalidate;
end;

Таким образом, нами достигнут результат приведенный в статье MSDN. Но использование интерфейсов для Direct2D, все таки не самое удобное для работы в Delphi. И разработчики предоставили нам некоторую "обертку" для использования методов Direct2D. Перепишем наш код с использованием модуля Direct2D. Поверхность для рисования представлена классом TDirect2DCanvas = class (TCustomCanvas), поэтому по сути расширяет и переопределяет с использованием Direct2D методы рисования на обычной канве. Изменим описание нашей формы, с тем чтобы вместо обычной канвы использовать d2d.

  private
    fCanvas : TDirect2DCanvas;
  public
    property Canvas : TDirect2DCanvas read fCanvas; 

При создании формы собственно ее инициализируем

procedure TMainForm.FormCreate(Sender: TObject);
begin
    fCanvas := TDirect2DCanvas.Create(handle);
    (Canvas.RenderTarget as ID2D1HwndRenderTarget).setDPI(96,96);
end;

Что касается вызова метода setDPI, то тут мне не совсем все понятно. У меня в системе 120 DPI, и при этом рисование квадратов выходило так сказать не очень удачным, хотя вроде как, это не должно зависеть от значения DPI. В обработчике события изменения формы требуется изменить размер канвы. Причем стоит отметить, что есть три переопределенных конструктора для создания объекта TDirect2DCanvas, если в конструктор передавать handle формы, то создается объект поддерживающий интерфейс ID2D1HwndRenderTarget, а если вызывать конструктор вида

 TDirect2DCanvas.Create(self.canvas, getClientRect)

то создается объект ID2D1RenderTarget, не поддерживающий изменение размеров.

procedure TMainForm.FormResize(Sender: TObject);
begin
    (fCanvas.RenderTarget as ID2D1HwndRenderTarget).Resize(D2D1SizeU(clientWidth,clientHeight));
    invalidate();
end;

Процедура рисования упрощается, по сравнению с использованием напрямую интерфейсов, собственно кто же будет использовать напрямую интерфейсы если есть TDirect2dCanvas.

procedure TMainForm.FormPaint(Sender: TObject);
var w,h : integer;
    x,y : integer;
    r1,r2: TRect;
begin
    h := ClientHeight;
    w := ClientWidth;

    with Canvas do begin
        BeginDraw;
        Brush.Color := clWhite;
        Rectangle(0,0,w,h);

        x :=0;
        Pen.Color := clLtGray;
        while x  < w do begin
            MoveTo(x,0);
            LineTo(x,h);
            inc(x,10);
        end;

        y :=0;
        while y < h do begin
            MoveTo(0,y);
            LineTo(w, y);
            inc(y,10);
        end;

        r1 := Rect(w div 2 - 50,  h div 2 - 50,  w div 2 + 50,  h div 2 + 50 );
        r2 := Rect(w div 2 - 100, h div 2 - 100, w div 2 + 100, h div 2 + 100 );

        Brush.Color := clSilver;
        FillRect(r1);
        Brush.Color := clGray;
        FrameRect(r2);

        EndDraw();
    end;
end;

В общем то, тоже самое можно было сделать и с использованием GDI, т.е приведенный пример ничего нового не открывает. Но Direct2D представляет и новый функционал, который мы быть может рассмотрим позже.

Метки:  windows 7  |  Direct2D 

Комментарии

ALLIGATOR
11.05.2010 в 17:43
Отличный материал.
Не верится только в то, что Вы ни разу не работали с GDI.
ter
11.05.2010 в 19:59
хз даже ячейки в StringGrid не раскрашивал в ручную никогда (:
ALLIGATOR
11.05.2010 в 17:18
да, ещё - сделайте кегль шрифта больше - читать без слёз не возможно.
ter
11.05.2010 в 19:49
да согласен, есть такая проблема (: я правда для себя ее решил ctrl+колесо покрутил в браузере, ибо вобще все шрифты кажутся маленькими, меню и заголовки в том числе, а не только текст статей. но если увеличивать именно текст, то да, можно и в самом посте это сделать. учту.
ALLIGATOR
12.05.2010 в 17:33
Решил также. Спасибо за статью.
Alijon
30.09.2010 в 10:43
mne nada aplication
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно