1 /* 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 */ 7 8 #include "SkColor.h" 9 #include "SkColorFilter.h" 10 #include "SkColorPriv.h" 11 #include "SkLumaColorFilter.h" 12 #include "SkReadBuffer.h" 13 #include "SkWriteBuffer.h" 14 #include "SkRandom.h" 15 #include "SkXfermode.h" 16 #include "Test.h" 17 18 static SkColorFilter* reincarnate_colorfilter(SkFlattenable* obj) { 19 SkWriteBuffer wb; 20 wb.writeFlattenable(obj); 21 22 size_t size = wb.bytesWritten(); 23 SkAutoSMalloc<1024> storage(size); 24 // make a copy into storage 25 wb.writeToMemory(storage.get()); 26 27 SkReadBuffer rb(storage.get(), size); 28 return rb.readColorFilter(); 29 } 30 31 /////////////////////////////////////////////////////////////////////////////// 32 33 static 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 38 static 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 (nullptr == 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 nullptr :( 51 } 52 53 #define ILLEGAL_MODE ((SkXfermode::Mode)-1) 54 55 DEF_TEST(ColorFilter, reporter) { 56 SkRandom rand; 57 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 nullptr (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 && nullptr == 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 } 93 } 94 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); 99 100 { 101 SkColorFilter* cf2 = reincarnate_colorfilter(cf); 102 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 } 112 113 test_composecolorfilter_limit(reporter); 114 } 115 116 /////////////////////////////////////////////////////////////////////////////// 117 118 DEF_TEST(LumaColorFilter, reporter) { 119 SkPMColor in, out; 120 SkAutoTUnref<SkColorFilter> lf(SkLumaColorFilter::Create()); 121 122 // Applying luma to white produces black with the same transparency. 123 for (unsigned i = 0; i < 256; ++i) { 124 in = SkPackARGB32(i, i, i, i); 125 lf->filterSpan(&in, 1, &out); 126 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); 130 } 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 139 // For general colors, a luma filter generates black with an attenuated alpha channel. 140 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); 145 REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0); 146 REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0); 147 REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0); 148 } 149 } 150 151 /////////////////////////////////////////////////////////////////////////////// 152 153 #include "SkColorMatrixFilter.h" 154 155 static 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 164 static 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 186 static SkColorFilter* make_cf0() { 187 SkScalar matrix[20]; 188 get_brightness_matrix(0.5f, matrix); 189 return SkColorMatrixFilter::Create(matrix); 190 } 191 static SkColorFilter* make_cf1() { 192 SkScalar matrix[20]; 193 get_grayscale_matrix(1, matrix); 194 return SkColorMatrixFilter::Create(matrix); 195 } 196 static 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 } 203 static 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 } 210 typedef SkColorFilter* (*CFProc)(); 211 212 // Test that a colormatrix that "should" preserve opaquness actually does. 213 DEF_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 } 236