4

Microsoft's Bitmap Compression documentation, specifically the description of BI_RLE8 (8-bit indexed color using run-length encoding compression), includes the following description of absolute mode:

In absolute mode, the first byte is zero and the second byte is a value in the range 03H through FFH. The second byte represents the number of bytes that follow, each of which contains the color index [into a color table] of a single pixel. [...] In absolute mode, each run must be aligned on a word boundary.

Presumably, this means that runs with an odd-numbered length must be zero-padded (although not specified, I assume words are 16 bits long in this context).

However, the example in the documentation includes a run with an odd-numbered length that doesn't end on a word boundary:

[03 04] [05 06] [00 03 45 56 67] [02 78] [00 02 05 01] [02 78] [00 00] [09 1E] [00 01] 

Should 00 03 45 56 67 be 00 03 45 56 67 00?

Reign of Error
  • 577
  • 5
  • 18
  • Don't they mean that it has to *start* on a word-aligned address (in uncompressed form)? It's certainly vague, but that would be true in the example. – harold Sep 21 '17 at 17:30
  • I've just [created a pull request](https://github.com/MicrosoftDocs/win32/pull/956) for this issue and it has been fixed. – MyNameIsTrez Aug 04 '21 at 16:50

2 Answers2

4

I modified an 8-bit indexed color bitmap and the results appears to indicate that runs should indeed be zero-padded to end on a 16-bit word boundary (so 00 03 45 56 67 should be 00 03 45 56 67 00).

Details:

  • In Microsoft Paint (version 1607, included with Windows 10), I created a 4x4 bitmap containing red (R), green (G), and blue (B) pixels in the following arrangement:

    BRBR
    BBBB
    GGBB
    RRRG
    
  • I saved the file as a 256-color bitmap (there was no option to use indexed color or run-length encoding).
  • I opened the file in the Gnu Image Manipulation Program (GIMP), changed the color mode to indexed (via Image > Mode > Indexed), and exported it as a "Windows BMP", checking off the "Run-Length Encoded" option.
  • I made a copy of the file and opened it in HxD (a hex editor).
  • At this stage, the pixel data were represented as follows (note that, per the documentation, the image starts in the bottom-left and proceeds left to right):

    03 4F // Three red pixels
    01 71 // One green pixel
    00 00 // EOL
    00 04 71 71 E8 E8 // GGBB
    00 00 // etc.
    04 E8
    00 00
    00 04 E8 4F E8 4F
    00 01 // EOF
    
  • At the start of the pixel data region, I replaced 03 4F (three red pixels specified in encoded mode; note that red was mapped to index 0x4F = 79 in the color table) with 00 03 4F 4F 4F (three red pixels specified in absolute mode with no zero padding), updated the file size in the header, and saved the changes.
  • When opened in both GIMP and Paint, the modified file was rendered incorrectly: the pixels in the first two rows and the bottom-right pixel were black, and the last two pixels in the third row were green instead of blue.
  • In HxD, I changed the 00 03 4F 4F 4F to 00 03 4F 4F 4F 00 (three red pixels specified in absolute mode, with a zero byte added to end the run on a 16-bit word boundary), updated the file size in the header, and saved the file.
  • When opened in GIMP, the file was now rendered correctly. When opened in Paint, the top row was black (I suspect I neglected to update a header field that is used by Paint but not GIMP), but otherwise the image was correct.
Reign of Error
  • 577
  • 5
  • 18
  • 2
    I tested this pretty thoroughly with a program in Rust and checking the generated image in mspaint. It is correct. Any run that is odd has to pad an extra byte. This makes every segment of an RLE pixel array an even number of bytes long, so no padding is ever needed at the start of a run. – DanielV Feb 03 '20 at 18:10
3

To strengthen the claim in the question and the existing answer I have found a document (http://www.martinreddy.net/gfx/2d/BMP.txt) that contains a certain BMP format specification.

Funnily enough the same example is shown there, except that the correct padding byte is added (below is the quote):

Absolute mode is signaled by the first byte in the pair being set to zero and
the second byte to a value between 0x03 and 0xFF. The second byte represents
the number of bytes that follow, each of which contains the color index of a
single pixel. Each run must be aligned on a word boundary.  Following is an
example of an 8-bit RLE bitmap (the two-digit hexadecimal values in the
second column represent a color index for a single pixel):

Compressed data     Expanded data

03 04               04 04 04 
05 06               06 06 06 06 06 
00 03 45 56 67 00   45 56 67 
02 78               78 78 
00 02 05 01         Move 5 right and 1 down 
02 78               78 78 
00 00               End of line 
09 1E               1E 1E 1E 1E 1E 1E 1E 1E 1E 
00 01               End of RLE bitmap 
argaz
  • 1,458
  • 10
  • 15