From 7f428a941b47c88d84da7adb656924245a55f050 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Tue, 25 Feb 2014 21:31:03 +0000 Subject: Check in today's exhaustive blend experiments. BUG=skia: R=reed@google.com, mtklein@google.com Author: mtklein@chromium.org Review URL: https://codereview.chromium.org/179733005 git-svn-id: http://skia.googlecode.com/svn/trunk@13588 2bbb7eff-a529-9590-31e7-b0007b416f81 --- tests/BlendTest.cpp | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 tests/BlendTest.cpp (limited to 'tests/BlendTest.cpp') diff --git a/tests/BlendTest.cpp b/tests/BlendTest.cpp new file mode 100644 index 0000000000..9b42e5629c --- /dev/null +++ b/tests/BlendTest.cpp @@ -0,0 +1,252 @@ +#include "Test.h" +#include "SkColor.h" + +#define ASSERT(x) REPORTER_ASSERT(r, x) + +// All algorithms we're testing have this interface. +// We want a single channel blend, src over dst, assuming src is premultiplied by srcAlpha. +typedef uint8_t(*Blend)(uint8_t dst, uint8_t src, uint8_t srcAlpha); + +// This is our golden algorithm. +static uint8_t blend_double_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + SkASSERT(src <= srcAlpha); + return 0.5 + src + dst * (255.0 - srcAlpha) / 255.0; +} + +static uint8_t abs_diff(uint8_t a, uint8_t b) { + const int diff = a - b; + return diff > 0 ? diff : -diff; +} + +static void test(skiatest::Reporter* r, int maxDiff, Blend algorithm, + uint8_t dst, uint8_t src, uint8_t alpha) { + const uint8_t golden = blend_double_round(dst, src, alpha); + const uint8_t blend = algorithm(dst, src, alpha); + if (abs_diff(blend, golden) > maxDiff) { + SkDebugf("dst %02x, src %02x, alpha %02x, |%02x - %02x| > %d\n", + dst, src, alpha, blend, golden, maxDiff); + ASSERT(abs_diff(blend, golden) <= maxDiff); + } +} + +// Exhaustively compare an algorithm against our golden, for a given alpha. +static void test_alpha(skiatest::Reporter* r, uint8_t alpha, int maxDiff, Blend algorithm) { + SkASSERT(maxDiff >= 0); + + for (unsigned src = 0; src <= alpha; src++) { + for (unsigned dst = 0; dst < 256; dst++) { + test(r, maxDiff, algorithm, dst, src, alpha); + } + } +} + +// Exhaustively compare an algorithm against our golden, for a given dst. +static void test_dst(skiatest::Reporter* r, uint8_t dst, int maxDiff, Blend algorithm) { + SkASSERT(maxDiff >= 0); + + for (unsigned alpha = 0; alpha < 256; alpha++) { + for (unsigned src = 0; src <= alpha; src++) { + test(r, maxDiff, algorithm, dst, src, alpha); + } + } +} + +static uint8_t blend_double_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + return src + dst * (255.0 - srcAlpha) / 255.0; +} + +static uint8_t blend_float_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + return src + dst * (255.0f - srcAlpha) / 255.0f; +} + +static uint8_t blend_float_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + return 0.5f + src + dst * (255.0f - srcAlpha) / 255.0f; +} + +static uint8_t blend_255_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint16_t invAlpha = 255 - srcAlpha; + const uint16_t product = dst * invAlpha; + return src + (product >> 8); +} + +static uint8_t blend_255_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint16_t invAlpha = 255 - srcAlpha; + const uint16_t product = dst * invAlpha + 128; + return src + (product >> 8); +} + +static uint8_t blend_256_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7)); + const uint16_t product = dst * invAlpha; + return src + (product >> 8); +} + +static uint8_t blend_256_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7)); + const uint16_t product = dst * invAlpha + 128; + return src + (product >> 8); +} + +static uint8_t blend_256_round_alt(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint8_t invAlpha8 = 255 - srcAlpha; + const uint16_t invAlpha = invAlpha8 + (invAlpha8 >> 7); + const uint16_t product = dst * invAlpha + 128; + return src + (product >> 8); +} + +static uint8_t blend_256_plus1_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint16_t invAlpha = 256 - (srcAlpha + 1); + const uint16_t product = dst * invAlpha; + return src + (product >> 8); +} + +static uint8_t blend_256_plus1_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint16_t invAlpha = 256 - (srcAlpha + 1); + const uint16_t product = dst * invAlpha + 128; + return src + (product >> 8); +} + +static uint8_t blend_perfect(uint8_t dst, uint8_t src, uint8_t srcAlpha) { + const uint8_t invAlpha = 255 - srcAlpha; + const uint16_t product = dst * invAlpha + 128; + return src + ((product + (product >> 8)) >> 8); +} + + +// We want 0 diff whenever src is fully transparent. +DEF_TEST(Blend_alpha_0x00, r) { + const uint8_t alpha = 0x00; + + // GOOD + test_alpha(r, alpha, 0, blend_256_round); + test_alpha(r, alpha, 0, blend_256_round_alt); + test_alpha(r, alpha, 0, blend_256_trunc); + test_alpha(r, alpha, 0, blend_double_trunc); + test_alpha(r, alpha, 0, blend_float_round); + test_alpha(r, alpha, 0, blend_float_trunc); + test_alpha(r, alpha, 0, blend_perfect); + + // BAD + test_alpha(r, alpha, 1, blend_255_round); + test_alpha(r, alpha, 1, blend_255_trunc); + test_alpha(r, alpha, 1, blend_256_plus1_round); + test_alpha(r, alpha, 1, blend_256_plus1_trunc); +} + +// We want 0 diff whenever dst is 0. +DEF_TEST(Blend_dst_0x00, r) { + const uint8_t dst = 0x00; + + // GOOD + test_dst(r, dst, 0, blend_255_round); + test_dst(r, dst, 0, blend_255_trunc); + test_dst(r, dst, 0, blend_256_plus1_round); + test_dst(r, dst, 0, blend_256_plus1_trunc); + test_dst(r, dst, 0, blend_256_round); + test_dst(r, dst, 0, blend_256_round_alt); + test_dst(r, dst, 0, blend_256_trunc); + test_dst(r, dst, 0, blend_double_trunc); + test_dst(r, dst, 0, blend_float_round); + test_dst(r, dst, 0, blend_float_trunc); + test_dst(r, dst, 0, blend_perfect); + + // BAD +} + +// We want 0 diff whenever src is fully opaque. +DEF_TEST(Blend_alpha_0xFF, r) { + const uint8_t alpha = 0xFF; + + // GOOD + test_alpha(r, alpha, 0, blend_255_round); + test_alpha(r, alpha, 0, blend_255_trunc); + test_alpha(r, alpha, 0, blend_256_plus1_round); + test_alpha(r, alpha, 0, blend_256_plus1_trunc); + test_alpha(r, alpha, 0, blend_256_round); + test_alpha(r, alpha, 0, blend_256_round_alt); + test_alpha(r, alpha, 0, blend_256_trunc); + test_alpha(r, alpha, 0, blend_double_trunc); + test_alpha(r, alpha, 0, blend_float_round); + test_alpha(r, alpha, 0, blend_float_trunc); + test_alpha(r, alpha, 0, blend_perfect); + + // BAD +} + +// We want 0 diff whenever dst is 0xFF. +DEF_TEST(Blend_dst_0xFF, r) { + const uint8_t dst = 0xFF; + + // GOOD + test_dst(r, dst, 0, blend_256_round); + test_dst(r, dst, 0, blend_256_round_alt); + test_dst(r, dst, 0, blend_double_trunc); + test_dst(r, dst, 0, blend_float_round); + test_dst(r, dst, 0, blend_float_trunc); + test_dst(r, dst, 0, blend_perfect); + + // BAD + test_dst(r, dst, 1, blend_255_round); + test_dst(r, dst, 1, blend_255_trunc); + test_dst(r, dst, 1, blend_256_plus1_round); + test_dst(r, dst, 1, blend_256_plus1_trunc); + test_dst(r, dst, 1, blend_256_trunc); +} + +// We'd like diff <= 1 everywhere. +DEF_TEST(Blend_alpha_Exhaustive, r) { + for (unsigned alpha = 0; alpha < 256; alpha++) { + // PERFECT + test_alpha(r, alpha, 0, blend_float_round); + test_alpha(r, alpha, 0, blend_perfect); + + // GOOD + test_alpha(r, alpha, 1, blend_255_round); + test_alpha(r, alpha, 1, blend_256_plus1_round); + test_alpha(r, alpha, 1, blend_256_round); + test_alpha(r, alpha, 1, blend_256_round_alt); + test_alpha(r, alpha, 1, blend_256_trunc); + test_alpha(r, alpha, 1, blend_double_trunc); + test_alpha(r, alpha, 1, blend_float_trunc); + + // BAD + test_alpha(r, alpha, 2, blend_255_trunc); + test_alpha(r, alpha, 2, blend_256_plus1_trunc); + } +} + +// We'd like diff <= 1 everywhere. +DEF_TEST(Blend_dst_Exhaustive, r) { + for (unsigned dst = 0; dst < 256; dst++) { + // PERFECT + test_dst(r, dst, 0, blend_float_round); + test_dst(r, dst, 0, blend_perfect); + + // GOOD + test_dst(r, dst, 1, blend_255_round); + test_dst(r, dst, 1, blend_256_plus1_round); + test_dst(r, dst, 1, blend_256_round); + test_dst(r, dst, 1, blend_256_round_alt); + test_dst(r, dst, 1, blend_256_trunc); + test_dst(r, dst, 1, blend_double_trunc); + test_dst(r, dst, 1, blend_float_trunc); + + // BAD + test_dst(r, dst, 2, blend_255_trunc); + test_dst(r, dst, 2, blend_256_plus1_trunc); + } +} +// Overall summary: +// PERFECT +// blend_double_round +// blend_float_round +// blend_perfect +// GOOD ENOUGH +// blend_double_trunc +// blend_float_trunc +// blend_256_round +// blend_256_round_alt +// NOT GOOD ENOUGH +// all others +// +// Algorithms that make sense to use in Skia: blend_256_round, blend_256_round_alt, blend_perfect -- cgit v1.2.3