From d8c552f3ab2dc7aeca9a6da40abcfab21780e662 Mon Sep 17 00:00:00 2001 From: Andrei Volykhin Date: Wed, 18 Jun 2025 09:52:42 +0300 Subject: [PATCH] pixels: Multiply by alpha with less loss of precision (#37503) Integer color components representation has performance and memory storage benefits but suffers from precision loss after multiple consequence alpha (un)premultiply operations. Rounding any fractional bits (to the nearest integer) during alpha multiplication should reduce the loss of precision. Expensive division will be replaced by multiplication and bits shift. https://research.swtch.com/divmult https://docs.google.com/document/d/1tNrMWShq55rfltcZxAx1N-6f82Dt7MWLDHm-5GQVEnE Other browsers and graphics libraries have the similar approach: - Chromium (Skia): https://github.com/google/skia/blob/main/include/private/base/SkMath.h#L73 - Firefox: https://github.com/mozilla/gecko-dev/blob/master/gfx/2d/Swizzle.cpp#L276 - Servo (Raqote): https://github.com/jrmuizel/sw-composite/blob/master/src/lib.rs#L878 Testing: Improvements in the following WPT test - html/canvas/element/manual/imagebitmap/createImageBitmap-premultiplyAlpha.html Signed-off-by: Andrei Volykhin --- components/pixels/lib.rs | 6 +++++- .../createImageBitmap-premultiplyAlpha.html.ini | 9 --------- 2 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-premultiplyAlpha.html.ini diff --git a/components/pixels/lib.rs b/components/pixels/lib.rs index 8b002e16190..c6f85348df1 100644 --- a/components/pixels/lib.rs +++ b/components/pixels/lib.rs @@ -195,9 +195,13 @@ pub fn rgba8_premultiply_inplace(pixels: &mut [u8]) -> bool { is_opaque } +/// Returns a*b/255, rounding any fractional bits to nearest integer +/// to reduce the loss of precision after multiple consequence alpha +/// (un)premultiply operations. #[inline(always)] pub fn multiply_u8_color(a: u8, b: u8) -> u8 { - (a as u32 * b as u32 / 255) as u8 + let c = a as u32 * b as u32 + 128; + ((c + (c >> 8)) >> 8) as u8 } pub fn clip( diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-premultiplyAlpha.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-premultiplyAlpha.html.ini deleted file mode 100644 index a6e6c78a2ad..00000000000 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-premultiplyAlpha.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[createImageBitmap-premultiplyAlpha.html] - [createImageBitmap: from Canvas2D, unpremultiplied, drawn to canvas] - expected: FAIL - - [createImageBitmap: from Canvas2D willReadFrequently:true, unpremultiplied, drawn to canvas] - expected: FAIL - - [createImageBitmap: from Canvas2D willReadFrequently:false, unpremultiplied, drawn to canvas] - expected: FAIL