Skip to content

Commit 7f6d0a1

Browse files
committed
Alpha conversion helpers
1 parent 672d180 commit 7f6d0a1

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

src/convert.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#[inline]
2+
pub(crate) fn premultiply(val: u8, alpha: u8) -> u8 {
3+
// TODO: Do we need to optimize this using bit shifts or similar?
4+
((val as u16 * alpha as u16) / 0xff) as u8
5+
}
6+
7+
#[inline]
8+
pub(crate) fn unpremultiply(val: u8, alpha: u8) -> u8 {
9+
// TODO: Can we find a cleaner / more efficient way to implement this?
10+
(val as u16 * u8::MAX as u16)
11+
.checked_div(alpha as u16)
12+
.unwrap_or(0)
13+
.min(u8::MAX as u16) as u8
14+
}

src/lib.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern crate core;
99
mod backend_dispatch;
1010
mod backend_interface;
1111
mod backends;
12+
mod convert;
1213
mod error;
1314
mod format;
1415
mod pixel;
@@ -623,6 +624,76 @@ impl Buffer<'_> {
623624
.map(move |(x, pixel)| (x as u32, y as u32, pixel))
624625
})
625626
}
627+
628+
/// Convert the alpha mode of the buffer in place.
629+
///
630+
/// # Examples
631+
///
632+
/// Write to the buffer as-if it was [`AlphaMode::Ignored`], and convert afterwards if
633+
/// necessary.
634+
///
635+
/// ```no_run
636+
/// # let buffer: Buffer<'_> = unimplemented!();
637+
///
638+
/// let needs_alpha_conversion = surface.supports_alpha_mode(AlphaMode::Ignored) {
639+
/// surface.set_alpha_mode(AlphaMode::Ignored);
640+
/// false
641+
/// } else {
642+
/// surface.set_alpha_mode(AlphaMode::Opaque);
643+
/// true
644+
/// };
645+
///
646+
/// for row in buffer.pixel_rows() {
647+
/// for pixel in row {
648+
/// // Write red pixels with an arbitrary alpha value.
649+
/// pixel = Pixel::new_rgba(0xff, 0x00, 0x00, 0x7f);
650+
/// }
651+
/// }
652+
///
653+
/// if needs_alpha_conversion {
654+
/// buffer.make_pixels_opaque();
655+
/// }
656+
///
657+
/// // Alpha value is ignored, either by compositor () or by us above.
658+
/// buffer.present();
659+
/// ```
660+
#[inline]
661+
pub fn make_pixels_opaque(&mut self) {
662+
for row in self.pixel_rows() {
663+
for pixel in row {
664+
// TODO: SIMD-optimize this somehow? Or maybe autovectorization is good enough.
665+
pixel.a = 0xff;
666+
}
667+
}
668+
}
669+
670+
/// Multiply pixel color components by the alpha component.
671+
#[inline]
672+
pub fn premultiply_pixels(&mut self) {
673+
for row in self.pixel_rows() {
674+
for pixel in row {
675+
// TODO: SIMD-optimize this somehow? Or maybe autovectorization is good enough.
676+
let a = pixel.a;
677+
pixel.r = convert::premultiply(pixel.r, a);
678+
pixel.g = convert::premultiply(pixel.g, a);
679+
pixel.b = convert::premultiply(pixel.b, a);
680+
}
681+
}
682+
}
683+
684+
/// Divide pixel color components by the alpha component.
685+
#[inline]
686+
pub fn unpremultiply_pixels(&mut self) {
687+
for row in self.pixel_rows() {
688+
for pixel in row {
689+
// TODO: SIMD-optimize this somehow? Or maybe autovectorization is good enough.
690+
let a = pixel.a;
691+
pixel.r = convert::unpremultiply(pixel.r, a);
692+
pixel.g = convert::unpremultiply(pixel.g, a);
693+
pixel.b = convert::unpremultiply(pixel.b, a);
694+
}
695+
}
696+
}
626697
}
627698

628699
/// Specifies how the alpha channel of the surface should be handled by the compositor.

0 commit comments

Comments
 (0)