简体   繁体   中英

Create Device Context with Icon and transparent background

I'm trying to get a image with HICON but the background is not transparent. How can I make it transparent? I need a winapi example because the code is in Dart, but it has all the windows calls/functions.

I've tried different version I've found on the internet but it didn't worked. I can access icon mask if that can help for a solution.

Current code:

    var icon = SendMessage(hWnd, WM_GETICON, 2, 0); // ICON_SMALL2 - User Made Apps
    if (icon == 0) icon = GetClassLongPtr(hWnd, -14); // GCLP_HICON - Microsoft Win Apps

    final int hScreen = GetDC(hWnd);
    final int hDC = CreateCompatibleDC(hScreen);
    final int hBitmap = CreateCompatibleBitmap(hScreen, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
    SelectObject(hDC, hBitmap);
    // SetBkMode(hDC, TRANSPARENT); - Works for text only
    // PatBlt(hDC, 0, 0, 545, 850, WHITENESS); - only white/black;
    DrawIconEx(hDC, 0, 0, icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), NULL, NULL, 3);

The icon is transparent:

在此处输入图像描述 在此处输入图像描述

Even if I don't draw the icon, the output is a black square.

Can you suggest how to remove the background? In basic winapi calls. The code can be in cpp if it doesn't use special classes from libraries, I can use only dllCalls

Here is full working code:

// ignore_for_file: depend_on_referenced_packages, non_constant_identifier_names, avoid_print, unrelated_type_equality_checks
import 'dart:ffi';
import 'dart:io';

import 'package:win32/win32.dart';
import 'package:ffi/ffi.dart';

int enumWindowsProc(int hWnd, int lparam) {
  if (IsWindowVisible(hWnd) == FALSE) return TRUE;
  final length = GetWindowTextLength(hWnd);
  if (length == 0) return TRUE;

  var icon = SendMessage(hWnd, WM_GETICON, 2, 0); // ICON_SMALL2 - User Made Apps
  if (icon == 0) icon = GetClassLongPtr(hWnd, -14); // GCLP_HICON - Microsoft Win Apps
  if (icon == 0) {
    icon = 0;
    return 1;
  }

  final int hScreen = GetDC(hWnd);
  final int hDC = CreateCompatibleDC(hScreen);
  final int hBitmap = CreateCompatibleBitmap(hScreen, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
  SelectObject(hDC, hBitmap);

  SetBkMode(hDC, TRANSPARENT); //- Works for text only
  PatBlt(hDC, 0, 0, GetSystemMetrics(SM_CXICON) ~/ 2, GetSystemMetrics(SM_CYICON), WHITENESS); // test, half white half black.
  DrawIconEx(hDC, 0, 0, icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), NULL, NULL, 3);

  //Turn to bytes
  final bmpScreen = calloc<BITMAP>();
  GetObject(hBitmap, sizeOf<BITMAP>(), bmpScreen);
  final bitmapFileHeader = calloc<BITMAPFILEHEADER>();
  final bitmapInfoHeader = calloc<BITMAPINFOHEADER>()
    ..ref.biSize = sizeOf<BITMAPINFOHEADER>()
    ..ref.biWidth = bmpScreen.ref.bmWidth
    ..ref.biHeight = bmpScreen.ref.bmHeight
    ..ref.biPlanes = 1
    ..ref.biBitCount = 32
    ..ref.biCompression = BI_RGB;

  final dwBmpSize = ((bmpScreen.ref.bmWidth * bitmapInfoHeader.ref.biBitCount + 31) / 32 * 4 * bmpScreen.ref.bmHeight).toInt();

  final lpBitmap = calloc<Uint8>(dwBmpSize);
  GetDIBits(hDC, hBitmap, 0, bmpScreen.ref.bmHeight, lpBitmap, bitmapInfoHeader.cast(), DIB_RGB_COLORS);

  final dwSizeOfDIB = dwBmpSize + sizeOf<BITMAPFILEHEADER>() + sizeOf<BITMAPINFOHEADER>();
  bitmapFileHeader.ref.bfOffBits = sizeOf<BITMAPFILEHEADER>() + sizeOf<BITMAPINFOHEADER>();

  bitmapFileHeader.ref.bfSize = dwSizeOfDIB;
  bitmapFileHeader.ref.bfType = 0x4D42; // BM

  var b = BytesBuilder();
  b.add(Pointer<Uint8>.fromAddress(bitmapFileHeader.address).asTypedList(sizeOf<BITMAPFILEHEADER>()));
  b.add(Pointer<Uint8>.fromAddress(bitmapInfoHeader.address).asTypedList(sizeOf<BITMAPINFOHEADER>()));
  b.add(lpBitmap.asTypedList(dwBmpSize));

  // I need the Bitmap in Bytes, I save it to file just for debugging.
  //capture?.icon = b.takeBytes();
  //
  DeleteDC(hDC);
  DeleteObject(hBitmap);
  free(bmpScreen);
  free(bitmapFileHeader);
  free(bitmapInfoHeader);
  free(lpBitmap);

  Directory current = Directory.current;
  File("${current.path}/imgs/i_${icon.toString()}.bmp").writeAsBytes(b.takeBytes());

  return 1;
}

void main() {
  final imgs = "${Directory.current.path}/imgs";
  if (Directory(imgs).exists() == true) {
    Directory(imgs).deleteSync(recursive: true);
  }
  final wndProc = Pointer.fromFunction<EnumWindowsProc>(enumWindowsProc, 0);
  EnumWindows(wndProc, 0);
}

final _user32 = DynamicLibrary.open('user32.dll');
int DrawIconEx(int hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags) =>
    _DrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags);

final _DrawIconEx = _user32.lookupFunction<
    Int32 Function(IntPtr hdc, Int32 xLeft, Int32 yTop, IntPtr hIcon, Int32 cxWidth, Int32 cyWidth, Uint32 istepIfAniCur, IntPtr hbrFlickerFreeDraw, Uint32 diFlags),
    int Function(int hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags)>('DrawIconEx');

final _gdi32 = DynamicLibrary.open('gdi32.dll');
int PatBlt(int hdc, int x, int y, int w, int h, int rop) => _PatBlt(hdc, x, y, w, h, rop);
final _PatBlt =
    _gdi32.lookupFunction<Int32 Function(IntPtr hdc, Int32 x, Int32 y, Int32 w, Int32 h, Uint32 rop), int Function(int hdc, int x, int y, int w, int h, int rop)>('PatBlt');

Found a even better solution, works with buffer and write to file, alpha channel and everything :) https://github.com/pelayomendez/exe-icon-extractor/blob/master/src/module.cc

#include <tchar.h>
#include <iostream>
#include <windows.h>
#include <fstream>
#include <cassert>
using namespace std;

// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif

// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif


typedef struct
{
    WORD idReserved; // must be 0
    WORD idType; // 1 = ICON, 2 = CURSOR
    WORD idCount; // number of images (and ICONDIRs)

    // ICONDIR [1...n]
    // ICONIMAGE [1...n]

} ICONHEADER;

//
// An array of ICONDIRs immediately follow the ICONHEADER
//
typedef struct
{
    BYTE bWidth;
    BYTE bHeight;
    BYTE bColorCount;
    BYTE bReserved;
    WORD wPlanes; // for cursors, this field = wXHotSpot
    WORD wBitCount; // for cursors, this field = wYHotSpot
    DWORD dwBytesInRes;
    DWORD dwImageOffset; // file-offset to the start of ICONIMAGE

} ICONDIR;

//
// After the ICONDIRs follow the ICONIMAGE structures -
// consisting of a BITMAPINFOHEADER, (optional) RGBQUAD array, then
// the color and mask bitmap bits (all packed together
//
typedef struct
{
    BITMAPINFOHEADER biHeader; // header for color bitmap (no mask header)
    //RGBQUAD rgbColors[1...n];
    //BYTE bXOR[1]; // DIB bits for color bitmap
    //BYTE bAND[1]; // DIB bits for mask bitmap

} ICONIMAGE;

//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP* pBitmap)
{
    int nWidthBytes = pBitmap->bmWidthBytes;

    // bitmap scanlines MUST be a multiple of 4 bytes when stored
    // inside a bitmap resource, so round up if necessary
    if (nWidthBytes & 3)
        nWidthBytes = (nWidthBytes + 4) & ~3;

    return nWidthBytes * pBitmap->bmHeight;
}

static BOOL GetIconBitmapInfo(HICON hIcon, ICONINFO* pIconInfo, BITMAP* pbmpColor, BITMAP* pbmpMask)
{
    if (!GetIconInfo(hIcon, pIconInfo))
        return FALSE;

    if (!GetObject(pIconInfo->hbmColor, sizeof(BITMAP), pbmpColor))
        return FALSE;

    if (!GetObject(pIconInfo->hbmMask, sizeof(BITMAP), pbmpMask))
        return FALSE;

    return TRUE;
}

//
// Write one icon directory entry - specify the index of the image
//
static void WriteIconDirectoryEntry(BYTE* buffer, int* pBufferOffset, int nIdx, HICON hIcon, UINT nImageOffset)
{
    ICONINFO iconInfo;
    ICONDIR iconDir;

    BITMAP bmpColor;
    BITMAP bmpMask;

    UINT nColorCount;
    UINT nImageBytes;

    GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);

    nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);

    if (bmpColor.bmBitsPixel >= 8)
        nColorCount = 0;
    else
        nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);

    // Create the ICONDIR structure
    iconDir.bWidth = (BYTE)bmpColor.bmWidth;
    iconDir.bHeight = (BYTE)bmpColor.bmHeight;
    iconDir.bColorCount = nColorCount;
    iconDir.bReserved = 0;
    iconDir.wPlanes = bmpColor.bmPlanes;
    iconDir.wBitCount = bmpColor.bmBitsPixel;
    iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
    iconDir.dwImageOffset = nImageOffset;

    // Write to disk
    memcpy(&buffer[*pBufferOffset], &iconDir, sizeof(iconDir));
    (*pBufferOffset) += sizeof(iconDir);

    // Free resources
    DeleteObject(iconInfo.hbmColor);
    DeleteObject(iconInfo.hbmMask);
}

static UINT WriteIconData(BYTE* buffer, int* pBufferOffset, HBITMAP hBitmap)
{
    BITMAP bmp;
    int i;
    BYTE* pIconData;

    UINT nBitmapBytes;

    GetObject(hBitmap, sizeof(BITMAP), &bmp);

    nBitmapBytes = NumBitmapBytes(&bmp);

    pIconData = (BYTE*)malloc(nBitmapBytes);

    GetBitmapBits(hBitmap, nBitmapBytes, pIconData);

    // bitmaps are stored inverted (vertically) when on disk..
    // so write out each line in turn, starting at the bottom + working
    // towards the top of the bitmap. Also, the bitmaps are stored in packed
    // in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
    for (i = bmp.bmHeight - 1; i >= 0; i--)
    {
        memcpy(&buffer[*pBufferOffset], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
        (*pBufferOffset) += bmp.bmWidthBytes;

        // extend to a 32bit boundary (in the file) if necessary
        if (bmp.bmWidthBytes & 3)
        {
            DWORD padding = 0;
            memcpy(&buffer[*pBufferOffset], &padding, 4 - bmp.bmWidthBytes);
            (*pBufferOffset) += 4 - bmp.bmWidthBytes;
        }
    }

    free(pIconData);

    return nBitmapBytes;
}

//
// Create a .ICO file, using the specified array of HICON images
//
BOOL SaveIcon3(HICON hIcon[], int nNumIcons, BYTE* buffer, int* pWritten)
{
    int i;
    int* pImageOffset = (int*)malloc(nNumIcons * sizeof(int));
    int bufferOffset = 0;

    if (hIcon == 0 || nNumIcons < 1)
        return 0;

    //
    // Write the iconheader first of all
    //

    ICONHEADER iconheader;

    // Setup the icon header
    iconheader.idReserved = 0; // Must be 0
    iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
    iconheader.idCount = nNumIcons; // number of ICONDIRs

    // Write the header to disk
    memcpy(&(buffer[bufferOffset]), &iconheader, sizeof(iconheader));
    bufferOffset += sizeof(iconheader);


    //
    // Leave space for the IconDir entries
    //
    bufferOffset += sizeof(ICONDIR) * nNumIcons;

    //
    // Now write the actual icon images!
    //
    for (i = 0; i < nNumIcons; i++) {
        ICONINFO iconInfo;
        BITMAP bmpColor, bmpMask;

        // GetIconBitmapInfo
        GetIconBitmapInfo(hIcon[i], &iconInfo, &bmpColor, &bmpMask);

        // record the file-offset of the icon image for when we write the icon directories
        pImageOffset[i] = bufferOffset;

        // WriteIconImageHeader

        BITMAPINFOHEADER biHeader;
        UINT nImageBytes;

        // calculate how much space the COLOR and MASK bitmaps take
        nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);

        // write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
        ZeroMemory(&biHeader, sizeof(biHeader));

        // Fill in only those fields that are necessary
        biHeader.biSize = sizeof(biHeader);
        biHeader.biWidth = bmpColor.bmWidth;
        biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
        biHeader.biPlanes = bmpColor.bmPlanes;
        biHeader.biBitCount = bmpColor.bmBitsPixel;
        biHeader.biSizeImage = nImageBytes;

        // write the BITMAPINFOHEADER
        memcpy(&(buffer[bufferOffset]), &biHeader, sizeof(biHeader));
        bufferOffset += sizeof(biHeader);

        // color and mask bitmaps
        WriteIconData(buffer, &bufferOffset, iconInfo.hbmColor);
        WriteIconData(buffer, &bufferOffset, iconInfo.hbmMask);

        DeleteObject(iconInfo.hbmColor);
        DeleteObject(iconInfo.hbmMask);
    }
    *pWritten = bufferOffset;

    //
    // Lastly, skip back and write the icon directories.
    //
    bufferOffset = sizeof(ICONHEADER);
    for (i = 0; i < nNumIcons; i++)
    {
        WriteIconDirectoryEntry(buffer, &bufferOffset, i, hIcon[i], pImageOffset[i]);
    }

    free(pImageOffset);

    return 1;
}
void main()
{
    HICON hIconLarge;
    HICON hIconSmall;

    int extractIcon = ExtractIconExW(L"E:\\Program Files\\Microsoft VS Code Insiders\\Code - Insiders.exe", 0, &hIconLarge, &hIconSmall, 1);
    if (extractIcon <= 0) {
        std::cout << "No icon";
        return;
    }

    BYTE buffer[(256 * 256) * 4]; // (256x256) Max Windows Icon Size x 4 bytes (32 bits)
    int written;
    SaveIcon3(&hIconLarge, 1, buffer, &written);
    std::ofstream file;
    file.open("E:/t.ico", std::ios_base::binary);
    assert(file.is_open());

    for (int i = 0; i < sizeof(buffer) / sizeof(buffer[0]); ++i)
        file.write((char*)(buffer + i * sizeof(buffer[0])), sizeof(buffer[0]));
    file.close();

}

Found a solution.

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;
}

Original post: https://stackoverflow.com/a/22885412/1456151

And also from PowerToys source code:


HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UINT height)
{
    HBITMAP hBitmapResult = NULL;

    // Create compatible DC
    HDC hDC = CreateCompatibleDC(NULL);
    if (hDC != NULL)
    {
        // Get bitmap rectangle size
        RECT rc = { 0 };
        rc.left = 0;
        rc.right = (width != 0) ? width : GetSystemMetrics(SM_CXSMICON);
        rc.top = 0;
        rc.bottom = (height != 0) ? height : GetSystemMetrics(SM_CYSMICON);

        // Create bitmap compatible with DC
        BITMAPINFO BitmapInfo;
        ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));

        BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        BitmapInfo.bmiHeader.biWidth = rc.right;
        BitmapInfo.bmiHeader.biHeight = rc.bottom;
        BitmapInfo.bmiHeader.biPlanes = 1;
        BitmapInfo.bmiHeader.biBitCount = 32;
        BitmapInfo.bmiHeader.biCompression = BI_RGB;

        HDC hDCBitmap = GetDC(NULL);

        HBITMAP hBitmap = CreateDIBSection(hDCBitmap, &BitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);

        ReleaseDC(NULL, hDCBitmap);

        if (hBitmap != NULL)
        {
            // Select bitmap into DC
            HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);
            if (hBitmapOld != NULL)
            {
                // Draw icon into DC
                if (DrawIconEx(hDC, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
                {
                    // Restore original bitmap in DC
                    hBitmapResult = (HBITMAP)SelectObject(hDC, hBitmapOld);
                    hBitmapOld = NULL;
                    hBitmap = NULL;
                }

                if (hBitmapOld != NULL)
                {
                    SelectObject(hDC, hBitmapOld);
                }
            }

            if (hBitmap != NULL)
            {
                DeleteObject(hBitmap);
            }
        }

        DeleteDC(hDC);
    }

    return hBitmapResult;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM