Skip to content

Commit c402bf0

Browse files
authored
Merge pull request #2762 from telecos/feature/bmp-alphabitfields
Implement support for alphabitfields in BMP
2 parents 5d0418d + 27b99ee commit c402bf0

File tree

3 files changed

+36
-17
lines changed

3 files changed

+36
-17
lines changed

src/codecs/bmp/decoder.rs

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,15 @@ enum FormatFullBytes {
388388
Format888,
389389
}
390390

391+
/// Compression type for bitfield-based formats.
392+
#[derive(PartialEq, Copy, Clone)]
393+
enum BitfieldCompression {
394+
/// BI_BITFIELDS: RGB masks only (3 masks, 12 bytes after header).
395+
Rgb,
396+
/// BI_ALPHABITFIELDS: RGBA masks (4 masks, 16 bytes after header).
397+
Rgba,
398+
}
399+
391400
enum Chunker<'a> {
392401
FromTop(ChunksExactMut<'a, u8>),
393402
FromBottom(Rev<ChunksExactMut<'a, u8>>),
@@ -1126,12 +1135,11 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
11261135
/// Read BITMAPINFOHEADER <https://msdn.microsoft.com/en-us/library/vs/alm/dd183376(v=vs.85).aspx>
11271136
/// or BITMAPV{2|3|4|5}HEADER.
11281137
///
1129-
/// returns Err if any of the values are invalid.
1130-
fn read_bitmap_info_header(&mut self) -> ImageResult<()> {
1138+
/// Returns the bitfield compression type or Err if any of the values are invalid.
1139+
fn read_bitmap_info_header(&mut self) -> ImageResult<BitfieldCompression> {
11311140
// Info header (after size field): 36 bytes minimum
11321141
let mut buffer = [0u8; 36];
11331142
self.reader.read_exact(&mut buffer)?;
1134-
11351143
let parsed = ParsedInfoHeader::parse(&buffer)?;
11361144

11371145
self.width = parsed.width;
@@ -1147,15 +1155,21 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
11471155

11481156
check_for_overflow(self.width, self.height, self.num_channels())?;
11491157

1150-
Ok(())
1158+
let compression = match parsed.compression {
1159+
BI_ALPHABITFIELDS => BitfieldCompression::Rgba,
1160+
_ => BitfieldCompression::Rgb,
1161+
};
1162+
Ok(compression)
11511163
}
11521164

1153-
fn read_bitmasks(&mut self) -> ImageResult<()> {
1154-
// Determine if we need to read alpha mask
1165+
fn read_bitmasks(&mut self, compression: BitfieldCompression) -> ImageResult<()> {
1166+
// Determine if we need to read alpha mask:
1167+
// - V3/V4/V5 headers have the alpha mask embedded in the header
1168+
// - BI_ALPHABITFIELDS compression has a 4th mask after the header
11551169
let has_alpha = matches!(
11561170
self.bmp_header_type,
11571171
BMPHeaderType::V3 | BMPHeaderType::V4 | BMPHeaderType::V5
1158-
);
1172+
) || compression == BitfieldCompression::Rgba;
11591173

11601174
// Read bitfield masks into buffer
11611175
let buffer_size = if has_alpha { 16 } else { 12 };
@@ -1337,35 +1351,40 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
13371351
}
13381352
};
13391353

1340-
match self.bmp_header_type {
1354+
let bitfield_compression = match self.bmp_header_type {
13411355
BMPHeaderType::Core => {
13421356
self.read_bitmap_core_header()?;
1357+
BitfieldCompression::Rgb
13431358
}
13441359
BMPHeaderType::Info
13451360
| BMPHeaderType::V2
13461361
| BMPHeaderType::V3
13471362
| BMPHeaderType::V4
1348-
| BMPHeaderType::V5 => {
1349-
self.read_bitmap_info_header()?;
1350-
}
1351-
}
1363+
| BMPHeaderType::V5 => self.read_bitmap_info_header()?,
1364+
};
13521365

13531366
let mut bitmask_bytes_offset = 0;
1354-
if self.image_type == ImageType::Bitfields16 || self.image_type == ImageType::Bitfields32 {
1355-
self.read_bitmasks()?;
1367+
if matches!(
1368+
self.image_type,
1369+
ImageType::Bitfields16 | ImageType::Bitfields32
1370+
) {
1371+
self.read_bitmasks(bitfield_compression)?;
13561372

13571373
// Per https://learn.microsoft.com/en-us/windows/win32/gdi/bitmap-header-types, bitmaps
13581374
// using the `BITMAPINFOHEADER`, `BITMAPV4HEADER`, or `BITMAPV5HEADER` structures with
1359-
// an image type of `BI_BITFIELD` contain RGB bitfield masks immediately after the header.
1375+
// an image type of `BI_BITFIELD` or `BI_ALPHABITFIELDS` contain bitfield masks
1376+
// immediately after the header.
13601377
//
13611378
// `read_bitmasks` correctly reads these from earlier in the header itself but we must
13621379
// ensure the reader starts on the image data itself, not these extra mask bytes.
13631380
if matches!(
13641381
self.bmp_header_type,
13651382
BMPHeaderType::Info | BMPHeaderType::V4 | BMPHeaderType::V5
13661383
) {
1367-
// This is `size_of::<u32>() * 3` (a red, green, and blue mask), but with less noise.
1368-
bitmask_bytes_offset = 12;
1384+
bitmask_bytes_offset = match bitfield_compression {
1385+
BitfieldCompression::Rgba => 16, // 4 masks * 4 bytes
1386+
BitfieldCompression::Rgb => 12, // 3 masks * 4 bytes
1387+
};
13691388
}
13701389
};
13711390

31.8 KB
Binary file not shown.
2.92 KB
Loading

0 commit comments

Comments
 (0)