0

First of all, I'm programming in the kernel context so no existing libraries exist. In fact this code is going to go into a library of my own.

Two questions, one more important than the other:

  1. As the title suggests, how can I efficiently render a 24-bpp image onto a 32-bpp device, assuming that I have the address of the frame buffer?

Currently I have this code:

void BitmapImage::Render24(uint16_t x, uint16_t y, void (*r)(uint16_t, uint16_t, uint32_t))
{
    uint32_t imght = Math::AbsoluteValue(this->DIB->GetBitmapHeight());
    uint64_t ptr = (uint64_t)this->ActualBMP + this->Header->BitmapArrayOffset;

    uint64_t rowsize = ((this->DIB->GetBitsPerPixel() * this->DIB->GetBitmapWidth() + 31) / 32) * 4;

    uint64_t oposx = x;
    uint64_t posx = oposx;
    uint64_t posy = y + (this->DIB->Type == InfoHeaderV1 && this->DIB->GetBitmapHeight() < 0 ? 0 : this->DIB->GetBitmapHeight());


    for(uint32_t d = 0; d < imght; d++)
    {
        for(uint32_t w = 0; w < rowsize / (this->DIB->GetBitsPerPixel() / 8); w++)
        {
            r(posx, posy, (*((uint32_t*)ptr) & 0xFFFFFF));
            ptr += this->DIB->GetBitsPerPixel() / 8;
            posx++;
        }
        posx = oposx;
        posy--;
    }
}

r is a function pointer to a PutPixel-esque thing that accepts x, y, and colour parameters. Obviously this code is terribly slow, since plotting pixels one at a time is never a good idea.

For my 32-bpp rendering code (which I also have a question about, more on that later) I can easily Memory::Copy() the bitmap array (I'm loading bmp files here) to the frame buffer. However, how do I do this with 24bpp images? On a 24bpp display this would be fine but I'm working with a 32bpp one.

One solution I can think of right now is to create another bitmap array which essentially contains values of 0x00(colour) and the use that to draw to the screen -- I don't think this is very good though, so I'm looking for a better alternative.

Next question: 2. Given, for obvious reasons, one cannot simply Memory::Copy() the entire array at once onto the frame buffer, the next best thing would be to copy them row by row.

Is there a better way?

zhiayang
  • 574
  • 1
  • 5
  • 14

1 Answers1

1

Basically something like this:

for (uint32_t l = 0; l < h; ++l) // l line index in pixels
{
    // srcPitch is distance between lines in bytes
    char*     srcLine = (char*)srcBuffer + l * srcPitch; 
    unsigned* trgLine = ((unsigned*)trgBuffer) + l * trgPitch;

    for (uint32_t c = 0; c < w; ++c) // c is column index in pixels
    {
        // build target pixel. arrange indexes to fit your render target (0, 1, 2)
        ++(*trgLine) = (srcLine[0] << 16) | (srcLine[1] << 8)
           | srcLine[2] | (0xff << 24);
        srcLine += 3;
    }
}

A few notes: - better to write to a different buffer than the render buffer so the image is displayed at once. - using functions for pixel placement like you did is very (very very) slow.

egur
  • 7,830
  • 2
  • 27
  • 47
  • I see, so this essentially means 'create another buffer'? Also I need to get around to implementing back buffering – zhiayang Dec 24 '13 at 19:45
  • You might be able to speed it up by processing 4 source pixels at once: 3 integer reads, which get transformed into 4 integer writes. And for back buffering, you will need a buffer anyway. For speed considerations, you'd want this in your target format. – Jongware Dec 24 '13 at 20:13
  • This is just the idea how it's done. You should optimize such low level code. This could also be done in more than 1 thread. – egur Dec 24 '13 at 20:16
  • @Jongware: Possibly, but you'll probably become memory-bound well before this. – Oliver Charlesworth Dec 24 '13 at 20:44