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 <andrei.volykhin@gmail.com>
This commit is contained in:
Andrei Volykhin 2025-06-18 09:52:42 +03:00 committed by GitHub
parent e26532e19b
commit d8c552f3ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 5 additions and 10 deletions

View file

@ -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(

View file

@ -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