Создание 24-битного BITMAP в Winapi - PullRequest
0 голосов
/ 05 июня 2019

Я использую следующий код для преобразования моего изображения ImageMagick в 32-битный HBITMAP:

BITMAP bitmap;
std::memset(&bitmap, 0, sizeof(bitmap));

bitmap.bmType = 0;
bitmap.bmWidth = image->image()->columns;
bitmap.bmHeight = image->image()->rows;
bitmap.bmWidthBytes = 4 * bitmap.bmWidth;
bitmap.bmPlanes = 1;
bitmap.bmBitsPixel = 32;
bitmap.bmBits = NULL;

const size_t size = bitmap.bmWidthBytes * bitmap.bmHeight;
auto buffer = (HANDLE)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);

RGBQUAD *bitmap_bits = (RGBQUAD *) GlobalLock((HGLOBAL) buffer);
register RGBQUAD *q = bitmap_bits;

for (size_t y = 0; y < image->image()->rows; y++)
{
    register auto p = GetVirtualPixels(image->image(), 0, y, image->image()->columns, 1, exception);
    if (!p) break;

    for (size_t x = 0; x < image->image()->columns; x++)
    {
        q->rgbRed = ScaleQuantumToChar(GetPixelRed(image->image(), p));
        q->rgbGreen = ScaleQuantumToChar(GetPixelGreen(image->image(), p));
        q->rgbBlue = ScaleQuantumToChar(GetPixelBlue(image->image(), p));
        q->rgbReserved = 0;

        p += GetPixelChannels(image->image());
        q++;
    }
}

bitmap.bmBits = bitmap_bits;
HBITMAP hbmp = CreateBitmapIndirect(&bitmap);

Работает хорошо, но я бы хотел сэкономить память, используя изображения с меньшей глубиной. К сожалению, я даже не могу заставить его работать с 24-битными изображениями. Я изменил свой код, чтобы он выглядел так:

BITMAP bitmap;
std::memset(&bitmap, 0, sizeof(bitmap));

bitmap.bmType = 0;
bitmap.bmWidth = image->image()->columns;
bitmap.bmHeight = image->image()->rows;
bitmap.bmWidthBytes = ((bitmap.bmWidth * 24 + 31) / 32) * 4;
bitmap.bmPlanes = 1;
bitmap.bmBitsPixel = 24;
bitmap.bmBits = NULL;

const size_t length = bitmap.bmWidthBytes * bitmap.bmHeight;
auto buffer = (HANDLE)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, length);

RGBTRIPLE *bitmap_bits = (RGBTRIPLE *) GlobalLock((HGLOBAL) buffer);
register RGBTRIPLE *q = bitmap_bits;

for (size_t y = 0; y < image->image()->rows; y++)
{
    register auto p = GetVirtualPixels(image->image(), 0, y, image->image()->columns, 1, exception);
    if (!p) break;

    for (size_t x = 0; x < image->image()->columns; x++)
    {
        q->rgbtRed = ScaleQuantumToChar(GetPixelRed(image->image(), p));
        q->rgbtGreen = ScaleQuantumToChar(GetPixelGreen(image->image(), p));
        q->rgbtBlue = ScaleQuantumToChar(GetPixelBlue(image->image(), p));

        p += GetPixelChannels(image->image());
        q++;
    }
}

bitmap.bmBits = bitmap_bits;
HBITMAP hbmp = CreateBitmapIndirect(&bitmap);

Но похоже, что этот код не может создать корректное растровое изображение. Что я делаю не так?

1 Ответ

1 голос
/ 05 июня 2019

Вы не учитываете шаг / выравнивание. Каждая строка должна быть выровнена по DWORD.

Расчет шага поверхности

В несжатом растровом изображении шаг - это количество байтов, необходимое для перехода от начала одной строки пикселей к началу следующей строки. Формат изображения определяет минимальный шаг для изображения. Кроме того, графическое оборудование может потребовать большего шага для поверхности, содержащей изображение. Для несжатых форматов RGB минимальным шагом всегда является ширина изображения в байтах, округленная до ближайшего DWORD. Вы можете использовать следующую формулу для расчета шага:

stride = ((((biWidth * biBitCount) + 31) & ~31) >> 3)

Вам необходимо исправить способ доступа к RGBTRIPLE в буфере.

Перед "x loop" вы должны сделать что-то вроде q = (RGBTRIPLE*) (((char*)bitmap_bits) + (y * bitmap.bmWidthBytes));

CreateBitmapIndirect создает DDB, который, возможно, не лучший выбор, вместо этого создайте DIB:

#define CalcStride(w, bpp) ( ((((w) * (bpp)) + 31) & ~31) >> 3 )
static void SetPixel24(UINT w, void*bits, UINT x, UINT y, COLORREF cr)
{
    RGBTRIPLE*p = ((RGBTRIPLE*) ( ((char*)bits) + (y * CalcStride(w, 24)) )) + x;
    p->rgbtRed = GetRValue(cr);
    p->rgbtGreen = GetGValue(cr);
    p->rgbtBlue = GetBValue(cr);
}
void Silly24BPPExample()
{
    HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, WC_STATIC, 0, WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_OVERLAPPEDWINDOW|SS_BITMAP|SS_REALSIZECONTROL, 0, 0, 99, 99, 0, 0, 0, 0);
    const INT w = 4, h = 4, bpp = 24;
    BITMAPINFO bi;
    ZeroMemory(&bi, sizeof(bi));
    BITMAPINFOHEADER&bih = bi.bmiHeader;
    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = w, bih.biHeight = -h;
    bih.biPlanes = 1, bih.biBitCount = bpp;
    bih.biCompression = BI_RGB;
    void*bits;
    HBITMAP hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
    for (UINT x = 0; x < w; ++x)
        for (UINT y = 0; y < h; ++y)
            SetPixel24(w, bits, x, y, RGB(255, 0, 0)); // All red
    SetPixel24(w, bits, 0, 0, RGB(0, 0, 255)); // except one blue
    SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBmp);
    for (MSG msg; IsWindow(hWnd) && GetMessage(&msg, 0, 0, 0); ) DispatchMessage(&msg);
    // DeleteObject(...)
}
...