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 "SkUnPreMultiply.h"
12
13static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
14                          unsigned b, unsigned a) {
15    return array[0] * r + array[1] * g  + array[2] * b + array[3] * a + array[4];
16}
17
18static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
19                       unsigned b) {
20    return array[0] * r + array[1] * g  + array[2] * b + array[4];
21}
22
23static void General(SkColorMatrixFilter::State* state,
24                    unsigned r, unsigned g, unsigned b, unsigned a) {
25    const int32_t* SK_RESTRICT array = state->fArray;
26    const int shift = state->fShift;
27    int32_t* SK_RESTRICT result = state->fResult;
28
29    result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
30    result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
31    result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
32    result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
33}
34
35static void General16(SkColorMatrixFilter::State* state,
36                      unsigned r, unsigned g, unsigned b, unsigned a) {
37    const int32_t* SK_RESTRICT array = state->fArray;
38    int32_t* SK_RESTRICT result = state->fResult;
39
40    result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
41    result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
42    result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
43    result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
44}
45
46static void AffineAdd(SkColorMatrixFilter::State* state,
47                      unsigned r, unsigned g, unsigned b, unsigned a) {
48    const int32_t* SK_RESTRICT array = state->fArray;
49    const int shift = state->fShift;
50    int32_t* SK_RESTRICT result = state->fResult;
51
52    result[0] = rowmul3(&array[0], r, g, b) >> shift;
53    result[1] = rowmul3(&array[5], r, g, b) >> shift;
54    result[2] = rowmul3(&array[10], r, g, b) >> shift;
55    result[3] = a;
56}
57
58static void AffineAdd16(SkColorMatrixFilter::State* state,
59                        unsigned r, unsigned g, unsigned b, unsigned a) {
60    const int32_t* SK_RESTRICT array = state->fArray;
61    int32_t* SK_RESTRICT result = state->fResult;
62
63    result[0] = rowmul3(&array[0], r, g, b) >> 16;
64    result[1] = rowmul3(&array[5], r, g, b) >> 16;
65    result[2] = rowmul3(&array[10], r, g, b) >> 16;
66    result[3] = a;
67}
68
69static void ScaleAdd(SkColorMatrixFilter::State* state,
70                     unsigned r, unsigned g, unsigned b, unsigned a) {
71    const int32_t* SK_RESTRICT array = state->fArray;
72    const int shift = state->fShift;
73    int32_t* SK_RESTRICT result = state->fResult;
74
75    // cast to (int) to keep the expression signed for the shift
76    result[0] = (array[0] * (int)r + array[4]) >> shift;
77    result[1] = (array[6] * (int)g + array[9]) >> shift;
78    result[2] = (array[12] * (int)b + array[14]) >> shift;
79    result[3] = a;
80}
81
82static void ScaleAdd16(SkColorMatrixFilter::State* state,
83                       unsigned r, unsigned g, unsigned b, unsigned a) {
84    const int32_t* SK_RESTRICT array = state->fArray;
85    int32_t* SK_RESTRICT result = state->fResult;
86
87    // cast to (int) to keep the expression signed for the shift
88    result[0] = (array[0] * (int)r + array[4]) >> 16;
89    result[1] = (array[6] * (int)g + array[9]) >> 16;
90    result[2] = (array[12] * (int)b + array[14]) >> 16;
91    result[3] = a;
92}
93
94static void Add(SkColorMatrixFilter::State* state,
95                unsigned r, unsigned g, unsigned b, unsigned a) {
96    const int32_t* SK_RESTRICT array = state->fArray;
97    const int shift = state->fShift;
98    int32_t* SK_RESTRICT result = state->fResult;
99
100    result[0] = r + (array[4] >> shift);
101    result[1] = g + (array[9] >> shift);
102    result[2] = b + (array[14] >> shift);
103    result[3] = a;
104}
105
106static void Add16(SkColorMatrixFilter::State* state,
107                  unsigned r, unsigned g, unsigned b, unsigned a) {
108    const int32_t* SK_RESTRICT array = state->fArray;
109    int32_t* SK_RESTRICT result = state->fResult;
110
111    result[0] = r + (array[4] >> 16);
112    result[1] = g + (array[9] >> 16);
113    result[2] = b + (array[14] >> 16);
114    result[3] = a;
115}
116
117#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag |  \
118                         SkColorFilter::kHasFilter16_Flag)
119
120// src is [20] but some compilers won't accept __restrict__ on anything
121// but an raw pointer or reference
122void SkColorMatrixFilter::setup(const SkScalar* SK_RESTRICT src) {
123    if (NULL == src) {
124        fProc = NULL;   // signals identity
125        fFlags  = kNO_ALPHA_FLAGS;
126        // fState is undefined, but that is OK, since we shouldn't look at it
127        return;
128    }
129
130    int32_t* array = fState.fArray;
131
132    int i;
133    SkFixed max = 0;
134
135    for (int i = 0; i < 20; i++) {
136        SkFixed value = SkScalarToFixed(src[i]);
137        array[i] = value;
138        value = SkAbs32(value);
139        max = SkMax32(max, value);
140    }
141
142    /*  All of fArray[] values must fit in 23 bits, to safely allow me to
143        multiply them by 8bit unsigned values, and get a signed answer without
144        overflow. This means clz needs to be 9 or bigger
145    */
146    int bits = SkCLZ(max);
147    int32_t one = SK_Fixed1;
148
149    fState.fShift = 16; // we are starting out as fixed 16.16
150    if (bits < 9) {
151        bits = 9 - bits;
152        fState.fShift -= bits;
153        for (i = 0; i < 20; i++) {
154            array[i] >>= bits;
155        }
156        one >>= bits;
157    }
158
159    // check if we have to munge Alpha
160    int32_t changesAlpha = (array[15] | array[16] | array[17] |
161                            (array[18] - one) | array[19]);
162    int32_t usesAlpha = (array[3] | array[8] | array[13]);
163    bool shiftIs16 = (16 == fState.fShift);
164
165    if (changesAlpha | usesAlpha) {
166        fProc = shiftIs16 ? General16 : General;
167        fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
168    } else {
169        fFlags = kNO_ALPHA_FLAGS;
170
171        int32_t needsScale = (array[0] - one) |       // red axis
172                             (array[6] - one) |       // green axis
173                             (array[12] - one);       // blue axis
174
175        int32_t needs3x3 =  array[1] | array[2] |     // red off-axis
176                            array[5] | array[7] |     // green off-axis
177                            array[10] | array[11];    // blue off-axis
178
179        if (needs3x3) {
180            fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
181        } else if (needsScale) {
182            fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
183        } else if (array[4] | array[9] | array[14]) {   // needs add
184            fProc = shiftIs16 ? Add16 : Add;
185        } else {
186            fProc = NULL;   // identity
187        }
188    }
189
190    /*  preround our add values so we get a rounded shift. We do this after we
191        analyze the array, so we don't miss the case where the caller has zeros
192        which could make us accidentally take the General or Add case.
193    */
194    if (NULL != fProc) {
195        int32_t add = 1 << (fState.fShift - 1);
196        array[4] += add;
197        array[9] += add;
198        array[14] += add;
199        array[19] += add;
200    }
201}
202
203///////////////////////////////////////////////////////////////////////////////
204
205static int32_t pin(int32_t value, int32_t max) {
206    if (value < 0) {
207        value = 0;
208    }
209    if (value > max) {
210        value = max;
211    }
212    return value;
213}
214
215SkColorMatrixFilter::SkColorMatrixFilter() {
216    this->setup(NULL);
217}
218
219SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) {
220    this->setup(cm.fMat);
221}
222
223SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
224    this->setup(array);
225}
226
227uint32_t SkColorMatrixFilter::getFlags() {
228    return this->INHERITED::getFlags() | fFlags;
229}
230
231void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
232                                     SkPMColor dst[]) {
233    Proc proc = fProc;
234    State* state = &fState;
235    int32_t* result = state->fResult;
236
237    if (NULL == proc) {
238        if (src != dst) {
239            memcpy(dst, src, count * sizeof(SkPMColor));
240        }
241        return;
242    }
243
244    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
245
246    for (int i = 0; i < count; i++) {
247        SkPMColor c = src[i];
248
249        unsigned r = SkGetPackedR32(c);
250        unsigned g = SkGetPackedG32(c);
251        unsigned b = SkGetPackedB32(c);
252        unsigned a = SkGetPackedA32(c);
253
254        // need our components to be un-premultiplied
255        if (255 != a) {
256            SkUnPreMultiply::Scale scale = table[a];
257            r = SkUnPreMultiply::ApplyScale(scale, r);
258            g = SkUnPreMultiply::ApplyScale(scale, g);
259            b = SkUnPreMultiply::ApplyScale(scale, b);
260
261            SkASSERT(r <= 255);
262            SkASSERT(g <= 255);
263            SkASSERT(b <= 255);
264        }
265
266        proc(state, r, g, b, a);
267
268        r = pin(result[0], SK_R32_MASK);
269        g = pin(result[1], SK_G32_MASK);
270        b = pin(result[2], SK_B32_MASK);
271        a = pin(result[3], SK_A32_MASK);
272        // re-prepremultiply if needed
273        dst[i] = SkPremultiplyARGBInline(a, r, g, b);
274    }
275}
276
277void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
278                                       uint16_t dst[]) {
279    SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
280
281    Proc   proc = fProc;
282    State* state = &fState;
283    int32_t* result = state->fResult;
284
285    if (NULL == proc) {
286        if (src != dst) {
287            memcpy(dst, src, count * sizeof(uint16_t));
288        }
289        return;
290    }
291
292    for (int i = 0; i < count; i++) {
293        uint16_t c = src[i];
294
295        // expand to 8bit components (since our matrix translate is 8bit biased
296        unsigned r = SkPacked16ToR32(c);
297        unsigned g = SkPacked16ToG32(c);
298        unsigned b = SkPacked16ToB32(c);
299
300        proc(state, r, g, b, 0);
301
302        r = pin(result[0], SK_R32_MASK);
303        g = pin(result[1], SK_G32_MASK);
304        b = pin(result[2], SK_B32_MASK);
305
306        // now packed it back down to 16bits (hmmm, could dither...)
307        dst[i] = SkPack888ToRGB16(r, g, b);
308    }
309}
310
311///////////////////////////////////////////////////////////////////////////////
312
313void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer)  {
314    this->INHERITED::flatten(buffer);
315
316    buffer.writeFunctionPtr((void*)fProc);
317    buffer.writeMul4(&fState, sizeof(fState));
318    buffer.write32(fFlags);
319}
320
321SkFlattenable::Factory SkColorMatrixFilter::getFactory() { return CreateProc;  }
322
323SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
324        : INHERITED(buffer) {
325    fProc = (Proc)buffer.readFunctionPtr();
326    buffer.read(&fState, sizeof(fState));
327    fFlags = buffer.readU32();
328}
329
330bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) {
331    int32_t* array = fState.fArray;
332    int unshift = 16 - fState.fShift;
333    for (int i = 0; i < 20; i++) {
334        matrix[i] = SkFixedToScalar(array[i] << unshift);
335    }
336    if (NULL != fProc) {
337        // Undo the offset applied to the constant column in setup().
338        SkFixed offset = 1 << (fState.fShift - 1);
339        matrix[4] = SkFixedToScalar((array[4] - offset) << unshift);
340        matrix[9] = SkFixedToScalar((array[9] - offset) << unshift);
341        matrix[14] = SkFixedToScalar((array[14] - offset) << unshift);
342        matrix[19] = SkFixedToScalar((array[19] - offset) << unshift);
343    }
344    return true;
345}
346
347SkFlattenable* SkColorMatrixFilter::CreateProc(SkFlattenableReadBuffer& buf) {
348    return SkNEW_ARGS(SkColorMatrixFilter, (buf));
349}
350
351void SkColorMatrixFilter::setMatrix(const SkColorMatrix& matrix) {
352    setup(matrix.fMat);
353}
354
355void SkColorMatrixFilter::setArray(const SkScalar array[20]) {
356    setup(array);
357}
358
359SK_DEFINE_FLATTENABLE_REGISTRAR(SkColorMatrixFilter)
360