Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

What’s wrong with this code, part 22 – Drawing Text…

What’s wrong with this code, part 22 – Drawing Text…

  • Comments 43

Recently I’ve been working on something that I’ve never done before in my almost 24 years at Microsoft. 

 

For the past 23ish years, I’ve been a plumber – all the work I’ve done has been under the covers.  But for the next version of Windows, I decided to stretch my boundaries a bit and try some UI programming.  I’ve just spent the past few days working on a cool change to the volume control (it’s not important what it is, and most people will never know about the change, but those that do will probably agree with me :)).

As part of the change, I needed to measure the dimensions of a text string.  This is a dummy version of some code I wrote, I simply called DrawText with the DT_CALCRECT into a memory DC that I created.

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }
<BEGIN LARRYS CODE>
   HDC hdc = CreateCompatibleDC(NULL);

   RECT rcText = {0, 0, 88, 34};

   DrawText(hdc, L"My Text String", -1, &rcText, DT_CENTER | DT_END_ELLIPSIS | DT_EDITCONTROL | DT_WORDBREAK | DT_NOPREFIX | DT_CALCRECT);

   CAtlString string;
   string.Format(L"Text String occupies: %d x %d pixels", rcText.right - rcText.left, rcText.bottom - rcText.top);
   MessageBox(hWnd, string, L"String Size", 0);
<END LARRYS CODE> 
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

This is just code I took by using Visual Studio to create a Windows Win32 project and inserting the code between “BEGIN LARRYS CODE” and “END LARRYS CODE”.  The meat of the code is just 3 lines of code.

Even though there’s almost no code here, it still has a bug in it that was quite subtle and took me several hours to find.

  • Ivo: Why is setting the correct size important?  And I'd missed Shog9's comment, thanks for pointing it out.

    Remember, I tried this code and it worked perfectly.  But while I was testing the change, I discovered that I had made a horrible but really subtle mistake.

  • From MSDN on CreateCompatibleDC:

    "When a memory DC is created, all attributes are set to normal default values." Default font is SYSTEM_FONT, which is not considered "nice" these days :). I believe that adding (as a simplest example)

     SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT))

    before

     DrawText(hDC, ...)

    would illustrate the problem that Larry is referring to.

  • Konstantin: In my case, selecting DEFAULT_GUI_FONT actually made things worse :).  

    The font's the right answer, but there's something more going on - what scenario did I test that nobody so far has mentioned?

  • "what scenario did I test that nobody so far has mentioned?"

    The scenario where the default font is meant to be written right-to-left?

  • Perhaps using "Large fonts"? Or changing the system's default GUI font in another fashion?

  • Something to do with right-to-left fonts maybe?

    Just guessing, I have no idea :)

  • I wonder if it's got something to do with ClearType -- CreateCompatibleDC will (if I remember correctly) give you a DC with a 1x1 black and white (single bit) bitmap initially selected into it.

    For bitmap, stroke and TrueType fonts, this won't be a problem -- they render in black and white. I wonder if, for ClearType fonts, it needs a bitmap of the correct colour depth, in case there's a pixel or so of extra ClearType-ness going on.

    To get a bitmap of the correct depth, you can use CreateCompatibleBitmap(NULL).

    Just guessing, though.

  • Jon, good thought, but that would be symetrical.  

  • > what scenario did I test that nobody so far has mentioned?

    Large fonts?  Does SYSTEM_FONT not scale properly?

  • Ding!Ding!Ding!

    Eldan nailed it: Large fonts.

    For whatever reason, when you set the system to 144 DPI (large fonts), the default font that's used for the control I'm using to draw the text gets dramatically larger than either the system font OR the default GUI font.  Which caused my size calculations to be wrong, which caused some ugly clipping.

  • Also Justin: Apparently the system font doesn't scale the same as other fonts do.

  • While we are talking volume controls. Could you explain why it's only possible to lower the volume in Windows (i.e. setting a volume between 0% and 100%) and not raise it (i.e setting it higher than 100%)?

  • Nils: Way off topic.  I'll answer in another post.

  • Ummm... Why not use GetTextExtentPoint32 [ http://msdn.microsoft.com/en-us/library/ms534223(VS.85).aspx ]?

    "The GetTextExtentPoint32 function computes the width and height of the specified string of text."

    - Oli

  • Oli: Because GetTextExtentPoint32 doesn't do word wrap, and for my case, I need the word wrap behavior.

Page 2 of 3 (43 items) 123