blob: c24a2b773ad74678a8d0c7e244d9646a72ad9005 [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"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000011#include "SkReadBuffer.h"
12#include "SkWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkUnPreMultiply.h"
robertphillips@google.com0fb775c2013-05-23 14:11:41 +000014#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015
16static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
17 unsigned b, unsigned a) {
18 return array[0] * r + array[1] * g + array[2] * b + array[3] * a + array[4];
19}
20
21static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
22 unsigned b) {
23 return array[0] * r + array[1] * g + array[2] * b + array[4];
24}
25
reed@google.combada6442012-12-17 20:21:44 +000026static void General(const SkColorMatrixFilter::State& state,
27 unsigned r, unsigned g, unsigned b, unsigned a,
28 int32_t* SK_RESTRICT result) {
29 const int32_t* SK_RESTRICT array = state.fArray;
30 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000031
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
33 result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
34 result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
35 result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
36}
37
reed@google.combada6442012-12-17 20:21:44 +000038static void General16(const SkColorMatrixFilter::State& state,
39 unsigned r, unsigned g, unsigned b, unsigned a,
40 int32_t* SK_RESTRICT result) {
41 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000042
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
44 result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
45 result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
46 result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
47}
48
reed@google.combada6442012-12-17 20:21:44 +000049static void AffineAdd(const SkColorMatrixFilter::State& state,
50 unsigned r, unsigned g, unsigned b, unsigned a,
51 int32_t* SK_RESTRICT result) {
52 const int32_t* SK_RESTRICT array = state.fArray;
53 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000054
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 result[0] = rowmul3(&array[0], r, g, b) >> shift;
56 result[1] = rowmul3(&array[5], r, g, b) >> shift;
57 result[2] = rowmul3(&array[10], r, g, b) >> shift;
58 result[3] = a;
59}
60
reed@google.combada6442012-12-17 20:21:44 +000061static void AffineAdd16(const SkColorMatrixFilter::State& state,
62 unsigned r, unsigned g, unsigned b, unsigned a,
63 int32_t* SK_RESTRICT result) {
64 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000065
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 result[0] = rowmul3(&array[0], r, g, b) >> 16;
67 result[1] = rowmul3(&array[5], r, g, b) >> 16;
68 result[2] = rowmul3(&array[10], r, g, b) >> 16;
69 result[3] = a;
70}
71
reed@google.combada6442012-12-17 20:21:44 +000072static void ScaleAdd(const SkColorMatrixFilter::State& state,
73 unsigned r, unsigned g, unsigned b, unsigned a,
74 int32_t* SK_RESTRICT result) {
75 const int32_t* SK_RESTRICT array = state.fArray;
76 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000077
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 // cast to (int) to keep the expression signed for the shift
reed@google.com31b30442014-02-06 20:59:47 +000079 result[0] = (array[SkColorMatrix::kR_Scale] * (int)r + array[4]) >> shift;
80 result[1] = (array[SkColorMatrix::kG_Scale] * (int)g + array[9]) >> shift;
81 result[2] = (array[SkColorMatrix::kB_Scale] * (int)b + array[14]) >> shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 result[3] = a;
83}
84
reed@google.combada6442012-12-17 20:21:44 +000085static void ScaleAdd16(const SkColorMatrixFilter::State& state,
86 unsigned r, unsigned g, unsigned b, unsigned a,
87 int32_t* SK_RESTRICT result) {
88 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +000089
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 // cast to (int) to keep the expression signed for the shift
reed@google.com31b30442014-02-06 20:59:47 +000091 result[0] = (array[SkColorMatrix::kR_Scale] * (int)r + array[4]) >> 16;
92 result[1] = (array[SkColorMatrix::kG_Scale] * (int)g + array[9]) >> 16;
93 result[2] = (array[SkColorMatrix::kB_Scale] * (int)b + array[14]) >> 16;
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 result[3] = a;
95}
96
reed@google.combada6442012-12-17 20:21:44 +000097static void Add(const SkColorMatrixFilter::State& state,
98 unsigned r, unsigned g, unsigned b, unsigned a,
99 int32_t* SK_RESTRICT result) {
100 const int32_t* SK_RESTRICT array = state.fArray;
101 const int shift = state.fShift;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000102
reed@google.com31b30442014-02-06 20:59:47 +0000103 result[0] = r + (array[SkColorMatrix::kR_Trans] >> shift);
104 result[1] = g + (array[SkColorMatrix::kG_Trans] >> shift);
105 result[2] = b + (array[SkColorMatrix::kB_Trans] >> shift);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 result[3] = a;
107}
108
reed@google.combada6442012-12-17 20:21:44 +0000109static void Add16(const SkColorMatrixFilter::State& state,
110 unsigned r, unsigned g, unsigned b, unsigned a,
111 int32_t* SK_RESTRICT result) {
112 const int32_t* SK_RESTRICT array = state.fArray;
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000113
reed@google.com31b30442014-02-06 20:59:47 +0000114 result[0] = r + (array[SkColorMatrix::kR_Trans] >> 16);
115 result[1] = g + (array[SkColorMatrix::kG_Trans] >> 16);
116 result[2] = b + (array[SkColorMatrix::kB_Trans] >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 result[3] = a;
118}
119
120#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag | \
121 SkColorFilter::kHasFilter16_Flag)
122
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +0000123// src is [20] but some compilers won't accept __restrict__ on anything
124// but an raw pointer or reference
reed@google.com7b6c1932012-06-04 19:07:41 +0000125void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
senorblanco@chromium.orga0b0c122012-01-04 22:00:34 +0000126 int32_t* array = fState.fArray;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 SkFixed max = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 for (int i = 0; i < 20; i++) {
129 SkFixed value = SkScalarToFixed(src[i]);
130 array[i] = value;
131 value = SkAbs32(value);
132 max = SkMax32(max, value);
133 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 /* All of fArray[] values must fit in 23 bits, to safely allow me to
136 multiply them by 8bit unsigned values, and get a signed answer without
137 overflow. This means clz needs to be 9 or bigger
138 */
139 int bits = SkCLZ(max);
140 int32_t one = SK_Fixed1;
141
142 fState.fShift = 16; // we are starting out as fixed 16.16
143 if (bits < 9) {
144 bits = 9 - bits;
145 fState.fShift -= bits;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000146 for (int i = 0; i < 20; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 array[i] >>= bits;
148 }
149 one >>= bits;
150 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 // check if we have to munge Alpha
153 int32_t changesAlpha = (array[15] | array[16] | array[17] |
154 (array[18] - one) | array[19]);
155 int32_t usesAlpha = (array[3] | array[8] | array[13]);
156 bool shiftIs16 = (16 == fState.fShift);
157
158 if (changesAlpha | usesAlpha) {
159 fProc = shiftIs16 ? General16 : General;
160 fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
161 } else {
162 fFlags = kNO_ALPHA_FLAGS;
163
reed@google.com31b30442014-02-06 20:59:47 +0000164 int32_t needsScale = (array[SkColorMatrix::kR_Scale] - one) |
165 (array[SkColorMatrix::kG_Scale] - one) |
166 (array[SkColorMatrix::kB_Scale] - one);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
168 int32_t needs3x3 = array[1] | array[2] | // red off-axis
169 array[5] | array[7] | // green off-axis
170 array[10] | array[11]; // blue off-axis
171
172 if (needs3x3) {
173 fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
174 } else if (needsScale) {
175 fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
reed@google.com31b30442014-02-06 20:59:47 +0000176 } else if (array[SkColorMatrix::kR_Trans] |
177 array[SkColorMatrix::kG_Trans] |
178 array[SkColorMatrix::kB_Trans]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 fProc = shiftIs16 ? Add16 : Add;
180 } else {
181 fProc = NULL; // identity
182 }
183 }
184
185 /* preround our add values so we get a rounded shift. We do this after we
186 analyze the array, so we don't miss the case where the caller has zeros
187 which could make us accidentally take the General or Add case.
188 */
189 if (NULL != fProc) {
190 int32_t add = 1 << (fState.fShift - 1);
191 array[4] += add;
192 array[9] += add;
193 array[14] += add;
194 array[19] += add;
195 }
196}
197
198///////////////////////////////////////////////////////////////////////////////
199
200static int32_t pin(int32_t value, int32_t max) {
201 if (value < 0) {
202 value = 0;
203 }
204 if (value > max) {
205 value = max;
206 }
207 return value;
208}
209
reed@google.com7b6c1932012-06-04 19:07:41 +0000210SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
211 this->initState(cm.fMat);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212}
213
214SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
reed@google.com7b6c1932012-06-04 19:07:41 +0000215 memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
216 this->initState(array);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217}
218
reed@google.combada6442012-12-17 20:21:44 +0000219uint32_t SkColorMatrixFilter::getFlags() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 return this->INHERITED::getFlags() | fFlags;
221}
222
223void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
reed@google.combada6442012-12-17 20:21:44 +0000224 SkPMColor dst[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 Proc proc = fProc;
reed@google.combada6442012-12-17 20:21:44 +0000226 const State& state = fState;
227 int32_t result[4];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000228
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 if (NULL == proc) {
230 if (src != dst) {
231 memcpy(dst, src, count * sizeof(SkPMColor));
232 }
233 return;
234 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
237
238 for (int i = 0; i < count; i++) {
239 SkPMColor c = src[i];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000240
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 unsigned r = SkGetPackedR32(c);
242 unsigned g = SkGetPackedG32(c);
243 unsigned b = SkGetPackedB32(c);
244 unsigned a = SkGetPackedA32(c);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000245
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 // need our components to be un-premultiplied
247 if (255 != a) {
248 SkUnPreMultiply::Scale scale = table[a];
249 r = SkUnPreMultiply::ApplyScale(scale, r);
250 g = SkUnPreMultiply::ApplyScale(scale, g);
251 b = SkUnPreMultiply::ApplyScale(scale, b);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkASSERT(r <= 255);
254 SkASSERT(g <= 255);
255 SkASSERT(b <= 255);
256 }
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000257
reed@google.combada6442012-12-17 20:21:44 +0000258 proc(state, r, g, b, a, result);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259
260 r = pin(result[0], SK_R32_MASK);
261 g = pin(result[1], SK_G32_MASK);
262 b = pin(result[2], SK_B32_MASK);
263 a = pin(result[3], SK_A32_MASK);
264 // re-prepremultiply if needed
senorblanco@chromium.orgdf28f072012-01-03 22:42:26 +0000265 dst[i] = SkPremultiplyARGBInline(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 }
267}
268
269void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
reed@google.combada6442012-12-17 20:21:44 +0000270 uint16_t dst[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
272
273 Proc proc = fProc;
reed@google.combada6442012-12-17 20:21:44 +0000274 const State& state = fState;
275 int32_t result[4];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000276
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 if (NULL == proc) {
278 if (src != dst) {
279 memcpy(dst, src, count * sizeof(uint16_t));
280 }
281 return;
282 }
283
284 for (int i = 0; i < count; i++) {
285 uint16_t c = src[i];
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 // expand to 8bit components (since our matrix translate is 8bit biased
288 unsigned r = SkPacked16ToR32(c);
289 unsigned g = SkPacked16ToG32(c);
290 unsigned b = SkPacked16ToB32(c);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000291
reed@google.combada6442012-12-17 20:21:44 +0000292 proc(state, r, g, b, 0, result);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000293
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 r = pin(result[0], SK_R32_MASK);
295 g = pin(result[1], SK_G32_MASK);
296 b = pin(result[2], SK_B32_MASK);
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 // now packed it back down to 16bits (hmmm, could dither...)
299 dst[i] = SkPack888ToRGB16(r, g, b);
300 }
301}
302
303///////////////////////////////////////////////////////////////////////////////
304
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000305void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 this->INHERITED::flatten(buffer);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000307 SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
308 buffer.writeScalarArray(fMatrix.fMat, 20);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000310
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000311SkColorMatrixFilter::SkColorMatrixFilter(SkReadBuffer& buffer)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 : INHERITED(buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000313 SkASSERT(buffer.getArrayCount() == 20);
commit-bot@chromium.orgc2e9db32013-12-06 20:14:46 +0000314 if (buffer.readScalarArray(fMatrix.fMat, 20)) {
315 this->initState(fMatrix.fMat);
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317}
tomhudson@google.com1447c6f2011-04-27 14:09:52 +0000318
reed@google.combada6442012-12-17 20:21:44 +0000319bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
reed@google.com7b6c1932012-06-04 19:07:41 +0000320 if (matrix) {
321 memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
senorblanco@chromium.orge5ff3ce2011-12-20 20:58:18 +0000322 }
323 return true;
324}
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000325
326#if SK_SUPPORT_GPU
bsalomon@google.coma469c282012-10-24 18:28:34 +0000327#include "GrEffect.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000328#include "GrTBackendEffectFactory.h"
bsalomon@google.comd698f772012-10-25 13:22:00 +0000329#include "gl/GrGLEffect.h"
bsalomon848faf02014-07-11 10:01:02 -0700330#include "gl/GrGLShaderBuilder.h"
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000331
bsalomon@google.coma469c282012-10-24 18:28:34 +0000332class ColorMatrixEffect : public GrEffect {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000333public:
bsalomon83d081a2014-07-08 09:56:10 -0700334 static GrEffect* Create(const SkColorMatrix& matrix) {
bsalomon55fad7a2014-07-08 07:34:20 -0700335 return SkNEW_ARGS(ColorMatrixEffect, (matrix));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000336 }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000337
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000338 static const char* Name() { return "Color Matrix"; }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000339
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000340 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
341 return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000342 }
343
bsalomon@google.com371e1052013-01-11 21:08:55 +0000344 virtual void getConstantColorComponents(GrColor* color,
345 uint32_t* validFlags) const SK_OVERRIDE {
346 // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
347 // type flags it might be worth checking the other components.
348
349 // The matrix is defined such the 4th row determines the output alpha. The first four
350 // columns of that row multiply the input r, g, b, and a, respectively, and the last column
351 // is the "translation".
bsalomon@google.comb8eb2e82013-03-28 13:46:42 +0000352 static const uint32_t kRGBAFlags[] = {
353 kR_GrColorComponentFlag,
354 kG_GrColorComponentFlag,
355 kB_GrColorComponentFlag,
356 kA_GrColorComponentFlag
bsalomon@google.com371e1052013-01-11 21:08:55 +0000357 };
358 static const int kShifts[] = {
359 GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
360 };
361 enum {
362 kAlphaRowStartIdx = 15,
363 kAlphaRowTranslateIdx = 19,
364 };
365
366 SkScalar outputA = 0;
367 for (int i = 0; i < 4; ++i) {
368 // If any relevant component of the color to be passed through the matrix is non-const
369 // then we can't know the final result.
370 if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
371 if (!(*validFlags & kRGBAFlags[i])) {
372 *validFlags = 0;
373 return;
374 } else {
375 uint32_t component = (*color >> kShifts[i]) & 0xFF;
376 outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
377 }
378 }
379 }
380 outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
bsalomon@google.comb8eb2e82013-03-28 13:46:42 +0000381 *validFlags = kA_GrColorComponentFlag;
bsalomon@google.com371e1052013-01-11 21:08:55 +0000382 // We pin the color to [0,1]. This would happen to the *final* color output from the frag
383 // shader but currently the effect does not pin its own output. So in the case of over/
384 // underflow this may deviate from the actual result. Maybe the effect should pin its
385 // result if the matrix could over/underflow for any component?
386 *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
387 }
388
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000389 GR_DECLARE_EFFECT_TEST;
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000390
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000391 class GLEffect : public GrGLEffect {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000392 public:
393 // this class always generates the same code.
bsalomon@google.comc7818882013-03-20 19:19:53 +0000394 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000395
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000396 GLEffect(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000397 const GrDrawEffect&)
commit-bot@chromium.org7425c122013-08-14 18:14:19 +0000398 : INHERITED(factory) {
399 }
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000400
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000401 virtual void emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000402 const GrDrawEffect&,
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000403 EffectKey,
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000404 const char* outputColor,
405 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000406 const TransformedCoordsArray&,
bsalomon@google.com22a800a2012-10-26 19:16:46 +0000407 const TextureSamplerArray&) SK_OVERRIDE {
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000408 fMatrixHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000409 kMat44f_GrSLType,
410 "ColorMatrix");
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000411 fVectorHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000412 kVec4f_GrSLType,
413 "ColorMatrixVector");
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000414
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000415 if (NULL == inputColor) {
416 // could optimize this case, but we aren't for now.
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000417 inputColor = "vec4(1)";
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000418 }
skia.committer@gmail.com989a95e2012-10-18 02:01:23 +0000419 // The max() is to guard against 0 / 0 during unpremul when the incoming color is
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000420 // transparent black.
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000421 builder->fsCodeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
422 builder->fsCodeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
423 outputColor,
424 builder->getUniformCStr(fMatrixHandle),
425 inputColor,
426 builder->getUniformCStr(fVectorHandle));
senorblanco@chromium.org2ca677e2014-03-12 21:37:42 +0000427 builder->fsCodeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor);
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000428 builder->fsCodeAppendf("\t%s.rgb *= %s.a;\n", outputColor, outputColor);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000429 }
430
431 virtual void setData(const GrGLUniformManager& uniManager,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000432 const GrDrawEffect& drawEffect) SK_OVERRIDE {
433 const ColorMatrixEffect& cme = drawEffect.castEffect<ColorMatrixEffect>();
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000434 const float* m = cme.fMatrix.fMat;
435 // The GL matrix is transposed from SkColorMatrix.
436 GrGLfloat mt[] = {
437 m[0], m[5], m[10], m[15],
438 m[1], m[6], m[11], m[16],
439 m[2], m[7], m[12], m[17],
440 m[3], m[8], m[13], m[18],
441 };
442 static const float kScale = 1.0f / 255.0f;
443 GrGLfloat vec[] = {
444 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
445 };
commit-bot@chromium.orgd3baf202013-11-07 22:06:08 +0000446 uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
447 uniManager.set4fv(fVectorHandle, 1, vec);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000448 }
449
450 private:
451 GrGLUniformManager::UniformHandle fMatrixHandle;
452 GrGLUniformManager::UniformHandle fVectorHandle;
sugoic197c8a2014-07-03 10:44:26 -0700453
454 typedef GrGLEffect INHERITED;
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000455 };
456
457private:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000458 ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
459
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000460 virtual bool onIsEqual(const GrEffect& s) const {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000461 const ColorMatrixEffect& cme = CastEffect<ColorMatrixEffect>(s);
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000462 return cme.fMatrix == fMatrix;
463 }
464
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000465 SkColorMatrix fMatrix;
bsalomon@google.com374e7592012-10-23 17:30:45 +0000466
sugoic197c8a2014-07-03 10:44:26 -0700467 typedef GrEffect INHERITED;
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000468};
469
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000470GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000471
bsalomon83d081a2014-07-08 09:56:10 -0700472GrEffect* ColorMatrixEffect::TestCreate(SkRandom* random,
473 GrContext*,
474 const GrDrawTargetCaps&,
475 GrTexture* dummyTextures[2]) {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000476 SkColorMatrix colorMatrix;
robertphillips@google.com93f03322012-12-03 17:35:19 +0000477 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000478 colorMatrix.fMat[i] = random->nextSScalar1();
479 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000480 return ColorMatrixEffect::Create(colorMatrix);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000481}
482
bsalomon83d081a2014-07-08 09:56:10 -0700483GrEffect* SkColorMatrixFilter::asNewEffect(GrContext*) const {
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000484 return ColorMatrixEffect::Create(fMatrix);
bsalomon@google.com67e78c92012-10-17 13:36:14 +0000485}
486
487#endif
robertphillips@google.com1202c2a2013-05-23 14:00:17 +0000488
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000489#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com1202c2a2013-05-23 14:00:17 +0000490void SkColorMatrixFilter::toString(SkString* str) const {
491 str->append("SkColorMatrixFilter: ");
492
493 str->append("matrix: (");
494 for (int i = 0; i < 20; ++i) {
495 str->appendScalar(fMatrix.fMat[i]);
496 if (i < 19) {
497 str->append(", ");
498 }
499 }
500 str->append(")");
501}
502#endif