Skip to content

Commit 59a64c5

Browse files
committed
feat: use solid fills with rle compressed images
1 parent d61a220 commit 59a64c5

2 files changed

Lines changed: 136 additions & 76 deletions

File tree

src/lib.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ pub use header::CompressionMethod;
211211
pub use header::{Bpp, ChannelMasks, Header, RowOrder};
212212
pub use iter::Pixels;
213213
pub use raw_bmp::RawBmp;
214-
pub use raw_iter::{DynamicRawColors, RawColors, RawPixel, RawPixels, Rle4Colors, Rle8Colors};
214+
pub use raw_iter::{DynamicRawColors, RawColors, RawPixel, RawPixels, RleColors};
215+
216+
use crate::raw_iter::{Rle4Runs, Rle8Runs};
215217

216218
/// A BMP-format bitmap.
217219
///
@@ -266,7 +268,6 @@ where
266268
D: DrawTarget<Color = C>,
267269
{
268270
let area = self.bounding_box();
269-
let slice_size = Size::new(area.size.width, 1);
270271

271272
match self.raw_bmp.color_type {
272273
ColorType::Index1 => {
@@ -293,20 +294,29 @@ where
293294
let fallback_color = C::from(Rgb888::BLACK);
294295
if let Some(color_table) = self.raw_bmp.color_table() {
295296
if header.compression_method == CompressionMethod::Rle4 {
296-
let mut colors = Rle4Colors::new(&self.raw_bmp);
297+
let mut runs = Rle4Runs::new(&self.raw_bmp);
297298
let map_color = |color: RawU4| {
298299
color_table
299300
.get(color.into_inner() as u32)
300301
.map(Into::into)
301302
.unwrap_or(fallback_color)
302303
};
304+
303305
// RLE produces pixels in bottom-up order, so we draw them line by line rather than the entire bitmap at once.
304306
for y in (0..area.size.height).rev() {
305-
colors.start_row();
306-
307-
let row = Rectangle::new(Point::new(0, y as i32), slice_size);
308-
let colors = colors.by_ref().map(map_color);
309-
target.fill_contiguous(&row, colors.take(area.size.width as usize))?;
307+
runs.start_row();
308+
309+
let mut point = Point::new(0, y as i32);
310+
for (raw_color, count) in runs.by_ref() {
311+
let size = Size::new(count as u32, 1);
312+
let color = map_color(raw_color);
313+
target.fill_solid(&Rectangle::new(point, size), color)?;
314+
point.x += count as i32;
315+
316+
if point.x >= area.size.width as i32 {
317+
break;
318+
}
319+
}
310320
}
311321
Ok(())
312322
} else {
@@ -328,20 +338,29 @@ where
328338
let fallback_color = C::from(Rgb888::BLACK);
329339
if let Some(color_table) = self.raw_bmp.color_table() {
330340
if header.compression_method == CompressionMethod::Rle8 {
331-
let mut colors = Rle8Colors::new(&self.raw_bmp);
341+
let mut runs = Rle8Runs::new(&self.raw_bmp);
332342
let map_color = |color: RawU8| {
333343
color_table
334344
.get(color.into_inner() as u32)
335345
.map(Into::into)
336346
.unwrap_or(fallback_color)
337347
};
348+
338349
// RLE produces pixels in bottom-up order, so we draw them line by line rather than the entire bitmap at once.
339350
for y in (0..area.size.height).rev() {
340-
colors.start_row();
341-
342-
let row = Rectangle::new(Point::new(0, y as i32), slice_size);
343-
let colors = colors.by_ref().map(map_color);
344-
target.fill_contiguous(&row, colors.take(area.size.width as usize))?;
351+
runs.start_row();
352+
353+
let mut point = Point::new(0, y as i32);
354+
for (raw_color, count) in runs.by_ref() {
355+
let size = Size::new(count as u32, 1);
356+
let color = map_color(raw_color);
357+
target.fill_solid(&Rectangle::new(point, size), color)?;
358+
point.x += count as i32;
359+
360+
if point.x >= area.size.width as i32 {
361+
break;
362+
}
363+
}
345364
}
346365
Ok(())
347366
} else {

src/raw_iter.rs

Lines changed: 103 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ pub enum DynamicRawColors<'a> {
7979
/// 32 bits per pixel
8080
Bpp32(RawColors<'a, RawU32>),
8181
/// RLE encoded with 4 bits per pixel
82-
Bpp4Rle(Rle4Colors<'a>),
82+
Bpp4Rle(RleColors<RawU4, Rle4Runs<'a>>),
8383
/// RLE encoded with 8 bits per pixel
84-
Bpp8Rle(Rle8Colors<'a>),
84+
Bpp8Rle(RleColors<RawU8, Rle8Runs<'a>>),
8585
}
8686

8787
impl core::fmt::Debug for DynamicRawColors<'_> {
@@ -193,22 +193,59 @@ impl Iterator for PixelPoints {
193193
}
194194
}
195195

196-
/// Iterator over individual BMP RLE8 encoded pixels.
197-
///
198-
/// Each pixel is returned as a `u32` regardless of the bit depth of the source image.
196+
/// Iterator over individual BMP RLE encoded pixels.
199197
#[derive(Debug)]
200-
pub struct Rle8Colors<'a> {
201-
/// Our source data
198+
pub struct RleColors<C, I: Iterator<Item = (C, usize)>> {
199+
runs: I,
200+
run: Option<(C, usize)>,
201+
}
202+
203+
impl<C, I: Iterator<Item = (C, usize)>> RleColors<C, I> {
204+
/// Create a new RLE pixel iterator.
205+
pub(crate) fn new(runs: I) -> Self {
206+
Self { runs, run: None }
207+
}
208+
209+
/// Get a mutable reference to the underlying runs iterator.
210+
pub fn runs(&mut self) -> &mut I {
211+
&mut self.runs
212+
}
213+
}
214+
215+
impl<C: Copy, I: Iterator<Item = (C, usize)>> Iterator for RleColors<C, I> {
216+
type Item = C;
217+
218+
fn next(&mut self) -> Option<Self::Item> {
219+
loop {
220+
let (raw, count) = match self.run.as_mut() {
221+
Some(run) => run,
222+
None => {
223+
self.run = Some(self.runs.next()?);
224+
self.run.as_mut().unwrap()
225+
}
226+
};
227+
if *count > 0 {
228+
*count -= 1;
229+
return Some(*raw);
230+
} else {
231+
self.run = None;
232+
}
233+
}
234+
}
235+
}
236+
237+
/// Iterator over BMP RLE8 runs.
238+
#[derive(Debug)]
239+
pub struct Rle8Runs<'a> {
202240
data: &'a [u8],
203-
/// Our state
204241
rle_state: RleState,
205242
start_of_row: bool,
206243
}
207244

208-
impl<'a> Rle8Colors<'a> {
245+
impl<'a> Rle8Runs<'a> {
209246
/// Create a new RLE pixel iterator.
210-
pub(crate) fn new(raw_bmp: &RawBmp<'a>) -> Rle8Colors<'a> {
211-
Rle8Colors {
247+
pub(crate) fn new(raw_bmp: &RawBmp<'a>) -> Rle8Runs<'a> {
248+
Rle8Runs {
212249
data: raw_bmp.image_data(),
213250
rle_state: RleState::Starting,
214251
start_of_row: false,
@@ -221,15 +258,14 @@ impl<'a> Rle8Colors<'a> {
221258
}
222259
}
223260

224-
impl<'a> Iterator for Rle8Colors<'a> {
225-
type Item = RawU8;
261+
impl Iterator for Rle8Runs<'_> {
262+
type Item = (RawU8, usize);
226263

227264
fn next(&mut self) -> Option<Self::Item> {
228265
loop {
229266
match self.rle_state {
230-
RleState::EndOfBitmap => {
231-
return None;
232-
}
267+
RleState::EndOfBitmap => return None,
268+
233269
RleState::Absolute {
234270
remaining,
235271
is_odd,
@@ -250,28 +286,25 @@ impl<'a> Iterator for Rle8Colors<'a> {
250286
} else {
251287
self.data = self.data.get(1..)?;
252288
}
253-
return Some(RawU8::from(value));
289+
return Some((RawU8::from(value), 1));
254290
}
255291
RleState::Running {
256292
remaining,
257293
value,
258-
is_odd,
294+
is_odd: _,
259295
} => {
260-
if remaining == 0 {
261-
self.rle_state = RleState::Starting;
262-
} else {
263-
self.rle_state = RleState::Running {
264-
remaining: remaining.saturating_sub(1),
265-
value,
266-
is_odd,
267-
};
268-
}
269-
return Some(RawU8::from(value));
296+
// total pixels represented by this run
297+
let total_pixels = (remaining as usize) + 1;
298+
299+
// consume whole run
300+
self.rle_state = RleState::Starting;
301+
302+
return Some((RawU8::from(value), total_pixels));
270303
}
304+
271305
RleState::Starting => {
272-
let length = *self.data.get(0)?;
273-
let param = *self.data.get(1)?;
274-
self.data = &self.data.get(2..)?;
306+
let (&[length, param], rest) = self.data.split_first_chunk()?;
307+
self.data = rest;
275308
match length {
276309
0 => {
277310
// The first byte of the pair can be set to zero to
@@ -320,22 +353,20 @@ impl<'a> Iterator for Rle8Colors<'a> {
320353
}
321354
}
322355

323-
/// Iterator over individual BMP RLE4 encoded pixels.
324-
///
325-
/// Each pixel is returned as a `u32` regardless of the bit depth of the source image.
356+
/// Iterator over BMP RLE4 runs.
326357
#[derive(Debug)]
327-
pub struct Rle4Colors<'a> {
358+
pub struct Rle4Runs<'a> {
328359
/// Our source data
329360
data: &'a [u8],
330361
/// Our state
331362
rle_state: RleState,
332363
start_of_row: bool,
333364
}
334365

335-
impl<'a> Rle4Colors<'a> {
366+
impl<'a> Rle4Runs<'a> {
336367
/// Create a new RLE pixel iterator.
337-
pub(crate) fn new(raw_bmp: &RawBmp<'a>) -> Rle4Colors<'a> {
338-
Rle4Colors {
368+
pub(crate) fn new(raw_bmp: &RawBmp<'a>) -> Rle4Runs<'a> {
369+
Rle4Runs {
339370
data: raw_bmp.image_data(),
340371
rle_state: RleState::Starting,
341372
start_of_row: false,
@@ -348,15 +379,13 @@ impl<'a> Rle4Colors<'a> {
348379
}
349380
}
350381

351-
impl<'a> Iterator for Rle4Colors<'a> {
352-
type Item = RawU4;
382+
impl<'a> Iterator for Rle4Runs<'a> {
383+
type Item = (RawU4, usize);
353384

354385
fn next(&mut self) -> Option<Self::Item> {
355386
loop {
356387
match self.rle_state {
357-
RleState::EndOfBitmap => {
358-
return None;
359-
}
388+
RleState::EndOfBitmap => return None,
360389
RleState::Absolute {
361390
remaining,
362391
is_odd,
@@ -398,7 +427,8 @@ impl<'a> Iterator for Rle4Colors<'a> {
398427
// remove the padding byte too
399428
self.data = self.data.get(1..)?;
400429
}
401-
return Some(RawU4::from(nibble_value));
430+
431+
return Some((RawU4::from(nibble_value), 1));
402432
}
403433
RleState::Running {
404434
remaining,
@@ -417,27 +447,38 @@ impl<'a> Iterator for Rle4Colors<'a> {
417447
// | +-- remaining is 2, is_odd is 0, we want right
418448
// +-- remaining is 3, is_odd is 0, we want left
419449

450+
// determine which nibble we should return next (same logic as in Iterator::next)
420451
let remaining_is_odd = (remaining % 2) != 0;
421452
let want_left = remaining_is_odd != is_odd;
422-
423453
let nibble_value = if want_left { value >> 4 } else { value & 0x0F };
424454

425-
if remaining == 0 {
455+
// total pixels represented by this run
456+
let total_pixels = (remaining as usize) + 1;
457+
458+
let hi = value >> 4;
459+
let lo = value & 0x0F;
460+
461+
if hi == lo {
462+
// entire run is a single repeated nibble -> consume whole run
426463
self.rle_state = RleState::Starting;
464+
return Some((RawU4::from(nibble_value), total_pixels));
427465
} else {
428-
self.rle_state = RleState::Running {
429-
remaining: remaining.saturating_sub(1),
430-
value,
431-
is_odd,
432-
};
466+
// alternating pattern -> only one pixel of this color now
467+
if remaining == 0 {
468+
self.rle_state = RleState::Starting;
469+
} else {
470+
self.rle_state = RleState::Running {
471+
remaining: remaining.saturating_sub(1),
472+
value,
473+
is_odd,
474+
};
475+
}
476+
return Some((RawU4::from(nibble_value), 1));
433477
}
434-
435-
return Some(RawU4::from(nibble_value));
436478
}
437479
RleState::Starting => {
438-
let length = *self.data.get(0)?;
439-
let param = *self.data.get(1)?;
440-
self.data = &self.data.get(2..)?;
480+
let (&[length, param], rest) = self.data.split_first_chunk()?;
481+
self.data = rest;
441482
match length {
442483
0 => {
443484
// The first byte of the pair can be set to zero to
@@ -452,10 +493,8 @@ impl<'a> Iterator for Rle4Colors<'a> {
452493
return None;
453494
}
454495
}
455-
1 => {
456-
// End of bitmap
457-
self.rle_state = RleState::EndOfBitmap;
458-
}
496+
// End of bitmap
497+
1 => self.rle_state = RleState::EndOfBitmap,
459498
2 => {
460499
// Delta encoding is unsupported.
461500
return None;
@@ -501,15 +540,17 @@ impl<'a> RawPixels<'a> {
501540
let header = raw_bmp.header();
502541
match header.compression_method {
503542
CompressionMethod::Rle4 => {
504-
let colors = Rle4Colors::new(raw_bmp);
543+
let runs = Rle4Runs::new(raw_bmp);
544+
let colors = RleColors::new(runs);
505545
let points = PixelPoints::new(header.image_size, RowOrder::BottomUp);
506546
Self {
507547
colors: DynamicRawColors::Bpp4Rle(colors),
508548
points,
509549
}
510550
}
511551
CompressionMethod::Rle8 => {
512-
let colors = Rle8Colors::new(raw_bmp);
552+
let runs = Rle8Runs::new(raw_bmp);
553+
let colors = RleColors::new(runs);
513554
let points = PixelPoints::new(header.image_size, RowOrder::BottomUp);
514555
Self {
515556
colors: DynamicRawColors::Bpp8Rle(colors),

0 commit comments

Comments
 (0)