blob: 00cb3fed27c2fdd16adbcea0645cf99dfd6c1541 [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
herb42da45d2016-04-08 14:24:37 -07008#include "SkBitmapProcShader.h"
reed6d3cef92016-01-22 01:04:29 -08009#include "SkColor.h"
reed5f34e8e2016-01-23 14:19:06 -080010#include "SkColorMatrixFilter.h"
reed7f225cf2016-01-23 19:37:56 -080011#include "SkGradientShader.h"
12#include "SkImage.h"
reeddd9ffea2016-02-18 12:39:14 -080013#include "SkPM4f.h"
reed7f225cf2016-01-23 19:37:56 -080014#include "SkShader.h"
15
reed6d3cef92016-01-22 01:04:29 -080016#include "Test.h"
17#include "SkRandom.h"
18
reed7f225cf2016-01-23 19:37:56 -080019const float kTolerance = 1.0f / (1 << 20);
20
21static bool nearly_equal(float a, float b, float tol = kTolerance) {
22 SkASSERT(tol >= 0);
23 return fabsf(a - b) <= tol;
24}
25
26static bool nearly_equal(const SkPM4f a, const SkPM4f& b, float tol = kTolerance) {
27 for (int i = 0; i < 4; ++i) {
28 if (!nearly_equal(a.fVec[i], b.fVec[i], tol)) {
29 return false;
30 }
31 }
32 return true;
33}
34
reed6d3cef92016-01-22 01:04:29 -080035DEF_TEST(SkColor4f_FromColor, reporter) {
36 const struct {
37 SkColor fC;
38 SkColor4f fC4;
39 } recs[] = {
40 { SK_ColorBLACK, { 1, 0, 0, 0 } },
41 { SK_ColorWHITE, { 1, 1, 1, 1 } },
42 { SK_ColorRED, { 1, 1, 0, 0 } },
43 { SK_ColorGREEN, { 1, 0, 1, 0 } },
44 { SK_ColorBLUE, { 1, 0, 0, 1 } },
45 { 0, { 0, 0, 0, 0 } },
46 { 0x55AAFF00, { 1/3.0f, 2/3.0f, 1, 0 } },
47 };
48
49 for (const auto& r : recs) {
50 SkColor4f c4 = SkColor4f::FromColor(r.fC);
51 REPORTER_ASSERT(reporter, c4 == r.fC4);
52 }
53}
54
reed7f225cf2016-01-23 19:37:56 -080055DEF_TEST(Color4f_premul, reporter) {
reed6d3cef92016-01-22 01:04:29 -080056 SkRandom rand;
57
58 for (int i = 0; i < 1000000; ++i) {
59 // First just test opaque colors, so that the premul should be exact
60 SkColor4f c4 {
61 1, rand.nextUScalar1(), rand.nextUScalar1(), rand.nextUScalar1()
62 };
63 SkPM4f pm4 = c4.premul();
reed93bb0802016-03-08 10:09:18 -080064 REPORTER_ASSERT(reporter, pm4.a() == c4.fA);
65 REPORTER_ASSERT(reporter, pm4.r() == c4.fA * c4.fR);
66 REPORTER_ASSERT(reporter, pm4.g() == c4.fA * c4.fG);
67 REPORTER_ASSERT(reporter, pm4.b() == c4.fA * c4.fB);
reed6d3cef92016-01-22 01:04:29 -080068
69 // We compare with a tolerance, in case our premul multiply is implemented at slightly
70 // different precision than the test code.
71 c4.fA = rand.nextUScalar1();
72 pm4 = c4.premul();
73 REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
reed93bb0802016-03-08 10:09:18 -080074 REPORTER_ASSERT(reporter, nearly_equal(pm4.r(), c4.fA * c4.fR));
75 REPORTER_ASSERT(reporter, nearly_equal(pm4.g(), c4.fA * c4.fG));
76 REPORTER_ASSERT(reporter, nearly_equal(pm4.b(), c4.fA * c4.fB));
reed6d3cef92016-01-22 01:04:29 -080077 }
78}
reed7f225cf2016-01-23 19:37:56 -080079
80//////////////////////////////////////////////////////////////////////////////////////////////////
81
reedd053ce92016-03-22 10:17:23 -070082static sk_sp<SkColorFilter> make_mode_cf() {
83 return SkColorFilter::MakeModeFilter(0xFFBB8855, SkXfermode::kPlus_Mode);
reed3b69b822016-01-24 11:15:14 -080084}
reed7f225cf2016-01-23 19:37:56 -080085
reedd053ce92016-03-22 10:17:23 -070086static sk_sp<SkColorFilter> make_mx_cf() {
reed3b69b822016-01-24 11:15:14 -080087 const float mx[] = {
88 0.5f, 0, 0, 0, 0.1f,
89 0, 0.5f, 0, 0, 0.2f,
90 0, 0, 1, 0, -0.1f,
91 0, 0, 0, 1, 0,
92 };
reedd053ce92016-03-22 10:17:23 -070093 return SkColorFilter::MakeMatrixFilterRowMajor255(mx);
reed3b69b822016-01-24 11:15:14 -080094}
95
reedd053ce92016-03-22 10:17:23 -070096static sk_sp<SkColorFilter> make_compose_cf() {
97 return SkColorFilter::MakeComposeFilter(make_mode_cf(), make_mx_cf());
reed3b69b822016-01-24 11:15:14 -080098}
99
reed5671c5b2016-03-09 14:47:34 -0800100static sk_sp<SkShader> make_color_sh() { return SkShader::MakeColorShader(0xFFBB8855); }
reed3b69b822016-01-24 11:15:14 -0800101
reed5671c5b2016-03-09 14:47:34 -0800102static sk_sp<SkShader> make_image_sh() {
reed7f225cf2016-01-23 19:37:56 -0800103 const SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
104 const SkPMColor pixels[] {
105 SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
106 SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
107 SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
108 SkPackARGB32(0xFF, 0xBB, 0x88, 0x55),
109 };
reed9ce9d672016-03-17 10:51:11 -0700110 sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, pixels, sizeof(SkPMColor) * 2)));
reed5671c5b2016-03-09 14:47:34 -0800111 return image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
reed7f225cf2016-01-23 19:37:56 -0800112}
113
reed5671c5b2016-03-09 14:47:34 -0800114static sk_sp<SkShader> make_grad_sh() {
reed93bb0802016-03-08 10:09:18 -0800115#if 0
reed7f225cf2016-01-23 19:37:56 -0800116 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);
reed93bb0802016-03-08 10:09:18 -0800119#else
120 // TODO: need to convert new gradient code to enforce PM4f --> RGBA order
121 return make_color_sh();
122#endif
reed7f225cf2016-01-23 19:37:56 -0800123}
124
reed5671c5b2016-03-09 14:47:34 -0800125static sk_sp<SkShader> make_cf_sh() {
reedd053ce92016-03-22 10:17:23 -0700126 return make_color_sh()->makeWithColorFilter(make_mx_cf());
reed3b69b822016-01-24 11:15:14 -0800127}
128
reed93bb0802016-03-08 10:09:18 -0800129static bool compare_spans(const SkPM4f span4f[], const SkPMColor span4b[], int count,
130 float tolerance = 1.0f/255) {
reed7f225cf2016-01-23 19:37:56 -0800131 for (int i = 0; i < count; ++i) {
132 SkPM4f c0 = SkPM4f::FromPMColor(span4b[i]);
133 SkPM4f c1 = span4f[i];
reed93bb0802016-03-08 10:09:18 -0800134 if (!nearly_equal(c0, c1, tolerance)) {
135 return false;
136 }
reed7f225cf2016-01-23 19:37:56 -0800137 }
reed93bb0802016-03-08 10:09:18 -0800138 return true;
reed7f225cf2016-01-23 19:37:56 -0800139}
140
141DEF_TEST(Color4f_shader, reporter) {
142 struct {
reed5671c5b2016-03-09 14:47:34 -0800143 sk_sp<SkShader> (*fFact)();
144 bool fSupports4f;
145 float fTolerance;
reed7f225cf2016-01-23 19:37:56 -0800146 } recs[] = {
fmalita55430a62016-02-23 13:26:28 -0800147 { make_color_sh, true, 1.0f/255 },
148 // PMColor 4f gradients are interpolated in 255-multiplied values, so we need a
149 // slightly relaxed tolerance to accommodate the cumulative precision deviation.
150 { make_grad_sh, true, 1.001f/255 },
151 { make_image_sh, false, 1.0f/255 },
152 { make_cf_sh, true, 1.0f/255 },
reed7f225cf2016-01-23 19:37:56 -0800153 };
154
155 SkPaint paint;
156 for (const auto& rec : recs) {
herb42da45d2016-04-08 14:24:37 -0700157 uint32_t storage[kSkBlitterContextSize];
reed5671c5b2016-03-09 14:47:34 -0800158 paint.setShader(rec.fFact());
fmalitad0c4e092016-02-22 17:19:04 -0800159 // Encourage 4f context selection. At some point we may need
160 // to instantiate two separate contexts for optimal 4b/4f selection.
161 const SkShader::ContextRec contextRec(paint, SkMatrix::I(), nullptr,
162 SkShader::ContextRec::kPM4f_DstType);
fmalita8d9f2e42016-02-22 10:39:41 -0800163 SkASSERT(paint.getShader()->contextSize(contextRec) <= sizeof(storage));
164 SkShader::Context* ctx = paint.getShader()->createContext(contextRec, storage);
fmalitaca058f52016-02-23 19:02:20 -0800165 if (rec.fSupports4f) {
reed7f225cf2016-01-23 19:37:56 -0800166 const int N = 100;
167 SkPM4f buffer4f[N];
168 ctx->shadeSpan4f(0, 0, buffer4f, N);
169 SkPMColor buffer4b[N];
170 ctx->shadeSpan(0, 0, buffer4b, N);
reed93bb0802016-03-08 10:09:18 -0800171 REPORTER_ASSERT(reporter, compare_spans(buffer4f, buffer4b, N, rec.fTolerance));
reed7f225cf2016-01-23 19:37:56 -0800172 }
173 ctx->~Context();
174 }
175}
176
reed7f225cf2016-01-23 19:37:56 -0800177DEF_TEST(Color4f_colorfilter, reporter) {
178 struct {
reedd053ce92016-03-22 10:17:23 -0700179 sk_sp<SkColorFilter> (*fFact)();
180 bool fSupports4f;
181 const char* fName;
reed7f225cf2016-01-23 19:37:56 -0800182 } recs[] = {
reed93bb0802016-03-08 10:09:18 -0800183 { make_mode_cf, true, "mode" },
184 { make_mx_cf, true, "matrix" },
185 { make_compose_cf, true, "compose" },
reed7f225cf2016-01-23 19:37:56 -0800186 };
187
188 // prepare the src
189 const int N = 100;
190 SkPMColor src4b[N];
191 SkPM4f src4f[N];
192 SkRandom rand;
193 for (int i = 0; i < N; ++i) {
194 src4b[i] = SkPreMultiplyColor(rand.nextU());
195 src4f[i] = SkPM4f::FromPMColor(src4b[i]);
196 }
197 // confirm that our srcs are (nearly) equal
reed93bb0802016-03-08 10:09:18 -0800198 REPORTER_ASSERT(reporter, compare_spans(src4f, src4b, N));
reed7f225cf2016-01-23 19:37:56 -0800199
200 for (const auto& rec : recs) {
reedd053ce92016-03-22 10:17:23 -0700201 auto filter(rec.fFact());
reedc5b12282016-02-19 13:38:53 -0800202 SkPMColor dst4b[N];
203 filter->filterSpan(src4b, N, dst4b);
204 SkPM4f dst4f[N];
205 filter->filterSpan4f(src4f, N, dst4f);
reed93bb0802016-03-08 10:09:18 -0800206 REPORTER_ASSERT(reporter, compare_spans(dst4f, dst4b, N));
reed7f225cf2016-01-23 19:37:56 -0800207 }
208}
reed31255652016-02-08 12:56:56 -0800209
210///////////////////////////////////////////////////////////////////////////////////////////////////
211
212typedef SkPM4f (*SkXfermodeProc4f)(const SkPM4f& src, const SkPM4f& dst);
213
214static bool compare_procs(SkXfermodeProc proc32, SkXfermodeProc4f proc4f) {
215 const float kTolerance = 1.0f / 255;
216
217 const SkColor colors[] = {
218 0, 0xFF000000, 0xFFFFFFFF, 0x80FF0000
219 };
220
221 for (auto s32 : colors) {
222 SkPMColor s_pm32 = SkPreMultiplyColor(s32);
223 SkPM4f s_pm4f = SkColor4f::FromColor(s32).premul();
224 for (auto d32 : colors) {
225 SkPMColor d_pm32 = SkPreMultiplyColor(d32);
226 SkPM4f d_pm4f = SkColor4f::FromColor(d32).premul();
227
228 SkPMColor r32 = proc32(s_pm32, d_pm32);
229 SkPM4f r4f = proc4f(s_pm4f, d_pm4f);
230
231 SkPM4f r32_4f = SkPM4f::FromPMColor(r32);
232 if (!nearly_equal(r4f, r32_4f, kTolerance)) {
233 return false;
234 }
235 }
236 }
237 return true;
238}
239
240// Check that our Proc and Proc4f return (nearly) the same results
241//
242DEF_TEST(Color4f_xfermode_proc4f, reporter) {
243 // TODO: extend xfermodes so that all cases can be tested.
244 //
245 for (int mode = SkXfermode::kClear_Mode; mode <= SkXfermode::kScreen_Mode; ++mode) {
246 SkXfermodeProc proc32 = SkXfermode::GetProc((SkXfermode::Mode)mode);
247 SkXfermodeProc4f proc4f = SkXfermode::GetProc4f((SkXfermode::Mode)mode);
248 REPORTER_ASSERT(reporter, compare_procs(proc32, proc4f));
249 }
250}