【问题标题】:Save HICON as a png将 HICON 保存为 png
【发布时间】:2010-12-21 14:20:57
【问题描述】:

我正在使用 IShellItemImageFactory 来提取文件的图标。我能够成功地提取它并使用它在对话框中显示它 SendDlgItemMessage(hDlg,IDC_STATIC2, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon);

查看输出:click here

问题是当我使用 GDI+ 将其保存为文件(PNG 格式)时,渐变不会保留校正。在下面找到我正在使用的代码。

GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 位图 *h = new Bitmap(256, 256, PixelFormat32bppARGB); Graphics* g = Graphics::FromImage(copy); HDC copyHdc = g->GetHDC(); DrawIconEx(copyHdc, 0, 0, hicon, 256, 256, 0, NULL, DI_NORMAL); g->ReleaseHDC(copyHdc);; CLSID 编码器Clsid; GetEncoderClsid(L"image/png", &encoderClsid); h->Save(L"D:\\mynew.png", &encoderClsid, NULL); GdiplusShutdown(gdiplusToken);

提取文本文件后得到的输出:click here

谁能帮我解决这个问题?

问候, 马诺杰

【问题讨论】:

  • 在链接的图像中看不到任何差异。
  • 看两张图的渐变,第二张有点黑。

标签: c++ winapi gdi+


【解决方案1】:

这个话题相当老了,但我也遇到了同样的问题,花了很多时间来找到一个可以保留 PNG 文件透明度的解决方案。

既然这个问题在Java中很容易解决...

    sun.awt.shell.ShellFolder sf = sun.awt.shell.ShellFolder.getShellFolder(file);
    ImageIcon icon = new ImageIcon(sf.getIcon(true));
    FileOutputStream bos = new FileOutputStream("d:\\icons\\icon.png");
    ImageIO.write((BufferedImage)icon.getImage(), "PNG", bos);

...我看了一下JDK源代码。在函数“Java_sun_awt_shell_Win32ShellFolder2_getIconBits”的文件“\jdk\src\windows\native\sun\windows\ShellFolder2.cpp”中,我找到了我需要的有价值的提示。

此函数从 HICON 中检索颜色位图并调用 GetDIBits 以获取图像数据。不需要绘制图标 - 无论如何都会丢失透明度。

非常感谢 JDK 开发人员。

这是我最终得到的代码:

static CLSID g_pngClsid = GUID_NULL;

// http://msdn.microsoft.com/en-us/library/windows/desktop/ms533843(v=vs.85).aspx
extern int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

static HICON getShellIconByIndex(int shilsize, int iImage)
{
    IImageListPtr spiml;
    SHGetImageList(shilsize, IID_PPV_ARGS(&spiml));

    HICON hico;
    spiml->GetIcon(iImage, ILD_TRANSPARENT, &hico);
    return hico;
}

static HICON getShellIcon(int shilsize, const std::wstring& fname) {
    UINT flags = SHGFI_SYSICONINDEX;
    SHFILEINFO fi = {0};
    HICON hIcon = NULL;

    if (SHGetFileInfo(fname.c_str(), 0, &fi, sizeof(fi), flags) != 0) {
        hIcon = getShellIconByIndex(shilsize, fi.iIcon);
    }

    return hIcon;
}

struct BITMAP_AND_BYTES {
    Gdiplus::Bitmap* bmp;
    int32_t* bytes;
};

static BITMAP_AND_BYTES createAlphaChannelBitmapFromIcon(HICON hIcon) {

    // Get the icon info
    ICONINFO iconInfo = {0};
    GetIconInfo(hIcon, &iconInfo);

    // Get the screen DC
    HDC dc = GetDC(NULL);

    // Get icon size info
    BITMAP bm = {0};
    GetObject( iconInfo.hbmColor, sizeof( BITMAP ), &bm );

    // Set up BITMAPINFO
    BITMAPINFO bmi = {0};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = bm.bmWidth;
    bmi.bmiHeader.biHeight = -bm.bmHeight;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;

    // Extract the color bitmap
    int nBits = bm.bmWidth * bm.bmHeight;
    int32_t* colorBits = new int32_t[nBits];
    GetDIBits(dc, iconInfo.hbmColor, 0, bm.bmHeight, colorBits, &bmi, DIB_RGB_COLORS);

    // Check whether the color bitmap has an alpha channel.
        // (On my Windows 7, all file icons I tried have an alpha channel.)
    BOOL hasAlpha = FALSE;
    for (int i = 0; i < nBits; i++) {
        if ((colorBits[i] & 0xff000000) != 0) {
            hasAlpha = TRUE;
            break;
        }
    }

    // If no alpha values available, apply the mask bitmap
    if (!hasAlpha) {
        // Extract the mask bitmap
        int32_t* maskBits = new int32_t[nBits];
        GetDIBits(dc, iconInfo.hbmMask, 0, bm.bmHeight, maskBits, &bmi, DIB_RGB_COLORS);
        // Copy the mask alphas into the color bits
        for (int i = 0; i < nBits; i++) {
            if (maskBits[i] == 0) {
                colorBits[i] |= 0xff000000;
            }
        }
        delete[] maskBits;
    } 

    // Release DC and GDI bitmaps
    ReleaseDC(NULL, dc); 
    ::DeleteObject(iconInfo.hbmColor);
    ::DeleteObject(iconInfo.hbmMask); 

    // Create GDI+ Bitmap
    Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(bm.bmWidth, bm.bmHeight, bm.bmWidth*4, PixelFormat32bppARGB, (BYTE*)colorBits);
    BITMAP_AND_BYTES ret = {bmp, colorBits};

    return ret;
}

static void saveFileIconAsPng(int shilsize, const std::wstring& fname, const std::wstring& pngFile) {
    HICON hIcon = getShellIcon(shilsize, fname);
    BITMAP_AND_BYTES bbs = createAlphaChannelBitmapFromIcon(hIcon);

    IStream* fstrm = NULL;
    SHCreateStreamOnFile(pngFile.c_str(), STGM_WRITE|STGM_CREATE, &fstrm);
    bbs.bmp->Save(fstrm, &g_pngClsid, NULL);
    fstrm->Release();

    delete bbs.bmp;
    delete[] bbs.bytes;
    DestroyIcon(hIcon);
}

调用示例:

GdiplusStartup(...);
GetEncoderClsid(L"image/png", &g_pngClsid);

wstring fname = L"d:\\index.html";
wstring pngFile = L"d:\\icons\\index.html.png";
saveFileIconAsPng(SHIL_JUMBO, fname, pngFile);

GdiplusShutdown(...);

【讨论】:

    【解决方案2】:

    我缺少您代码中的“副本”:

    Graphics* g = Graphics::FromImage(copy);
    

    查看图像,您绘制图标的设备上下文似乎没有 32 位颜色(缺少 alpha 通道)。

    尝试像这样创建 DC:

    HDC hDC = CreateCompatibleDC(NULL);
    

    然后在该 dc 中选择一个 32 位彩色(空)位图。之后,您可以绘制图标并保存它。

    【讨论】:

      猜你喜欢
      • 2022-11-04
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-16
      相关资源
      最近更新 更多