Skip to content

Commit 7ce18df

Browse files
authored
Merge pull request #2737 from telecos/refactor/bmp-rowread-byte-slice
Refactor BMP row decoding to work on byte slices
2 parents f4e08ff + cb86a22 commit 7ce18df

File tree

1 file changed

+131
-36
lines changed

1 file changed

+131
-36
lines changed

src/codecs/bmp/decoder.rs

Lines changed: 131 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,10 @@ use std::iter::{repeat, Rev};
55
use std::slice::ChunksExactMut;
66
use std::{error, fmt};
77

8-
use byteorder_lite::{LittleEndian, ReadBytesExt};
9-
108
use crate::color::ColorType;
119
use crate::error::{
1210
DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
1311
};
14-
use crate::io::ReadExt;
1512
use crate::{ImageDecoder, ImageFormat};
1613

1714
const BITMAPCOREHEADER_SIZE: u32 = 12;
@@ -428,6 +425,21 @@ fn calculate_row_padding(bytes_per_row: usize) -> usize {
428425
(4 - (bytes_per_row % 4)) % 4
429426
}
430427

428+
/// Allocate a row buffer with OOM protection.
429+
fn allocate_row_buffer(size: usize) -> ImageResult<Vec<u8>> {
430+
let mut buffer = vec_try_with_capacity(size).map_err(|_| {
431+
ImageError::Unsupported(UnsupportedError::from_format_and_kind(
432+
ImageFormat::Bmp.into(),
433+
UnsupportedErrorKind::GenericFeature(format!(
434+
"Row buffer allocation ({} bytes) too large",
435+
size
436+
)),
437+
))
438+
})?;
439+
buffer.resize(size, 0);
440+
Ok(buffer)
441+
}
442+
431443
/// Convenience function to check if the combination of width, length and number of
432444
/// channels would result in a buffer that would overflow.
433445
fn check_for_overflow(width: i32, length: i32, channels: usize) -> ImageResult<()> {
@@ -663,6 +675,54 @@ impl Bitfields {
663675
}
664676
}
665677

678+
/// Helper to read RLE data using the already-buffered reader.
679+
/// Avoids double-buffering since BmpDecoder already requires BufRead.
680+
struct RleReader<'a, R> {
681+
reader: &'a mut R,
682+
}
683+
684+
impl<'a, R: BufRead> RleReader<'a, R> {
685+
fn new(reader: &'a mut R) -> Self {
686+
Self { reader }
687+
}
688+
689+
fn read_byte(&mut self) -> io::Result<u8> {
690+
let buf = self.reader.fill_buf()?;
691+
if buf.is_empty() {
692+
return Err(io::Error::new(
693+
io::ErrorKind::UnexpectedEof,
694+
"unexpected end of RLE data",
695+
));
696+
}
697+
let byte = buf[0];
698+
self.reader.consume(1);
699+
Ok(byte)
700+
}
701+
702+
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
703+
let mut remaining = buf.len();
704+
let mut offset = 0;
705+
706+
while remaining > 0 {
707+
let available = self.reader.fill_buf()?;
708+
if available.is_empty() {
709+
return Err(io::Error::new(
710+
io::ErrorKind::UnexpectedEof,
711+
"unexpected end of RLE data",
712+
));
713+
}
714+
715+
let to_read = remaining.min(available.len());
716+
buf[offset..offset + to_read].copy_from_slice(&available[..to_read]);
717+
self.reader.consume(to_read);
718+
offset += to_read;
719+
remaining -= to_read;
720+
}
721+
722+
Ok(())
723+
}
724+
}
725+
666726
/// A bmp decoder
667727
pub struct BmpDecoder<R> {
668728
reader: R,
@@ -686,11 +746,11 @@ pub struct BmpDecoder<R> {
686746
icc_profile: Option<Vec<u8>>,
687747
}
688748

689-
enum RLEInsn {
749+
enum RLEInsn<'a> {
690750
EndOfFile,
691751
EndOfRow,
692752
Delta(u8, u8),
693-
Absolute(u8, Vec<u8>),
753+
Absolute(u8, &'a [u8]),
694754
PixelRun(u8, u8),
695755
}
696756

@@ -1206,8 +1266,6 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
12061266
bitfields: Option<&Bitfields>,
12071267
) -> ImageResult<()> {
12081268
let num_channels = self.num_channels();
1209-
let row_padding_len = self.width as usize % 2 * 2;
1210-
let row_padding = &mut [0; 2][..row_padding_len];
12111269
let bitfields = match bitfields {
12121270
Some(b) => b,
12131271
None => self.bitfields.as_ref().unwrap(),
@@ -1216,15 +1274,27 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
12161274

12171275
reader.seek(SeekFrom::Start(self.data_offset))?;
12181276

1277+
// Calculate row size in bytes (2 bytes per pixel + padding)
1278+
let row_data_len = self.width as usize * 2;
1279+
let row_padding_len = calculate_row_padding(row_data_len);
1280+
let total_row_len = row_data_len + row_padding_len;
1281+
1282+
let mut row_buffer = allocate_row_buffer(total_row_len)?;
1283+
12191284
with_rows(
12201285
buf,
12211286
self.width,
12221287
self.height,
12231288
num_channels,
12241289
self.top_down,
12251290
|row| {
1226-
for pixel in row.chunks_mut(num_channels) {
1227-
let data = u32::from(reader.read_u16::<LittleEndian>()?);
1291+
reader.read_exact(&mut row_buffer)?;
1292+
1293+
for (row_data, pixel) in row_buffer
1294+
.chunks_exact(2)
1295+
.zip(row.chunks_exact_mut(num_channels))
1296+
{
1297+
let data = u32::from(u16::from_le_bytes(row_data.try_into().unwrap()));
12281298

12291299
pixel[0] = bitfields.r.read(data);
12301300
pixel[1] = bitfields.g.read(data);
@@ -1237,7 +1307,7 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
12371307
}
12381308
}
12391309
}
1240-
reader.read_exact(row_padding)
1310+
Ok(())
12411311
},
12421312
)?;
12431313

@@ -1253,15 +1323,25 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
12531323
let reader = &mut self.reader;
12541324
reader.seek(SeekFrom::Start(self.data_offset))?;
12551325

1326+
// Calculate row size in bytes (4 bytes per pixel, no padding for 32-bit)
1327+
let row_data_len = self.width as usize * 4;
1328+
1329+
let mut row_buffer = allocate_row_buffer(row_data_len)?;
1330+
12561331
with_rows(
12571332
buf,
12581333
self.width,
12591334
self.height,
12601335
num_channels,
12611336
self.top_down,
12621337
|row| {
1263-
for pixel in row.chunks_mut(num_channels) {
1264-
let data = reader.read_u32::<LittleEndian>()?;
1338+
reader.read_exact(&mut row_buffer)?;
1339+
1340+
for (row_data, pixel) in row_buffer
1341+
.chunks_exact(4)
1342+
.zip(row.chunks_exact_mut(num_channels))
1343+
{
1344+
let data = u32::from_le_bytes(row_data.try_into().unwrap());
12651345

12661346
pixel[0] = bitfields.r.read(data);
12671347
pixel[1] = bitfields.g.read(data);
@@ -1288,46 +1368,55 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
12881368
format: &FormatFullBytes,
12891369
) -> ImageResult<()> {
12901370
let num_channels = self.num_channels();
1371+
let row_data_len = match *format {
1372+
FormatFullBytes::RGB24 => self.width as usize * 3,
1373+
FormatFullBytes::Format888 => self.width as usize * 4,
1374+
FormatFullBytes::RGB32 | FormatFullBytes::RGBA32 => self.width as usize * 4,
1375+
};
12911376
let row_padding_len = match *format {
1292-
FormatFullBytes::RGB24 => calculate_row_padding(self.width as usize * 3),
1377+
FormatFullBytes::RGB24 => calculate_row_padding(row_data_len),
12931378
_ => 0,
12941379
};
1295-
let row_padding = &mut [0; 4][..row_padding_len];
1380+
let total_row_len = row_data_len + row_padding_len;
12961381

12971382
self.reader.seek(SeekFrom::Start(self.data_offset))?;
12981383

12991384
let reader = &mut self.reader;
13001385

1386+
let mut row_buffer = allocate_row_buffer(total_row_len)?;
1387+
13011388
with_rows(
13021389
buf,
13031390
self.width,
13041391
self.height,
13051392
num_channels,
13061393
self.top_down,
13071394
|row| {
1308-
for pixel in row.chunks_mut(num_channels) {
1309-
if *format == FormatFullBytes::Format888 {
1310-
reader.read_u8()?;
1311-
}
1395+
reader.read_exact(&mut row_buffer)?;
13121396

1313-
// Read the colour values (b, g, r).
1314-
// Reading 3 bytes and reversing them is significantly faster than reading one
1315-
// at a time.
1316-
reader.read_exact(&mut pixel[0..3])?;
1317-
pixel[0..3].reverse();
1397+
for (i, pixel) in row.chunks_mut(num_channels).enumerate() {
1398+
let offset = match *format {
1399+
FormatFullBytes::Format888 => i * 4 + 1, // Skip first byte
1400+
_ => {
1401+
i * match *format {
1402+
FormatFullBytes::RGB24 => 3,
1403+
_ => 4,
1404+
}
1405+
}
1406+
};
13181407

1319-
if *format == FormatFullBytes::RGB32 {
1320-
reader.read_u8()?;
1321-
}
1408+
// Read the colour values (b, g, r) and reverse to (r, g, b)
1409+
pixel[0..3].copy_from_slice(&row_buffer[offset..offset + 3]);
1410+
pixel[0..3].reverse();
13221411

13231412
// Read the alpha channel if present
13241413
if *format == FormatFullBytes::RGBA32 {
1325-
reader.read_exact(&mut pixel[3..4])?;
1414+
pixel[3] = row_buffer[offset + 3];
13261415
} else if num_channels == 4 {
13271416
pixel[3] = ALPHA_OPAQUE;
13281417
}
13291418
}
1330-
reader.read_exact(row_padding)
1419+
Ok(())
13311420
},
13321421
)?;
13331422

@@ -1347,23 +1436,30 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
13471436
// two rows.
13481437
let mut row_iter = self.rows(buf);
13491438

1439+
// Wrap reader in buffered RLE reader for efficient byte-by-byte access
1440+
let mut rle_reader = RleReader::new(&mut self.reader);
1441+
1442+
// Pre-allocate buffer for RLE absolute mode (max 256 bytes)
1443+
let mut rle_indices_buffer = [0u8; 256];
1444+
13501445
while let Some(row) = row_iter.next() {
13511446
let mut pixel_iter = row.chunks_exact_mut(num_channels);
13521447

13531448
let mut x = 0;
13541449
loop {
13551450
let instruction = {
1356-
let control_byte = self.reader.read_u8()?;
1451+
let control_byte = rle_reader.read_byte()?;
1452+
13571453
match control_byte {
13581454
RLE_ESCAPE => {
1359-
let op = self.reader.read_u8()?;
1455+
let op = rle_reader.read_byte()?;
13601456

13611457
match op {
13621458
RLE_ESCAPE_EOL => RLEInsn::EndOfRow,
13631459
RLE_ESCAPE_EOF => RLEInsn::EndOfFile,
13641460
RLE_ESCAPE_DELTA => {
1365-
let xdelta = self.reader.read_u8()?;
1366-
let ydelta = self.reader.read_u8()?;
1461+
let xdelta = rle_reader.read_byte()?;
1462+
let ydelta = rle_reader.read_byte()?;
13671463
RLEInsn::Delta(xdelta, ydelta)
13681464
}
13691465
_ => {
@@ -1372,14 +1468,13 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
13721468
length = length.div_ceil(2);
13731469
}
13741470
length += length & 1;
1375-
let mut buffer = Vec::new();
1376-
self.reader.read_exact_vec(&mut buffer, length)?;
1377-
RLEInsn::Absolute(op, buffer)
1471+
rle_reader.read_exact(&mut rle_indices_buffer[..length])?;
1472+
RLEInsn::Absolute(op, &rle_indices_buffer[..length])
13781473
}
13791474
}
13801475
}
13811476
_ => {
1382-
let palette_index = self.reader.read_u8()?;
1477+
let palette_index = rle_reader.read_byte()?;
13831478
RLEInsn::PixelRun(control_byte, palette_index)
13841479
}
13851480
}

0 commit comments

Comments
 (0)