blob: b6456a6414c9c930fae2f7ced6c89aaaf0b79370 [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
Hal Canary95e3c052017-01-11 12:44:43 -05008#include "SkAutoMalloc.h"
reed@google.com43c50c82011-04-14 15:50:52 +00009#include "SkColor.h"
10#include "SkColorFilter.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000011#include "SkColorPriv.h"
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +000012#include "SkLumaColorFilter.h"
Hal Canary95e3c052017-01-11 12:44:43 -050013#include "SkRandom.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000014#include "SkReadBuffer.h"
15#include "SkWriteBuffer.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000016#include "SkRandom.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000017#include "Test.h"
reed@google.com43c50c82011-04-14 15:50:52 +000018
reedd053ce92016-03-22 10:17:23 -070019static sk_sp<SkColorFilter> reincarnate_colorfilter(SkFlattenable* obj) {
brianosmanfad98562016-05-04 11:06:28 -070020 SkBinaryWriteBuffer wb;
djsollen@google.comcefc8652012-03-26 15:52:10 +000021 wb.writeFlattenable(obj);
reed@google.com43c50c82011-04-14 15:50:52 +000022
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000023 size_t size = wb.bytesWritten();
reed@google.com43c50c82011-04-14 15:50:52 +000024 SkAutoSMalloc<1024> storage(size);
25 // make a copy into storage
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000026 wb.writeToMemory(storage.get());
reed@google.com43c50c82011-04-14 15:50:52 +000027
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000028 SkReadBuffer rb(storage.get(), size);
reed@google.com35348222013-10-16 13:05:06 +000029 return rb.readColorFilter();
reed@google.com43c50c82011-04-14 15:50:52 +000030}
31
32///////////////////////////////////////////////////////////////////////////////
33
reedd053ce92016-03-22 10:17:23 -070034static sk_sp<SkColorFilter> make_filter() {
reeddc812222015-03-05 07:21:02 -080035 // pick a filter that cannot compose with itself via newComposed()
Mike Reed7d954ad2016-10-28 15:42:34 -040036 return SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kColorBurn);
reeddc812222015-03-05 07:21:02 -080037}
38
39static void test_composecolorfilter_limit(skiatest::Reporter* reporter) {
40 // Test that CreateComposeFilter() has some finite limit (i.e. that the factory can return null)
41 const int way_too_many = 100;
reedd053ce92016-03-22 10:17:23 -070042 auto parent(make_filter());
reeddc812222015-03-05 07:21:02 -080043 for (int i = 2; i < way_too_many; ++i) {
reedd053ce92016-03-22 10:17:23 -070044 auto filter(make_filter());
45 parent = SkColorFilter::MakeComposeFilter(parent, filter);
halcanary96fcdcc2015-08-27 07:41:13 -070046 if (nullptr == parent) {
reeddc812222015-03-05 07:21:02 -080047 REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once!
48 return;
49 }
50 }
halcanary96fcdcc2015-08-27 07:41:13 -070051 REPORTER_ASSERT(reporter, false); // we never saw a nullptr :(
reeddc812222015-03-05 07:21:02 -080052}
53
Mike Reed7d954ad2016-10-28 15:42:34 -040054#define ILLEGAL_MODE ((SkBlendMode)-1)
reed@google.com43c50c82011-04-14 15:50:52 +000055
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +000056DEF_TEST(ColorFilter, reporter) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000057 SkRandom rand;
reed@google.com43c50c82011-04-14 15:50:52 +000058
Mike Reed7d954ad2016-10-28 15:42:34 -040059 for (int mode = 0; mode <= (int)SkBlendMode::kLastMode; mode++) {
reed@google.com43c50c82011-04-14 15:50:52 +000060 SkColor color = rand.nextU();
61
62 // ensure we always get a filter, by avoiding the possibility of a
halcanary96fcdcc2015-08-27 07:41:13 -070063 // special case that would return nullptr (if color's alpha is 0 or 0xFF)
reed@google.com43c50c82011-04-14 15:50:52 +000064 color = SkColorSetA(color, 0x7F);
65
Mike Reed7d954ad2016-10-28 15:42:34 -040066 auto cf = SkColorFilter::MakeModeFilter(color, (SkBlendMode)mode);
reed@google.com43c50c82011-04-14 15:50:52 +000067
68 // allow for no filter if we're in Dst mode (its a no op)
Mike Reed7d954ad2016-10-28 15:42:34 -040069 if (SkBlendMode::kDst == (SkBlendMode)mode && nullptr == cf) {
reed@google.com43c50c82011-04-14 15:50:52 +000070 continue;
71 }
72
reed@google.com43c50c82011-04-14 15:50:52 +000073 REPORTER_ASSERT(reporter, cf);
74
75 SkColor c = ~color;
Mike Reed7d954ad2016-10-28 15:42:34 -040076 SkBlendMode m = ILLEGAL_MODE;
reed@google.com43c50c82011-04-14 15:50:52 +000077
78 SkColor expectedColor = color;
Mike Reed7d954ad2016-10-28 15:42:34 -040079 SkBlendMode expectedMode = (SkBlendMode)mode;
reed@google.com43c50c82011-04-14 15:50:52 +000080
81// SkDebugf("--- mc [%d %x] ", mode, color);
82
Mike Reedfaba3712016-11-03 14:45:31 -040083 REPORTER_ASSERT(reporter, cf->asColorMode(&c, (SkBlendMode*)&m));
reed@google.com43c50c82011-04-14 15:50:52 +000084 // handle special-case folding by the factory
Mike Reed7d954ad2016-10-28 15:42:34 -040085 if (SkBlendMode::kClear == (SkBlendMode)mode) {
reed@google.com43c50c82011-04-14 15:50:52 +000086 if (c != expectedColor) {
87 expectedColor = 0;
88 }
89 if (m != expectedMode) {
Mike Reed7d954ad2016-10-28 15:42:34 -040090 expectedMode = SkBlendMode::kSrc;
reed@google.com43c50c82011-04-14 15:50:52 +000091 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000092 }
reed@google.com43c50c82011-04-14 15:50:52 +000093
94// SkDebugf("--- got [%d %x] expected [%d %x]\n", m, c, expectedMode, expectedColor);
95
96 REPORTER_ASSERT(reporter, c == expectedColor);
97 REPORTER_ASSERT(reporter, m == expectedMode);
rmistry@google.comd6176b02012-08-23 18:14:13 +000098
reed@google.com43c50c82011-04-14 15:50:52 +000099 {
reedd053ce92016-03-22 10:17:23 -0700100 auto cf2 = reincarnate_colorfilter(cf.get());
reed@google.com43c50c82011-04-14 15:50:52 +0000101 REPORTER_ASSERT(reporter, cf2);
102
103 SkColor c2 = ~color;
Mike Reed7d954ad2016-10-28 15:42:34 -0400104 SkBlendMode m2 = ILLEGAL_MODE;
Mike Reedfaba3712016-11-03 14:45:31 -0400105 REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, (SkBlendMode*)&m2));
reed@google.com43c50c82011-04-14 15:50:52 +0000106 REPORTER_ASSERT(reporter, c2 == expectedColor);
107 REPORTER_ASSERT(reporter, m2 == expectedMode);
108 }
109 }
reeddc812222015-03-05 07:21:02 -0800110
111 test_composecolorfilter_limit(reporter);
reed@google.com43c50c82011-04-14 15:50:52 +0000112}
113
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000114///////////////////////////////////////////////////////////////////////////////
115
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000116DEF_TEST(LumaColorFilter, reporter) {
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000117 SkPMColor in, out;
reedd053ce92016-03-22 10:17:23 -0700118 auto lf(SkLumaColorFilter::Make());
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000119
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000120 // Applying luma to white produces black with the same transparency.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000121 for (unsigned i = 0; i < 256; ++i) {
122 in = SkPackARGB32(i, i, i, i);
123 lf->filterSpan(&in, 1, &out);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000124 REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i);
125 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
126 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
127 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000128 }
129
130 // Applying luma to black yields transparent black (luminance(black) == 0)
131 for (unsigned i = 0; i < 256; ++i) {
132 in = SkPackARGB32(i, 0, 0, 0);
133 lf->filterSpan(&in, 1, &out);
134 REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT);
135 }
136
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000137 // For general colors, a luma filter generates black with an attenuated alpha channel.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000138 for (unsigned i = 1; i < 256; ++i) {
139 in = SkPackARGB32(i, i, i / 2, i / 3);
140 lf->filterSpan(&in, 1, &out);
141 REPORTER_ASSERT(reporter, out != in);
142 REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000143 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
144 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
145 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000146 }
147}
reed0b18c352015-04-09 08:27:27 -0700148
149///////////////////////////////////////////////////////////////////////////////
150
151#include "SkColorMatrixFilter.h"
152
153static void get_brightness_matrix(float amount, float matrix[20]) {
154 // Spec implementation
155 // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
156 // <feFunc[R|G|B] type="linear" slope="[amount]">
157 memset(matrix, 0, 20 * sizeof(SkScalar));
158 matrix[0] = matrix[6] = matrix[12] = amount;
159 matrix[18] = 1.f;
160}
161
162static void get_grayscale_matrix(float amount, float matrix[20]) {
163 // Note, these values are computed to ensure MatrixNeedsClamping is false
164 // for amount in [0..1]
165 matrix[0] = 0.2126f + 0.7874f * amount;
166 matrix[1] = 0.7152f - 0.7152f * amount;
167 matrix[2] = 1.f - (matrix[0] + matrix[1]);
168 matrix[3] = matrix[4] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700169
reed0b18c352015-04-09 08:27:27 -0700170 matrix[5] = 0.2126f - 0.2126f * amount;
171 matrix[6] = 0.7152f + 0.2848f * amount;
172 matrix[7] = 1.f - (matrix[5] + matrix[6]);
173 matrix[8] = matrix[9] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700174
reed0b18c352015-04-09 08:27:27 -0700175 matrix[10] = 0.2126f - 0.2126f * amount;
176 matrix[11] = 0.7152f - 0.7152f * amount;
177 matrix[12] = 1.f - (matrix[10] + matrix[11]);
178 matrix[13] = matrix[14] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700179
reed0b18c352015-04-09 08:27:27 -0700180 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
181 matrix[18] = 1.f;
182}
183
reedd053ce92016-03-22 10:17:23 -0700184static sk_sp<SkColorFilter> make_cf0() {
reed0b18c352015-04-09 08:27:27 -0700185 SkScalar matrix[20];
186 get_brightness_matrix(0.5f, matrix);
reedd053ce92016-03-22 10:17:23 -0700187 return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
reed0b18c352015-04-09 08:27:27 -0700188}
reedd053ce92016-03-22 10:17:23 -0700189static sk_sp<SkColorFilter> make_cf1() {
reed0b18c352015-04-09 08:27:27 -0700190 SkScalar matrix[20];
191 get_grayscale_matrix(1, matrix);
reedd053ce92016-03-22 10:17:23 -0700192 return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
reed0b18c352015-04-09 08:27:27 -0700193}
reedd053ce92016-03-22 10:17:23 -0700194static sk_sp<SkColorFilter> make_cf2() {
reed0b18c352015-04-09 08:27:27 -0700195 SkColorMatrix m0, m1;
196 get_brightness_matrix(0.5f, m0.fMat);
197 get_grayscale_matrix(1, m1.fMat);
198 m0.preConcat(m1);
reedd053ce92016-03-22 10:17:23 -0700199 return SkColorFilter::MakeMatrixFilterRowMajor255(m0.fMat);
reed0b18c352015-04-09 08:27:27 -0700200}
reedd053ce92016-03-22 10:17:23 -0700201static sk_sp<SkColorFilter> make_cf3() {
reed0b18c352015-04-09 08:27:27 -0700202 SkColorMatrix m0, m1;
203 get_brightness_matrix(0.5f, m0.fMat);
204 get_grayscale_matrix(1, m1.fMat);
205 m0.postConcat(m1);
reedd053ce92016-03-22 10:17:23 -0700206 return SkColorFilter::MakeMatrixFilterRowMajor255(m0.fMat);
reed0b18c352015-04-09 08:27:27 -0700207}
reedd053ce92016-03-22 10:17:23 -0700208typedef sk_sp<SkColorFilter> (*CFProc)();
reed0b18c352015-04-09 08:27:27 -0700209
210// Test that a colormatrix that "should" preserve opaquness actually does.
211DEF_TEST(ColorMatrixFilter, reporter) {
212 const CFProc procs[] = {
213 make_cf0, make_cf1, make_cf2, make_cf3,
214 };
215
216 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
reedd053ce92016-03-22 10:17:23 -0700217 auto cf(procs[i]());
reed0b18c352015-04-09 08:27:27 -0700218
219 // generate all possible r,g,b triples
220 for (int r = 0; r < 256; ++r) {
221 for (int g = 0; g < 256; ++g) {
222 SkPMColor storage[256];
223 for (int b = 0; b < 256; ++b) {
224 storage[b] = SkPackARGB32(0xFF, r, g, b);
225 }
226 cf->filterSpan(storage, 256, storage);
227 for (int b = 0; b < 256; ++b) {
228 REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b]));
229 }
230 }
231 }
232 }
233}