blob: 5b36a8f7e55df1dec60b77de2b29ba6fa81aec39 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkColorMatrixFilter.h"
9#include "SkColorMatrix.h"
10#include "SkColorPriv.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000011#include "SkFlattenableBuffers.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkUnPreMultiply.h"
robertphillips@google.com0fb775c2013-05-23 14:11:41 +000013#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014
15static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
16 unsigned b, unsigned a) {
17 return array[0] * r + array[1] * g + array[2] * b + array[3] * a + array[4];
18}
19
20static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
21 unsigned b) {
22 return array[0] * r + array[1] * g + array[2] * b + array[4];
23}
24
reed@google.combada6442012-12-17 20:21:44 +000025static void General(const SkColorMatrixFilter::State& state,
26 unsigned r, unsigned g, unsigned b, unsigned a,
27 int32_t* SK_RESTRICT result) {
28 const int32_t* SK_RESTRICT array = state.fArray;
29 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000030
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
32 result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
33 result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
34 result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
35}
36
reed@google.combada6442012-12-17 20:21:44 +000037static void General16(const SkColorMatrixFilter::State& state,
38 unsigned r, unsigned g, unsigned b, unsigned a,
39 int32_t* SK_RESTRICT result) {
40 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000041
reed@android.com8a1c16f2008-12-17 15:59:43 +000042 result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
43 result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
44 result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
45 result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
46}
47
reed@google.combada6442012-12-17 20:21:44 +000048static void AffineAdd(const SkColorMatrixFilter::State& state,
49 unsigned r, unsigned g, unsigned b, unsigned a,
50 int32_t* SK_RESTRICT result) {
51 const int32_t* SK_RESTRICT array = state.fArray;
52 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000053
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 result[0] = rowmul3(&array[0], r, g, b) >> shift;
55 result[1] = rowmul3(&array[5], r, g, b) >> shift;
56 result[2] = rowmul3(&array[10], r, g, b) >> shift;
57 result[3] = a;
58}
59
reed@google.combada6442012-12-17 20:21:44 +000060static void AffineAdd16(const SkColorMatrixFilter::State& state,
61 unsigned r, unsigned g, unsigned b, unsigned a,
62 int32_t* SK_RESTRICT result) {
63 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000064
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 result[0] = rowmul3(&array[0], r, g, b) >> 16;
66 result[1] = rowmul3(&array[5], r, g, b) >> 16;
67 result[2] = rowmul3(&array[10], r, g, b) >> 16;
68 result[3] = a;
69}
70
reed@google.combada6442012-12-17 20:21:44 +000071static void ScaleAdd(const SkColorMatrixFilter::State& state,
72 unsigned r, unsigned g, unsigned b, unsigned a,
73 int32_t* SK_RESTRICT result) {
74 const int32_t* SK_RESTRICT array = state.fArray;
75 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000076
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 // cast to (int) to keep the expression signed for the shift
78 result[0] = (array[0] * (int)r + array[4]) >> shift;
79 result[1] = (array[6] * (int)g + array[9]) >> shift;
80 result[2] = (array[12] * (int)b + array[14]) >> shift;
81 result[3] = a;
82}
83
reed@google.combada6442012-12-17 20:21:44 +000084static void ScaleAdd16(const SkColorMatrixFilter::State& state,
85 unsigned r, unsigned g, unsigned b, unsigned a,
86 int32_t* SK_RESTRICT result) {
87 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000088
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 // cast to (int) to keep the expression signed for the shift
90 result[0] = (array[0] * (int)r + array[4]) >> 16;
91 result[1] = (array[6] * (int)g + array[9]) >> 16;
92 result[2] = (array[12] * (int)b + array[14]) >> 16;
93 result[3] = a;
94}
95
reed@google.combada6442012-12-17 20:21:44 +000096static void Add(const SkColorMatrixFilter::State& state,
97 unsigned r, unsigned g, unsigned b, unsigned a,
98 int32_t* SK_RESTRICT result) {
99 const int32_t* SK_RESTRICT array = state.fArray;
100 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000101
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 result[0] = r + (array[4] >> shift);
103 result[1] = g + (array[9] >> shift);
104 result[2] = b + (array[14] >> shift);
105 result[3] = a;
106}
107
reed@google.combada6442012-12-17 20:21:44 +0000108static void Add16(const SkColorMatrixFilter::State& state,
109 unsigned r, unsigned g, unsigned b, unsigned a,
110 int32_t* SK_RESTRICT result) {
111 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000112
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 result[0] = r + (array[4] >> 16);
114 result[1] = g + (array[9] >> 16);
115 result[2] = b + (array[14] >> 16);
116 result[3] = a;
117}
118
119#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag | \
120 SkColorFilter::kHasFilter16_Flag)
121
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000122// src is [20] but some compilers won't accept __restrict__ on anything
123// but an raw pointer or reference
reed@google.com7b6c1932012-06-04 19:07:41 +0000124void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
senorblanco@chromium.orga0b0c122012-01-04 22:00:34 +0000125 int32_t* array = fState.fArray;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 SkFixed max = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 for (int i = 0; i < 20; i++) {
128 SkFixed value = SkScalarToFixed(src[i]);
129 array[i] = value;
130 value = SkAbs32(value);
131 max = SkMax32(max, value);
132 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 /* All of fArray[] values must fit in 23 bits, to safely allow me to
135 multiply them by 8bit unsigned values, and get a signed answer without
136 overflow. This means clz needs to be 9 or bigger
137 */
138 int bits = SkCLZ(max);
139 int32_t one = SK_Fixed1;
140
141 fState.fShift = 16; // we are starting out as fixed 16.16
142 if (bits < 9) {
143 bits = 9 - bits;
144 fState.fShift -= bits;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000145 for (int i = 0; i < 20; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 array[i] >>= bits;
147 }
148 one >>= bits;
149 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000150
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 // check if we have to munge Alpha
152 int32_t changesAlpha = (array[15] | array[16] | array[17] |
153 (array[18] - one) | array[19]);
154 int32_t usesAlpha = (array[3] | array[8] | array[13]);
155 bool shiftIs16 = (16 == fState.fShift);
156
157 if (changesAlpha | usesAlpha) {
158 fProc = shiftIs16 ? General16 : General;
159 fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
160 } else {
161 fFlags = kNO_ALPHA_FLAGS;
162
163 int32_t needsScale = (array[0] - one) | // red axis
164 (array[6] - one) | // green axis
165 (array[12] - one); // blue axis
166
167 int32_t needs3x3 = array[1] | array[2] | // red off-axis
168 array[5] | array[7] | // green off-axis
169 array[10] | array[11]; // blue off-axis
170
171 if (needs3x3) {
172 fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
173 } else if (needsScale) {
174 fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
175 } else if (array[4] | array[9] | array[14]) { // needs add
176 fProc = shiftIs16 ? Add16 : Add;
177 } else {
178 fProc = NULL; // identity
179 }
180 }
181
182 /* preround our add values so we get a rounded shift. We do this after we
183 analyze the array, so we don't miss the case where the caller has zeros
184 which could make us accidentally take the General or Add case.
185 */
186 if (NULL != fProc) {
187 int32_t add = 1 << (fState.fShift - 1);
188 array[4] += add;
189 array[9] += add;
190 array[14] += add;
191 array[19] += add;
192 }
193}
194
195///////////////////////////////////////////////////////////////////////////////
196
197static int32_t pin(int32_t value, int32_t max) {
198 if (value < 0) {
199 value = 0;
200 }
201 if (value > max) {
202 value = max;
203 }
204 return value;
205}
206
reed@google.com7b6c1932012-06-04 19:07:41 +0000207SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
208 this->initState(cm.fMat);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209}
210
211SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
reed@google.com7b6c1932012-06-04 19:07:41 +0000212 memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
213 this->initState(array);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214}
215
reed@google.combada6442012-12-17 20:21:44 +0000216uint32_t SkColorMatrixFilter::getFlags() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 return this->INHERITED::getFlags() | fFlags;
218}
219
220void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
reed@google.combada6442012-12-17 20:21:44 +0000221 SkPMColor dst[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 Proc proc = fProc;
reed@google.combada6442012-12-17 20:21:44 +0000223 const State& state = fState;
224 int32_t result[4];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 if (NULL == proc) {
227 if (src != dst) {
228 memcpy(dst, src, count * sizeof(SkPMColor));
229 }
230 return;
231 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
234
235 for (int i = 0; i < count; i++) {
236 SkPMColor c = src[i];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 unsigned r = SkGetPackedR32(c);
239 unsigned g = SkGetPackedG32(c);
240 unsigned b = SkGetPackedB32(c);
241 unsigned a = SkGetPackedA32(c);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 // need our components to be un-premultiplied
244 if (255 != a) {
245 SkUnPreMultiply::Scale scale = table[a];
246 r = SkUnPreMultiply::ApplyScale(scale, r);
247 g = SkUnPreMultiply::ApplyScale(scale, g);
248 b = SkUnPreMultiply::ApplyScale(scale, b);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000249
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 SkASSERT(r <= 255);
251 SkASSERT(g <= 255);
252 SkASSERT(b <= 255);
253 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000254
reed@google.combada6442012-12-17 20:21:44 +0000255 proc(state, r, g, b, a, result);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256
257 r = pin(result[0], SK_R32_MASK);
258 g = pin(result[1], SK_G32_MASK);
259 b = pin(result[2], SK_B32_MASK);
260 a = pin(result[3], SK_A32_MASK);
261 // re-prepremultiply if needed
senorblanco@chromium.orgdf28f072012-01-03 22:42:26 +0000262 dst[i] = SkPremultiplyARGBInline(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 }
264}
265
266void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
reed@google.combada6442012-12-17 20:21:44 +0000267 uint16_t dst[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
269
270 Proc proc = fProc;
reed@google.combada6442012-12-17 20:21:44 +0000271 const State& state = fState;
272 int32_t result[4];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 if (NULL == proc) {
275 if (src != dst) {
276 memcpy(dst, src, count * sizeof(uint16_t));
277 }
278 return;
279 }
280
281 for (int i = 0; i < count; i++) {
282 uint16_t c = src[i];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 // expand to 8bit components (since our matrix translate is 8bit biased
285 unsigned r = SkPacked16ToR32(c);
286 unsigned g = SkPacked16ToG32(c);
287 unsigned b = SkPacked16ToB32(c);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000288
reed@google.combada6442012-12-17 20:21:44 +0000289 proc(state, r, g, b, 0, result);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000290
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 r = pin(result[0], SK_R32_MASK);
292 g = pin(result[1], SK_G32_MASK);
293 b = pin(result[2], SK_B32_MASK);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 // now packed it back down to 16bits (hmmm, could dither...)
296 dst[i] = SkPack888ToRGB16(r, g, b);
297 }
298}
299
300///////////////////////////////////////////////////////////////////////////////
301
djsollen@google.com54924242012-03-29 15:18:04 +0000302void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 this->INHERITED::flatten(buffer);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000304 SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
305 buffer.writeScalarArray(fMatrix.fMat, 20);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
309 : INHERITED(buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000310 SkASSERT(buffer.getArrayCount() == 20);
commit-bot@chromium.org02512882013-10-31 18:37:50 +0000311 buffer.readScalarArray(fMatrix.fMat, 20);
reed@google.com7b6c1932012-06-04 19:07:41 +0000312 this->initState(fMatrix.fMat);
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000313 for (int i = 0; i < 20; ++i) {
314 buffer.validate(SkScalarIsFinite(fMatrix.fMat[i]));
315 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000317
reed@google.combada6442012-12-17 20:21:44 +0000318bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
reed@google.com7b6c1932012-06-04 19:07:41 +0000319 if (matrix) {
320 memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
senorblanco@chromium.orge5ff3ce2011-12-20 20:58:18 +0000321 }
322 return true;
323}
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000324
325#if SK_SUPPORT_GPU
bsalomon@google.coma469c282012-10-24 18:28:34 +0000326#include "GrEffect.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000327#include "GrTBackendEffectFactory.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +0000328#include "gl/GrGLEffect.h"
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000329
bsalomon@google.coma469c282012-10-24 18:28:34 +0000330class ColorMatrixEffect : public GrEffect {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000331public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000332 static GrEffectRef* Create(const SkColorMatrix& matrix) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000333 AutoEffectUnref effect(SkNEW_ARGS(ColorMatrixEffect, (matrix)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000334 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000335 }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000336
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000337 static const char* Name() { return "Color Matrix"; }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000338
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000339 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
340 return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000341 }
342
bsalomon@google.com371e1052013-01-11 21:08:55 +0000343 virtual void getConstantColorComponents(GrColor* color,
344 uint32_t* validFlags) const SK_OVERRIDE {
345 // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
346 // type flags it might be worth checking the other components.
347
348 // The matrix is defined such the 4th row determines the output alpha. The first four
349 // columns of that row multiply the input r, g, b, and a, respectively, and the last column
350 // is the "translation".
bsalomon@google.comb8eb2e82013-03-28 13:46:42 +0000351 static const uint32_t kRGBAFlags[] = {
352 kR_GrColorComponentFlag,
353 kG_GrColorComponentFlag,
354 kB_GrColorComponentFlag,
355 kA_GrColorComponentFlag
bsalomon@google.com371e1052013-01-11 21:08:55 +0000356 };
357 static const int kShifts[] = {
358 GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
359 };
360 enum {
361 kAlphaRowStartIdx = 15,
362 kAlphaRowTranslateIdx = 19,
363 };
364
365 SkScalar outputA = 0;
366 for (int i = 0; i < 4; ++i) {
367 // If any relevant component of the color to be passed through the matrix is non-const
368 // then we can't know the final result.
369 if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
370 if (!(*validFlags & kRGBAFlags[i])) {
371 *validFlags = 0;
372 return;
373 } else {
374 uint32_t component = (*color >> kShifts[i]) & 0xFF;
375 outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
376 }
377 }
378 }
379 outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
bsalomon@google.comb8eb2e82013-03-28 13:46:42 +0000380 *validFlags = kA_GrColorComponentFlag;
bsalomon@google.com371e1052013-01-11 21:08:55 +0000381 // We pin the color to [0,1]. This would happen to the *final* color output from the frag
382 // shader but currently the effect does not pin its own output. So in the case of over/
383 // underflow this may deviate from the actual result. Maybe the effect should pin its
384 // result if the matrix could over/underflow for any component?
385 *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
386 }
387
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000388 GR_DECLARE_EFFECT_TEST;
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000389
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000390 class GLEffect : public GrGLEffect {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000391 public:
392 // this class always generates the same code.
bsalomon@google.comc7818882013-03-20 19:19:53 +0000393 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000394
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000395 GLEffect(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000396 const GrDrawEffect&)
commit-bot@chromium.org7425c122013-08-14 18:14:19 +0000397 : INHERITED(factory) {
398 }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000399
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000400 virtual void emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000401 const GrDrawEffect&,
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000402 EffectKey,
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000403 const char* outputColor,
404 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000405 const TransformedCoordsArray&,
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000406 const TextureSamplerArray&) SK_OVERRIDE {
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000407 fMatrixHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000408 kMat44f_GrSLType,
409 "ColorMatrix");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000410 fVectorHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000411 kVec4f_GrSLType,
412 "ColorMatrixVector");
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000413
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000414 if (NULL == inputColor) {
415 // could optimize this case, but we aren't for now.
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000416 inputColor = "vec4(1)";
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000417 }
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000418 // The max() is to guard against 0 / 0 during unpremul when the incoming color is
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000419 // transparent black.
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000420 builder->fsCodeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
421 builder->fsCodeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
422 outputColor,
423 builder->getUniformCStr(fMatrixHandle),
424 inputColor,
425 builder->getUniformCStr(fVectorHandle));
426 builder->fsCodeAppendf("\t%s.rgb *= %s.a;\n", outputColor, outputColor);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000427 }
428
429 virtual void setData(const GrGLUniformManager& uniManager,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000430 const GrDrawEffect& drawEffect) SK_OVERRIDE {
431 const ColorMatrixEffect& cme = drawEffect.castEffect<ColorMatrixEffect>();
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000432 const float* m = cme.fMatrix.fMat;
433 // The GL matrix is transposed from SkColorMatrix.
434 GrGLfloat mt[] = {
435 m[0], m[5], m[10], m[15],
436 m[1], m[6], m[11], m[16],
437 m[2], m[7], m[12], m[17],
438 m[3], m[8], m[13], m[18],
439 };
440 static const float kScale = 1.0f / 255.0f;
441 GrGLfloat vec[] = {
442 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
443 };
commit-bot@chromium.orgd3baf202013-11-07 22:06:08 +0000444 uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
445 uniManager.set4fv(fVectorHandle, 1, vec);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000446 }
447
448 private:
449 GrGLUniformManager::UniformHandle fMatrixHandle;
450 GrGLUniformManager::UniformHandle fVectorHandle;
451 };
452
453private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000454 ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
455
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000456 virtual bool onIsEqual(const GrEffect& s) const {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000457 const ColorMatrixEffect& cme = CastEffect<ColorMatrixEffect>(s);
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000458 return cme.fMatrix == fMatrix;
459 }
460
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000461 SkColorMatrix fMatrix;
bsalomon@google.com374e7592012-10-23 17:30:45 +0000462
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000463 typedef GrGLEffect INHERITED;
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000464};
465
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000466GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000467
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000468GrEffectRef* ColorMatrixEffect::TestCreate(SkRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000469 GrContext*,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000470 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000471 GrTexture* dummyTextures[2]) {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000472 SkColorMatrix colorMatrix;
robertphillips@google.com93f03322012-12-03 17:35:19 +0000473 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000474 colorMatrix.fMat[i] = random->nextSScalar1();
475 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000476 return ColorMatrixEffect::Create(colorMatrix);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000477}
478
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000479GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
480 return ColorMatrixEffect::Create(fMatrix);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000481}
482
483#endif
robertphillips@google.com1202c2a2013-05-23 14:00:17 +0000484
485#ifdef SK_DEVELOPER
486void SkColorMatrixFilter::toString(SkString* str) const {
487 str->append("SkColorMatrixFilter: ");
488
489 str->append("matrix: (");
490 for (int i = 0; i < 20; ++i) {
491 str->appendScalar(fMatrix.fMat[i]);
492 if (i < 19) {
493 str->append(", ");
494 }
495 }
496 str->append(")");
497}
498#endif