Why is my icon being drawn at the wrong size when I call DrawIcon?


Some time ago I had a problem with icon drawing. When I tried to draw an icon with Draw­Icon it ended up being drawn at the wrong size. A call to Get­Icon­Info confirmed that the icon was 48×48, but it drew at 32×32.

The answer is documented in a backwards sort of way in the Draw­Icon­Ex function, which says at the bottom,

To duplicate DrawIcon (hDC, X, Y, hIcon), call DrawIconEx as follows:

DrawIconEx (hDC, X, Y, hIcon, 0, 0, 0, NULL,
            DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE); 

Aha, if you use Draw­Icon, then the icon size is ignored and it is drawn with DI_DEFAULT­SIZE.

The fix, therefore, was to switch to the Draw­Icon­Ex function so I could remove the DI_DEFAULT­SIZE flag, thereby permitting the icon to be drawn at its actual size.

- DrawIcon(hdc, pt.x, pt.y, hico);
+ DrawIconEx(hdc, pt.x, pt.y, hico, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);

A bonus quirk of the DI_DEFAULT­SIZE flag (and therefore of the Draw­Icon function) is that the drawing is done at the default icon size, even if you asked it to draw a cursor.

Comments (6)
  1. Anonymous Coward says:

    ‘DrawIcon draws the icon or cursor using the width and height specified by the system metric values for icons; for more information, see GetSystemMetrics.’ – So people can't blame the docs.

    [No points for guessing who filed the doc bug to have that sentence added. -Raymond]
  2. Miral says:

    I think I recall hitting this one in the past, except with a 16×16 icon that was relentlessly being drawn at 32×32 (ie. upscaled and nasty looking).  I did get it sorted out in the end. :)

    The default behaviour is a bit surprising, especially when you get your icon from LoadImage and explicitly specify the size you want.  Still, no doubt there's some mysterious historic reason for the behaviour.  (Future topic?)

  3. Nick says:

    @Miral

    I would guess it's for applications which use the "default" values when using LoadIcon.  From LoadIcon:

    "The width, in pixels, of the icon or cursor. If this parameter is zero and the fuLoad parameter is LR_DEFAULTSIZE, the function uses the SM_CXICON or SM_CXCURSOR system metric value to set the width."

    So the app calls LoadIcon with 0 for cxDesired and cyDesired and gets whatever the system values specify.  This lets them then call DrawIcon and get an appropriately sized icon (without, I guess, actually querying the system metric values).

    But that's just a guess :)

  4. Smith says:

    Does Windows icon resizing use bicubic interpolation anywhere icons are used? e.g. Explorer toolbars?

  5. Neil says:

    Aside from when they are loaded from a resource (since icons can be chosen from a group) I don't see why anyone cares whether a given handle is an icon or a cursor.

  6. Roland says:

    I found out that in high DPI mode, DrawIcon draws 32 pixel-based icons in a very ugly way beginning with Vista – it seems to just StretchBlt them. If you use DrawIconEx instead, the icon is scaled up quite niceley, even if it's the same 32-pixel icon.

    In a nutshell, never use DrawIcon in the future. The icon might look fine on your development Windows 7 machine at 96 dpi, but it will look ugly if a user has Large Fonts on. Replace all DrawIcon calls with DrawIconEx.

Comments are closed.