3 * Copyright 2011 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
8 #include "SkColorMatrixFilter.h"
9 #include "SkColorMatrix.h"
10 #include "SkColorPriv.h"
11 #include "SkReadBuffer.h"
12 #include "SkWriteBuffer.h"
13 #include "SkUnPreMultiply.h"
16 static 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];
21 static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
23 return array[0] * r + array[1] * g + array[2] * b + array[4];
26 static 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;
32 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;
38 static 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;
43 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;
49 static 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;
55 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;
61 static 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;
66 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;
72 static 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;
78 // cast to (int) to keep the expression signed for the shift
79 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;
85 static 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;
90 // cast to (int) to keep the expression signed for the shift
91 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;
97 static 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;
103 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);
109 static 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;
114 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);
120 #define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag | \
121 SkColorFilter::kHasFilter16_Flag)
123 // src is [20] but some compilers won't accept __restrict__ on anything
124 // but an raw pointer or reference
125 void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
126 int32_t* array = fState.fArray;
128 for (int i = 0; i < 20; i++) {
129 SkFixed value = SkScalarToFixed(src[i]);
131 value = SkAbs32(value);
132 max = SkMax32(max, value);
135 /* 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
139 int bits = SkCLZ(max);
140 int32_t one = SK_Fixed1;
142 fState.fShift = 16; // we are starting out as fixed 16.16
145 fState.fShift -= bits;
146 for (int i = 0; i < 20; i++) {
152 // 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);
158 if (changesAlpha | usesAlpha) {
159 fProc = shiftIs16 ? General16 : General;
160 fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
162 fFlags = kNO_ALPHA_FLAGS;
164 int32_t needsScale = (array[SkColorMatrix::kR_Scale] - one) |
165 (array[SkColorMatrix::kG_Scale] - one) |
166 (array[SkColorMatrix::kB_Scale] - one);
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
173 fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
174 } else if (needsScale) {
175 fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
176 } else if (array[SkColorMatrix::kR_Trans] |
177 array[SkColorMatrix::kG_Trans] |
178 array[SkColorMatrix::kB_Trans]) {
179 fProc = shiftIs16 ? Add16 : Add;
181 fProc = NULL; // identity
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.
190 int32_t add = 1 << (fState.fShift - 1);
198 ///////////////////////////////////////////////////////////////////////////////
200 static int32_t pin(int32_t value, int32_t max) {
210 SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
211 this->initState(cm.fMat);
214 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
215 memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
216 this->initState(array);
219 uint32_t SkColorMatrixFilter::getFlags() const {
220 return this->INHERITED::getFlags() | fFlags;
223 void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
224 SkPMColor dst[]) const {
226 const State& state = fState;
231 memcpy(dst, src, count * sizeof(SkPMColor));
236 const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
238 for (int i = 0; i < count; i++) {
239 SkPMColor c = src[i];
241 unsigned r = SkGetPackedR32(c);
242 unsigned g = SkGetPackedG32(c);
243 unsigned b = SkGetPackedB32(c);
244 unsigned a = SkGetPackedA32(c);
246 // need our components to be un-premultiplied
248 SkUnPreMultiply::Scale scale = table[a];
249 r = SkUnPreMultiply::ApplyScale(scale, r);
250 g = SkUnPreMultiply::ApplyScale(scale, g);
251 b = SkUnPreMultiply::ApplyScale(scale, b);
258 proc(state, r, g, b, a, result);
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
265 dst[i] = SkPremultiplyARGBInline(a, r, g, b);
269 void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
270 uint16_t dst[]) const {
271 SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
274 const State& state = fState;
279 memcpy(dst, src, count * sizeof(uint16_t));
284 for (int i = 0; i < count; i++) {
287 // 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);
292 proc(state, r, g, b, 0, result);
294 r = pin(result[0], SK_R32_MASK);
295 g = pin(result[1], SK_G32_MASK);
296 b = pin(result[2], SK_B32_MASK);
298 // now packed it back down to 16bits (hmmm, could dither...)
299 dst[i] = SkPack888ToRGB16(r, g, b);
303 ///////////////////////////////////////////////////////////////////////////////
305 void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const {
306 this->INHERITED::flatten(buffer);
307 SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
308 buffer.writeScalarArray(fMatrix.fMat, 20);
311 SkColorMatrixFilter::SkColorMatrixFilter(SkReadBuffer& buffer)
312 : INHERITED(buffer) {
313 SkASSERT(buffer.getArrayCount() == 20);
314 if (buffer.readScalarArray(fMatrix.fMat, 20)) {
315 this->initState(fMatrix.fMat);
319 bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
321 memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
327 #include "GrEffect.h"
328 #include "GrTBackendEffectFactory.h"
329 #include "gl/GrGLEffect.h"
331 class ColorMatrixEffect : public GrEffect {
333 static GrEffectRef* Create(const SkColorMatrix& matrix) {
334 AutoEffectUnref effect(SkNEW_ARGS(ColorMatrixEffect, (matrix)));
335 return CreateEffectRef(effect);
338 static const char* Name() { return "Color Matrix"; }
340 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
341 return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
344 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.
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".
352 static const uint32_t kRGBAFlags[] = {
353 kR_GrColorComponentFlag,
354 kG_GrColorComponentFlag,
355 kB_GrColorComponentFlag,
356 kA_GrColorComponentFlag
358 static const int kShifts[] = {
359 GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
362 kAlphaRowStartIdx = 15,
363 kAlphaRowTranslateIdx = 19,
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])) {
375 uint32_t component = (*color >> kShifts[i]) & 0xFF;
376 outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
380 outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
381 *validFlags = kA_GrColorComponentFlag;
382 // 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;
389 GR_DECLARE_EFFECT_TEST;
391 class GLEffect : public GrGLEffect {
393 // this class always generates the same code.
394 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
396 GLEffect(const GrBackendEffectFactory& factory,
398 : INHERITED(factory) {
401 virtual void emitCode(GrGLShaderBuilder* builder,
404 const char* outputColor,
405 const char* inputColor,
406 const TransformedCoordsArray&,
407 const TextureSamplerArray&) SK_OVERRIDE {
408 fMatrixHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
411 fVectorHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
413 "ColorMatrixVector");
415 if (NULL == inputColor) {
416 // could optimize this case, but we aren't for now.
417 inputColor = "vec4(1)";
419 // The max() is to guard against 0 / 0 during unpremul when the incoming color is
420 // transparent black.
421 builder->fsCodeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
422 builder->fsCodeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
424 builder->getUniformCStr(fMatrixHandle),
426 builder->getUniformCStr(fVectorHandle));
427 builder->fsCodeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor);
428 builder->fsCodeAppendf("\t%s.rgb *= %s.a;\n", outputColor, outputColor);
431 virtual void setData(const GrGLUniformManager& uniManager,
432 const GrDrawEffect& drawEffect) SK_OVERRIDE {
433 const ColorMatrixEffect& cme = drawEffect.castEffect<ColorMatrixEffect>();
434 const float* m = cme.fMatrix.fMat;
435 // The GL matrix is transposed from SkColorMatrix.
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],
442 static const float kScale = 1.0f / 255.0f;
444 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
446 uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
447 uniManager.set4fv(fVectorHandle, 1, vec);
451 GrGLUniformManager::UniformHandle fMatrixHandle;
452 GrGLUniformManager::UniformHandle fVectorHandle;
456 ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
458 virtual bool onIsEqual(const GrEffect& s) const {
459 const ColorMatrixEffect& cme = CastEffect<ColorMatrixEffect>(s);
460 return cme.fMatrix == fMatrix;
463 SkColorMatrix fMatrix;
465 typedef GrGLEffect INHERITED;
468 GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
470 GrEffectRef* ColorMatrixEffect::TestCreate(SkRandom* random,
472 const GrDrawTargetCaps&,
473 GrTexture* dummyTextures[2]) {
474 SkColorMatrix colorMatrix;
475 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
476 colorMatrix.fMat[i] = random->nextSScalar1();
478 return ColorMatrixEffect::Create(colorMatrix);
481 GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
482 return ColorMatrixEffect::Create(fMatrix);
487 #ifndef SK_IGNORE_TO_STRING
488 void SkColorMatrixFilter::toString(SkString* str) const {
489 str->append("SkColorMatrixFilter: ");
491 str->append("matrix: (");
492 for (int i = 0; i < 20; ++i) {
493 str->appendScalar(fMatrix.fMat[i]);