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