2

I have a CListCtrl control (or a ListView in Win32) that is created with LVS_REPORT style.

I am intending to display icons in its items as such:

enter image description here

But the question is what size of icons do I need to make and load?

Let me explain. From the old Win32 samples, I can see that everyone creates image lists with 15x15 pixel icons. But the issue with those is that it looks horribly pixelated on any modern PC with higher DPI settings. Thus I was looking for a dynamic way to determine the appropriate size of image lists for the CListCtrl.

And also the first part of the question, what icon size should I make originally?

EDIT PS: Since DPI scaling came up, how do you find it out? I'm currently using the following approach:

//No error handling for brevity
HDC hDC = ::GetDC(hAppsMainWindowHandle);
int nCx = ::GetDeviceCaps(hDC, LOGPIXELSX);
int nCy = ::GetDeviceCaps(hDC, LOGPIXELSY);
::ReleaseDC(hAppsMainWindowHandle, hDC);

//I technically get horizontal & vertical scaling --
//can those be different?
double scalingCx = (double)nCx / 96.0;  //1.0 = 100%
double scalingCy = (double)nCy / 96.0;

Is font scaling something different?

c00000fd
  • 20,994
  • 29
  • 177
  • 400

2 Answers2

3

A list view uses a "small" or "large" image list depending on its mode. In report mode, it uses the "small" image list. You can use GetSystemMetrics() to get the dimensions of "small" images using the SM_CXSMICON and SM_CYSMICON metrics (use SM_CXICON and SM_CYICON for "large" images).

Note that the returned values will be virtual/scaled if your app is not DPI-aware, so to get accurate values, make sure it is DPI-aware via SetProcessDPIAware(), SetProcessDpiAwareness(), or a DPI manifest.

Update: I just ran across this function, might be useful for you when writing a DPI-aware app:

LoadIconWithScaleDown()

Make larger images and let the API scale them down to smaller sizes.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Not really. `GetSystemMetrics(SM_CYSMICON)` for a default 100% DPI returns `16`, which is clearly 1 pixel over. If I load and display my image list with 16-pixel height, the top pixel gets cut off, or the icon gets somewhat squished. The correct size that is being displayed is 15x15. But I can't find out any system metric value for it. – c00000fd Jun 15 '14 at 03:02
  • 3
    15x15 is not the correct size for small icons, 16x16 is. I use 16x16 in my Win32 list views (I don't use MFC) and they work fine without squishing. I even use 20x20 (or 24x24, I forget which right now), which causes the list item heights to grow, and they work fine, too. So maybe it is an issue with CListCtrl, not the underlying list view. – Remy Lebeau Jun 15 '14 at 04:16
  • 1
    `LoadIconWithScaleDown` is pretty neat. Vista up of course. Does require a good selection of icon sizes available. And still results in scaling artifacts that are inevitable. – David Heffernan Jun 15 '14 at 18:33
  • OK, you might have a point. I think 15px is the icon height for the context menu. (I had to scale those as well.) – c00000fd Jun 15 '14 at 20:16
  • 1
    15x15 is not a standard image size anywhere in the Win32 API. Standard sizes are 16x16, 24x24, 32x32, and 48x48. – Remy Lebeau Jun 16 '14 at 00:15
0

The report style list view wants small icons, that is icons with SM_CXSMICON by SM_CYSMICON metrics. Assuming your app is high DPI aware, then the actual value of these metrics depends on the user's chosen font scaling or DPI setting. So up front you cannot know what size icons should be used. You have to query the system metrics at runtime, and use appropriately sized icons.

Now, what size icons you include in your executable depend on what DPI settings you wish to support. Back in XP days you could reasonably expect to encounter 100% and 125% font scaling. These days, high density display panels are common and you really need to support larger ratios. At least 150% and 200%, and quite probably 175%.

The closest I can find to guidelines is in this MSDN article: http://msdn.microsoft.com/en-US/library/windows/desktop/dn742485.aspx

This article was written around the Vista time frame and already shows its age. I think you have to use these articles as a guide and adapt to the hardware of the day.

You will also find that people run their machines at font scaling values in between the round numbers listed above, and in the article I link to. One of my colleagues runs at 120%. Interestingly this has highlighted various bugs in our code so it's always useful to have someone dog-fooding your program.

When you build your image lists at runtime, size them according to system metrics. And try to avoid scaling icons. For instance, if system metrics suggest an 18px icons, and you only have 16px and 20px icons, then make a new 18px icons. Fill the image with transparent pixels, and blit the 16px icon into the middle of this 18px image. Make the icon out of that. This approach will avoid aliasing problems.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks. The issue is like you pointed out, you don't know what scaling the user will run the UI at. I can literally specify 115% in my Windows 8.1 if I want. (The interesting thing is that even Windows Explorer doesn't always handle it correctly.) So one cannot predict each of those settings with a set of "nice" Photoshop-scaled icons. Unfortunately, even in today's OS, Microsoft still "doesn't get" and has no decent API to do the down-scaling. `LoadIconWithScaleDown` as you pointed above is still pretty bad, still resulting in "funny looking" artifacts (even in Windows Explorer in my 8.1.) – c00000fd Jun 15 '14 at 20:03
  • I explained how to deal with that. Find the icon size at runtime. Find the largest icon resource that you have that is smaller than that. Make a new icon based on that smaller resource that is the correct size, and has a transparent border around the edge. My last paragraph. When you do that you have an app that works at any of these non standard scalings. – David Heffernan Jun 15 '14 at 20:06
  • There is no decent way to downscale. It is an impossible task. That's why you need to supply lots of versions of icons. My app ships with 16, 20, 24, 32 and 48px versions of toolbar icons. And I use the techniques described to display the largest one that I can that fits in the small icon size. It works very well. It's really the only option you have. – David Heffernan Jun 15 '14 at 20:08
  • There's a way to scale them down. It's just MS doesn't have an API for that. Photoshop and other software can do it pretty neatly. Anyway, I updated my original post. Is this how you get the current scaling factor? – c00000fd Jun 15 '14 at 20:12
  • There's no way to scale down general raster images reliably and well. As for the new question that you ask, I don't think you need to know that here, but yes it is LOGPIXELSX. And in any case, I think I answered the question you asked. I guess you are not keen on either of the answers but that's up to you. – David Heffernan Jun 15 '14 at 20:19
  • But yes, use LOGPIXELSX to find dpi setting – David Heffernan Jun 15 '14 at 20:22