Author: Sinisa P29531
In my previous post I have translated Windows 7 SDK Direct2D “Advanced Geometries” example from C++ to Delphi 2010 code. That was a lot of fun, so I have decided to continue the adventure in the realm of Direct2D programming and this time converted one of the DirectWrite examples – DirectWrite sample “Hello World”.
In order to avoid writing over and over again the same Direct2D-specific code for creating TDirect2DCanvas instance and implementing “Resize” and “WMEraseBkgnd” methods, I have decided to refactor this common code into a reusable base class called “TFormD2D”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
<br> <strong>unit</strong> D2DUtils;<br> <br> <strong>interface</strong><br> <br> <strong>uses</strong><br> Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,<br> Dialogs, D2D1, Direct2D;<br> <br> <strong>type</strong><br> TFormD2D = <strong>class</strong>(TForm)<br> <strong>private</strong><br> FD2DCanvas: TDirect2DCanvas;<br> <strong>procedure</strong> WMEraseBkgnd(<strong>var</strong> Message: TWMEraseBkgnd); message WM_ERASEBKGND;<br> <strong>protected</strong><br> <strong>procedure</strong> Resize; <strong>override</strong>;<br> <strong>procedure</strong> Paint; <strong>override</strong>;<br> <strong>procedure</strong> CreateD2DResources; <strong>virtual</strong>;<br> <strong>procedure</strong> PaintD2D; <strong>virtual</strong>;<br> <strong>function</strong> rt: ID2D1RenderTarget; <span style="color:#003399;"><em>// conveniency function</em></span><br> <strong>public</strong><br> <strong>constructor</strong> Create(AOwner: TComponent); <strong>override</strong>;<br> <strong>destructor</strong> Destroy; <strong>override</strong>;<br> <strong>property</strong> D2DCanvas: TDirect2DCanvas <strong>read</strong> FD2DCanvas;<br> <strong>end</strong>;<br> <br> <strong>implementation</strong><br> <br> <strong>constructor</strong> TFormD2D.Create(AOwner: TComponent);<br> <strong>begin</strong><br> <strong>inherited</strong>;<br> <br> <strong>if</strong> <strong>not</strong> TDirect2DCanvas.Supported <strong>then</strong><br> <strong>raise</strong> Exception.Create(<span style="color:#9933cc;">'Direct2D not supported!'</span>);<br> <br> FD2DCanvas := TDirect2DCanvas.Create(Handle);<br> <br> CreateD2DResources;<br> <strong>end</strong>;<br> <br> <strong>destructor</strong> TFormD2D.Destroy;<br> <strong>begin</strong><br> FD2DCanvas.Free;<br> <strong>inherited</strong>;<br> <strong>end</strong>;<br> <br> <strong>procedure</strong> TFormD2D.CreateD2DResources;<br> <strong>begin</strong><br> <span style="color:#003399;"><em>// create Direct2D resources in descendant class</em></span><br> <strong>end</strong>;<br> <br> <strong>function</strong> TFormD2D.rt: ID2D1RenderTarget;<br> <strong>begin</strong><br> Result := D2DCanvas.RenderTarget;<br> <strong>end</strong>;<br> <br> <strong>procedure</strong> TFormD2D.Resize;<br> <strong>var</strong><br> HwndTarget: ID2D1HwndRenderTarget;<br> <strong>begin</strong><br> <strong>inherited</strong>;<br> <br> <strong>if</strong> Assigned(D2DCanvas) <strong>then</strong><br> <strong>if</strong> Supports(<br> rt, ID2D1HwndRenderTarget, HwndTarget) <strong>then</strong><br> HwndTarget.Resize(D2D1SizeU(ClientWidth, ClientHeight));<br> <br> Invalidate;<br> <strong>end</strong>;<br> <br> <strong>procedure</strong> TFormD2D.WMEraseBkgnd(<strong>var</strong> Message: TWMEraseBkgnd);<br> <strong>begin</strong><br> <span style="color:#003399;"><em>// avoid flicker as described here:</em></span><br> <span style="color:#003399;"><em>// http://chrisbensen.blogspot.com/2009/09/touch-demo-part-i.html</em></span><br> Message.Result := 1;<br> <strong>end</strong>;<br> <br> <strong>procedure</strong> TFormD2D.Paint;<br> <strong>begin</strong><br> <strong>inherited</strong>;<br> D2DCanvas.BeginDraw;<br> <strong>try</strong><br> PaintD2D;<br> <strong>finally</strong><br> D2DCanvas.EndDraw;<br> <strong>end</strong>;<br> <strong>end</strong>;<br> <br> <strong>procedure</strong> TFormD2D.PaintD2D;<br> <strong>begin</strong><br> <span style="color:#003399;"><em>// implement painting code in descendant class</em></span><br> <strong>end</strong>;<br> <br> <strong>end</strong>.<br> |
Using this base Direct2D class is simple. If you want to “Direct2D-enable” a VCL form class, just add “D2DUtils” unit to your project, add “D2DUtils” to the “uses” clause in the interface section of your form’s unit, and change your form’s base class from “TForm” to “TForm2D”.
Now you only need to override “CreateD2DResources” and “PaintD2D” virtual methods and declare private members for different resources used for painting. In the first step you should create all Direct2D resources needed for painting like brushes, fonts, pens, geometries, and assign them to private variables in your form class. The second step is to implement “PaintD2D” that will include your painting code, most likely using “rt” convenience method that returns “D2DCanvas.RenderTarget” interface.
This is the actual code that paints “‘Hello World using DirectWrite in Delphi 2010”. Note that the form class derives from “TFormD2” and not “TForm”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
<br> <strong>unit</strong> Unit38;<br> <br> <strong>interface</strong><br> <br> <strong>uses</strong><br> Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,<br> Dialogs, D2DUtils, D2D1, Direct2D;<br> <br> <strong>type</strong><br> <span style="color:#003399;"><em>// http://msdn.microsoft.com/en-us/library/ee264320(VS.85).aspx</em></span><br> TForm38 = <strong>class</strong>(TFormD2D)<br> <strong>private</strong><br> FBlackBrush: ID2D1SolidColorBrush;<br> FTextFormat: IDWriteTextFormat;<br> <strong>protected</strong><br> <strong>procedure</strong> PaintD2D; <strong>override</strong>;<br> <strong>procedure</strong> CreateD2DResources; <strong>override</strong>;<br> <strong>public</strong><br> <br> <span style="color:#003399;"><em>{ Public declarations }</em></span><br> <strong>end</strong>;<br> <br> <strong>var</strong><br> Form38: TForm38;<br> <br> <strong>implementation</strong><br> <br> <span style="color:#003399;"><em>{$R *.dfm}</em></span><br> <br> <span style="color:#003399;"><em>{ TForm38 }</em></span><br> <br> <strong>procedure</strong> TForm38.CreateD2DResources;<br> <br> <strong>begin</strong><br> <strong>inherited</strong>;<br> <br> rt.CreateSolidColorBrush(<br> D2D1ColorF(clBlack, 1),<br> <strong>nil</strong>,<br> FBlackBrush<br> );<br> <br> DWriteFactory.CreateTextFormat(<br> PWideChar(<span style="color:#9933cc;">'Gabriola'</span>),<br> <strong>nil</strong>,<br> DWRITE_FONT_WEIGHT_REGULAR,<br> DWRITE_FONT_STYLE_NORMAL,<br> DWRITE_FONT_STRETCH_NORMAL,<br> 72,<br> PWideChar(<span style="color:#9933cc;">'en-us'</span>),<br> FTextFormat<br> );<br> <br> FTextFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);<br> FTextFormat.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);<br> <strong>end</strong>;<br> <br> <strong>procedure</strong> TForm38.PaintD2D;<br> <strong>var</strong><br> aDisplayText: <strong>string</strong>;<br> aRect: TD2D1RectF;<br> <strong>begin</strong><br> <br> <span style="color:#003399;"><em>// fill with white color the whole window</em></span><br> rt.Clear(D2D1ColorF(clWhite));<br> <br> aDisplayText := <span style="color:#9933cc;">'Hello World using DirectWrite in Delphi 2010'</span>;<br> <br> rt.DrawText(<br> PWideChar(aDisplayText),<br> Length(aDisplayText),<br> FTextFormat,<br> D2D1RectF(0, 0, ClientWidth, ClientHeight),<br> FBlackBrush<br> );<br> <strong>end</strong>;<br> <br> <strong>end</strong>.<br> |
The “TForm38” class derives from “TFormD2” class. We need two Direct2D resources to render “Hello World”: a text format reference and a black brush. That’s why the first thing to do is to define two fields in our form class:
1 2 3 |
FBlackBrush: ID2D1SolidColorBrush;<br> FTextFormat: IDWriteTextFormat;<br> |
In the “D2DCreateResource” method both fields are initialized and they are used in “PaintD2D” for rendering text on the form. In order to simplify code that needs to be there for painting surrounding calls to “D2DCanvas.BeginDraw” and “D2DCanvas.EndDraw” have been moved to the ancestor class and very frequently used calls to “D2DCanvas.RenderTarget. …” have been replaced with “rt” function that returns the same thing, but with fewer lines of code.
The source code for this application can be downloaded from EDN CodeCentral.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition