🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Question about enlarging a bitmap.

Started by
20 comments, last by Josheir 4 years, 4 months ago

Well, with a little research I got the bitmap to create so that it looks right. I used 48 as out pitch and 24 as in pitch. I am still a little confused by the “dump." (if that is the correct terminology.) It doesn't seem right, but the bitmap looks right. I am still using the same clumsy couts with numbers. Could you explain how this is correct?

Here is the dump:

Thanks.

I am still confused about the padding, it seems as though if something is a multiple of four than there is no need for it.

Advertisement

Well, the bitmap data is okay now. However I would still like an easy description of where the padding goes, and when I need it, especially with 24 bit images.

https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header

[hex-dump of 8x8, split in the fields as described by the web page.]

00000000: 42 4d

"BM", so some windows image.

       2: f6 12 00 00

size: 0x000012f6

       6: 00 00
       8: 00 00

2 reserved fields

       A: 36 00 00 00

offset to the image data 0x00000036

       E: 28 00
00000010: 00 00

DIB header size: 0x28 = 2*16+8=40 -> BITMAPINFOHEADER


      12: 28 00 00 00

width: 0x28 = 40 pixels

      16: 28 00 00 00

height: 0x28 = 40 pixels

      1A: 01 00

number of color planes: 1

      1C: 18 00

bits per pixel: 0x18 = 24

      1E: 00 00
00000020: 00 00

compression method 0: BI_RGB

      22: c0 12 00 00

raw image size: 0x12c0

      26: 00 00 00 00
      2A: 00 00 00 00
      2E: 00 00 00 00
      32: 00 00 00 00

no extra bit masks

      36: e8 a2 00 e8 a2 00 e8 a2 00 e8  ...

And we have arrived at offset 0x36.
Not sure how the header says there is no color table.
I assume we have arrived at the pixel data now.

https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage

A pixel is 24 bit, and we have 40 pixels width, so one row is 24*40 = 960 bit.
A row is always a multiple of 32 bit (ie multiple of 4 byte), so we may have
to add a few bits to get that.

The simple/stupid way to do that is

r = 960 // row storage length in bits
while (r % 32) != 0 do
    r := r + 1
end

The usual way to do that is using the floor and ceiling functions as described at the web-page, in code:

r = 32 * ((r+31) / 32), or
r = (r +31) & ~31

In this case, 960 is already a multiple of 32 bit, so no change.
For simplicity, convert the length to bytes, and add some other variables:

rb = r / 4 // row storage length in bytes
base = 0x36  // Base address of the row.
pw = 24/8 = 3 // number of bytes in a pixel
w = 40 // number of pixels in a row
h = 40 // number of rows

for y in 1..h:
    address = base
    for x in 1..w:
        // read pixel starting at 'address', length 'pw' bits ←- EDIT: bytes, not bits ?
        address = address + pw
    end
    // end of the row, jump to the next row
    base = base + rb
end

The file format is quite old, as your wiki link will tell you. The various paddings are in place to work better with assorted hardware spanning back to nearly four decades ago.

The bitmap was originally an actual bit map, color images came later. Modern iterations can contain jpeg and png data, which are themselves container formats that can contain significant additional data.

Well, this is hard to explain, but what I'm wondering is where do I put the padding and how much, please.

For example, take this example:

00 00 FF
FF FF FF
00 00
FF 00 00
00 FF 00
00 00

It looks like the padding is added to complete each 8 bytes. So, is this the only combination for 24 bit bitmaps? Not to include if the amount of bytes is a multiple of four, than padding is not needed.

Thanks,

Josheir

The padding is per row to full 32bit. The wikipedia entry (https://en.wikipedia.org/wiki/BMP_file_format)

or official docs show that too (https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader)

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Josheir said:
Well, this is hard to explain, but what I'm wondering is where do I put the padding and how much, please.

Always at the end, and such that a row is a multiple of 4 bytes (or 32 bit as I explained).

In my post, padding is between the end of the last pixel on a row (ie ‘address’ after the ‘x’ loop), and the start of the next row (ie ‘base + rb’)

void scale(const Image &in, Image &out)
{
    for (int y = 0; y < out.height; ++y) // each output row
    {
        for (int x = 0; x < out.width; ++x) // each output pixel
        {
            int sx = x * in.width / out.width;
            int sy = y * in.height / out.height;
            out.data[y * out.pitch + x * 3 + 0] = in.data[sy * in.pitch + sx * 3 + 0];
            out.data[y * out.pitch + x * 3 + 1] = in.data[sy * in.pitch + sx * 3 + 1];
            out.data[y * out.pitch + x * 3 + 2] = in.data[sy * in.pitch + sx * 3 + 2];
        }
    }
}

Could the above be explained please. What does sx / sy stand for and how do the pitches work? Thanks.

Josheir

So pitch here is the number of bytes per row including both the pixels and padding. Thus indexing multiplies of pitch are the starts of each row.

e.g. say you have a 10 pixel wide image in 24bit RGB (3 bytes), that makes each row of pixels 3*10 bytes, 30 bytes. Now the BMP spec says each row must be a multiple of 32bits (4 bytes). 30/4 is 7.5 so clearly not a multiple, but adding 2 extra bytes (30%4 == 2) giving 32bytes is a multiple (32/4 = 8). So that gives `width=10` and `pitch=32`. You might also see it referred to as the "stride".

Sorry, “sx”, “sy” were maybe not the best named variables for a quick example ? I was referring to “source x” and “source y”, while x,y is “destination x” and “destination y”..While "+ 0", "+ 1", "+ 2" is for the 3 8bit components of a pixel. You can see this on the 3 assignment lines, (x,y) is on the left side while (sx,sy) on the right side.

"x * in.width / out.width" lines being an integer scale I came up with really quickly that I believe should be correct for a simple nearest neighbour. e.g. say "in.width" is 50 and "out.width" is 150, for x starting from zero [0, 0, 0, 1, 1, 1, 2, 2, 2, .... 48, 48, 48, 49, 49, 49] so 3x scale up repeating each pixel 3 times, or for say "in.width" is 100 and "out.width" is 50, [0, 2, 4, 6, ... 94, 96, 98] so 2x scale down taking every other pixel.

This topic is closed to new replies.

Advertisement