From f4eb14af1f4aea11e87ac159600e4b1d80170dde Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 24 Jan 2026 00:00:53 +0100 Subject: [PATCH] Alpha conversion helpers --- src/convert.rs | 14 ++++++++++ src/lib.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/convert.rs diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 00000000..c5017527 --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,14 @@ +#[inline] +pub(crate) fn premultiply(val: u8, alpha: u8) -> u8 { + // TODO: Do we need to optimize this using bit shifts or similar? + ((val as u16 * alpha as u16) / 0xff) as u8 +} + +#[inline] +pub(crate) fn unpremultiply(val: u8, alpha: u8) -> u8 { + // TODO: Can we find a cleaner / more efficient way to implement this? + (val as u16 * u8::MAX as u16) + .checked_div(alpha as u16) + .unwrap_or(0) + .min(u8::MAX as u16) as u8 +} diff --git a/src/lib.rs b/src/lib.rs index 5e24ede2..90db8607 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ extern crate core; mod backend_dispatch; mod backend_interface; mod backends; +mod convert; mod error; mod format; mod pixel; @@ -623,6 +624,76 @@ impl Buffer<'_> { .map(move |(x, pixel)| (x as u32, y as u32, pixel)) }) } + + /// Convert the alpha mode of the buffer in place. + /// + /// # Examples + /// + /// Write to the buffer as-if it was [`AlphaMode::Ignored`], and convert afterwards if + /// necessary. + /// + /// ```no_run + /// # let buffer: Buffer<'_> = unimplemented!(); + /// + /// surface.supports_alpha_mode(AlphaMode::Ignored) { + /// surface.set_alpha_mode(AlphaMode::Ignored); + /// } else { + /// surface.set_alpha_mode(AlphaMode::Opaque); + /// } + /// + /// for row in buffer.pixel_rows() { + /// for pixel in row { + /// // Write red pixels with an arbitrary alpha value. + /// pixel = Pixel::new_rgba(0xff, 0x00, 0x00, 0x7f); + /// } + /// } + /// + /// buffer.make_pixels_opaque(); + /// + /// // Alpha value is ignored, either by compositor () or by us above. + /// buffer.present(); + /// ``` + #[inline] + pub fn make_pixels_opaque(&mut self) { + if self.alpha_mode == AlphaMode::Ignored { + // Alpha mode is ignored, no need to change the pixels + return; + } + for row in self.pixel_rows() { + for pixel in row { + // TODO: SIMD-optimize this somehow? Or maybe autovectorization is good enough. + pixel.a = 0xff; + } + } + } + + /// Multiply pixel color components by the alpha component. + #[inline] + pub fn premultiply_pixels(&mut self) { + for row in self.pixel_rows() { + for pixel in row { + // TODO: SIMD-optimize this somehow? Or maybe autovectorization is good enough. + let a = pixel.a; + pixel.r = convert::premultiply(pixel.r, a); + pixel.g = convert::premultiply(pixel.g, a); + pixel.b = convert::premultiply(pixel.b, a); + } + } + } + + /// Divide pixel color components by the alpha component. + #[inline] + pub fn unpremultiply_pixels(&mut self) { + for row in self.pixel_rows() { + for pixel in row { + // TODO: SIMD-optimize this somehow? Or maybe autovectorization is good enough. + let a = pixel.a; + pixel.r = convert::unpremultiply(pixel.r, a); + pixel.g = convert::unpremultiply(pixel.g, a); + pixel.b = convert::unpremultiply(pixel.b, a); + } + } + } } /// Specifies how the alpha channel of the surface should be handled by the compositor.