1
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 */
8#include "SkColorMatrixFilter.h"
9#include "SkColorMatrix.h"
10#include "SkColorPriv.h"
11#include "SkFlattenableBuffers.h"
12#include "SkUnPreMultiply.h"
13#include "SkString.h"
14
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
25static 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;
30
31    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
37static 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;
41
42    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
48static 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;
53
54    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
60static 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;
64
65    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
71static 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;
76
77    // 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
84static 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;
88
89    // 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
96static 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;
101
102    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
108static 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;
112
113    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
122// src is [20] but some compilers won't accept __restrict__ on anything
123// but an raw pointer or reference
124void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
125    int32_t* array = fState.fArray;
126    SkFixed max = 0;
127    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    }
133
134    /*  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;
145        for (int i = 0; i < 20; i++) {
146            array[i] >>= bits;
147        }
148        one >>= bits;
149    }
150
151    // 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
207SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
208    this->initState(cm.fMat);
209}
210
211SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
212    memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
213    this->initState(array);
214}
215
216uint32_t SkColorMatrixFilter::getFlags() const {
217    return this->INHERITED::getFlags() | fFlags;
218}
219
220void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
221                                     SkPMColor dst[]) const {
222    Proc proc = fProc;
223    const State& state = fState;
224    int32_t result[4];
225
226    if (NULL == proc) {
227        if (src != dst) {
228            memcpy(dst, src, count * sizeof(SkPMColor));
229        }
230        return;
231    }
232
233    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
234
235    for (int i = 0; i < count; i++) {
236        SkPMColor c = src[i];
237
238        unsigned r = SkGetPackedR32(c);
239        unsigned g = SkGetPackedG32(c);
240        unsigned b = SkGetPackedB32(c);
241        unsigned a = SkGetPackedA32(c);
242
243        // 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);
249
250            SkASSERT(r <= 255);
251            SkASSERT(g <= 255);
252            SkASSERT(b <= 255);
253        }
254
255        proc(state, r, g, b, a, result);
256
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
262        dst[i] = SkPremultiplyARGBInline(a, r, g, b);
263    }
264}
265
266void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
267                                       uint16_t dst[]) const {
268    SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
269
270    Proc   proc = fProc;
271    const State& state = fState;
272    int32_t result[4];
273
274    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];
283
284        // 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);
288
289        proc(state, r, g, b, 0, result);
290
291        r = pin(result[0], SK_R32_MASK);
292        g = pin(result[1], SK_G32_MASK);
293        b = pin(result[2], SK_B32_MASK);
294
295        // now packed it back down to 16bits (hmmm, could dither...)
296        dst[i] = SkPack888ToRGB16(r, g, b);
297    }
298}
299
300///////////////////////////////////////////////////////////////////////////////
301
302void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
303    this->INHERITED::flatten(buffer);
304    SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
305    buffer.writeScalarArray(fMatrix.fMat, 20);
306}
307
308SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
309        : INHERITED(buffer) {
310    SkASSERT(buffer.getArrayCount() == 20);
311    buffer.readScalarArray(fMatrix.fMat);
312    this->initState(fMatrix.fMat);
313}
314
315bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
316    if (matrix) {
317        memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
318    }
319    return true;
320}
321
322#if SK_SUPPORT_GPU
323#include "GrEffect.h"
324#include "GrTBackendEffectFactory.h"
325#include "gl/GrGLEffect.h"
326
327class ColorMatrixEffect : public GrEffect {
328public:
329    static GrEffectRef* Create(const SkColorMatrix& matrix) {
330        AutoEffectUnref effect(SkNEW_ARGS(ColorMatrixEffect, (matrix)));
331        return CreateEffectRef(effect);
332    }
333
334    static const char* Name() { return "Color Matrix"; }
335
336    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
337        return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
338    }
339
340    virtual void getConstantColorComponents(GrColor* color,
341                                            uint32_t* validFlags) const SK_OVERRIDE {
342        // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
343        // type flags it might be worth checking the other components.
344
345        // The matrix is defined such the 4th row determines the output alpha. The first four
346        // columns of that row multiply the input r, g, b, and a, respectively, and the last column
347        // is the "translation".
348        static const uint32_t kRGBAFlags[] = {
349            kR_GrColorComponentFlag,
350            kG_GrColorComponentFlag,
351            kB_GrColorComponentFlag,
352            kA_GrColorComponentFlag
353        };
354        static const int kShifts[] = {
355            GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
356        };
357        enum {
358            kAlphaRowStartIdx = 15,
359            kAlphaRowTranslateIdx = 19,
360        };
361
362        SkScalar outputA = 0;
363        for (int i = 0; i < 4; ++i) {
364            // If any relevant component of the color to be passed through the matrix is non-const
365            // then we can't know the final result.
366            if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
367                if (!(*validFlags & kRGBAFlags[i])) {
368                    *validFlags = 0;
369                    return;
370                } else {
371                    uint32_t component = (*color >> kShifts[i]) & 0xFF;
372                    outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
373                }
374            }
375        }
376        outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
377        *validFlags = kA_GrColorComponentFlag;
378        // We pin the color to [0,1]. This would happen to the *final* color output from the frag
379        // shader but currently the effect does not pin its own output. So in the case of over/
380        // underflow this may deviate from the actual result. Maybe the effect should pin its
381        // result if the matrix could over/underflow for any component?
382        *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
383    }
384
385    GR_DECLARE_EFFECT_TEST;
386
387    class GLEffect : public GrGLEffect {
388    public:
389        // this class always generates the same code.
390        static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
391
392        GLEffect(const GrBackendEffectFactory& factory,
393                 const GrDrawEffect&)
394        : INHERITED(factory)
395        , fMatrixHandle(GrGLUniformManager::kInvalidUniformHandle)
396        , fVectorHandle(GrGLUniformManager::kInvalidUniformHandle) {}
397
398        virtual void emitCode(GrGLShaderBuilder* builder,
399                              const GrDrawEffect&,
400                              EffectKey,
401                              const char* outputColor,
402                              const char* inputColor,
403                              const TextureSamplerArray&) SK_OVERRIDE {
404            fMatrixHandle = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
405                                                kMat44f_GrSLType,
406                                                "ColorMatrix");
407            fVectorHandle = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
408                                                kVec4f_GrSLType,
409                                                "ColorMatrixVector");
410
411            if (NULL == inputColor) {
412                // could optimize this case, but we aren't for now.
413                inputColor = GrGLSLOnesVecf(4);
414            }
415            // The max() is to guard against 0 / 0 during unpremul when the incoming color is
416            // transparent black.
417            builder->fsCodeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
418            builder->fsCodeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
419                                   outputColor,
420                                   builder->getUniformCStr(fMatrixHandle),
421                                   inputColor,
422                                   builder->getUniformCStr(fVectorHandle));
423            builder->fsCodeAppendf("\t%s.rgb *= %s.a;\n", outputColor, outputColor);
424        }
425
426        virtual void setData(const GrGLUniformManager& uniManager,
427                             const GrDrawEffect& drawEffect) SK_OVERRIDE {
428            const ColorMatrixEffect& cme = drawEffect.castEffect<ColorMatrixEffect>();
429            const float* m = cme.fMatrix.fMat;
430            // The GL matrix is transposed from SkColorMatrix.
431            GrGLfloat mt[]  = {
432                m[0], m[5], m[10], m[15],
433                m[1], m[6], m[11], m[16],
434                m[2], m[7], m[12], m[17],
435                m[3], m[8], m[13], m[18],
436            };
437            static const float kScale = 1.0f / 255.0f;
438            GrGLfloat vec[] = {
439                m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
440            };
441            uniManager.setMatrix4fv(fMatrixHandle, 0, 1, mt);
442            uniManager.set4fv(fVectorHandle, 0, 1, vec);
443        }
444
445    private:
446        GrGLUniformManager::UniformHandle fMatrixHandle;
447        GrGLUniformManager::UniformHandle fVectorHandle;
448    };
449
450private:
451    ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
452
453    virtual bool onIsEqual(const GrEffect& s) const {
454        const ColorMatrixEffect& cme = CastEffect<ColorMatrixEffect>(s);
455        return cme.fMatrix == fMatrix;
456    }
457
458    SkColorMatrix fMatrix;
459
460    typedef GrGLEffect INHERITED;
461};
462
463GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
464
465GrEffectRef* ColorMatrixEffect::TestCreate(SkMWCRandom* random,
466                                           GrContext*,
467                                           const GrDrawTargetCaps&,
468                                           GrTexture* dummyTextures[2]) {
469    SkColorMatrix colorMatrix;
470    for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
471        colorMatrix.fMat[i] = random->nextSScalar1();
472    }
473    return ColorMatrixEffect::Create(colorMatrix);
474}
475
476GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
477    return ColorMatrixEffect::Create(fMatrix);
478}
479
480#endif
481
482#ifdef SK_DEVELOPER
483void SkColorMatrixFilter::toString(SkString* str) const {
484    str->append("SkColorMatrixFilter: ");
485
486    str->append("matrix: (");
487    for (int i = 0; i < 20; ++i) {
488        str->appendScalar(fMatrix.fMat[i]);
489        if (i < 19) {
490            str->append(", ");
491        }
492    }
493    str->append(")");
494}
495#endif
496