1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkColorMatrixFilterRowMajor255.h"
9#include "SkColorPriv.h"
10#include "SkNx.h"
11#include "SkPM4fPriv.h"
12#include "SkRasterPipeline.h"
13#include "SkReadBuffer.h"
14#include "SkRefCnt.h"
15#include "SkString.h"
16#include "SkUnPreMultiply.h"
17#include "SkWriteBuffer.h"
18
19static void transpose_and_scale01(float dst[20], const float src[20]) {
20    const float* srcR = src + 0;
21    const float* srcG = src + 5;
22    const float* srcB = src + 10;
23    const float* srcA = src + 15;
24
25    for (int i = 0; i < 16; i += 4) {
26        dst[i + 0] = *srcR++;
27        dst[i + 1] = *srcG++;
28        dst[i + 2] = *srcB++;
29        dst[i + 3] = *srcA++;
30    }
31    // Might as well scale these translates down to [0,1] here instead of every filter call.
32    dst[16] = *srcR * (1/255.0f);
33    dst[17] = *srcG * (1/255.0f);
34    dst[18] = *srcB * (1/255.0f);
35    dst[19] = *srcA * (1/255.0f);
36}
37
38void SkColorMatrixFilterRowMajor255::initState() {
39    transpose_and_scale01(fTranspose, fMatrix);
40
41    const float* array = fMatrix;
42
43    // check if we have to munge Alpha
44    bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]);
45    bool usesAlpha = (array[3] || array[8] || array[13]);
46
47    if (changesAlpha || usesAlpha) {
48        fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag;
49    } else {
50        fFlags = kAlphaUnchanged_Flag;
51    }
52}
53
54///////////////////////////////////////////////////////////////////////////////
55
56SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) {
57    memcpy(fMatrix, array, 20 * sizeof(SkScalar));
58    this->initState();
59}
60
61uint32_t SkColorMatrixFilterRowMajor255::getFlags() const {
62    return this->INHERITED::getFlags() | fFlags;
63}
64
65static Sk4f scale_rgb(float scale) {
66    static_assert(SkPM4f::A == 3, "Alpha is lane 3");
67    return Sk4f(scale, scale, scale, 1);
68}
69
70static Sk4f premul(const Sk4f& x) {
71    return x * scale_rgb(x[SkPM4f::A]);
72}
73
74static Sk4f unpremul(const Sk4f& x) {
75    return x * scale_rgb(1 / x[SkPM4f::A]);  // TODO: fast/approx invert?
76}
77
78static Sk4f clamp_0_1(const Sk4f& x) {
79    return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0));
80}
81
82static SkPMColor round(const Sk4f& x) {
83    SkPMColor c;
84    SkNx_cast<uint8_t>(x * Sk4f(255) + Sk4f(0.5f)).store(&c);
85    return c;
86}
87
88template <typename Adaptor, typename T>
89void filter_span(const float array[], const T src[], int count, T dst[]) {
90    const Sk4f c0 = Sk4f::Load(array + 0);
91    const Sk4f c1 = Sk4f::Load(array + 4);
92    const Sk4f c2 = Sk4f::Load(array + 8);
93    const Sk4f c3 = Sk4f::Load(array + 12);
94    const Sk4f c4 = Sk4f::Load(array + 16);
95
96    // todo: we could cache this in the constructor...
97    T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4)));
98
99    for (int i = 0; i < count; i++) {
100        Sk4f srcf = Adaptor::To4f(src[i]);
101        float srcA = srcf[SkPM4f::A];
102
103        if (0 == srcA) {
104            dst[i] = matrix_translate_pmcolor;
105            continue;
106        }
107        if (1 != srcA) {
108            srcf = unpremul(srcf);
109        }
110
111        Sk4f r4 = srcf[Adaptor::R];
112        Sk4f g4 = srcf[Adaptor::G];
113        Sk4f b4 = srcf[Adaptor::B];
114        Sk4f a4 = srcf[Adaptor::A];
115        // apply matrix
116        Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4;
117
118        dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4)));
119    }
120}
121
122struct SkPMColorAdaptor {
123    enum {
124        R = SK_R_INDEX,
125        G = SK_G_INDEX,
126        B = SK_B_INDEX,
127        A = SK_A_INDEX,
128    };
129    static SkPMColor From4f(const Sk4f& c4) {
130        return round(swizzle_rb_if_bgra(c4));
131    }
132    static Sk4f To4f(SkPMColor c) {
133        return Sk4f_fromL32(c);
134    }
135};
136void SkColorMatrixFilterRowMajor255::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
137    filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst);
138}
139
140struct SkPM4fAdaptor {
141    enum {
142        R = SkPM4f::R,
143        G = SkPM4f::G,
144        B = SkPM4f::B,
145        A = SkPM4f::A,
146    };
147    static SkPM4f From4f(const Sk4f& c4) {
148        return SkPM4f::From4f(c4);
149    }
150    static Sk4f To4f(const SkPM4f& c) {
151        return c.to4f();
152    }
153};
154void SkColorMatrixFilterRowMajor255::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
155    filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst);
156}
157
158///////////////////////////////////////////////////////////////////////////////
159
160void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const {
161    SkASSERT(sizeof(fMatrix)/sizeof(SkScalar) == 20);
162    buffer.writeScalarArray(fMatrix, 20);
163}
164
165sk_sp<SkFlattenable> SkColorMatrixFilterRowMajor255::CreateProc(SkReadBuffer& buffer) {
166    SkScalar matrix[20];
167    if (buffer.readScalarArray(matrix, 20)) {
168        return sk_make_sp<SkColorMatrixFilterRowMajor255>(matrix);
169    }
170    return nullptr;
171}
172
173bool SkColorMatrixFilterRowMajor255::asColorMatrix(SkScalar matrix[20]) const {
174    if (matrix) {
175        memcpy(matrix, fMatrix, 20 * sizeof(SkScalar));
176    }
177    return true;
178}
179
180///////////////////////////////////////////////////////////////////////////////
181//  This code was duplicated from src/effects/SkColorMatrixc.cpp in order to be used in core.
182//////
183
184// To detect if we need to apply clamping after applying a matrix, we check if
185// any output component might go outside of [0, 255] for any combination of
186// input components in [0..255].
187// Each output component is an affine transformation of the input component, so
188// the minimum and maximum values are for any combination of minimum or maximum
189// values of input components (i.e. 0 or 255).
190// E.g. if R' = x*R + y*G + z*B + w*A + t
191// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
192// minimum value will be for R=0 if x>0 or R=255 if x<0.
193// Same goes for all components.
194static bool component_needs_clamping(const SkScalar row[5]) {
195    SkScalar maxValue = row[4] / 255;
196    SkScalar minValue = row[4] / 255;
197    for (int i = 0; i < 4; ++i) {
198        if (row[i] > 0)
199            maxValue += row[i];
200        else
201            minValue += row[i];
202    }
203    return (maxValue > 1) || (minValue < 0);
204}
205
206static bool needs_clamping(const SkScalar matrix[20]) {
207    return component_needs_clamping(matrix)
208        || component_needs_clamping(matrix+5)
209        || component_needs_clamping(matrix+10)
210        || component_needs_clamping(matrix+15);
211}
212
213static void set_concat(SkScalar result[20], const SkScalar outer[20], const SkScalar inner[20]) {
214    int index = 0;
215    for (int j = 0; j < 20; j += 5) {
216        for (int i = 0; i < 4; i++) {
217            result[index++] =   outer[j + 0] * inner[i + 0] +
218                                outer[j + 1] * inner[i + 5] +
219                                outer[j + 2] * inner[i + 10] +
220                                outer[j + 3] * inner[i + 15];
221        }
222        result[index++] =   outer[j + 0] * inner[4] +
223                            outer[j + 1] * inner[9] +
224                            outer[j + 2] * inner[14] +
225                            outer[j + 3] * inner[19] +
226                            outer[j + 4];
227    }
228}
229
230///////////////////////////////////////////////////////////////////////////////
231//  End duplication
232//////
233
234bool SkColorMatrixFilterRowMajor255::onAppendStages(SkRasterPipeline* p,
235                                                    SkColorSpace* dst,
236                                                    SkArenaAlloc* scratch,
237                                                    bool shaderIsOpaque) const {
238    bool willStayOpaque = shaderIsOpaque && (fFlags & kAlphaUnchanged_Flag);
239    bool needsClamp0 = false,
240         needsClamp1 = false;
241    for (int i = 0; i < 4; i++) {
242        SkScalar min = fTranspose[i+16],
243                 max = fTranspose[i+16];
244        (fTranspose[i+ 0] < 0 ? min : max) += fTranspose[i+ 0];
245        (fTranspose[i+ 4] < 0 ? min : max) += fTranspose[i+ 4];
246        (fTranspose[i+ 8] < 0 ? min : max) += fTranspose[i+ 8];
247        (fTranspose[i+12] < 0 ? min : max) += fTranspose[i+12];
248        needsClamp0 = needsClamp0 || min < 0;
249        needsClamp1 = needsClamp1 || max > 1;
250    }
251
252    if (!shaderIsOpaque) { p->append(SkRasterPipeline::unpremul); }
253    if (           true) { p->append(SkRasterPipeline::matrix_4x5, fTranspose); }
254    if (!willStayOpaque) { p->append(SkRasterPipeline::premul); }
255    if (    needsClamp0) { p->append(SkRasterPipeline::clamp_0); }
256    if (    needsClamp1) { p->append(SkRasterPipeline::clamp_a); }
257    return true;
258}
259
260sk_sp<SkColorFilter>
261SkColorMatrixFilterRowMajor255::makeComposed(sk_sp<SkColorFilter> innerFilter) const {
262    SkScalar innerMatrix[20];
263    if (innerFilter->asColorMatrix(innerMatrix) && !needs_clamping(innerMatrix)) {
264        SkScalar concat[20];
265        set_concat(concat, fMatrix, innerMatrix);
266        return sk_make_sp<SkColorMatrixFilterRowMajor255>(concat);
267    }
268    return nullptr;
269}
270
271#if SK_SUPPORT_GPU
272#include "GrFragmentProcessor.h"
273#include "glsl/GrGLSLFragmentProcessor.h"
274#include "glsl/GrGLSLFragmentShaderBuilder.h"
275#include "glsl/GrGLSLProgramDataManager.h"
276#include "glsl/GrGLSLUniformHandler.h"
277
278class ColorMatrixEffect : public GrFragmentProcessor {
279public:
280    static sk_sp<GrFragmentProcessor> Make(const SkScalar matrix[20]) {
281        return sk_sp<GrFragmentProcessor>(new ColorMatrixEffect(matrix));
282    }
283
284    const char* name() const override { return "Color Matrix"; }
285
286    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
287
288    class GLSLProcessor : public GrGLSLFragmentProcessor {
289    public:
290        // this class always generates the same code.
291        static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
292
293        void emitCode(EmitArgs& args) override {
294            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
295            fMatrixHandle = uniformHandler->addUniform(kFragment_GrShaderFlag,
296                                                       kMat44f_GrSLType, kDefault_GrSLPrecision,
297                                                       "ColorMatrix");
298            fVectorHandle = uniformHandler->addUniform(kFragment_GrShaderFlag,
299                                                       kVec4f_GrSLType, kDefault_GrSLPrecision,
300                                                       "ColorMatrixVector");
301
302            if (nullptr == args.fInputColor) {
303                // could optimize this case, but we aren't for now.
304                args.fInputColor = "vec4(1)";
305            }
306            GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
307            // The max() is to guard against 0 / 0 during unpremul when the incoming color is
308            // transparent black.
309            fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n",
310                                     args.fInputColor);
311            fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
312                                     args.fOutputColor,
313                                     uniformHandler->getUniformCStr(fMatrixHandle),
314                                     args.fInputColor,
315                                     uniformHandler->getUniformCStr(fVectorHandle));
316            fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n",
317                                     args.fOutputColor, args.fOutputColor);
318            fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
319        }
320
321    protected:
322        void onSetData(const GrGLSLProgramDataManager& uniManager,
323                       const GrProcessor& proc) override {
324            const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
325            const float* m = cme.fMatrix;
326            // The GL matrix is transposed from SkColorMatrix.
327            float mt[]  = {
328                m[0], m[5], m[10], m[15],
329                m[1], m[6], m[11], m[16],
330                m[2], m[7], m[12], m[17],
331                m[3], m[8], m[13], m[18],
332            };
333            static const float kScale = 1.0f / 255.0f;
334            float vec[] = {
335                m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
336            };
337            uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
338            uniManager.set4fv(fVectorHandle, 1, vec);
339        }
340
341    private:
342        GrGLSLProgramDataManager::UniformHandle fMatrixHandle;
343        GrGLSLProgramDataManager::UniformHandle fVectorHandle;
344
345        typedef GrGLSLFragmentProcessor INHERITED;
346    };
347private:
348    // We could implement the constant input->constant output optimization but haven't. Other
349    // optimizations would be matrix-dependent.
350    ColorMatrixEffect(const SkScalar matrix[20]) : INHERITED(kNone_OptimizationFlags) {
351        memcpy(fMatrix, matrix, sizeof(SkScalar) * 20);
352        this->initClassID<ColorMatrixEffect>();
353    }
354
355    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
356        return new GLSLProcessor;
357    }
358
359    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
360                                       GrProcessorKeyBuilder* b) const override {
361        GLSLProcessor::GenKey(*this, caps, b);
362    }
363
364    bool onIsEqual(const GrFragmentProcessor& s) const override {
365        const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>();
366        return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix));
367    }
368
369    SkScalar fMatrix[20];
370
371    typedef GrFragmentProcessor INHERITED;
372};
373
374GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect);
375
376#if GR_TEST_UTILS
377sk_sp<GrFragmentProcessor> ColorMatrixEffect::TestCreate(GrProcessorTestData* d) {
378    SkScalar colorMatrix[20];
379    for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) {
380        colorMatrix[i] = d->fRandom->nextSScalar1();
381    }
382    return ColorMatrixEffect::Make(colorMatrix);
383}
384#endif
385
386sk_sp<GrFragmentProcessor> SkColorMatrixFilterRowMajor255::asFragmentProcessor(
387                                                                  GrContext*, SkColorSpace*) const {
388    return ColorMatrixEffect::Make(fMatrix);
389}
390
391#endif
392
393#ifndef SK_IGNORE_TO_STRING
394void SkColorMatrixFilterRowMajor255::toString(SkString* str) const {
395    str->append("SkColorMatrixFilterRowMajor255: ");
396
397    str->append("matrix: (");
398    for (int i = 0; i < 20; ++i) {
399        str->appendScalar(fMatrix[i]);
400        if (i < 19) {
401            str->append(", ");
402        }
403    }
404    str->append(")");
405}
406#endif
407
408///////////////////////////////////////////////////////////////////////////////
409
410sk_sp<SkColorFilter> SkColorFilter::MakeMatrixFilterRowMajor255(const SkScalar array[20]) {
411    return sk_sp<SkColorFilter>(new SkColorMatrixFilterRowMajor255(array));
412}
413
414///////////////////////////////////////////////////////////////////////////////
415
416sk_sp<SkColorFilter>
417SkColorMatrixFilterRowMajor255::MakeSingleChannelOutput(const SkScalar row[5]) {
418    SkASSERT(row);
419    auto cf = sk_make_sp<SkColorMatrixFilterRowMajor255>();
420    static_assert(sizeof(SkScalar) * 5 * 4 == sizeof(cf->fMatrix), "sizes don't match");
421    for (int i = 0; i < 4; ++i) {
422        memcpy(cf->fMatrix + 5 * i, row, sizeof(SkScalar) * 5);
423    }
424    cf->initState();
425    return cf;
426}
427