Introduction

Windows 7 has two interesting new API: Direct2D and DirectWrite. Direct2D replaces GDI and GDI+. It can render more accurate results and has support for hardware acceleration on your graphics hardware. DirectWrite is a new API to render text. We will look at how to use DirectWrite to draw text outlines in this article. This article is the result of the assessing DirectWrite for inclusion in the coming version 2 of TextDesigner. The tentative plan for version is to include WPF in addition to C++ GDI+ and C# GDI+. As you can see on the TextDesigner home page that text outline can produce many fantastic text effects.

Source Code

In the normal DirectWrite text rendering, we would need to use a text layout object. However, the text outline in DirectWrite does not require the use of text layout object and its text layout object does not manage text outlines. The downside is we do not have the flexibility of a text layout object to assist us to render the text specifically the way we wanted.

In our use of Direct2D and DirectWrite, we use the _COM_SMARTPTR_TYPEDEF macro to define the COM smart pointers of _com_ptr_t type for Direct2D and DirectWrite interfaces.

We shall also make use of a helper method, ConvertPointSizeToDIP to convert point size to Device Independent Pixel (DIP). Device Independent Pixel is defined as 1/96 of an inch and a point is 1/72 of an inch.

First, we need to create the device independent resources (like Direct2D factory, DirectWrite factory and font face object) first in the our own CreateDevInDependentResources method. Direct2D factory is used to create the window render target and path geometry object while DirectWrite factory is responsible for loading the font file and creating the font face.

Next, we create our font face object from a collection of fonts in the same font family. The reader may ask why a font family consists of several fonts; The reason is different font may store regular or bold or italic glyphs of the same family. In our case, we only load 1 font file (Ruzicka TypeK.ttf) using CreateFontFileReference method. Then we proceed to create the font face with CreateFontFace.

After loading the font from file, we can create the glyphs used to render the outlines of the text. What is a glyph? A glyph is a series of lines and curves which form the shape of letter. The curves are usually specified as bezier curves. In the source code, we get the code points of every letters in the string (szOutline) in order to get the glyph indices. It is best to illustrate the reason we do that with an example: every glyph is stored at the specific index inside the glyph table in the font file. For example, the letter A has a ASCII value of 65 which is its code point but its glyph may not be stored at index, 65! That's why we need to get its glyph index before we can retrieve the glyph to render A!

After we retrieved all the glyphs information onto the m_pPathGeometry, we have no longer use for the code points and glyph indices, so we would delete them before returning from the function.

We would release our device independent resources in ReleaseDevInDependentResources method which should be call at the exit of the application. Our device independent resources consists of geometry sink, path geometry, DirectWrite factory and Direct2D factory. They should be released in the reverse order of their creation.

After creating our device independent resources, let's create device dependent resources (like render target and brushes) in our custom CreateDevDependentResources function. Render target is being used to call the drawing methods. The reason they are device dependent resources because they are created using the GPU resources. We only need 2 brushes; 1 gradient brush for the text body and 1 solid color brush for the text outline.

Now, we can render our path geometry in the OnPaint handler. We put all our drawing code in between BeginDraw and EndDraw. The reason that we need to set the transform to translate the text downwards, is because without the downward translation, the text would appear at too far top which is cut off by the windows title bar.

Conclusion

One of the requirements for TextDesigner version 2 is the API for all different platforms (C++ GDI+, C# GDI+, C# WPF) should stay similar with minor difference. The good news is it is possible to achieve this requirement. However, DirectWrite portion of TextDesigner will not be a drop-in replacement of C++ GDI+ because their objects (brushes, fontface and etc) are incompatible to be reused in GDI+.

Drawing text outlines with GDI is not 3 lines of code. Drawing text outlines with GDI involved many things. Drawing normal text with GDI code is 3 lines and drawing normal text with DirectWrite is over a few hundreds of code but you can have more control over your text layout and gain hardware acceleration if available. GDI is very slow code. I hope I have cleared the air.