简体   繁体   中英

Display bitmap graphics on a microcontroller using C++

I'm using a bitmap 888 to 565 format in hex format. So I'm trying to display the bitmap on a simulator that uses SDL, with frame buffer resoultion is 16bit.

one of the bitmap data ( first row ) looks like that

0x42, 0x4D, 0xFE, 0x82, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x7B, 0x0, 0x0, 0x0, 0x5A, 0x0, 0x0, 0x0, 0x1, 0x0, 0x18, 0x0, 0x0, 0x0, 

Now I'm trying to draw that bitmap using C++ on SDL, but I get garbage image with scan lines, looks like the pitch is not calculated correctly.

void Rasterizer::DrawBitmap(int w, int h, int x, int y, int transparent)
{
    if (!bitmap)
        return;

    const uint8_t bytesPerPixel = 2;
    uint16_t bytesPerRow = (bytesPerPixel * h ); // bytes Per Row including padding to 4 byte row boundary
    uint16_t paddingSize = bytesPerRow - (bytesPerPixel * w); // paddingSize for each row
    uint16_t pixel;
    uint16_t row, column;


    for (row = 0; row < h; row++) {
        for (column = 0; column < w; column++) {
            pixel = bitmap[row + column* bytesPerRow]<<8;
            pixel |= bitmap[1+row + column* bytesPerRow] & 0xFF;
            SetPixel(x+column, y+row, pixel);
        }
    }

}



void Rasterizer::SetPixel(int x, int y, uint16_t color)
{
    m_FrameBuffer[y * m_Width + x] = color;
}

在此处输入图片说明

0x42, 0x4D

The first 2 bytes are B and M , that's just the bitmap file header which is 54 bytes in total. It's not part of the first row.

The size is 0x7B x 0x5A pixels

Towards the end you have 0x18 0x00 which is 24, for 24-bit bitmap, not 16-bit

So you have to skip 54 byte, and read as 24-bit

int width_in_bytes = ((width * 24 + 31) / 32) * 4 * height;
for(int row = height - 1; row >= 0; row--)
{
    for(int col = 0; col < width; col++)
    {
        int i = row * width_in_bytes + col * 3;
        unsigned char blu = bitmap[54 + i + 0];
        unsigned char grn = bitmap[54 + i + 1];
        unsigned char red = bitmap[54 + i + 2];
        int pixel = red | ((uint16_t)grn << 8) | ((uint32_t)blu << 16);
        SetPixel(row, col, pixel);
    }
}

If the device is expecting 16-bit bitmap, then try to obtain 16-bit bitmap in the first place. For example when taking screen shot, Windows allows 16-bit format.

SDL supports SDL_PIXELFORMAT_RGB565 as well. GDI+ is another option if you are coding in Windows.

If your source bitmap is 24-bit, and you want to convert to 16-bit 565 format, write the formula based on the MCVE below

24-bit bitmap has color range from 0-255, whereas 16-bit has color range from 0-31 (0-63 for green in the case of 565 format). You have to normalize the color, for example by multiplying the red value by 31/255. And then shift the values to put in 16-bit integer.

16-bit bitmap format expects 3 colors (a total of 12 bytes) before the pixels start. These colors contain information about 565 format.

#include <Windows.h>
#include <stdint.h>
#include <iostream>
#include <fstream>
#include <vector>

int main()
{
    HBITMAP hbitmap = (HBITMAP)LoadImage(NULL, "24bit.bmp",
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
    if(!hbitmap)
        return 0;

    BITMAP bm;
    GetObject(hbitmap, sizeof(bm), &bm);
    if(bm.bmBitsPixel != 24)
    {
        DeleteObject(hbitmap);
        std::cout << "Expecting 24-bit bitmap\n";
        return 0;
    }

    BYTE *source = (BYTE*)bm.bmBits;
    int w = bm.bmWidth;
    int h = bm.bmHeight;

    //calculate width in bytes (wb) for source and destination
    DWORD wb_src = ((w * 24 + 31) / 32) * 4;
    DWORD wb_dst = ((w * 16 + 31) / 32) * 4;

    int size = wb_dst * h;
    std::vector<BYTE> dest(size);
    for(int r = 0; r < h; r++)
    {
        for(int c = 0; c < w; c++)
        {
            int src = r * wb_src + c * 3;
            int dst = r * wb_dst + c * 2;

            uint16_t blu = (uint16_t)(source[src + 0] * 31.f / 255.f);
            uint16_t grn = (uint16_t)(source[src + 1] * 63.f / 255.f);
            uint16_t red = (uint16_t)(source[src + 2] * 31.f / 255.f);

            uint16_t res = (red) | (grn << 5) | (blu << 11);
            memcpy(&dest[dst], &res, 2);
        }
    }

    //prepare header files for 16-bit file
    BITMAPINFOHEADER bi = { sizeof(bi), w, h, 1, 16, BI_BITFIELDS };
    BITMAPFILEHEADER bf = { (WORD)'MB', 54 + 12 + wb_dst * h, 0, 0, 54 };

    std::ofstream of("16bit.bmp", std::ios::binary);
    if(of)
    {
        //add file header
        of.write((char*)&bf, sizeof(bf));
        of.write((char*)&bi, sizeof(bi));

        //color table
        COLORREF c1 = 31;
        COLORREF c2 = 63 << 5;
        COLORREF c3 = 31 << 11;
        of.write((char*)&c1, 4);
        of.write((char*)&c2, 4);
        of.write((char*)&c3, 4);

        //add pixels
        of.write((char*)&dest[0], dest.size());
    }

    DeleteObject(hbitmap);

    return 0;
}

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