in ArchivedSamples/High-DPI_Images_Icons/Cpp/VsUIDpiHelper.cpp [568:694]
HIMAGELIST CDpiHelper::CreateDeviceFromLogicalImage(HIMAGELIST _In_ hImageList, ImageScalingMode scalingMode)
{
IfNullAssertRetNull(hImageList, "No imagelist given to convert");
// If no scaling is required, return an image copy
if (!IsScalingRequired())
return ImageList_Duplicate(hImageList);
int nCount = ImageList_GetImageCount(hImageList);
int cxImage = 0;
int cyImage = 0;
IfFailRetNull( ImageList_GetIconSize(hImageList, &cxImage, &cyImage) );
int cxImageDevice = LogicalToDeviceUnitsX(cxImage);
int cyImageDevice = LogicalToDeviceUnitsY(cyImage);
// Create the new device imagelist. Use ILC_COLOR24 instead of ILC_COLOR32 because the images we're adding with
// ImageList_AddMasked don't have alpha channel (have 0 bytes), which would otherwise be interpreted by imagelist themeing code
// imagelist shell theming code as being completely transparent (and losing color information), later resulting in black pixels when the themed imagelist is drawn
bool fImageListComplete = false;
HIMAGELIST hImageListDevice = ImageList_Create(cxImageDevice, cyImageDevice, ILC_COLOR24 | ILC_MASK, nCount /*cInitial*/, 0 /*cGrow*/);
IfNullRetNull(hImageListDevice);
SCOPE_GUARD({
if (!fImageListComplete)
ImageList_Destroy(hImageListDevice);
});
ImageList_SetBkColor(hImageListDevice, ImageList_GetBkColor(hImageList));
if (nCount != 0)
{
CWinClientDC dcScreen(NULL);
IfNullRetNull(dcScreen);
CWinManagedDC dcMemoryLogical(CreateCompatibleDC(dcScreen));
IfNullRetNull(dcMemoryLogical);
HIMAGELIST hImageListNoTransparency = NULL;
SCOPE_GUARD({
if (hImageListNoTransparency)
ImageList_Destroy(hImageListNoTransparency);
});
// If the source imagelist uses ILC_COLOR32, the color bitmap may have partial transparent pixels
// If we were to paint them on a Magenta background for our ILC_COLOR24 output bitmap, those pixels
// would get a magenta tint. To get rid of the partial transparency, we'll create first a 24bpp
// Imagelist with background of Halo color, and we'll copy the images from the original list.
// The imagelist background color is used for interpolation of partial transparent pixels.
IMAGEINFO imageInfo = {0};
if (ImageList_GetImageInfo(hImageList, 0, &imageInfo))
{
BITMAPINFO bi = {0};
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
// Call GetDIBits without the underlying array to determine bitmap attributes
if (GetDIBits(dcScreen, imageInfo.hbmImage, /* uStartScan */ 0, /* cScanLines */ 0, /* lpvBits */ nullptr, &bi, DIB_RGB_COLORS))
{
if (bi.bmiHeader.biBitCount == 32)
{
VSASSERT(imageInfo.hbmMask != NULL, "The imagelist contains 32bpp image with no mask, not supported yet. The results will be incorrect.");
hImageListNoTransparency = ImageList_Create(cxImage, cyImage, ILC_COLOR24 | ILC_MASK, nCount /*cInitial*/, 0 /*cGrow*/);
IfNullRetNull(hImageListNoTransparency);
ImageList_SetBkColor(hImageListNoTransparency, HaloColor.ToCOLORREF());
for (int iImage = 0; iImage < nCount; iImage++)
{
// Unfortunately ImageList_Copy can only copy within same imagelist,
// so we have to extract icons one by one and add into the other imagelist
HICON hIcon = ImageList_GetIcon(hImageList, iImage, 0);
IfNullRetNull(hIcon);
SCOPE_GUARD( DestroyIcon(hIcon); );
if (ImageList_AddIcon(hImageListNoTransparency, hIcon) == -1)
return NULL;
}
// Set the background color, so further draw operations will use the mask
ImageList_SetBkColor(hImageListNoTransparency, CLR_NONE);
}
}
}
HIMAGELIST hImageListToDraw = hImageListNoTransparency ? hImageListNoTransparency : hImageList;
// Use Magenta for transparency
const Color& clrTransparency = MagentaColor;
CWinManagedBrush brTransparent;
brTransparent.CreateSolidBrush(clrTransparency.ToCOLORREF());
IfNullRetNull(brTransparent);
RECT rcImage = { 0, 0, cxImage, cyImage};
for (int iImage = 0; iImage < nCount; iImage++)
{
CWinManagedBitmap bmpMemory;
bmpMemory.CreateCompatibleBitmap(dcScreen, cxImage, cyImage);
IfNullRetNull(bmpMemory);
// Select the logical bitmap
dcMemoryLogical.SelectBitmap(bmpMemory);
// Draw image by image in dcMemoryLogical
IfFailRetNull( dcMemoryLogical.FillRect(&rcImage, brTransparent) );
IfFailRetNull( ImageList_Draw(hImageListToDraw, iImage, dcMemoryLogical, 0, 0, ILD_NORMAL) );
// Restore the original bitmap in the DC
dcMemoryLogical.SelectBitmap(dcMemoryLogical.m_hOriginalBitmap);
// Now scale the image according with the current DPI
HBITMAP hbmp = bmpMemory.Detach();
LogicalToDeviceUnits(&hbmp, scalingMode, clrTransparency);
bmpMemory.Attach(hbmp);
// Add the device image to the new imagelist
if (ImageList_AddMasked(hImageListDevice, bmpMemory, clrTransparency.ToCOLORREF()) == -1)
return NULL;
}
}
// Flag that scop guard should not delete the image we'll be returning
fImageListComplete = true;
return hImageListDevice;
}