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