From 67432e35f107bc6f2edcd5e37104784916fa356b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 23 Aug 2025 13:44:28 +0200 Subject: [PATCH] LibGfx: Match vImage premultiply/unpremultiply rounding behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our Color::to_premultiplied() and Color::to_unpremultiplied() used integer truncation. Apple’s Accelerate framework (and many other libraries) use round-to-nearest, which avoids bias and produces results that differ by ±1 in many cases. This commit switches both helpers to round-to-nearest and clamps the results to [0,255]. For alpha==0 we now return fully transparent black (0,0,0,0) to align with common conventions, instead of preserving RGB. --- Libraries/LibGfx/Color.h | 24 ++++++++++++----------- Tests/LibWeb/Screenshot/images/acid2.png | Bin 10112 -> 10112 bytes 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Libraries/LibGfx/Color.h b/Libraries/LibGfx/Color.h index d4078bfe9bf..0ee8820551a 100644 --- a/Libraries/LibGfx/Color.h +++ b/Libraries/LibGfx/Color.h @@ -317,22 +317,24 @@ public: constexpr Color to_premultiplied() const { - return Color( - red() * alpha() / 255, - green() * alpha() / 255, - blue() * alpha() / 255, - alpha()); + u32 a = alpha(); + u8 r = static_cast((red() * a + 127) / 255); + u8 g = static_cast((green() * a + 127) / 255); + u8 b = static_cast((blue() * a + 127) / 255); + return Color(r, g, b, a); } constexpr Color to_unpremultiplied() const { - if (alpha() == 0 || alpha() == 255) + u32 a = alpha(); + if (a == 0) + return Color(0, 0, 0, 0); + if (a == 255) return *this; - return Color( - red() * 255 / alpha(), - green() * 255 / alpha(), - blue() * 255 / alpha(), - alpha()); + u8 r = static_cast(min(255u, (red() * 255u + a / 2) / a)); + u8 g = static_cast(min(255u, (green() * 255u + a / 2) / a)); + u8 b = static_cast(min(255u, (blue() * 255u + a / 2) / a)); + return Color(r, g, b, a); } constexpr Color blend(Color source) const diff --git a/Tests/LibWeb/Screenshot/images/acid2.png b/Tests/LibWeb/Screenshot/images/acid2.png index 712629ada2199b1133b6450b33ef76084235b437..11705a1061d6074667018fbe8755097b448bf0c6 100644 GIT binary patch delta 5093 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 delta 5049 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 RU|?Wi@O1TaS?83{1OQ^G&87eV