0

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;
}

enter image description here

5
  • 1
    FYI, you are programming in C++ because the C language doesn't have the :: operator. Adjust your tags accordingly. Commented May 30, 2018 at 0:53
  • @ThomasMatthews done.. Commented May 30, 2018 at 0:55
  • What type is bitmap? You're not passing it into the function. Also what type is m_FrameBuffer? Commented May 30, 2018 at 2:09
  • FWIW, SDL have SDL_LoadBMP Commented May 30, 2018 at 6:21
  • @Mark Ransom bitmap is uint8_t, framebuffer is uint16_t Commented May 30, 2018 at 9:05

1 Answer 1

6

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;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, I get an exception, please look at the edited post
It seems your problem remains. I don't know why clicked the accept button. See edit on how to convert 24-bit to 16-bit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.