7979//! # Ok::<(), core::convert::Infallible>(()) }
8080//! ```
8181//!
82+ //! Note that you currently cannot access invidual pixels when working with RLE4
83+ //! or RLE8 compressed indexed bitmaps. With these formats the `pixel()`
84+ //! function will always return `None`.
85+ //!
8286//! ## Accessing the raw image data
8387//!
8488//! For most applications the higher level access provided by [`Bmp`] is sufficient. But in case
9296//!
9397//! ```
9498//! use embedded_graphics::prelude::*;
95- //! use tinybmp::{RawBmp, Bpp, Header, RawPixel, RowOrder};
99+ //! use tinybmp::{RawBmp, Bpp, Header, RawPixel, RowOrder, CompressionMethod };
96100//!
97101//! let bmp = RawBmp::from_slice(include_bytes!("../tests/chessboard-8px-24bit.bmp"))
98102//! .expect("Failed to parse BMP image");
108112//! image_data_len: 192,
109113//! channel_masks: None,
110114//! row_order: RowOrder::BottomUp,
115+ //! compression_method: CompressionMethod::Rgb,
111116//! }
112117//! );
113118//!
128133//!
129134//! # Minimum supported Rust version
130135//!
131- //! The minimum supported Rust version for tinybmp is `1.61 ` or greater. Ensure you have the correct
136+ //! The minimum supported Rust version for tinybmp is `1.71 ` or greater. Ensure you have the correct
132137//! version of Rust installed, preferably through <https://rustup.rs>.
133138//!
134139//! <!-- README-LINKS
@@ -188,9 +193,10 @@ mod raw_bmp;
188193mod raw_iter;
189194
190195use raw_bmp:: ColorType ;
191- use raw_iter:: RawColors ;
196+ use raw_iter:: { RawColors , Rle4Pixels , Rle8Pixels } ;
192197
193198pub use color_table:: ColorTable ;
199+ pub use header:: CompressionMethod ;
194200pub use header:: { Bpp , ChannelMasks , Header , RowOrder } ;
195201pub use iter:: Pixels ;
196202pub use raw_bmp:: RawBmp ;
@@ -249,6 +255,7 @@ where
249255 D : DrawTarget < Color = C > ,
250256 {
251257 let area = self . bounding_box ( ) ;
258+ let slice_size = Size :: new ( area. size . width , 1 ) ;
252259
253260 match self . raw_bmp . color_type {
254261 ColorType :: Index1 => {
@@ -271,33 +278,69 @@ where
271278 }
272279 }
273280 ColorType :: Index4 => {
281+ let header = self . raw_bmp . header ( ) ;
282+ let fallback_color = C :: from ( Rgb888 :: BLACK ) ;
274283 if let Some ( color_table) = self . raw_bmp . color_table ( ) {
275- let fallback_color = C :: from ( Rgb888 :: BLACK ) ;
276-
277- let colors = RawColors :: < RawU4 > :: new ( & self . raw_bmp ) . map ( |index| {
278- color_table
279- . get ( u32:: from ( index. into_inner ( ) ) )
280- . map ( Into :: into)
281- . unwrap_or ( fallback_color)
282- } ) ;
283-
284- target. fill_contiguous ( & area, colors)
284+ if header. compression_method == CompressionMethod :: Rle4 {
285+ let mut colors = Rle4Pixels :: new ( & self . raw_bmp ) . map ( |raw_pixel| {
286+ color_table
287+ . get ( raw_pixel. color )
288+ . map ( Into :: into)
289+ . unwrap_or ( fallback_color)
290+ } ) ;
291+ // RLE produces pixels in bottom-up order, so we draw them line by line rather than the entire bitmap at once.
292+ for y in ( 0 ..area. size . height ) . rev ( ) {
293+ let row = Rectangle :: new ( Point :: new ( 0 , y as i32 ) , slice_size) ;
294+ target. fill_contiguous (
295+ & row,
296+ colors. by_ref ( ) . take ( area. size . width as usize ) ,
297+ ) ?;
298+ }
299+ Ok ( ( ) )
300+ } else {
301+ // If we didn't detect a supported compression method, just intepret it as raw indexed nibbles.
302+ let colors = RawColors :: < RawU4 > :: new ( & self . raw_bmp ) . map ( |index| {
303+ color_table
304+ . get ( u32:: from ( index. into_inner ( ) ) )
305+ . map ( Into :: into)
306+ . unwrap_or ( fallback_color)
307+ } ) ;
308+ target. fill_contiguous ( & area, colors)
309+ }
285310 } else {
286311 Ok ( ( ) )
287312 }
288313 }
289314 ColorType :: Index8 => {
315+ let header = self . raw_bmp . header ( ) ;
316+ let fallback_color = C :: from ( Rgb888 :: BLACK ) ;
290317 if let Some ( color_table) = self . raw_bmp . color_table ( ) {
291- let fallback_color = C :: from ( Rgb888 :: BLACK ) ;
292-
293- let colors = RawColors :: < RawU8 > :: new ( & self . raw_bmp ) . map ( |index| {
294- color_table
295- . get ( u32:: from ( index. into_inner ( ) ) )
296- . map ( Into :: into)
297- . unwrap_or ( fallback_color)
298- } ) ;
299-
300- target. fill_contiguous ( & area, colors)
318+ if header. compression_method == CompressionMethod :: Rle8 {
319+ let mut colors = Rle8Pixels :: new ( & self . raw_bmp ) . map ( |raw_pixel| {
320+ color_table
321+ . get ( raw_pixel. color )
322+ . map ( Into :: into)
323+ . unwrap_or ( fallback_color)
324+ } ) ;
325+ // RLE produces pixels in bottom-up order, so we draw them line by line rather than the entire bitmap at once.
326+ for y in ( 0 ..area. size . height ) . rev ( ) {
327+ let row = Rectangle :: new ( Point :: new ( 0 , y as i32 ) , slice_size) ;
328+ target. fill_contiguous (
329+ & row,
330+ colors. by_ref ( ) . take ( area. size . width as usize ) ,
331+ ) ?;
332+ }
333+ Ok ( ( ) )
334+ } else {
335+ // If we didn't detect a supported compression method, just intepret it as raw indexed bytes.
336+ let colors = RawColors :: < RawU8 > :: new ( & self . raw_bmp ) . map ( |index| {
337+ color_table
338+ . get ( u32:: from ( index. into_inner ( ) ) )
339+ . map ( Into :: into)
340+ . unwrap_or ( fallback_color)
341+ } ) ;
342+ target. fill_contiguous ( & area, colors)
343+ }
301344 } else {
302345 Ok ( ( ) )
303346 }
0 commit comments