Search code examples
windowsgdicolor-palette

Retrieving the palette of a bitmap image


I am loading a bitmap image from a file (type BMP) via the GDI function LoadImage, which returns a BITMAP handle.

I know how to access the bitmap bits. But the image is in the format 8BPP, hence palettized. How can I obtain the palette entries ?


Solution

  • Select the bitmap in to dc and call GetDIBColorTable. A temporary memory dc can be used here:

    RGBQUAD rgb[256] = { 0 };
    HDC memdc = CreateCompatibleDC(hdc);
    auto oldbmp = SelectObject(memdc, hbitmap);
    GetDIBColorTable(memdc, 0, 256, rgb);
    SelectObject(memdc, oldbmp);
    DeleteDC(memdc);
    

    Alternatively use GetDIBits to read BITMAPINFO. You have to reserve enough memory to read the color table + all bytes + sizeof(BITMAPINFO).

    Color table will be copied to BITMAPINFO -> bmiColors

    Gdi+ is another option. Here is GDI example:

    int main()
    {
        HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"source.bmp", 
                IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
        if (!hbitmap)
            return 0;
    
        BITMAP bm;
        GetObject(hbitmap, sizeof(bm), &bm);
        int width = bm.bmWidth;
        int height = bm.bmHeight;
    
        WORD clrbits = (WORD)(bm.bmPlanes * bm.bmBitsPixel);
        if (clrbits == 8)       clrbits = 1;
        else if (clrbits <= 4)  clrbits = 4;
        else if (clrbits <= 8)  clrbits = 8;
        else if (clrbits <= 16) clrbits = 16;
        else if (clrbits <= 24) clrbits = 24;
        else clrbits = 32;
    
        HDC hdc = GetDC(0);
    
        if(clrbits == 8)
        {
            RGBQUAD rgb[256] = { 0 };
            HDC memdc = CreateCompatibleDC(hdc);
            auto oldbmp = SelectObject(memdc, hbitmap);
            GetDIBColorTable(memdc, 0, 256, rgb);
            SelectObject(memdc, oldbmp);
            DeleteDC(memdc);
        }
    
        int palette_size = (clrbits < 24) ? sizeof(RGBQUAD) * (1 << clrbits) : 0;
        BITMAPINFO* bmpinfo = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFO) + palette_size];
        int width_in_bytes = ((width * clrbits + 31) & ~31) / 8;
        DWORD size = width_in_bytes * height;
        bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmpinfo->bmiHeader.biWidth = width;
        bmpinfo->bmiHeader.biHeight = height;
        bmpinfo->bmiHeader.biPlanes = bm.bmPlanes;
        bmpinfo->bmiHeader.biBitCount = bm.bmBitsPixel;
        bmpinfo->bmiHeader.biClrUsed = (clrbits < 24) ? (1 << clrbits) : 0;
        bmpinfo->bmiHeader.biCompression = BI_RGB;
        bmpinfo->bmiHeader.biSizeImage = size;
    
        BYTE* bits = new BYTE[size];
        GetDIBits(hdc, hbitmap, 0, height, bits, bmpinfo, 0);
       
        //palette size should be 1024 for 256 color
        //it should be stored in `bmpinfo->bmiColors`
    
        delete[]bits;
        delete[](BYTE*)bmpinfo;
        DeleteObject(hbitmap);
        ReleaseDC(0, hdc);
    
        return 0;
    }