blob: 9b42e5629cbb15eac1f57758e82c1de59658eabc [file] [log] [blame]
commit-bot@chromium.org7f428a92014-02-25 21:31:03 +00001#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 by srcAlpha.
8typedef uint8_t(*Blend)(uint8_t dst, uint8_t src, uint8_t srcAlpha);
9
10// This is our golden algorithm.
11static 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
16static uint8_t abs_diff(uint8_t a, uint8_t b) {
17 const int diff = a - b;
18 return diff > 0 ? diff : -diff;
19}
20
21static 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.
33static 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.
44static void test_dst(skiatest::Reporter* r, uint8_t dst, int maxDiff, Blend algorithm) {
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
54static 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
58static 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
62static 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
66static 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
72static 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
78static 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
84static 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
90static 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
97static 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
103static 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
109static 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.
117DEF_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.
137DEF_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.
157DEF_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.
177DEF_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.
197DEF_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.
219DEF_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