diff --git a/src/header/mod.rs b/src/header/mod.rs index 56a10b6..aad58a2 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -201,6 +201,14 @@ impl ChannelMasks { blue: 0x0000FF, alpha: 0, }; + + /// Argb888 color masks. + pub const ARGB888: Self = Self { + red: 0xFF000000, + green: 0xFF0000, + blue: 0xFF00, + alpha: 0xFF, + }; } /// Describes how the BMP file is compressed. diff --git a/src/iter.rs b/src/iter.rs index a5011e4..c6613fd 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -56,6 +56,7 @@ where ColorType::Rgb555 => Rgb555::from(RawU16::from_u32(color)).into(), ColorType::Rgb565 => Rgb565::from(RawU16::from_u32(color)).into(), ColorType::Rgb888 | ColorType::Xrgb8888 => Rgb888::from(RawU24::from_u32(color)).into(), + ColorType::Argb8888 => Rgb888::from(RawU24::from_u32(color >> 8)).into(), }; Some(Pixel(position, color)) diff --git a/src/lib.rs b/src/lib.rs index 224d9c7..ab558e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,6 +209,7 @@ pub use raw_iter::{RawPixel, RawPixels}; pub struct Bmp<'a, C> { raw_bmp: RawBmp<'a>, color_type: PhantomData, + alpha_bg: Rgb888, } impl<'a, C> Bmp<'a, C> @@ -225,9 +226,18 @@ where Ok(Self { raw_bmp, color_type: PhantomData, + alpha_bg: Rgb888::BLACK, }) } + /// If this image contains transparent pixels (pixels with an alpha channel), then blend these + /// pixels with the provided color. Note that this will only be used when drawing to a target. + /// It will not be applied when querying pixels from the image. + pub fn with_alpha_bg>(mut self, alpha_bg: BG) -> Self { + self.alpha_bg = alpha_bg.into(); + self + } + /// Returns an iterator over the pixels in this image. /// /// The iterator always starts at the top left corner of the image, regardless of the row order @@ -362,6 +372,37 @@ where RawColors::::new(&self.raw_bmp) .map(|raw| Rgb888::from(RawU24::new(raw.into_inner())).into()), ), + ColorType::Argb8888 => { + target.fill_contiguous( + &area, + RawColors::::new(&self.raw_bmp).map(|raw| { + // integer blending approach from https://stackoverflow.com/a/12016968 + let v = raw.into_inner(); + let mut alpha = v & 0xFF; + let inv_alpha = 256 - alpha; + alpha += 1; + if alpha == 0 { + // pixel is completely transparent, use bg color + self.alpha_bg + } else if alpha == 255 { + // pixel is completely opaque, just use its color + Rgb888::from(RawU24::new(v >> 8)) + } else { + // pixel has transparency, blend with BG color + let col = Rgb888::from(RawU24::new(v >> 8)); + Rgb888::new( + ((alpha * col.r() as u32 + inv_alpha * self.alpha_bg.r() as u32) + >> 8) as u8, + ((alpha * col.g() as u32 + inv_alpha * self.alpha_bg.g() as u32) + >> 8) as u8, + ((alpha * col.b() as u32 + inv_alpha * self.alpha_bg.b() as u32) + >> 8) as u8, + ) + } + .into() + }), + ) + } } } @@ -421,6 +462,10 @@ where .raw_bmp .pixel(p) .map(|raw| Rgb888::from(RawU24::from_u32(raw)).into()), + ColorType::Argb8888 => self + .raw_bmp + .pixel(p) + .map(|raw| Rgb888::from(RawU24::from_u32(raw >> 8)).into()), } } } diff --git a/src/raw_bmp.rs b/src/raw_bmp.rs index 7d73192..4c640fc 100644 --- a/src/raw_bmp.rs +++ b/src/raw_bmp.rs @@ -157,6 +157,7 @@ pub enum ColorType { Rgb565, Rgb888, Xrgb8888, + Argb8888, } impl ColorType { @@ -184,6 +185,8 @@ impl ColorType { if let Some(masks) = header.channel_masks { if masks == ChannelMasks::RGB888 { ColorType::Xrgb8888 + } else if masks == ChannelMasks::ARGB888 { + ColorType::Argb8888 } else { return Err(ParseError::UnsupportedChannelMasks); }