首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从HWND获取和保存位图(8,4,1位/像素-黑色图像)

从HWND获取和保存位图(8,4,1位/像素-黑色图像)
EN

Stack Overflow用户
提问于 2012-07-22 22:49:53
回答 2查看 3.5K关注 0票数 0

我编写了这段简单的代码,用于从HWND中将位图保存到文件(*.bmp)。

代码语言:javascript
复制
#include <stdio.h>
#include <windows.h>

bool captureAndSave(const HWND& hWnd, int nBitCount, const char* szFilePath)
{
    if(!szFilePath || !strlen(szFilePath))
    {
        printf("bad function arguments\n");
        return false;
    }

    //calculate the number of color indexes in the color table
    int nColorTableEntries = -1;
    switch(nBitCount) 
    {
        case 1:
            nColorTableEntries = 2;
            break;
        case 4:
            nColorTableEntries = 16;
            break;
        case 8:
            nColorTableEntries = 256;
            break;
        case 16:
        case 24:
        case 32:
            nColorTableEntries = 0;
            break;
        default:
            nColorTableEntries = -1;
            break;
    }

    if(nColorTableEntries == -1)
    {
        printf("bad bits-per-pixel argument\n");
        return false;
    }

    HDC hDC = GetDC(hWnd);
    HDC hMemDC = CreateCompatibleDC(hDC);

    int nWidth = 0;
    int nHeight = 0;

    if(hWnd != HWND_DESKTOP)
    {
        RECT rect;
        GetClientRect(hWnd, &rect);
        nWidth = rect.right - rect.left;
        nHeight = rect.bottom - rect.top;
    }
    else
    {
        nWidth = ::GetSystemMetrics(SM_CXSCREEN);
        nHeight = ::GetSystemMetrics(SM_CYSCREEN);
    }


    HBITMAP hBMP = CreateCompatibleBitmap(hDC, nWidth, nHeight);
    SelectObject(hMemDC, hBMP);
    BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);

    int nStructLength = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
    LPBITMAPINFOHEADER lpBitmapInfoHeader = (LPBITMAPINFOHEADER)new char[nStructLength];
    ::ZeroMemory(lpBitmapInfoHeader, nStructLength);

    lpBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
    lpBitmapInfoHeader->biWidth = nWidth;
    lpBitmapInfoHeader->biHeight = nHeight;
    lpBitmapInfoHeader->biPlanes = 1;
    lpBitmapInfoHeader->biBitCount = nBitCount;
    lpBitmapInfoHeader->biCompression = BI_RGB;
    lpBitmapInfoHeader->biXPelsPerMeter = 0;
    lpBitmapInfoHeader->biYPelsPerMeter = 0;
    lpBitmapInfoHeader->biClrUsed = nColorTableEntries;
    lpBitmapInfoHeader->biClrImportant = nColorTableEntries;

    DWORD dwBytes = ((DWORD) nWidth * nBitCount) / 32;
    if(((DWORD) nWidth * nBitCount) % 32) {
        dwBytes++;
    }
    dwBytes *= 4;

    DWORD dwSizeImage = dwBytes * nHeight;
    lpBitmapInfoHeader->biSizeImage = dwSizeImage;

    LPBYTE lpDibBits = 0;
    HBITMAP hBitmap = ::CreateDIBSection(hMemDC, (LPBITMAPINFO)lpBitmapInfoHeader, DIB_RGB_COLORS,  (void**)&lpDibBits, NULL, 0);
    SelectObject(hMemDC, hBitmap);
    BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
    ReleaseDC(hWnd, hDC);

    BITMAPFILEHEADER bmfh;
    bmfh.bfType = 0x4d42;  // 'BM'
    int nHeaderSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;
    bmfh.bfSize = 0;
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries;    

    FILE *pFile = 0;
    fopen_s(&pFile, szFilePath, "wb");
    if(!pFile)
    {
        ::DeleteObject(hBMP);
        ::DeleteObject(hBitmap);
        delete[]lpBitmapInfoHeader;
        printf("can not open file\n");
        return false;   
    }

    DWORD nColorTableSize = 0;
    if (nBitCount != 24)
        nColorTableSize = (1 << nBitCount) * sizeof(RGBQUAD);
    else
        nColorTableSize = 0;


    fwrite(&bmfh, sizeof(BITMAPFILEHEADER), 1, pFile);
    fwrite(lpBitmapInfoHeader, nHeaderSize,1,pFile);

    if(nBitCount < 16)
    {
        int nBytesWritten = 0;
        RGBQUAD *rgbTable = new RGBQUAD[nColorTableEntries * sizeof(RGBQUAD)];
        //fill RGBQUAD table and write it in file
        for(int i = 0; i < nColorTableEntries; ++i)
        {
            rgbTable[i].rgbRed = rgbTable[i].rgbGreen = rgbTable[i].rgbBlue = i;
            rgbTable[i].rgbReserved = 0;

            fwrite(&rgbTable[i], sizeof(RGBQUAD), 1, pFile);
        }
        delete[]rgbTable;

        /*
        RGBQUAD rgb;
        for (DWORD i = 0; i < nColorTableEntries ; i++)
        {
            rgb.rgbBlue = rgb.rgbGreen = rgb.rgbRed = (BYTE)(i*(255/(nColorTableEntries-1)));
            nBytesWritten = fwrite(&rgb, 1, sizeof(rgb), pFile);
            if (nBytesWritten != sizeof(rgb)) 
            {
                printf("error while writing rgb header\n");
                fclose(pFile);

                ::DeleteObject(hBMP);
                ::DeleteObject(hBitmap);
                delete[]lpBitmapInfoHeader;

                return false;
            }
        }
        */
    }

    fwrite(lpDibBits, dwSizeImage, 1, pFile);

    fclose(pFile);

    ::DeleteObject(hBMP);
    ::DeleteObject(hBitmap);
    delete[]lpBitmapInfoHeader;
}

int main(int argc, char **argv)
{
    captureAndSave(HWND_DESKTOP, 1,  "1.bmp");
    captureAndSave(HWND_DESKTOP, 4,  "4.bmp");
    captureAndSave(HWND_DESKTOP, 8,  "8.bmp");
    captureAndSave(HWND_DESKTOP, 16, "16.bmp");
    captureAndSave(HWND_DESKTOP, 24, "24.bmp");
    captureAndSave(HWND_DESKTOP, 32, "32.bmp");

    return 0;
}

图像被正确地保存为每像素32、24和16位。但是对于每像素8、4和1位,图像只包含黑色像素。

请告诉我我哪里做错了。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-07-23 13:11:18

当GDI将图像复制到索引表面时,它需要将源上的颜色映射到目标上可用的颜色。

除非您在目标DC中创建并选择了调色板,否则GDI将不知道哪些颜色可用,并将使用默认调色板映射颜色,默认调色板将仅定义黑白。

这可能涉及到相当多的内容-理想情况下,您需要扫描源图像,创建所有使用的颜色及其频率的映射,并使用该映射来计算理想的调色板。

或者,只使用CreateHalfTonePalette

在您的例子中,您使用的是具有所需位深度的DIBSection,因此您需要在执行blit之前初始化DIBSections颜色表。当用CreateHalfTonePalette创建DIB部分时,设置HPALETTE颜色表比选择一个HPALETTE到DC中更重要-但您可以使用由DIB创建的调色板,并提取结果颜色表。

票数 1
EN

Stack Overflow用户

发布于 2012-07-22 23:42:07

对于8/4/1位图像,即索引图像,必须将RGBQUAD表写入文件,因为原始位图数据不是颜色,而是索引到RGBQUAD表。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11601263

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档