aboutsummaryrefslogtreecommitdiffhomepage
path: root/include/core/SkColorPriv.h
diff options
context:
space:
mode:
authorGravatar lsalzman <lsalzman@mozilla.com>2016-08-05 11:48:45 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-08-05 11:48:45 -0700
commit40254c2c2dc28a34f96294d5a1ad94a99b0be8a6 (patch)
treedbc9c6b38ca6be76f0011623f3a529a60ce1d570 /include/core/SkColorPriv.h
parentf77c47b78217883e6f074dd7e3e5bed5f82e144d (diff)
SkBlendARGB32 and S32[A]_Blend_BlitRow32 are currently formulated as: SkAlphaMulQ(src, src_scale) + SkAlphaMulQ(dst, dst_scale), which boils down to ((src*src_scale)>>8) + ((dst*dst_scale)>>8). In particular, note that the intermediate precision is discarded before the two parts are added together, causing the final result to possibly inaccurate.
In Firefox, we use SkCanvas::saveLayer in combination with a backdrop that initializes the layer to the background. When this is blended back onto background using transparency, where the source and destination pixel colors are the same, the resulting color after the blend is not preserved due to the lost precision mentioned above. In cases where this operation is repeatedly performed, this causes substantially noticeable differences in color as evidenced in this downstream Firefox bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=1200684 In the test-case in the downstream report, essentially it does blend(src=0xFF2E3338, dst=0xFF2E3338, scale=217), which gives the result 0xFF2E3237, while we would expect to get back 0xFF2E3338. This problem goes away if the blend is instead reformulated to effectively do (src*src_scale + dst*dst_scale)>>8, which keeps the intermediate precision during the addition before shifting it off. This modifies the blending operations thusly. The performance should remain mostly unchanged, or possibly improve slightly, so there should be no real downside to doing this, with the benefit of making the results more accurate. Without this, it is currently unsafe for Firefox to blend a layer back onto itself that was initialized with a copy of its background. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2097883002 CQ_INCLUDE_TRYBOTS=master.client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot [mtklein adds...] No public API changes. TBR=reed@google.com Review-Url: https://codereview.chromium.org/2097883002
Diffstat (limited to 'include/core/SkColorPriv.h')
-rw-r--r--include/core/SkColorPriv.h37
1 files changed, 37 insertions, 0 deletions
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
index 40e6e15dc8..694d32472f 100644
--- a/include/core/SkColorPriv.h
+++ b/include/core/SkColorPriv.h
@@ -200,6 +200,18 @@ static inline unsigned Sk255To256(U8CPU value) {
*/
#define SkAlphaMul(value, alpha256) (((value) * (alpha256)) >> 8)
+/** Calculates 256 - (value * alpha256) / 255 in range [0,256],
+ * for [0,255] value and [0,256] alpha256.
+ */
+static inline U16CPU SkAlphaMulInv256(U16CPU value, U16CPU alpha256) {
+#ifdef SK_SUPPORT_LEGACY_BROKEN_LERP
+ return SkAlpha255To256(255 - SkAlphaMul(value, alpha256));
+#else
+ unsigned prod = 0xFFFF - value * alpha256;
+ return (prod + (prod >> 8)) >> 8;
+#endif
+}
+
// The caller may want negative values, so keep all params signed (int)
// so we don't accidentally slip into unsigned math and lose the sign
// extension when we shift (in SkAlphaMul)
@@ -568,13 +580,38 @@ static inline SkPMColor SkPMSrcOver(SkPMColor src, SkPMColor dst) {
return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
}
+/**
+ * Interpolates between colors src and dst using [0,256] scale.
+ */
+static inline SkPMColor SkPMLerp(SkPMColor src, SkPMColor dst, unsigned scale) {
+#ifdef SK_SUPPORT_LEGACY_BROKEN_LERP
+ return SkAlphaMulQ(src, scale) + SkAlphaMulQ(dst, 256 - scale);
+#else
+ return SkFastFourByteInterp256(src, dst, scale);
+#endif
+}
+
static inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) {
SkASSERT((unsigned)aa <= 255);
unsigned src_scale = SkAlpha255To256(aa);
+#ifdef SK_SUPPORT_LEGACY_BROKEN_LERP
unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(SkGetPackedA32(src), src_scale));
return SkAlphaMulQ(src, src_scale) + SkAlphaMulQ(dst, dst_scale);
+#else
+ unsigned dst_scale = SkAlphaMulInv256(SkGetPackedA32(src), src_scale);
+
+ const uint32_t mask = 0xFF00FF;
+
+ uint32_t src_rb = (src & mask) * src_scale;
+ uint32_t src_ag = ((src >> 8) & mask) * src_scale;
+
+ uint32_t dst_rb = (dst & mask) * dst_scale;
+ uint32_t dst_ag = ((dst >> 8) & mask) * dst_scale;
+
+ return (((src_rb + dst_rb) >> 8) & mask) | ((src_ag + dst_ag) & ~mask);
+#endif
}
////////////////////////////////////////////////////////////////////////////////////////////