1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkColor.h"
9 #include "SkColorMatrixFilter.h"
10 #include "SkGradientShader.h"
11 #include "SkImage.h"
12 #include "SkPM4f.h"
13 #include "SkShader.h"
14 
15 #include "Test.h"
16 #include "SkRandom.h"
17 
18 const float kTolerance = 1.0f / (1 << 20);
19 
nearly_equal(float a,float b,float tol=kTolerance)20 static bool nearly_equal(float a, float b, float tol = kTolerance) {
21     SkASSERT(tol >= 0);
22     return fabsf(a - b) <= tol;
23 }
24 
nearly_equal(const SkPM4f a,const SkPM4f & b,float tol=kTolerance)25 static bool nearly_equal(const SkPM4f a, const SkPM4f& b, float tol = kTolerance) {
26     for (int i = 0; i < 4; ++i) {
27         if (!nearly_equal(a.fVec[i], b.fVec[i], tol)) {
28             return false;
29         }
30     }
31     return true;
32 }
33 
DEF_TEST(SkColor4f_FromColor,reporter)34 DEF_TEST(SkColor4f_FromColor, reporter) {
35     const struct {
36         SkColor     fC;
37         SkColor4f   fC4;
38     } recs[] = {
39         { SK_ColorBLACK, { 1, 0, 0, 0 } },
40         { SK_ColorWHITE, { 1, 1, 1, 1 } },
41         { SK_ColorRED,   { 1, 1, 0, 0 } },
42         { SK_ColorGREEN, { 1, 0, 1, 0 } },
43         { SK_ColorBLUE,  { 1, 0, 0, 1 } },
44         { 0,             { 0, 0, 0, 0 } },
45         { 0x55AAFF00,    { 1/3.0f, 2/3.0f, 1, 0 } },
46     };
47 
48     for (const auto& r : recs) {
49         SkColor4f c4 = SkColor4f::FromColor(r.fC);
50         REPORTER_ASSERT(reporter, c4 == r.fC4);
51     }
52 }
53 
DEF_TEST(Color4f_premul,reporter)54 DEF_TEST(Color4f_premul, reporter) {
55     SkRandom rand;
56 
57     for (int i = 0; i < 1000000; ++i) {
58         // First just test opaque colors, so that the premul should be exact
59         SkColor4f c4 {
60             1, rand.nextUScalar1(), rand.nextUScalar1(), rand.nextUScalar1()
61         };
62         SkPM4f pm4 = c4.premul();
63         REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
64         REPORTER_ASSERT(reporter, pm4.fVec[SK_R_INDEX] == c4.fA * c4.fR);
65         REPORTER_ASSERT(reporter, pm4.fVec[SK_G_INDEX] == c4.fA * c4.fG);
66         REPORTER_ASSERT(reporter, pm4.fVec[SK_B_INDEX] == c4.fA * c4.fB);
67 
68         // We compare with a tolerance, in case our premul multiply is implemented at slightly
69         // different precision than the test code.
70         c4.fA = rand.nextUScalar1();
71         pm4 = c4.premul();
72         REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
73         REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_R_INDEX], c4.fA * c4.fR));
74         REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_G_INDEX], c4.fA * c4.fG));
75         REPORTER_ASSERT(reporter, nearly_equal(pm4.fVec[SK_B_INDEX], c4.fA * c4.fB));
76     }
77 }
78 
79 //////////////////////////////////////////////////////////////////////////////////////////////////
80 
make_mode_cf()81 static SkColorFilter* make_mode_cf() {
82     return SkColorFilter::CreateModeFilter(0xFFBB8855, SkXfermode::kPlus_Mode);
83 }
84 
make_mx_cf()85 static SkColorFilter* make_mx_cf() {
86     const float mx[] = {
87         0.5f, 0,    0, 0, 0.1f,
88         0,    0.5f, 0, 0, 0.2f,
89         0,    0,    1, 0, -0.1f,
90         0,    0,    0, 1, 0,
91     };
92     return SkColorMatrixFilter::Create(mx);
93 }
94 
make_compose_cf()95 static SkColorFilter* make_compose_cf() {
96     SkAutoTUnref<SkColorFilter> cf0(make_mode_cf());
97     SkAutoTUnref<SkColorFilter> cf1(make_mx_cf());
98     return SkColorFilter::CreateComposeFilter(cf0, cf1);
99 }
100 
make_color_sh()101 static SkShader* make_color_sh() { return SkShader::CreateColorShader(0xFFBB8855); }
102 
make_image_sh()103 static SkShader* make_image_sh() {
104     const SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
105     const SkPMColor pixels[] {
106         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
107         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
108         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
109         SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
110     };
111     SkAutoTUnref<SkImage> image(SkImage::NewRasterCopy(info, pixels, sizeof(SkPMColor) * 2));
112     return image->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
113 }
114 
make_grad_sh()115 static SkShader* make_grad_sh() {
116     const SkPoint pts[] {{ 0, 0 }, { 100, 100 }};
117     const SkColor colors[] { SK_ColorRED, SK_ColorBLUE };
118     return SkGradientShader::CreateLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
119 }
120 
make_cf_sh()121 static SkShader* make_cf_sh() {
122     SkAutoTUnref<SkColorFilter> filter(make_mx_cf());
123     SkAutoTUnref<SkShader> shader(make_color_sh());
124     return shader->newWithColorFilter(filter);
125 }
126 
compare_spans(const SkPM4f span4f[],const SkPMColor span4b[],int count,skiatest::Reporter * reporter,float tolerance=1.0f/255)127 static void compare_spans(const SkPM4f span4f[], const SkPMColor span4b[], int count,
128                           skiatest::Reporter* reporter, float tolerance = 1.0f/255) {
129     for (int i = 0; i < count; ++i) {
130         SkPM4f c0 = SkPM4f::FromPMColor(span4b[i]);
131         SkPM4f c1 = span4f[i];
132         REPORTER_ASSERT(reporter, nearly_equal(c0, c1, tolerance));
133     }
134 }
135 
DEF_TEST(Color4f_shader,reporter)136 DEF_TEST(Color4f_shader, reporter) {
137     struct {
138         SkShader* (*fFact)();
139         bool      fSupports4f;
140         float     fTolerance;
141     } recs[] = {
142         { make_color_sh, true,  1.0f/255   },
143         // PMColor 4f gradients are interpolated in 255-multiplied values, so we need a
144         // slightly relaxed tolerance to accommodate the cumulative precision deviation.
145         { make_grad_sh,  true,  1.001f/255 },
146         { make_image_sh, false, 1.0f/255   },
147         { make_cf_sh,    true,  1.0f/255   },
148     };
149 
150     SkPaint paint;
151     for (const auto& rec : recs) {
152         uint32_t storage[200];
153         paint.setShader(rec.fFact())->unref();
154         // Encourage 4f context selection. At some point we may need
155         // to instantiate two separate contexts for optimal 4b/4f selection.
156         const SkShader::ContextRec contextRec(paint, SkMatrix::I(), nullptr,
157                                               SkShader::ContextRec::kPM4f_DstType);
158         SkASSERT(paint.getShader()->contextSize(contextRec) <= sizeof(storage));
159         SkShader::Context* ctx = paint.getShader()->createContext(contextRec, storage);
160         if (rec.fSupports4f) {
161             const int N = 100;
162             SkPM4f buffer4f[N];
163             ctx->shadeSpan4f(0, 0, buffer4f, N);
164             SkPMColor buffer4b[N];
165             ctx->shadeSpan(0, 0, buffer4b, N);
166             compare_spans(buffer4f, buffer4b, N, reporter, rec.fTolerance);
167         }
168         ctx->~Context();
169     }
170 }
171 
DEF_TEST(Color4f_colorfilter,reporter)172 DEF_TEST(Color4f_colorfilter, reporter) {
173     struct {
174         SkColorFilter* (*fFact)();
175         bool           fSupports4f;
176     } recs[] = {
177         { make_mode_cf,     true },
178         { make_mx_cf,       true },
179         { make_compose_cf,  true },
180     };
181 
182     // prepare the src
183     const int N = 100;
184     SkPMColor src4b[N];
185     SkPM4f    src4f[N];
186     SkRandom rand;
187     for (int i = 0; i < N; ++i) {
188         src4b[i] = SkPreMultiplyColor(rand.nextU());
189         src4f[i] = SkPM4f::FromPMColor(src4b[i]);
190     }
191     // confirm that our srcs are (nearly) equal
192     compare_spans(src4f, src4b, N, reporter);
193 
194     for (const auto& rec : recs) {
195         SkAutoTUnref<SkColorFilter> filter(rec.fFact());
196         SkPMColor dst4b[N];
197         filter->filterSpan(src4b, N, dst4b);
198         SkPM4f dst4f[N];
199         filter->filterSpan4f(src4f, N, dst4f);
200         compare_spans(dst4f, dst4b, N, reporter);
201     }
202 }
203 
204 ///////////////////////////////////////////////////////////////////////////////////////////////////
205 
206 typedef SkPM4f (*SkXfermodeProc4f)(const SkPM4f& src, const SkPM4f& dst);
207 
compare_procs(SkXfermodeProc proc32,SkXfermodeProc4f proc4f)208 static bool compare_procs(SkXfermodeProc proc32, SkXfermodeProc4f proc4f) {
209     const float kTolerance = 1.0f / 255;
210 
211     const SkColor colors[] = {
212         0, 0xFF000000, 0xFFFFFFFF, 0x80FF0000
213     };
214 
215     for (auto s32 : colors) {
216         SkPMColor s_pm32 = SkPreMultiplyColor(s32);
217         SkPM4f    s_pm4f = SkColor4f::FromColor(s32).premul();
218         for (auto d32 : colors) {
219             SkPMColor d_pm32 = SkPreMultiplyColor(d32);
220             SkPM4f    d_pm4f = SkColor4f::FromColor(d32).premul();
221 
222             SkPMColor r32 = proc32(s_pm32, d_pm32);
223             SkPM4f    r4f = proc4f(s_pm4f, d_pm4f);
224 
225             SkPM4f r32_4f = SkPM4f::FromPMColor(r32);
226             if (!nearly_equal(r4f, r32_4f, kTolerance)) {
227                 return false;
228             }
229         }
230     }
231     return true;
232 }
233 
234 // Check that our Proc and Proc4f return (nearly) the same results
235 //
DEF_TEST(Color4f_xfermode_proc4f,reporter)236 DEF_TEST(Color4f_xfermode_proc4f, reporter) {
237     // TODO: extend xfermodes so that all cases can be tested.
238     //
239     for (int mode = SkXfermode::kClear_Mode; mode <= SkXfermode::kScreen_Mode; ++mode) {
240         SkXfermodeProc   proc32 = SkXfermode::GetProc((SkXfermode::Mode)mode);
241         SkXfermodeProc4f proc4f = SkXfermode::GetProc4f((SkXfermode::Mode)mode);
242         REPORTER_ASSERT(reporter, compare_procs(proc32, proc4f));
243     }
244 }
245