C ++ MFC StretchDIBits PixelFormat8bppIndexed

时间:2018-07-26 12:14:10

标签: c++ mfc gdi+

在使用MFC时,似乎在绘制PixelFormat8bppIndexed时出现了问题。

示例PixelFormat8bppIndexed

enter image description here

使用其他格式都可以。

我认为问题出在

(*pBitmapInfo)->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
(*pBitmapInfo)->bmiHeader.biBitCount = 8;
(*pBitmapInfo)->bmiHeader.biCompression = BI_RGB;
(*pBitmapInfo)->bmiHeader.biWidth = (*width);
(*pBitmapInfo)->bmiHeader.biHeight = (*height);
(*pBitmapInfo)->bmiHeader.biPlanes = 1;
(*pBitmapInfo)->bmiHeader.biSizeImage = ((*width) * (*height) * 1);
(*pBitmapInfo)->bmiHeader.biClrUsed = 256;
(*pBitmapInfo)->bmiHeader.biClrImportant = 0;
for (int i = 0; i < 256; i++)
{
    (*pBitmapInfo)->bmiColors[0].rgbBlue = i;
    (*pBitmapInfo)->bmiColors[0].rgbRed = i;
    (*pBitmapInfo)->bmiColors[0].rgbGreen = i;
    (*pBitmapInfo)->bmiColors[0].rgbReserved = 0;
}

该如何解决?

代码:

void ConvertImage(WCHAR *filename, Gdiplus::PixelFormat pixelformat, BITMAPINFO** pBitmapInfo, void** imageData, int* width, int* height)
{
#pragma region GDI+

    *pBitmapInfo = new BITMAPINFO();
    ULONG written;
    LARGE_INTEGER zero;
    zero.QuadPart = 0;

    ULARGE_INTEGER liSize;

    Gdiplus::GdiplusStartupInput tmp;
    ULONG_PTR token;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);

    Gdiplus::Bitmap *image = Gdiplus::Bitmap::FromFile(filename, false);

    Gdiplus::Bitmap* destination = image->Clone(0, 0, image->GetWidth(), image->GetHeight(),
        pixelformat);

    CLSID clsid_bmp;
    CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid_bmp);

    IStream *stream = NULL;
    HRESULT hr = CreateStreamOnHGlobal(0, TRUE, &stream);

    if (!SUCCEEDED(hr))
        printf("problems");

    destination->Save(stream, &clsid_bmp);
    IStream_Size(stream, &liSize);

    stream->Seek(zero, STREAM_SEEK_SET, NULL);

    unsigned char *info = new unsigned char[liSize.QuadPart];

    stream->Read(info, liSize.QuadPart, &written);

    BYTE *outImageData = new BYTE[(liSize.QuadPart - 54)];

    memcpy(outImageData, info + 54, (liSize.QuadPart - 54));

    *width = *(int*)&info[18];
    *height = *(int*)&info[22];
    switch (pixelformat)
    {
    case PixelFormat32bppARGB:
        (*pBitmapInfo)->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        (*pBitmapInfo)->bmiHeader.biBitCount = 32;
        (*pBitmapInfo)->bmiHeader.biCompression = BI_RGB;
        (*pBitmapInfo)->bmiHeader.biWidth = *width;
        (*pBitmapInfo)->bmiHeader.biHeight = *height;
        (*pBitmapInfo)->bmiHeader.biPlanes = 1;
        (*pBitmapInfo)->bmiHeader.biSizeImage = ((*width) * (*height) * 4);
        break;

    case PixelFormat24bppRGB:
        (*pBitmapInfo)->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        (*pBitmapInfo)->bmiHeader.biBitCount = 24;
        (*pBitmapInfo)->bmiHeader.biCompression = BI_RGB;
        (*pBitmapInfo)->bmiHeader.biWidth = *width;
        (*pBitmapInfo)->bmiHeader.biHeight = *height;
        (*pBitmapInfo)->bmiHeader.biPlanes = 1;
        (*pBitmapInfo)->bmiHeader.biSizeImage = ((*width) * (*height) * 3);
        break;

    case PixelFormat16bppRGB555:
        (*pBitmapInfo)->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        (*pBitmapInfo)->bmiHeader.biBitCount = 16;
        (*pBitmapInfo)->bmiHeader.biCompression = BI_RGB;
        (*pBitmapInfo)->bmiHeader.biWidth = *width;
        (*pBitmapInfo)->bmiHeader.biHeight = *height;
        (*pBitmapInfo)->bmiHeader.biPlanes = 1;
        (*pBitmapInfo)->bmiHeader.biSizeImage = ((*width) * (*height) * 2);
        break;

    case PixelFormat8bppIndexed:
        (*pBitmapInfo)->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        (*pBitmapInfo)->bmiHeader.biBitCount = 8;
        (*pBitmapInfo)->bmiHeader.biCompression = BI_RGB;
        (*pBitmapInfo)->bmiHeader.biWidth = (*width);
        (*pBitmapInfo)->bmiHeader.biHeight = (*height);
        (*pBitmapInfo)->bmiHeader.biPlanes = 1;
        (*pBitmapInfo)->bmiHeader.biSizeImage = ((*width) * (*height) * 1);

        (*pBitmapInfo)->bmiHeader.biClrUsed = 256;
        (*pBitmapInfo)->bmiHeader.biClrImportant = 0;
        for (int i = 0; i < 256; i++)
        {
            (*pBitmapInfo)->bmiColors[0].rgbBlue = i;
            (*pBitmapInfo)->bmiColors[0].rgbRed = i;
            (*pBitmapInfo)->bmiColors[0].rgbGreen = i;
            (*pBitmapInfo)->bmiColors[0].rgbReserved = 0;
        }

        break;
    }
    *imageData = outImageData;

    delete destination;
    delete image;
    delete[]info;

    Gdiplus::GdiplusShutdown(token);

#pragma endregion
}

void CMFCApplicationColorsView::OnDraw(CDC* pDC)
{
    CMFCApplicationColorsDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    BITMAPINFO* pBitmapInfo = NULL;
    void *imageData = NULL;
    int *width = new int;
    int *height = new int;

    ConvertImage(L"E:\TestImage.bmp", PixelFormat8bppIndexed, &pBitmapInfo, &imageData, width, height);
    //ConvertImage(L"E:\TestImage.bmp", PixelFormat16bppRGB555, &pBitmapInfo, &imageData, width, height);
    ::StretchDIBits(pDC->GetSafeHdc(), 0, 0, *width, *height, 0, 0, *width, *height, imageData, pBitmapInfo, DIB_PAL_COLORS, SRCCOPY);

    delete[] imageData;
    delete pBitmapInfo;
    delete width;
    delete height;
    // TODO: add draw code for native data here
}

1 个答案:

答案 0 :(得分:2)

Gdiplus::GetHBITMAP允许直接检索HBITMAP。在大多数情况下,这足以在GDI +中使用GDI功能。

如果由于某种原因必须使用StretchDIBits,则首先使用GetDIBits来检索bitsBITMAPINFO

BITMAPINFO分配内存时,请确保为调色板添加额外的内存,以防使用8位位图:

sizeof(BITMAPINFO) + palettesize

您还可以使用帮助器类gdiplus_init确保始终调用启动/关闭。

#include <vector>
...

class gdiplus_init
{
    ULONG_PTR token;
public:
    gdiplus_init()
    {
        Gdiplus::GdiplusStartupInput tmp;
        Gdiplus::GdiplusStartup(&token, &tmp, NULL);
    }
    ~gdiplus_init()
    {
        Gdiplus::GdiplusShutdown(token);
    }
};

bool getbits(const wchar_t *filename, Gdiplus::PixelFormat pixelformat, 
    std::vector<BYTE> &bitmapinfo, std::vector<BYTE> &bits, int &w, int &h)
{
    gdiplus_init init;

    WORD bpp = 0;
    int usage = DIB_RGB_COLORS;
    int palettesize = 0;

    switch(pixelformat)
    {
    case PixelFormat8bppIndexed: 
        bpp = 8; 
        usage = DIB_PAL_COLORS;  
        palettesize = 256 * sizeof(RGBQUAD);
        break;
    case PixelFormat16bppRGB555: bpp = 16; break;
    case PixelFormat16bppRGB565: bpp = 16; break;
    case PixelFormat24bppRGB: bpp = 24; break;
    case PixelFormat32bppRGB: bpp = 32; break;
    default:return false;
    }

    auto src = Gdiplus::Bitmap::FromFile(filename);
    if(src->GetLastStatus() != Gdiplus::Status::Ok)
        return false;

    auto dst = src->Clone(0, 0, src->GetWidth(), src->GetHeight(),
        pixelformat);

    w = src->GetWidth();
    h = src->GetHeight();

    HBITMAP hbitmap;
    Gdiplus::Color color;
    dst->GetHBITMAP(color, &hbitmap);

    //allocate enough memory for bitmapinfo and initialize to zero
    //it's sizeof BITMAPINFO structure + size of palette
    bitmapinfo.resize(sizeof(BITMAPINFO) + palettesize, 0);

    //fill the first 6 parameters
    BITMAPINFO* ptr = (BITMAPINFO*)bitmapinfo.data();
    ptr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); //don't skip
    ptr->bmiHeader.biWidth = w;
    ptr->bmiHeader.biHeight = h;
    ptr->bmiHeader.biPlanes = 1;
    ptr->bmiHeader.biBitCount = bpp;
    ptr->bmiHeader.biCompression = BI_RGB;

    //magic formula to calculate the size:
    //this is roughly w * h * bytes_per_pixel, it's written this way
    //to account for "bitmap padding"
    DWORD size = ((w * bpp + 31) / 32) * 4 * h;

    //allocate memory for image
    bits.resize(size, 0);

    //finally call GetDIBits to fill bits and bitmapinfo
    HDC hdc = GetDC(0);
    GetDIBits(hdc, hbitmap, 0, h, &bits[0], (BITMAPINFO*)&bitmapinfo[0], usage);
    ReleaseDC(0, hdc);

    //cleanup
    delete src;
    delete dst;

    return true;
}

void CMFCApplicationColorsView::OnDraw(CDC* pDC)
{
    ...
    std::vector<BYTE> bi; //automatic storage
    std::vector<BYTE> bits; 
    int w, h;

    //24-bit test
    if(getbits(L"c:\\test\\24bit.bmp", PixelFormat24bppRGB, bi, bits, w, h))
        StretchDIBits(dc, 0, 0, w, h, 0, 0, w, h, 
            bits.data(), (BITMAPINFO*)bi.data(), DIB_RGB_COLORS, SRCCOPY);

    //8-bit test
    if(getbits(L"c:\\test\\8bit.bmp", PixelFormat8bppIndexed, bi, bits, w, h))
        StretchDIBits(dc, 0, 220, w, h, 0, 0, w, h, 
            bits.data(), (BITMAPINFO*)bi.data(), DIB_PAL_COLORS, SRCCOPY);
}