Creating a Simple Direct2D Application
Данный пример представляет некоторую адаптацию статьи MSDN Creating a Simple Direct2D Application с переносом использованного кода на, как не трудно догадаться, Delphi. Результатом работы кода, приведенного ниже, является форма следующего вида:
Вообще зачем нам спрашивается использовать 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 представляет и новый функционал, который мы быть может рассмотрим позже.
11.05.2010 в 17:43
Не верится только в то, что Вы ни разу не работали с GDI.
11.05.2010 в 19:59
11.05.2010 в 17:18
11.05.2010 в 19:49
12.05.2010 в 17:33
30.09.2010 в 10:43