blob: ec40000dbb8fa2aeaa34b147e8ea205ed90dabfa [file] [log] [blame]
reed6d3cef92016-01-22 01:04:29 -08001/*
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"
reed5f34e8e2016-01-23 14:19:06 -08009#include "SkColorMatrixFilter.h"
reed7f225cf2016-01-23 19:37:56 -080010#include "SkGradientShader.h"
11#include "SkImage.h"
reeddd9ffea2016-02-18 12:39:14 -080012#include "SkPM4f.h"
reed7f225cf2016-01-23 19:37:56 -080013#include "SkShader.h"
14
reed6d3cef92016-01-22 01:04:29 -080015#include "Test.h"
16#include "SkRandom.h"
17
reed7f225cf2016-01-23 19:37:56 -080018const float kTolerance = 1.0f / (1 << 20);
19
20static bool nearly_equal(float a, float b, float tol = kTolerance) {
21 SkASSERT(tol >= 0);
22 return fabsf(a - b) <= tol;
23}
24
25static 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
reed6d3cef92016-01-22 01:04:29 -080034DEF_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
reed7f225cf2016-01-23 19:37:56 -080054DEF_TEST(Color4f_premul, reporter) {
reed6d3cef92016-01-22 01:04:29 -080055 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();
reed93bb0802016-03-08 10:09:18 -080063 REPORTER_ASSERT(reporter, pm4.a() == c4.fA);
64 REPORTER_ASSERT(reporter, pm4.r() == c4.fA * c4.fR);
65 REPORTER_ASSERT(reporter, pm4.g() == c4.fA * c4.fG);
66 REPORTER_ASSERT(reporter, pm4.b() == c4.fA * c4.fB);
reed6d3cef92016-01-22 01:04:29 -080067
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);
reed93bb0802016-03-08 10:09:18 -080073 REPORTER_ASSERT(reporter, nearly_equal(pm4.r(), c4.fA * c4.fR));
74 REPORTER_ASSERT(reporter, nearly_equal(pm4.g(), c4.fA * c4.fG));
75 REPORTER_ASSERT(reporter, nearly_equal(pm4.b(), c4.fA * c4.fB));
reed6d3cef92016-01-22 01:04:29 -080076 }
77}
reed7f225cf2016-01-23 19:37:56 -080078
79//////////////////////////////////////////////////////////////////////////////////////////////////
80
reed3b69b822016-01-24 11:15:14 -080081static SkColorFilter* make_mode_cf() {
82 return SkColorFilter::CreateModeFilter(0xFFBB8855, SkXfermode::kPlus_Mode);
83}
reed7f225cf2016-01-23 19:37:56 -080084
reed3b69b822016-01-24 11:15:14 -080085static 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
95static 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
101static SkShader* make_color_sh() { return SkShader::CreateColorShader(0xFFBB8855); }
102
103static SkShader* make_image_sh() {
reed7f225cf2016-01-23 19:37:56 -0800104 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
reed3b69b822016-01-24 11:15:14 -0800115static SkShader* make_grad_sh() {
reed93bb0802016-03-08 10:09:18 -0800116#if 0
reed7f225cf2016-01-23 19:37:56 -0800117 const SkPoint pts[] {{ 0, 0 }, { 100, 100 }};
118 const SkColor colors[] { SK_ColorRED, SK_ColorBLUE };
119 return SkGradientShader::CreateLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode);
reed93bb0802016-03-08 10:09:18 -0800120#else
121 // TODO: need to convert new gradient code to enforce PM4f --> RGBA order
122 return make_color_sh();
123#endif
reed7f225cf2016-01-23 19:37:56 -0800124}
125
reed3b69b822016-01-24 11:15:14 -0800126static SkShader* make_cf_sh() {
127 SkAutoTUnref<SkColorFilter> filter(make_mx_cf());
128 SkAutoTUnref<SkShader> shader(make_color_sh());
129 return shader->newWithColorFilter(filter);
130}
131
reed93bb0802016-03-08 10:09:18 -0800132static bool compare_spans(const SkPM4f span4f[], const SkPMColor span4b[], int count,
133 float tolerance = 1.0f/255) {
reed7f225cf2016-01-23 19:37:56 -0800134 for (int i = 0; i < count; ++i) {
135 SkPM4f c0 = SkPM4f::FromPMColor(span4b[i]);
136 SkPM4f c1 = span4f[i];
reed93bb0802016-03-08 10:09:18 -0800137 if (!nearly_equal(c0, c1, tolerance)) {
138 return false;
139 }
reed7f225cf2016-01-23 19:37:56 -0800140 }
reed93bb0802016-03-08 10:09:18 -0800141 return true;
reed7f225cf2016-01-23 19:37:56 -0800142}
143
144DEF_TEST(Color4f_shader, reporter) {
145 struct {
fmalita55430a62016-02-23 13:26:28 -0800146 SkShader* (*fFact)();
147 bool fSupports4f;
148 float fTolerance;
reed7f225cf2016-01-23 19:37:56 -0800149 } recs[] = {
fmalita55430a62016-02-23 13:26:28 -0800150 { make_color_sh, true, 1.0f/255 },
151 // PMColor 4f gradients are interpolated in 255-multiplied values, so we need a
152 // slightly relaxed tolerance to accommodate the cumulative precision deviation.
153 { make_grad_sh, true, 1.001f/255 },
154 { make_image_sh, false, 1.0f/255 },
155 { make_cf_sh, true, 1.0f/255 },
reed7f225cf2016-01-23 19:37:56 -0800156 };
157
158 SkPaint paint;
159 for (const auto& rec : recs) {
reedd8829012016-03-04 11:07:43 -0800160 uint32_t storage[300];
reed7f225cf2016-01-23 19:37:56 -0800161 paint.setShader(rec.fFact())->unref();
fmalitad0c4e092016-02-22 17:19:04 -0800162 // Encourage 4f context selection. At some point we may need
163 // to instantiate two separate contexts for optimal 4b/4f selection.
164 const SkShader::ContextRec contextRec(paint, SkMatrix::I(), nullptr,
165 SkShader::ContextRec::kPM4f_DstType);
fmalita8d9f2e42016-02-22 10:39:41 -0800166 SkASSERT(paint.getShader()->contextSize(contextRec) <= sizeof(storage));
167 SkShader::Context* ctx = paint.getShader()->createContext(contextRec, storage);
fmalitaca058f52016-02-23 19:02:20 -0800168 if (rec.fSupports4f) {
reed7f225cf2016-01-23 19:37:56 -0800169 const int N = 100;
170 SkPM4f buffer4f[N];
171 ctx->shadeSpan4f(0, 0, buffer4f, N);
172 SkPMColor buffer4b[N];
173 ctx->shadeSpan(0, 0, buffer4b, N);
reed93bb0802016-03-08 10:09:18 -0800174 REPORTER_ASSERT(reporter, compare_spans(buffer4f, buffer4b, N, rec.fTolerance));
reed7f225cf2016-01-23 19:37:56 -0800175 }
176 ctx->~Context();
177 }
178}
179
reed7f225cf2016-01-23 19:37:56 -0800180DEF_TEST(Color4f_colorfilter, reporter) {
181 struct {
182 SkColorFilter* (*fFact)();
183 bool fSupports4f;
reed93bb0802016-03-08 10:09:18 -0800184 const char* fName;
reed7f225cf2016-01-23 19:37:56 -0800185 } recs[] = {
reed93bb0802016-03-08 10:09:18 -0800186 { make_mode_cf, true, "mode" },
187 { make_mx_cf, true, "matrix" },
188 { make_compose_cf, true, "compose" },
reed7f225cf2016-01-23 19:37:56 -0800189 };
190
191 // prepare the src
192 const int N = 100;
193 SkPMColor src4b[N];
194 SkPM4f src4f[N];
195 SkRandom rand;
196 for (int i = 0; i < N; ++i) {
197 src4b[i] = SkPreMultiplyColor(rand.nextU());
198 src4f[i] = SkPM4f::FromPMColor(src4b[i]);
199 }
200 // confirm that our srcs are (nearly) equal
reed93bb0802016-03-08 10:09:18 -0800201 REPORTER_ASSERT(reporter, compare_spans(src4f, src4b, N));
reed7f225cf2016-01-23 19:37:56 -0800202
203 for (const auto& rec : recs) {
204 SkAutoTUnref<SkColorFilter> filter(rec.fFact());
reedc5b12282016-02-19 13:38:53 -0800205 SkPMColor dst4b[N];
206 filter->filterSpan(src4b, N, dst4b);
207 SkPM4f dst4f[N];
208 filter->filterSpan4f(src4f, N, dst4f);
reed93bb0802016-03-08 10:09:18 -0800209 REPORTER_ASSERT(reporter, compare_spans(dst4f, dst4b, N));
reed7f225cf2016-01-23 19:37:56 -0800210 }
211}
reed31255652016-02-08 12:56:56 -0800212
213///////////////////////////////////////////////////////////////////////////////////////////////////
214
215typedef SkPM4f (*SkXfermodeProc4f)(const SkPM4f& src, const SkPM4f& dst);
216
217static bool compare_procs(SkXfermodeProc proc32, SkXfermodeProc4f proc4f) {
218 const float kTolerance = 1.0f / 255;
219
220 const SkColor colors[] = {
221 0, 0xFF000000, 0xFFFFFFFF, 0x80FF0000
222 };
223
224 for (auto s32 : colors) {
225 SkPMColor s_pm32 = SkPreMultiplyColor(s32);
226 SkPM4f s_pm4f = SkColor4f::FromColor(s32).premul();
227 for (auto d32 : colors) {
228 SkPMColor d_pm32 = SkPreMultiplyColor(d32);
229 SkPM4f d_pm4f = SkColor4f::FromColor(d32).premul();
230
231 SkPMColor r32 = proc32(s_pm32, d_pm32);
232 SkPM4f r4f = proc4f(s_pm4f, d_pm4f);
233
234 SkPM4f r32_4f = SkPM4f::FromPMColor(r32);
235 if (!nearly_equal(r4f, r32_4f, kTolerance)) {
236 return false;
237 }
238 }
239 }
240 return true;
241}
242
243// Check that our Proc and Proc4f return (nearly) the same results
244//
245DEF_TEST(Color4f_xfermode_proc4f, reporter) {
246 // TODO: extend xfermodes so that all cases can be tested.
247 //
248 for (int mode = SkXfermode::kClear_Mode; mode <= SkXfermode::kScreen_Mode; ++mode) {
249 SkXfermodeProc proc32 = SkXfermode::GetProc((SkXfermode::Mode)mode);
250 SkXfermodeProc4f proc4f = SkXfermode::GetProc4f((SkXfermode::Mode)mode);
251 REPORTER_ASSERT(reporter, compare_procs(proc32, proc4f));
252 }
253}