1#include "Test.h"
2#include "SkColor.h"
3
4#define ASSERT(x) REPORTER_ASSERT(r, x)
5
6static 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.
14typedef uint8_t(*Blend)(uint8_t dst, uint8_t src, uint8_t srcAlpha);
15
16// This is our golden algorithm.
17static 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
22static uint8_t abs_diff(uint8_t a, uint8_t b) {
23    const int diff = a - b;
24    return diff > 0 ? diff : -diff;
25}
26
27static 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.
39static 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.
50static 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
60static 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
64static 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
68static 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
72static 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
78static 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
84static 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
90static 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
96static 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
103static 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
109static 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
115static 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.
123DEF_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.
143DEF_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.
163DEF_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.
183DEF_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.
203DEF_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.
225DEF_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