blob: ad2dd0fc353a869d013ca7eb59fc7702f5fe98d2 [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"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000015#include "Test.h"
reed@google.com43c50c82011-04-14 15:50:52 +000016
reedd053ce92016-03-22 10:17:23 -070017static sk_sp<SkColorFilter> reincarnate_colorfilter(SkFlattenable* obj) {
brianosmanfad98562016-05-04 11:06:28 -070018 SkBinaryWriteBuffer wb;
djsollen@google.comcefc8652012-03-26 15:52:10 +000019 wb.writeFlattenable(obj);
reed@google.com43c50c82011-04-14 15:50:52 +000020
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000021 size_t size = wb.bytesWritten();
reed@google.com43c50c82011-04-14 15:50:52 +000022 SkAutoSMalloc<1024> storage(size);
23 // make a copy into storage
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000024 wb.writeToMemory(storage.get());
reed@google.com43c50c82011-04-14 15:50:52 +000025
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000026 SkReadBuffer rb(storage.get(), size);
reed@google.com35348222013-10-16 13:05:06 +000027 return rb.readColorFilter();
reed@google.com43c50c82011-04-14 15:50:52 +000028}
29
30///////////////////////////////////////////////////////////////////////////////
31
reedd053ce92016-03-22 10:17:23 -070032static sk_sp<SkColorFilter> make_filter() {
reeddc812222015-03-05 07:21:02 -080033 // pick a filter that cannot compose with itself via newComposed()
Mike Reed7d954ad2016-10-28 15:42:34 -040034 return SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kColorBurn);
reeddc812222015-03-05 07:21:02 -080035}
36
37static void test_composecolorfilter_limit(skiatest::Reporter* reporter) {
38 // Test that CreateComposeFilter() has some finite limit (i.e. that the factory can return null)
39 const int way_too_many = 100;
reedd053ce92016-03-22 10:17:23 -070040 auto parent(make_filter());
reeddc812222015-03-05 07:21:02 -080041 for (int i = 2; i < way_too_many; ++i) {
reedd053ce92016-03-22 10:17:23 -070042 auto filter(make_filter());
43 parent = SkColorFilter::MakeComposeFilter(parent, filter);
halcanary96fcdcc2015-08-27 07:41:13 -070044 if (nullptr == parent) {
reeddc812222015-03-05 07:21:02 -080045 REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once!
46 return;
47 }
48 }
halcanary96fcdcc2015-08-27 07:41:13 -070049 REPORTER_ASSERT(reporter, false); // we never saw a nullptr :(
reeddc812222015-03-05 07:21:02 -080050}
51
Mike Reed7d954ad2016-10-28 15:42:34 -040052#define ILLEGAL_MODE ((SkBlendMode)-1)
reed@google.com43c50c82011-04-14 15:50:52 +000053
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +000054DEF_TEST(ColorFilter, reporter) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000055 SkRandom rand;
reed@google.com43c50c82011-04-14 15:50:52 +000056
Mike Reed7d954ad2016-10-28 15:42:34 -040057 for (int mode = 0; mode <= (int)SkBlendMode::kLastMode; mode++) {
reed@google.com43c50c82011-04-14 15:50:52 +000058 SkColor color = rand.nextU();
59
60 // ensure we always get a filter, by avoiding the possibility of a
halcanary96fcdcc2015-08-27 07:41:13 -070061 // special case that would return nullptr (if color's alpha is 0 or 0xFF)
reed@google.com43c50c82011-04-14 15:50:52 +000062 color = SkColorSetA(color, 0x7F);
63
Mike Reed7d954ad2016-10-28 15:42:34 -040064 auto cf = SkColorFilter::MakeModeFilter(color, (SkBlendMode)mode);
reed@google.com43c50c82011-04-14 15:50:52 +000065
66 // allow for no filter if we're in Dst mode (its a no op)
Mike Reed7d954ad2016-10-28 15:42:34 -040067 if (SkBlendMode::kDst == (SkBlendMode)mode && nullptr == cf) {
reed@google.com43c50c82011-04-14 15:50:52 +000068 continue;
69 }
70
reed@google.com43c50c82011-04-14 15:50:52 +000071 REPORTER_ASSERT(reporter, cf);
72
73 SkColor c = ~color;
Mike Reed7d954ad2016-10-28 15:42:34 -040074 SkBlendMode m = ILLEGAL_MODE;
reed@google.com43c50c82011-04-14 15:50:52 +000075
76 SkColor expectedColor = color;
Mike Reed7d954ad2016-10-28 15:42:34 -040077 SkBlendMode expectedMode = (SkBlendMode)mode;
reed@google.com43c50c82011-04-14 15:50:52 +000078
79// SkDebugf("--- mc [%d %x] ", mode, color);
80
Mike Reedfaba3712016-11-03 14:45:31 -040081 REPORTER_ASSERT(reporter, cf->asColorMode(&c, (SkBlendMode*)&m));
reed@google.com43c50c82011-04-14 15:50:52 +000082 // handle special-case folding by the factory
Mike Reed7d954ad2016-10-28 15:42:34 -040083 if (SkBlendMode::kClear == (SkBlendMode)mode) {
reed@google.com43c50c82011-04-14 15:50:52 +000084 if (c != expectedColor) {
85 expectedColor = 0;
86 }
87 if (m != expectedMode) {
Mike Reed7d954ad2016-10-28 15:42:34 -040088 expectedMode = SkBlendMode::kSrc;
reed@google.com43c50c82011-04-14 15:50:52 +000089 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000090 }
reed@google.com43c50c82011-04-14 15:50:52 +000091
92// SkDebugf("--- got [%d %x] expected [%d %x]\n", m, c, expectedMode, expectedColor);
93
94 REPORTER_ASSERT(reporter, c == expectedColor);
95 REPORTER_ASSERT(reporter, m == expectedMode);
rmistry@google.comd6176b02012-08-23 18:14:13 +000096
reed@google.com43c50c82011-04-14 15:50:52 +000097 {
reedd053ce92016-03-22 10:17:23 -070098 auto cf2 = reincarnate_colorfilter(cf.get());
reed@google.com43c50c82011-04-14 15:50:52 +000099 REPORTER_ASSERT(reporter, cf2);
100
101 SkColor c2 = ~color;
Mike Reed7d954ad2016-10-28 15:42:34 -0400102 SkBlendMode m2 = ILLEGAL_MODE;
Mike Reedfaba3712016-11-03 14:45:31 -0400103 REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, (SkBlendMode*)&m2));
reed@google.com43c50c82011-04-14 15:50:52 +0000104 REPORTER_ASSERT(reporter, c2 == expectedColor);
105 REPORTER_ASSERT(reporter, m2 == expectedMode);
106 }
107 }
reeddc812222015-03-05 07:21:02 -0800108
109 test_composecolorfilter_limit(reporter);
reed@google.com43c50c82011-04-14 15:50:52 +0000110}
111
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000112///////////////////////////////////////////////////////////////////////////////
113
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000114DEF_TEST(LumaColorFilter, reporter) {
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000115 SkPMColor in, out;
reedd053ce92016-03-22 10:17:23 -0700116 auto lf(SkLumaColorFilter::Make());
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000117
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000118 // Applying luma to white produces black with the same transparency.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000119 for (unsigned i = 0; i < 256; ++i) {
120 in = SkPackARGB32(i, i, i, i);
121 lf->filterSpan(&in, 1, &out);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000122 REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i);
123 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
124 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
125 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000126 }
127
128 // Applying luma to black yields transparent black (luminance(black) == 0)
129 for (unsigned i = 0; i < 256; ++i) {
130 in = SkPackARGB32(i, 0, 0, 0);
131 lf->filterSpan(&in, 1, &out);
132 REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT);
133 }
134
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000135 // For general colors, a luma filter generates black with an attenuated alpha channel.
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000136 for (unsigned i = 1; i < 256; ++i) {
137 in = SkPackARGB32(i, i, i / 2, i / 3);
138 lf->filterSpan(&in, 1, &out);
139 REPORTER_ASSERT(reporter, out != in);
140 REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i);
commit-bot@chromium.orgd494b092013-10-10 20:13:51 +0000141 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
142 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
143 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
commit-bot@chromium.org6c1ee2d2013-10-07 18:00:17 +0000144 }
145}
reed0b18c352015-04-09 08:27:27 -0700146
147///////////////////////////////////////////////////////////////////////////////
148
149#include "SkColorMatrixFilter.h"
150
151static void get_brightness_matrix(float amount, float matrix[20]) {
152 // Spec implementation
153 // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
154 // <feFunc[R|G|B] type="linear" slope="[amount]">
155 memset(matrix, 0, 20 * sizeof(SkScalar));
156 matrix[0] = matrix[6] = matrix[12] = amount;
157 matrix[18] = 1.f;
158}
159
160static void get_grayscale_matrix(float amount, float matrix[20]) {
161 // Note, these values are computed to ensure MatrixNeedsClamping is false
162 // for amount in [0..1]
163 matrix[0] = 0.2126f + 0.7874f * amount;
164 matrix[1] = 0.7152f - 0.7152f * amount;
165 matrix[2] = 1.f - (matrix[0] + matrix[1]);
166 matrix[3] = matrix[4] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700167
reed0b18c352015-04-09 08:27:27 -0700168 matrix[5] = 0.2126f - 0.2126f * amount;
169 matrix[6] = 0.7152f + 0.2848f * amount;
170 matrix[7] = 1.f - (matrix[5] + matrix[6]);
171 matrix[8] = matrix[9] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700172
reed0b18c352015-04-09 08:27:27 -0700173 matrix[10] = 0.2126f - 0.2126f * amount;
174 matrix[11] = 0.7152f - 0.7152f * amount;
175 matrix[12] = 1.f - (matrix[10] + matrix[11]);
176 matrix[13] = matrix[14] = 0.f;
halcanary9d524f22016-03-29 09:03:52 -0700177
reed0b18c352015-04-09 08:27:27 -0700178 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
179 matrix[18] = 1.f;
180}
181
reedd053ce92016-03-22 10:17:23 -0700182static sk_sp<SkColorFilter> make_cf0() {
reed0b18c352015-04-09 08:27:27 -0700183 SkScalar matrix[20];
184 get_brightness_matrix(0.5f, matrix);
reedd053ce92016-03-22 10:17:23 -0700185 return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
reed0b18c352015-04-09 08:27:27 -0700186}
reedd053ce92016-03-22 10:17:23 -0700187static sk_sp<SkColorFilter> make_cf1() {
reed0b18c352015-04-09 08:27:27 -0700188 SkScalar matrix[20];
189 get_grayscale_matrix(1, matrix);
reedd053ce92016-03-22 10:17:23 -0700190 return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
reed0b18c352015-04-09 08:27:27 -0700191}
reedd053ce92016-03-22 10:17:23 -0700192static sk_sp<SkColorFilter> make_cf2() {
reed0b18c352015-04-09 08:27:27 -0700193 SkColorMatrix m0, m1;
194 get_brightness_matrix(0.5f, m0.fMat);
195 get_grayscale_matrix(1, m1.fMat);
196 m0.preConcat(m1);
reedd053ce92016-03-22 10:17:23 -0700197 return SkColorFilter::MakeMatrixFilterRowMajor255(m0.fMat);
reed0b18c352015-04-09 08:27:27 -0700198}
reedd053ce92016-03-22 10:17:23 -0700199static sk_sp<SkColorFilter> make_cf3() {
reed0b18c352015-04-09 08:27:27 -0700200 SkColorMatrix m0, m1;
201 get_brightness_matrix(0.5f, m0.fMat);
202 get_grayscale_matrix(1, m1.fMat);
203 m0.postConcat(m1);
reedd053ce92016-03-22 10:17:23 -0700204 return SkColorFilter::MakeMatrixFilterRowMajor255(m0.fMat);
reed0b18c352015-04-09 08:27:27 -0700205}
reedd053ce92016-03-22 10:17:23 -0700206typedef sk_sp<SkColorFilter> (*CFProc)();
reed0b18c352015-04-09 08:27:27 -0700207
208// Test that a colormatrix that "should" preserve opaquness actually does.
209DEF_TEST(ColorMatrixFilter, reporter) {
210 const CFProc procs[] = {
211 make_cf0, make_cf1, make_cf2, make_cf3,
212 };
213
214 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
reedd053ce92016-03-22 10:17:23 -0700215 auto cf(procs[i]());
reed0b18c352015-04-09 08:27:27 -0700216
217 // generate all possible r,g,b triples
218 for (int r = 0; r < 256; ++r) {
219 for (int g = 0; g < 256; ++g) {
220 SkPMColor storage[256];
221 for (int b = 0; b < 256; ++b) {
222 storage[b] = SkPackARGB32(0xFF, r, g, b);
223 }
224 cf->filterSpan(storage, 256, storage);
225 for (int b = 0; b < 256; ++b) {
226 REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b]));
227 }
228 }
229 }
230 }
231}