A blog which discusses various GPU applications including visualization, GPGPU and games.

Saturday, January 31, 2009

Direct2D and DirectWrite: An example

I heavily use Direct3D in my Windows graphics programs. So why would I want to use an API meant for 2D graphics? The answer is simple: text rendering.

Text rendering has always been a massive pain in 3D APIs, but rightfully so. Why should a low-level GPU API care about text? One solution to this is to write one's own text rendering class. I would much rather use a standard library, though. That's where Direct2D and DirectWrite come into play.

As I mentioned in a previous post, Direct2D is actually independent from Direct3D. You can write an application that only uses Direct2D and never actually touch Direct3D in your code. (Direct2D, of course, uses Direct3D internally). This may sound inflexible when wanting to mix it with Direct3D, but the situation is quite the opposite. Thanks to DXGI, it is possible to obtain the DXGI surface representation of a Direct3D texture and hand it off to Direct2D.

I demonstrate a simple example. In this program (a 3D vector field plotter, as a matter of fact), I am interested in displaying the time it takes to render a frame, as well as a simple performance log graph. I was able to eliminate a good chunk of D3D code and replace it with a small section of D2D code.



Big deal, right? Check this out.



Would you want to try rendering Gabriola by hand in a 3D graphics API? :)

How about a thicker line in the performance graph, and with a dashed stroke style?



I plan on posting more complete code snippets later, but for now I will get right down to the fundamental code.

if (FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&m_spDWriteFactory)))) exit(EXIT_FAILURE);
m_spDWriteFactory->CreateTextFormat(L"Gabriola", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 30, L"", &m_spTextFormat);

The first eye-pleasing line creates a DirectWrite factory object. We then use the factory to create a new text format. A text format encapsulates basic information such as the font, font weight, font style, font size, etc.

We then use Direct2D to draw the string.

wstring mystring = L"Hello, world!";
m_spBackBufferRT->DrawText(mystring, mystring.length(), m_spTextFormat, D2D1::RectF(0.0f, 0.0f, 150.0f, 50.0f), m_spTextBrush, D2D1_DRAW_TEXT_OPTIONS_NO_CLIP);

As can be seen above, one of the arguments to the DrawText function is the DirectWrite text format we created earlier. In a future post I will cover in greater detail how I obtained m_spBackBufferRT.

I have not forgotten about OpenGL: in such situations I highly recommend the QuesoGLC text renderer. I expect to write up QuesoGLC examples as well in the future.

4 comments:

  1. Hi there. I too have been experimenting with Direct2d (not so much directwrite...yet). One of the frustrating things is that when you encounter issues it takes a lot of time to sort them out because there aren't many resources available at the time.

    I've noticed some issues with the way scrollbars are handled with a hwndrendertarget. It's a challenge to figure out whether the problem is my code or direct2d. I was just wondering if maybe you were having any issues in that regard.

    (And geez with the comment authentication...)

    ReplyDelete
  2. Sorry about the authentication, I haven't really configured that part of my blog too much as you can tell.

    I haven't tried mixing in scroll bars yet, but I have had problems with the Ribbon not rendering correctly. I think the problem is that another window needs to be created for the area under the ribbon, which is then used for D2D/D3D.

    If I discover anything, I'll definitely post it here.

    ReplyDelete
  3. Follow up:

    I managed to get around my scrollbar issues by simply bypassing the ScrollWindow function.

    As for the ribbon, is there any chance you might be talking about the way how the title bar gets overwritten by direct2d? I have a bit of an untested hunch; calling SetTranform on the render target and clipping it down by the height of the window's title might work (so that it won't render that high anymore).

    ReplyDelete
  4. I'm glad you figured it out. I'll have to try the clipping idea, thanks. :)

    ReplyDelete

Followers