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