blob: ec34a151bec05bbf515fe6bd538e9f74a9a4d4d0 [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
reedd053ce92016-03-22 10:17:23 -070018static sk_sp<SkColorFilter> reincarnate_colorfilter(SkFlattenable* obj) {
brianosmanfad98562016-05-04 11:06:28 -070019 SkBinaryWriteBuffer 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
reedd053ce92016-03-22 10:17:23 -070033static sk_sp<SkColorFilter> make_filter() {
reeddc812222015-03-05 07:21:02 -080034 // pick a filter that cannot compose with itself via newComposed()
Mike Reed7d954ad2016-10-28 15:42:34 -040035 return SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kColorBurn);
reeddc812222015-03-05 07:21:02 -080036}
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;
reedd053ce92016-03-22 10:17:23 -070041 auto parent(make_filter());
reeddc812222015-03-05 07:21:02 -080042 for (int i = 2; i < way_too_many; ++i) {
reedd053ce92016-03-22 10:17:23 -070043 auto filter(make_filter());
44 parent = SkColorFilter::MakeComposeFilter(parent, filter);
halcanary96fcdcc2015-08-27 07:41:13 -070045 if (nullptr == parent) {
reeddc812222015-03-05 07:21:02 -080046 REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once!
47 return;
48 }
49 }
halcanary96fcdcc2015-08-27 07:41:13 -070050 REPORTER_ASSERT(reporter, false); // we never saw a nullptr :(
reeddc812222015-03-05 07:21:02 -080051}
52
Mike Reed7d954ad2016-10-28 15:42:34 -040053#define ILLEGAL_MODE ((SkBlendMode)-1)
reed@google.com43c50c82011-04-14 15:50:52 +000054
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
Mike Reed7d954ad2016-10-28 15:42:34 -040058 for (int mode = 0; mode <= (int)SkBlendMode::kLastMode; mode++) {
reed@google.com43c50c82011-04-14 15:50:52 +000059 SkColor color = rand.nextU();
60
61 // ensure we always get a filter, by avoiding the possibility of a
halcanary96fcdcc2015-08-27 07:41:13 -070062 // special case that would return nullptr (if color's alpha is 0 or 0xFF)
reed@google.com43c50c82011-04-14 15:50:52 +000063 color = SkColorSetA(color, 0x7F);
64
Mike Reed7d954ad2016-10-28 15:42:34 -040065 auto cf = SkColorFilter::MakeModeFilter(color, (SkBlendMode)mode);
reed@google.com43c50c82011-04-14 15:50:52 +000066
67 // allow for no filter if we're in Dst mode (its a no op)
Mike Reed7d954ad2016-10-28 15:42:34 -040068 if (SkBlendMode::kDst == (SkBlendMode)mode && nullptr == cf) {
reed@google.com43c50c82011-04-14 15:50:52 +000069 continue;
70 }
71
reed@google.com43c50c82011-04-14 15:50:52 +000072 REPORTER_ASSERT(reporter, cf);
73
74 SkColor c = ~color;
Mike Reed7d954ad2016-10-28 15:42:34 -040075 SkBlendMode m = ILLEGAL_MODE;
reed@google.com43c50c82011-04-14 15:50:52 +000076
77 SkColor expectedColor = color;
Mike Reed7d954ad2016-10-28 15:42:34 -040078 SkBlendMode expectedMode = (SkBlendMode)mode;
reed@google.com43c50c82011-04-14 15:50:52 +000079
80// SkDebugf("--- mc [%d %x] ", mode, color);
81
Mike Reedfaba3712016-11-03 14:45:31 -040082 REPORTER_ASSERT(reporter, cf->asColorMode(&c, (SkBlendMode*)&m));
reed@google.com43c50c82011-04-14 15:50:52 +000083 // handle special-case folding by the factory
Mike Reed7d954ad2016-10-28 15:42:34 -040084 if (SkBlendMode::kClear == (SkBlendMode)mode) {
reed@google.com43c50c82011-04-14 15:50:52 +000085 if (c != expectedColor) {
86 expectedColor = 0;
87 }
88 if (m != expectedMode) {
Mike Reed7d954ad2016-10-28 15:42:34 -040089 expectedMode = SkBlendMode::kSrc;
reed@google.com43c50c82011-04-14 15:50:52 +000090 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000091 }
reed@google.com43c50c82011-04-14 15:50:52 +000092
93// SkDebugf("--- got [%d %x] expected [%d %x]\n", m, c, expectedMode, expectedColor);
94
95 REPORTER_ASSERT(reporter, c == expectedColor);
96 REPORTER_ASSERT(reporter, m == expectedMode);
rmistry@google.comd6176b02012-08-23 18:14:13 +000097
reed@google.com43c50c82011-04-14 15:50:52 +000098 {
reedd053ce92016-03-22 10:17:23 -070099 auto cf2 = reincarnate_colorfilter(cf.get());
reed@google.com43c50c82011-04-14 15:50:52 +0000100 REPORTER_ASSERT(reporter, cf2);
101
102 SkColor c2 = ~color;
Mike Reed7d954ad2016-10-28 15:42:34 -0400103 SkBlendMode m2 = ILLEGAL_MODE;
Mike Reedfaba3712016-11-03 14:45:31 -0400104 REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, (SkBlendMode*)&m2));
reed@google.com43c50c82011-04-14 15:50:52 +0000105 REPORTER_ASSERT(reporter, c2 == expectedColor);
106 REPORTER_ASSERT(reporter, m2 == expectedMode);
107 }
108 }
reeddc812222015-03-05 07:21:02 -0800109
110 test_composecolorfilter_limit(reporter);
reed@google.com43c50c82011-04-14 15:50:52 +0000111}
112
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000113///////////////////////////////////////////////////////////////////////////////
114
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000115DEF_TEST(LumaColorFilter, reporter) {
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000116 SkPMColor in, out;
reedd053ce92016-03-22 10:17:23 -0700117 auto lf(SkLumaColorFilter::Make());
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000118
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000119 // Applying luma to white produces black with the same transparency.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000120 for (unsigned i = 0; i < 256; ++i) {
121 in = SkPackARGB32(i, i, i, i);
122 lf->filterSpan(&in, 1, &out);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000123 REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i);
124 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
125 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
126 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000127 }
128
129 // Applying luma to black yields transparent black (luminance(black) == 0)
130 for (unsigned i = 0; i < 256; ++i) {
131 in = SkPackARGB32(i, 0, 0, 0);
132 lf->filterSpan(&in, 1, &out);
133 REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT);
134 }
135
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000136 // For general colors, a luma filter generates black with an attenuated alpha channel.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000137 for (unsigned i = 1; i < 256; ++i) {
138 in = SkPackARGB32(i, i, i / 2, i / 3);
139 lf->filterSpan(&in, 1, &out);
140 REPORTER_ASSERT(reporter, out != in);
141 REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000142 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
143 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
144 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000145 }
146}
reed0b18c352015-04-09 08:27:27 -0700147
148///////////////////////////////////////////////////////////////////////////////
149
150#include "SkColorMatrixFilter.h"
151
152static void get_brightness_matrix(float amount, float matrix[20]) {
153 // Spec implementation
154 // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
155 // <feFunc[R|G|B] type="linear" slope="[amount]">
156 memset(matrix, 0, 20 * sizeof(SkScalar));
157 matrix[0] = matrix[6] = matrix[12] = amount;
158 matrix[18] = 1.f;
159}
160
161static void get_grayscale_matrix(float amount, float matrix[20]) {
162 // Note, these values are computed to ensure MatrixNeedsClamping is false
163 // for amount in [0..1]
164 matrix[0] = 0.2126f + 0.7874f * amount;
165 matrix[1] = 0.7152f - 0.7152f * amount;
166 matrix[2] = 1.f - (matrix[0] + matrix[1]);
167 matrix[3] = matrix[4] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700168
reed0b18c352015-04-09 08:27:27 -0700169 matrix[5] = 0.2126f - 0.2126f * amount;
170 matrix[6] = 0.7152f + 0.2848f * amount;
171 matrix[7] = 1.f - (matrix[5] + matrix[6]);
172 matrix[8] = matrix[9] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700173
reed0b18c352015-04-09 08:27:27 -0700174 matrix[10] = 0.2126f - 0.2126f * amount;
175 matrix[11] = 0.7152f - 0.7152f * amount;
176 matrix[12] = 1.f - (matrix[10] + matrix[11]);
177 matrix[13] = matrix[14] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700178
reed0b18c352015-04-09 08:27:27 -0700179 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
180 matrix[18] = 1.f;
181}
182
reedd053ce92016-03-22 10:17:23 -0700183static sk_sp<SkColorFilter> make_cf0() {
reed0b18c352015-04-09 08:27:27 -0700184 SkScalar matrix[20];
185 get_brightness_matrix(0.5f, matrix);
reedd053ce92016-03-22 10:17:23 -0700186 return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
reed0b18c352015-04-09 08:27:27 -0700187}
reedd053ce92016-03-22 10:17:23 -0700188static sk_sp<SkColorFilter> make_cf1() {
reed0b18c352015-04-09 08:27:27 -0700189 SkScalar matrix[20];
190 get_grayscale_matrix(1, matrix);
reedd053ce92016-03-22 10:17:23 -0700191 return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
reed0b18c352015-04-09 08:27:27 -0700192}
reedd053ce92016-03-22 10:17:23 -0700193static sk_sp<SkColorFilter> make_cf2() {
reed0b18c352015-04-09 08:27:27 -0700194 SkColorMatrix m0, m1;
195 get_brightness_matrix(0.5f, m0.fMat);
196 get_grayscale_matrix(1, m1.fMat);
197 m0.preConcat(m1);
reedd053ce92016-03-22 10:17:23 -0700198 return SkColorFilter::MakeMatrixFilterRowMajor255(m0.fMat);
reed0b18c352015-04-09 08:27:27 -0700199}
reedd053ce92016-03-22 10:17:23 -0700200static sk_sp<SkColorFilter> make_cf3() {
reed0b18c352015-04-09 08:27:27 -0700201 SkColorMatrix m0, m1;
202 get_brightness_matrix(0.5f, m0.fMat);
203 get_grayscale_matrix(1, m1.fMat);
204 m0.postConcat(m1);
reedd053ce92016-03-22 10:17:23 -0700205 return SkColorFilter::MakeMatrixFilterRowMajor255(m0.fMat);
reed0b18c352015-04-09 08:27:27 -0700206}
reedd053ce92016-03-22 10:17:23 -0700207typedef sk_sp<SkColorFilter> (*CFProc)();
reed0b18c352015-04-09 08:27:27 -0700208
209// Test that a colormatrix that "should" preserve opaquness actually does.
210DEF_TEST(ColorMatrixFilter, reporter) {
211 const CFProc procs[] = {
212 make_cf0, make_cf1, make_cf2, make_cf3,
213 };
214
215 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
reedd053ce92016-03-22 10:17:23 -0700216 auto cf(procs[i]());
reed0b18c352015-04-09 08:27:27 -0700217
218 // generate all possible r,g,b triples
219 for (int r = 0; r < 256; ++r) {
220 for (int g = 0; g < 256; ++g) {
221 SkPMColor storage[256];
222 for (int b = 0; b < 256; ++b) {
223 storage[b] = SkPackARGB32(0xFF, r, g, b);
224 }
225 cf->filterSpan(storage, 256, storage);
226 for (int b = 0; b < 256; ++b) {
227 REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b]));
228 }
229 }
230 }
231 }
232}