From b2b853044d6cf74c11cd2e31d5603fff2b29fadf Mon Sep 17 00:00:00 2001 From: yanorei32 Date: Mon, 11 May 2026 18:43:18 +0900 Subject: [PATCH 1/4] fix(rle): return all pixels instead of only first row `start_of_row` was initialized to `false` in both `Rle8Colors` and `Rle4Colors`. Additionally, the end-of-line escape handler (`00 00`) returned `None` if `start_of_row` was not set. This combination caused the iterator to terminate prematurely after processing the first row when using `Bmp::pixels()`. Fix by: - Initializing `start_of_row` to `true`. - Setting `start_of_row = false` after yielding each pixel. - Changing the end-of-line handler to set `start_of_row = true` instead of returning `None`. --- CHANGELOG.md | 4 ++++ src/raw_iter.rs | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a630eb0..34a0f69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - [#55](https://github.com/embedded-graphics/tinybmp/pull/55) Added methods `CompressionMethod::is_compressed` and `Header::bytes_per_row`. The latter is essential information when walking through the bytes yourself. +### Fixed + +- Fixed RLE4/RLE8 compressed BMP pixel iterator returning only the first row of pixels instead of all pixels. + ## [0.7.0] - 2026-01-14 ### Fixed diff --git a/src/raw_iter.rs b/src/raw_iter.rs index 17d0389..dcddc4e 100644 --- a/src/raw_iter.rs +++ b/src/raw_iter.rs @@ -213,7 +213,7 @@ impl<'a> Rle8Colors<'a> { Rle8Colors { data: raw_bmp.image_data(), rle_state: RleState::Starting, - start_of_row: false, + start_of_row: true, } } @@ -252,6 +252,7 @@ impl<'a> Iterator for Rle8Colors<'a> { } else { self.data = self.data.get(1..)?; } + self.start_of_row = false; return Some(RawU8::from(value)); } RleState::Running { @@ -268,6 +269,7 @@ impl<'a> Iterator for Rle8Colors<'a> { is_odd, }; } + self.start_of_row = false; return Some(RawU8::from(value)); } RleState::Starting => { @@ -284,9 +286,8 @@ impl<'a> Iterator for Rle8Colors<'a> { // the pair, which can be one of the following values. match param { 0 => { - if !self.start_of_row { - return None; - } + // End of line - move to next row + self.start_of_row = true; } 1 => { // End of bitmap @@ -340,7 +341,7 @@ impl<'a> Rle4Colors<'a> { Rle4Colors { data: raw_bmp.image_data(), rle_state: RleState::Starting, - start_of_row: false, + start_of_row: true, } } @@ -400,6 +401,7 @@ impl<'a> Iterator for Rle4Colors<'a> { // remove the padding byte too self.data = self.data.get(1..)?; } + self.start_of_row = false; return Some(RawU4::from(nibble_value)); } RleState::Running { @@ -434,6 +436,7 @@ impl<'a> Iterator for Rle4Colors<'a> { }; } + self.start_of_row = false; return Some(RawU4::from(nibble_value)); } RleState::Starting => { @@ -450,9 +453,8 @@ impl<'a> Iterator for Rle4Colors<'a> { // the pair, which can be one of the following values. match param { 0 => { - if !self.start_of_row { - return None; - } + // End of line - move to next row + self.start_of_row = true; } 1 => { // End of bitmap From ea128da9ce12a2fe514f0c7eed7346347a5c948f Mon Sep 17 00:00:00 2001 From: yanorei32 Date: Wed, 13 May 2026 21:59:50 +0900 Subject: [PATCH 2/4] test: add pixel iteration tests for various BMP formats This adds regression tests verifying that pixels().count() returns the correct total for indexed (1bpp, 4bpp, 8bpp), RLE-compressed (RLE4, RLE8), and RGB formats (555, 565, 24bpp, 32bpp) ensuring the earlier start_of_row fix holds across all supported formats. --- tests/logo.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/logo.rs b/tests/logo.rs index b748cce..2066266 100644 --- a/tests/logo.rs +++ b/tests/logo.rs @@ -139,6 +139,12 @@ fn logo_indexed_1bpp_pixel_getter() { bmp.assert_eq(&raw); } +#[test] +fn logo_indexed_1bpp_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-1bpp.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_indexed_4bpp() { let raw = draw_raw::(include_bytes!("logo-indexed-4bpp.raw")); @@ -147,6 +153,12 @@ fn logo_indexed_4bpp() { bmp.assert_eq(&raw); } +#[test] +fn logo_indexed_4bpp_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-4bpp.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_indexed_4bpp_rle4() { let raw = draw_raw::(include_bytes!("logo-indexed-4bpp.raw")); @@ -155,6 +167,12 @@ fn logo_indexed_4bpp_rle4() { bmp.assert_eq(&raw); } +#[test] +fn logo_indexed_4bpp_rle4_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-4bpp-rle4.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_indexed_4bpp_pixel_getter() { let raw = draw_raw::(include_bytes!("logo-indexed-4bpp.raw")); @@ -179,6 +197,12 @@ fn logo_indexed_8bpp_pixel_getter() { bmp.assert_eq(&raw); } +#[test] +fn logo_indexed_8bpp_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-8bpp.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_indexed_8bpp_rle8() { let raw = draw_raw::(include_bytes!("logo-indexed-8bpp.raw")); @@ -187,6 +211,12 @@ fn logo_indexed_8bpp_rle8() { bmp.assert_eq(&raw); } +#[test] +fn logo_indexed_8bpp_rle8_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-8bpp-rle8.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_rgb555() { let raw = draw_raw::(include_bytes!("logo-rgb555.raw")); @@ -203,6 +233,12 @@ fn logo_rgb555_pixel_getter() { bmp.assert_eq(&raw); } +#[test] +fn logo_rgb555_pixel_iteration() { + let bmp: Bmp<'_, Rgb555> = Bmp::from_slice(include_bytes!("logo-rgb555.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_rgb565() { let raw = draw_raw::(include_bytes!("logo-rgb565.raw")); @@ -219,6 +255,12 @@ fn logo_rgb565_pixel_getter() { bmp.assert_eq(&raw); } +#[test] +fn logo_rgb565_pixel_iteration() { + let bmp: Bmp<'_, Rgb565> = Bmp::from_slice(include_bytes!("logo-rgb565.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_rgb888_24bpp() { let raw = draw_raw::(include_bytes!("logo-rgb888.raw")); @@ -235,6 +277,12 @@ fn logo_rgb888_24bpp_pixel_getter() { bmp.assert_eq(&raw); } +#[test] +fn logo_rgb888_24bpp_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-rgb888-24bpp.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} + #[test] fn logo_rgb888_32bpp() { let raw = draw_raw::(include_bytes!("logo-rgb888.raw")); @@ -250,3 +298,9 @@ fn logo_rgb888_32bpp_pixel_getter() { bmp.assert_eq(&raw); } + +#[test] +fn logo_rgb888_32bpp_pixel_iteration() { + let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-rgb888-32bpp.bmp")).unwrap(); + assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); +} From 338290f400df2b223628cfda02c4f37683208c6c Mon Sep 17 00:00:00 2001 From: Ralf Fuest Date: Wed, 13 May 2026 15:50:27 +0200 Subject: [PATCH 3/4] Fix format of CHANGELOG entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a0f69..eb1dc01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ ### Fixed -- Fixed RLE4/RLE8 compressed BMP pixel iterator returning only the first row of pixels instead of all pixels. +- [#57](https://github.com/embedded-graphics/tinybmp/pull/57) Fixed RLE4/RLE8 compressed BMP pixel iterator returning only the first row of pixels instead of all pixels. ## [0.7.0] - 2026-01-14 From 2ccd2656efd44acca2da938a015ac1505b06ca96 Mon Sep 17 00:00:00 2001 From: Ralf Fuest Date: Wed, 13 May 2026 15:58:42 +0200 Subject: [PATCH 4/4] Reformat code --- tests/logo.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/logo.rs b/tests/logo.rs index 2066266..d3d877c 100644 --- a/tests/logo.rs +++ b/tests/logo.rs @@ -169,7 +169,8 @@ fn logo_indexed_4bpp_rle4() { #[test] fn logo_indexed_4bpp_rle4_pixel_iteration() { - let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-4bpp-rle4.bmp")).unwrap(); + let bmp: Bmp<'_, Bgr888> = + Bmp::from_slice(include_bytes!("logo-indexed-4bpp-rle4.bmp")).unwrap(); assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); } @@ -213,7 +214,8 @@ fn logo_indexed_8bpp_rle8() { #[test] fn logo_indexed_8bpp_rle8_pixel_iteration() { - let bmp: Bmp<'_, Bgr888> = Bmp::from_slice(include_bytes!("logo-indexed-8bpp-rle8.bmp")).unwrap(); + let bmp: Bmp<'_, Bgr888> = + Bmp::from_slice(include_bytes!("logo-indexed-8bpp-rle8.bmp")).unwrap(); assert_eq!(bmp.pixels().count(), WIDTH * HEIGHT); }