Home | History | Annotate | Download | only in tests
      1 #include "Test.h"
      2 #include "SkColor.h"
      3 
      4 #define ASSERT(x) REPORTER_ASSERT(r, x)
      5 
      6 static uint8_t double_to_u8(double d) {
      7     SkASSERT(d >= 0);
      8     SkASSERT(d < 256);
      9     return uint8_t(d);
     10 }
     11 
     12 // All algorithms we're testing have this interface.
     13 // We want a single channel blend, src over dst, assuming src is premultiplied by srcAlpha.
     14 typedef uint8_t(*Blend)(uint8_t dst, uint8_t src, uint8_t srcAlpha);
     15 
     16 // This is our golden algorithm.
     17 static uint8_t blend_double_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     18     SkASSERT(src <= srcAlpha);
     19     return double_to_u8(0.5 + src + dst * (255.0 - srcAlpha) / 255.0);
     20 }
     21 
     22 static uint8_t abs_diff(uint8_t a, uint8_t b) {
     23     const int diff = a - b;
     24     return diff > 0 ? diff : -diff;
     25 }
     26 
     27 static void test(skiatest::Reporter* r, int maxDiff, Blend algorithm,
     28                  uint8_t dst, uint8_t src, uint8_t alpha) {
     29     const uint8_t golden = blend_double_round(dst, src, alpha);
     30     const uint8_t  blend =          algorithm(dst, src, alpha);
     31     if (abs_diff(blend, golden) > maxDiff) {
     32         SkDebugf("dst %02x, src %02x, alpha %02x, |%02x - %02x| > %d\n",
     33                  dst, src, alpha, blend, golden, maxDiff);
     34         ASSERT(abs_diff(blend, golden) <= maxDiff);
     35     }
     36 }
     37 
     38 // Exhaustively compare an algorithm against our golden, for a given alpha.
     39 static void test_alpha(skiatest::Reporter* r, uint8_t alpha, int maxDiff, Blend algorithm) {
     40     SkASSERT(maxDiff >= 0);
     41 
     42     for (unsigned src = 0; src <= alpha; src++) {
     43         for (unsigned dst = 0; dst < 256; dst++) {
     44             test(r, maxDiff, algorithm, dst, src, alpha);
     45         }
     46     }
     47 }
     48 
     49 // Exhaustively compare an algorithm against our golden, for a given dst.
     50 static void test_dst(skiatest::Reporter* r, uint8_t dst, int maxDiff, Blend algorithm) {
     51     SkASSERT(maxDiff >= 0);
     52 
     53     for (unsigned alpha = 0; alpha < 256; alpha++) {
     54         for (unsigned src = 0; src <= alpha; src++) {
     55             test(r, maxDiff, algorithm, dst, src, alpha);
     56         }
     57     }
     58 }
     59 
     60 static uint8_t blend_double_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     61     return double_to_u8(src + dst * (255.0 - srcAlpha) / 255.0);
     62 }
     63 
     64 static uint8_t blend_float_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     65     return double_to_u8(src + dst * (255.0f - srcAlpha) / 255.0f);
     66 }
     67 
     68 static uint8_t blend_float_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     69     return double_to_u8(0.5f + src + dst * (255.0f - srcAlpha) / 255.0f);
     70 }
     71 
     72 static uint8_t blend_255_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     73     const uint16_t invAlpha = 255 - srcAlpha;
     74     const uint16_t product = dst * invAlpha;
     75     return src + (product >> 8);
     76 }
     77 
     78 static uint8_t blend_255_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     79     const uint16_t invAlpha = 255 - srcAlpha;
     80     const uint16_t product = dst * invAlpha + 128;
     81     return src + (product >> 8);
     82 }
     83 
     84 static uint8_t blend_256_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     85     const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7));
     86     const uint16_t product = dst * invAlpha;
     87     return src + (product >> 8);
     88 }
     89 
     90 static uint8_t blend_256_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     91     const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7));
     92     const uint16_t product = dst * invAlpha + 128;
     93     return src + (product >> 8);
     94 }
     95 
     96 static uint8_t blend_256_round_alt(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
     97     const uint8_t invAlpha8 = 255 - srcAlpha;
     98     const uint16_t invAlpha = invAlpha8 + (invAlpha8 >> 7);
     99     const uint16_t product = dst * invAlpha + 128;
    100     return src + (product >> 8);
    101 }
    102 
    103 static uint8_t blend_256_plus1_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
    104     const uint16_t invAlpha = 256 - (srcAlpha + 1);
    105     const uint16_t product = dst * invAlpha;
    106     return src + (product >> 8);
    107 }
    108 
    109 static uint8_t blend_256_plus1_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
    110     const uint16_t invAlpha = 256 - (srcAlpha + 1);
    111     const uint16_t product = dst * invAlpha + 128;
    112     return src + (product >> 8);
    113 }
    114 
    115 static uint8_t blend_perfect(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
    116     const uint8_t invAlpha = 255 - srcAlpha;
    117     const uint16_t product = dst * invAlpha + 128;
    118     return src + ((product + (product >> 8)) >> 8);
    119 }
    120 
    121 
    122 // We want 0 diff whenever src is fully transparent.
    123 DEF_TEST(Blend_alpha_0x00, r) {
    124     const uint8_t alpha = 0x00;
    125 
    126     // GOOD
    127     test_alpha(r, alpha, 0, blend_256_round);
    128     test_alpha(r, alpha, 0, blend_256_round_alt);
    129     test_alpha(r, alpha, 0, blend_256_trunc);
    130     test_alpha(r, alpha, 0, blend_double_trunc);
    131     test_alpha(r, alpha, 0, blend_float_round);
    132     test_alpha(r, alpha, 0, blend_float_trunc);
    133     test_alpha(r, alpha, 0, blend_perfect);
    134 
    135     // BAD
    136     test_alpha(r, alpha, 1, blend_255_round);
    137     test_alpha(r, alpha, 1, blend_255_trunc);
    138     test_alpha(r, alpha, 1, blend_256_plus1_round);
    139     test_alpha(r, alpha, 1, blend_256_plus1_trunc);
    140 }
    141 
    142 // We want 0 diff whenever dst is 0.
    143 DEF_TEST(Blend_dst_0x00, r) {
    144     const uint8_t dst = 0x00;
    145 
    146     // GOOD
    147     test_dst(r, dst, 0, blend_255_round);
    148     test_dst(r, dst, 0, blend_255_trunc);
    149     test_dst(r, dst, 0, blend_256_plus1_round);
    150     test_dst(r, dst, 0, blend_256_plus1_trunc);
    151     test_dst(r, dst, 0, blend_256_round);
    152     test_dst(r, dst, 0, blend_256_round_alt);
    153     test_dst(r, dst, 0, blend_256_trunc);
    154     test_dst(r, dst, 0, blend_double_trunc);
    155     test_dst(r, dst, 0, blend_float_round);
    156     test_dst(r, dst, 0, blend_float_trunc);
    157     test_dst(r, dst, 0, blend_perfect);
    158 
    159     // BAD
    160 }
    161 
    162 // We want 0 diff whenever src is fully opaque.
    163 DEF_TEST(Blend_alpha_0xFF, r) {
    164     const uint8_t alpha = 0xFF;
    165 
    166     // GOOD
    167     test_alpha(r, alpha, 0, blend_255_round);
    168     test_alpha(r, alpha, 0, blend_255_trunc);
    169     test_alpha(r, alpha, 0, blend_256_plus1_round);
    170     test_alpha(r, alpha, 0, blend_256_plus1_trunc);
    171     test_alpha(r, alpha, 0, blend_256_round);
    172     test_alpha(r, alpha, 0, blend_256_round_alt);
    173     test_alpha(r, alpha, 0, blend_256_trunc);
    174     test_alpha(r, alpha, 0, blend_double_trunc);
    175     test_alpha(r, alpha, 0, blend_float_round);
    176     test_alpha(r, alpha, 0, blend_float_trunc);
    177     test_alpha(r, alpha, 0, blend_perfect);
    178 
    179     // BAD
    180 }
    181 
    182 // We want 0 diff whenever dst is 0xFF.
    183 DEF_TEST(Blend_dst_0xFF, r) {
    184     const uint8_t dst = 0xFF;
    185 
    186     // GOOD
    187     test_dst(r, dst, 0, blend_256_round);
    188     test_dst(r, dst, 0, blend_256_round_alt);
    189     test_dst(r, dst, 0, blend_double_trunc);
    190     test_dst(r, dst, 0, blend_float_round);
    191     test_dst(r, dst, 0, blend_float_trunc);
    192     test_dst(r, dst, 0, blend_perfect);
    193 
    194     // BAD
    195     test_dst(r, dst, 1, blend_255_round);
    196     test_dst(r, dst, 1, blend_255_trunc);
    197     test_dst(r, dst, 1, blend_256_plus1_round);
    198     test_dst(r, dst, 1, blend_256_plus1_trunc);
    199     test_dst(r, dst, 1, blend_256_trunc);
    200 }
    201 
    202 // We'd like diff <= 1 everywhere.
    203 DEF_TEST(Blend_alpha_Exhaustive, r) {
    204     for (unsigned alpha = 0; alpha < 256; alpha++) {
    205         // PERFECT
    206         test_alpha(r, alpha, 0, blend_float_round);
    207         test_alpha(r, alpha, 0, blend_perfect);
    208 
    209         // GOOD
    210         test_alpha(r, alpha, 1, blend_255_round);
    211         test_alpha(r, alpha, 1, blend_256_plus1_round);
    212         test_alpha(r, alpha, 1, blend_256_round);
    213         test_alpha(r, alpha, 1, blend_256_round_alt);
    214         test_alpha(r, alpha, 1, blend_256_trunc);
    215         test_alpha(r, alpha, 1, blend_double_trunc);
    216         test_alpha(r, alpha, 1, blend_float_trunc);
    217 
    218         // BAD
    219         test_alpha(r, alpha, 2, blend_255_trunc);
    220         test_alpha(r, alpha, 2, blend_256_plus1_trunc);
    221     }
    222 }
    223 
    224 // We'd like diff <= 1 everywhere.
    225 DEF_TEST(Blend_dst_Exhaustive, r) {
    226     for (unsigned dst = 0; dst < 256; dst++) {
    227         // PERFECT
    228         test_dst(r, dst, 0, blend_float_round);
    229         test_dst(r, dst, 0, blend_perfect);
    230 
    231         // GOOD
    232         test_dst(r, dst, 1, blend_255_round);
    233         test_dst(r, dst, 1, blend_256_plus1_round);
    234         test_dst(r, dst, 1, blend_256_round);
    235         test_dst(r, dst, 1, blend_256_round_alt);
    236         test_dst(r, dst, 1, blend_256_trunc);
    237         test_dst(r, dst, 1, blend_double_trunc);
    238         test_dst(r, dst, 1, blend_float_trunc);
    239 
    240         // BAD
    241         test_dst(r, dst, 2, blend_255_trunc);
    242         test_dst(r, dst, 2, blend_256_plus1_trunc);
    243     }
    244 }
    245 // Overall summary:
    246 // PERFECT
    247 //  blend_double_round
    248 //  blend_float_round
    249 //  blend_perfect
    250 // GOOD ENOUGH
    251 //  blend_double_trunc
    252 //  blend_float_trunc
    253 //  blend_256_round
    254 //  blend_256_round_alt
    255 // NOT GOOD ENOUGH
    256 //  all others
    257 //
    258 //  Algorithms that make sense to use in Skia: blend_256_round, blend_256_round_alt, blend_perfect
    259