embedded-graphics / embedded-graphics/tinybmp

Can't read some 16-bit bitmaps

Open
#54 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
34
Forks
13
PR merge metrics
No merged PRs in 30d

Description

I tried to create a 16-bit bitmap and searched for an online converter. I found , which can convert to RGB565. But the file produced by it couldn't be read by `tinybmp`: I got `ParseError::UnexpectedEndOfFile`. Because of that, I later converted to RGB565 using GIMP, which worked. But since the format produced by online-converting.com seems to be valid according to my inspection of it using [ImHex](https://github.com/WerWolv/ImHex), which can decode and visualize the bitmap struct fields, and because XnView can display it, I'm posting this issue.

How to create an RGB565 bitmap with online-converting.com:

- Go to .
- Deselect the checkbox "Automatically start" at the bottom of the form *before uploading any file*.
- In the "Color" drop-down box, choose "16 (5:6:5, RGB Hi color)".
- Upload a file.
- Click "Convert".

Here's an image I converted: [5x1px-16bpp-rgb565.bmp](https://github.com/user-attachments/files/27157730/5x1px-16bpp-rgb565.bmp).

The file uses the `BITMAPINFOHEADER`. For it's `biCompression` member, which contains `BI_BITFIELDS`, Microsoft [documents](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader):

> Value | Meaning
> -- | --
> BI_RGB | Uncompressed RGB.
> BI_BITFIELDS | Uncompressed RGB with color masks. Valid for 16-bpp and 32-bpp bitmaps.
>
> [...]
>
> For 16-bpp bitmaps, if biCompression equals BI_RGB, the format is always RGB 555. If biCompression equals BI_BITFIELDS, the format is either RGB 555 or RGB 565. Use the subtype GUID in the [AM_MEDIA_TYPE](https://learn.microsoft.com/en-us/windows/desktop/api/strmif/ns-strmif-am_media_type) structure to determine the specific RGB type.

Since we're not decoding video and there's no `AM_MEDIA_TYPE`, this isn't very helpful. But there are hints about how to handle `BI_BITFIELDS` when only having `BITMAPINFOHEADER`:

- In an old Windows CE [documentation](https://learn.microsoft.com/en-us/previous-versions/windows/embedded/aa452885%28v=msdn.10%29) for `BITMAPINFOHEADER`, it says under `biCompression`:

> Value | Description
> -- | --
> BI_RGB | An uncompressed format.
> BI_BITFIELDS | Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components of each pixel. This is valid when used with 16- and 32-bpp bitmaps.

- In the ["Color table"](https://en.wikipedia.org/wiki/BMP_file_format#Color_table) section of Wikipedia's "BMP file format" article, its says:

> The color table (palette) occurs in the BMP image file directly after the BMP file header, the DIB header, and after the optional three or four bitmasks if the `BITMAPINFOHEADER` header with `BI_BITFIELDS` (12 bytes) or `BI_ALPHABITFIELDS` (16 bytes) option is used. Therefore, its offset is the size of the `BITMAPFILEHEADER` plus the size of the DIB header (plus optional 12-16 bytes for the three or four bit masks).

I didn't dive into your code in detail, but your `src/header/dib_header.rs` only knows `HeaderType::Info` and `HeaderType::V3` and not `HeaderType::V2`, and it has this bit of code:

```rust
let (_dib_header_data, channel_masks) = if header_type.is_at_least(HeaderType::V3)
&& matches!(compression_method, CompressionMethod::Bitfields)
{
let (dib_header_data, mask_red) = try_const!(le_u32(dib_header_data));
let (dib_header_data, mask_green) = try_const!(le_u32(dib_header_data));
let (dib_header_data, mask_blue) = try_const!(le_u32(dib_header_data));
let (dib_header_data, mask_alpha) = try_const!(le_u32(dib_header_data));

(
dib_header_data,
Some(ChannelMasks {
red: mask_red,
green: mask_green,
blue: mask_blue,
alpha: mask_alpha,
}),
)
} else {
(dib_header_data, None)
};
```

But the ["DIB header (bitmap information header)"](https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_%28bitmap_information_header%29) section of the Wikipedia article also lists a `BITMAPV2INFOHEADER`, which only added "RGB bit masks," whereas, the `BITMAPV3INFOHEADER` added the single additional "alpha channel bit mask." So, it seems, `tinybmp` needs to differentiate between just interpreting the first 3 vs. the first 4 `RGBQUAD`s from [`BITMAPINFO`](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo). Adding `HeaderType::V2` seems to be incorrect, because your code reads `dib_header_length`, which would still be `sizeof(BITMAPINFOHEADER)`. As a quick assessment, you'd need to remove the first part of the condition `if header_type.is_at_least(HeaderType::V3) && matches!(compression_method, CompressionMethod::Bitfields)` and then only retrieve `mask_alpha`, if `header_type.is_at_least(HeaderType::V3)` is true.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.