blob: d0b0d77ed43a8a1547b07f501c49850194d65e0e [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
reed@google.com43c50c82011-04-14 15:50:52 +00008#include "SkColor.h"
9#include "SkColorFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000010#include "SkColorPriv.h"
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +000011#include "SkLumaColorFilter.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000012#include "SkReadBuffer.h"
13#include "SkWriteBuffer.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000014#include "SkRandom.h"
15#include "SkXfermode.h"
16#include "Test.h"
reed@google.com43c50c82011-04-14 15:50:52 +000017
reed@google.com35348222013-10-16 13:05:06 +000018static SkColorFilter* reincarnate_colorfilter(SkFlattenable* obj) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000019 SkWriteBuffer wb;
djsollen@google.comcefc8652012-03-26 15:52:10 +000020 wb.writeFlattenable(obj);
reed@google.com43c50c82011-04-14 15:50:52 +000021
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000022 size_t size = wb.bytesWritten();
reed@google.com43c50c82011-04-14 15:50:52 +000023 SkAutoSMalloc<1024> storage(size);
24 // make a copy into storage
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000025 wb.writeToMemory(storage.get());
reed@google.com43c50c82011-04-14 15:50:52 +000026
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000027 SkReadBuffer rb(storage.get(), size);
reed@google.com35348222013-10-16 13:05:06 +000028 return rb.readColorFilter();
reed@google.com43c50c82011-04-14 15:50:52 +000029}
30
31///////////////////////////////////////////////////////////////////////////////
32
reeddc812222015-03-05 07:21:02 -080033static SkColorFilter* make_filter() {
34 // pick a filter that cannot compose with itself via newComposed()
35 return SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kColorBurn_Mode);
36}
37
38static void test_composecolorfilter_limit(skiatest::Reporter* reporter) {
39 // Test that CreateComposeFilter() has some finite limit (i.e. that the factory can return null)
40 const int way_too_many = 100;
41 SkAutoTUnref<SkColorFilter> parent(make_filter());
42 for (int i = 2; i < way_too_many; ++i) {
43 SkAutoTUnref<SkColorFilter> filter(make_filter());
44 parent.reset(SkColorFilter::CreateComposeFilter(parent, filter));
45 if (NULL == parent) {
46 REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once!
47 return;
48 }
49 }
50 REPORTER_ASSERT(reporter, false); // we never saw a NULL :(
51}
52
reed@google.com43c50c82011-04-14 15:50:52 +000053#define ILLEGAL_MODE ((SkXfermode::Mode)-1)
54
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +000055DEF_TEST(ColorFilter, reporter) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000056 SkRandom rand;
reed@google.com43c50c82011-04-14 15:50:52 +000057
58 for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
59 SkColor color = rand.nextU();
60
61 // ensure we always get a filter, by avoiding the possibility of a
62 // special case that would return NULL (if color's alpha is 0 or 0xFF)
63 color = SkColorSetA(color, 0x7F);
64
65 SkColorFilter* cf = SkColorFilter::CreateModeFilter(color,
66 (SkXfermode::Mode)mode);
67
68 // allow for no filter if we're in Dst mode (its a no op)
69 if (SkXfermode::kDst_Mode == mode && NULL == cf) {
70 continue;
71 }
72
73 SkAutoUnref aur(cf);
74 REPORTER_ASSERT(reporter, cf);
75
76 SkColor c = ~color;
77 SkXfermode::Mode m = ILLEGAL_MODE;
78
79 SkColor expectedColor = color;
80 SkXfermode::Mode expectedMode = (SkXfermode::Mode)mode;
81
82// SkDebugf("--- mc [%d %x] ", mode, color);
83
84 REPORTER_ASSERT(reporter, cf->asColorMode(&c, &m));
85 // handle special-case folding by the factory
86 if (SkXfermode::kClear_Mode == mode) {
87 if (c != expectedColor) {
88 expectedColor = 0;
89 }
90 if (m != expectedMode) {
91 expectedMode = SkXfermode::kSrc_Mode;
92 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000093 }
reed@google.com43c50c82011-04-14 15:50:52 +000094
95// SkDebugf("--- got [%d %x] expected [%d %x]\n", m, c, expectedMode, expectedColor);
96
97 REPORTER_ASSERT(reporter, c == expectedColor);
98 REPORTER_ASSERT(reporter, m == expectedMode);
rmistry@google.comd6176b02012-08-23 18:14:13 +000099
reed@google.com43c50c82011-04-14 15:50:52 +0000100 {
reed@google.com35348222013-10-16 13:05:06 +0000101 SkColorFilter* cf2 = reincarnate_colorfilter(cf);
reed@google.com43c50c82011-04-14 15:50:52 +0000102 SkAutoUnref aur2(cf2);
103 REPORTER_ASSERT(reporter, cf2);
104
105 SkColor c2 = ~color;
106 SkXfermode::Mode m2 = ILLEGAL_MODE;
107 REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, &m2));
108 REPORTER_ASSERT(reporter, c2 == expectedColor);
109 REPORTER_ASSERT(reporter, m2 == expectedMode);
110 }
111 }
reeddc812222015-03-05 07:21:02 -0800112
113 test_composecolorfilter_limit(reporter);
reed@google.com43c50c82011-04-14 15:50:52 +0000114}
115
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000116///////////////////////////////////////////////////////////////////////////////
117
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000118DEF_TEST(LumaColorFilter, reporter) {
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000119 SkPMColor in, out;
120 SkAutoTUnref<SkColorFilter> lf(SkLumaColorFilter::Create());
121
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000122 // Applying luma to white produces black with the same transparency.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000123 for (unsigned i = 0; i < 256; ++i) {
124 in = SkPackARGB32(i, i, i, i);
125 lf->filterSpan(&in, 1, &out);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000126 REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i);
127 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
128 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
129 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000130 }
131
132 // Applying luma to black yields transparent black (luminance(black) == 0)
133 for (unsigned i = 0; i < 256; ++i) {
134 in = SkPackARGB32(i, 0, 0, 0);
135 lf->filterSpan(&in, 1, &out);
136 REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT);
137 }
138
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000139 // For general colors, a luma filter generates black with an attenuated alpha channel.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000140 for (unsigned i = 1; i < 256; ++i) {
141 in = SkPackARGB32(i, i, i / 2, i / 3);
142 lf->filterSpan(&in, 1, &out);
143 REPORTER_ASSERT(reporter, out != in);
144 REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000145 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
146 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
147 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000148 }
149}
reed0b18c352015-04-09 08:27:27 -0700150
151///////////////////////////////////////////////////////////////////////////////
152
153#include "SkColorMatrixFilter.h"
154
155static void get_brightness_matrix(float amount, float matrix[20]) {
156 // Spec implementation
157 // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
158 // <feFunc[R|G|B] type="linear" slope="[amount]">
159 memset(matrix, 0, 20 * sizeof(SkScalar));
160 matrix[0] = matrix[6] = matrix[12] = amount;
161 matrix[18] = 1.f;
162}
163
164static void get_grayscale_matrix(float amount, float matrix[20]) {
165 // Note, these values are computed to ensure MatrixNeedsClamping is false
166 // for amount in [0..1]
167 matrix[0] = 0.2126f + 0.7874f * amount;
168 matrix[1] = 0.7152f - 0.7152f * amount;
169 matrix[2] = 1.f - (matrix[0] + matrix[1]);
170 matrix[3] = matrix[4] = 0.f;
171
172 matrix[5] = 0.2126f - 0.2126f * amount;
173 matrix[6] = 0.7152f + 0.2848f * amount;
174 matrix[7] = 1.f - (matrix[5] + matrix[6]);
175 matrix[8] = matrix[9] = 0.f;
176
177 matrix[10] = 0.2126f - 0.2126f * amount;
178 matrix[11] = 0.7152f - 0.7152f * amount;
179 matrix[12] = 1.f - (matrix[10] + matrix[11]);
180 matrix[13] = matrix[14] = 0.f;
181
182 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
183 matrix[18] = 1.f;
184}
185
186static SkColorFilter* make_cf0() {
187 SkScalar matrix[20];
188 get_brightness_matrix(0.5f, matrix);
189 return SkColorMatrixFilter::Create(matrix);
190}
191static SkColorFilter* make_cf1() {
192 SkScalar matrix[20];
193 get_grayscale_matrix(1, matrix);
194 return SkColorMatrixFilter::Create(matrix);
195}
196static SkColorFilter* make_cf2() {
197 SkColorMatrix m0, m1;
198 get_brightness_matrix(0.5f, m0.fMat);
199 get_grayscale_matrix(1, m1.fMat);
200 m0.preConcat(m1);
201 return SkColorMatrixFilter::Create(m0);
202}
203static SkColorFilter* make_cf3() {
204 SkColorMatrix m0, m1;
205 get_brightness_matrix(0.5f, m0.fMat);
206 get_grayscale_matrix(1, m1.fMat);
207 m0.postConcat(m1);
208 return SkColorMatrixFilter::Create(m0);
209}
210typedef SkColorFilter* (*CFProc)();
211
212// Test that a colormatrix that "should" preserve opaquness actually does.
213DEF_TEST(ColorMatrixFilter, reporter) {
214 const CFProc procs[] = {
215 make_cf0, make_cf1, make_cf2, make_cf3,
216 };
217
218 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
219 SkAutoTUnref<SkColorFilter> cf(procs[i]());
220
221 // generate all possible r,g,b triples
222 for (int r = 0; r < 256; ++r) {
223 for (int g = 0; g < 256; ++g) {
224 SkPMColor storage[256];
225 for (int b = 0; b < 256; ++b) {
226 storage[b] = SkPackARGB32(0xFF, r, g, b);
227 }
228 cf->filterSpan(storage, 256, storage);
229 for (int b = 0; b < 256; ++b) {
230 REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b]));
231 }
232 }
233 }
234 }
235}