0
votes

I am using a button which is not DPI aware. I am using it everywhere in my code so ideally I'd like to add a few lines in the SetIcon of that button.

The SetIcon method (unsurprisingly) takes a HICON as a parameter - and I want to change the icon depending on the DPI resolution (so most of the time increase its size).

edit As pointed below, my question was not clear. I do not want to change the size of the icon - as read from a resource - but I want to transform a HICON od 16x16 into a new HICON of 40x40. I have got some code which reads a PNG (which returns a HICON) and LoadImage (nor LoadIconWithScaleDown) won't work with it.

Any ideas?

2
Changing the question after answer has already been given is not recommended. Also your modified question is still unclear. You have "some code" which makes 16x16 icon, just get it to make 40x40 icon. Or scale up the icon if you don't care about resolution. What does that have to do with DPI? unless your DPI settings is at 250 percent!Barmak Shemirani
@BarmakShemirani I've asked OP to edit his question for clarification. I think it's quite clear right now. I currently don't have time to update my answer, which would propably go like: 1. Create a 32 bpp memory device context. 2. DrawIconEx() into that memory DC to scale the icon. 3. CreateIcon() from the bits of the memory DC to turn that back into an icon.zett42
@zett42 - thanks for this - would the transparency work though?BadJerry

2 Answers

4
votes

Use LoadIconWithScaleDown() (available since Windows Vista) to load an icon image in the desired size, which is already scaled by the DPI scale factor.

Another answer suggests using LoadImage(), but LoadIconWithScaleDown() will greatly improve image quality in cases where an exact match of the desired image size is not available in the icon resource. In this case, LoadImage() loads the image with the next lower size and scales that up, whereas LoadIconWithScaleDown() loads the next bigger image and scales that down, which makes the scaled image appear more detailed and sharper.

int cx = GetSystemMetrics( SM_CXSMICON );  // scaled for system DPI!
int cy = GetSystemMetrics( SM_CYSMICON );  // scaled for system DPI!

HICON hIcon = NULL;
HRESULT hr = LoadIconWithScaleDown( AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1),
                                    cx, cy, &hIcon );

if( SUCCEEDED( hr ) )
{
    button.SetIcon( hIcon );
}

Note: I'm using GetSystemMetrics() to get the standard size of a small icon, scaled for the current system DPI. This will be good enough, if your application is only system-DPI-aware. If your application is per-Monitor-DPI-aware, you may be interested in GetSystemMetricsForDpi(), which has been added by a recent Windows 10 update.

3
votes

Get DPI scale using GetDeviceCaps and divide by 96 to obtain DPI factor.

Multiply the icon size by this DPI factor. Example,

int default_size = 16;
CClientDC dc(GetDesktopWindow());
int logpixy = dc.GetDeviceCaps(LOGPIXELSY);
size = MulDiv(default_size, logpixy, 96);

hicon = (HICON)LoadImage(AfxGetApp()->m_hInstance,
            MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, size, size, LR_DEFAULTCOLOR);
button.SetIcon(hicon);

You will need to store different icon sizes. For example 16x16, 20x20, 24x24, 32x32.

If the exact size is not available then LoadImage will pick the closest match.