1 #include "Test.h"
2 #include "SkColor.h"
3
4 #define ASSERT(x) REPORTER_ASSERT(r, x)
5
double_to_u8(double d)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.
blend_double_round(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
abs_diff(uint8_t a,uint8_t b)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
test(skiatest::Reporter * r,int maxDiff,Blend algorithm,uint8_t dst,uint8_t src,uint8_t alpha)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.
test_alpha(skiatest::Reporter * r,uint8_t alpha,int maxDiff,Blend algorithm)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.
test_dst(skiatest::Reporter * r,uint8_t dst,int maxDiff,Blend algorithm)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
blend_double_trunc(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_float_trunc(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_float_round(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_255_trunc(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_255_round(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_256_trunc(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_256_round(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_256_round_alt(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_256_plus1_trunc(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_256_plus1_round(uint8_t dst,uint8_t src,uint8_t srcAlpha)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
blend_perfect(uint8_t dst,uint8_t src,uint8_t srcAlpha)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.
DEF_TEST(Blend_alpha_0x00,r)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.
DEF_TEST(Blend_dst_0x00,r)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.
DEF_TEST(Blend_alpha_0xFF,r)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.
DEF_TEST(Blend_dst_0xFF,r)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.
DEF_TEST(Blend_alpha_Exhaustive,r)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.
DEF_TEST(Blend_dst_Exhaustive,r)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