Home | History | Annotate | Download | only in core
      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 
     19 static 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 
     38 void 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 
     56 SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) {
     57     memcpy(fMatrix, array, 20 * sizeof(SkScalar));
     58     this->initState();
     59 }
     60 
     61 uint32_t SkColorMatrixFilterRowMajor255::getFlags() const {
     62     return this->INHERITED::getFlags() | fFlags;
     63 }
     64 
     65 static Sk4f scale_rgb(float scale) {
     66     static_assert(SkPM4f::A == 3, "Alpha is lane 3");
     67     return Sk4f(scale, scale, scale, 1);
     68 }
     69 
     70 static Sk4f premul(const Sk4f& x) {
     71     return x * scale_rgb(x[SkPM4f::A]);
     72 }
     73 
     74 static Sk4f unpremul(const Sk4f& x) {
     75     return x * scale_rgb(1 / x[SkPM4f::A]);  // TODO: fast/approx invert?
     76 }
     77 
     78 static Sk4f clamp_0_1(const Sk4f& x) {
     79     return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0));
     80 }
     81 
     82 static 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 
     88 template <typename Adaptor, typename T>
     89 void 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 
    122 struct 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 };
    136 void SkColorMatrixFilterRowMajor255::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
    137     filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst);
    138 }
    139 
    140 struct 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 };
    154 void SkColorMatrixFilterRowMajor255::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
    155     filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst);
    156 }
    157 
    158 ///////////////////////////////////////////////////////////////////////////////
    159 
    160 void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const {
    161     SkASSERT(sizeof(fMatrix)/sizeof(SkScalar) == 20);
    162     buffer.writeScalarArray(fMatrix, 20);
    163 }
    164 
    165 sk_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 
    173 bool 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.
    194 static 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 
    206 static 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 
    213 static 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 
    234 bool 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 
    260 sk_sp<SkColorFilter>
    261 SkColorMatrixFilterRowMajor255::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 
    278 class ColorMatrixEffect : public GrFragmentProcessor {
    279 public:
    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     };
    347 private:
    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 
    374 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect);
    375 
    376 #if GR_TEST_UTILS
    377 sk_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 
    386 sk_sp<GrFragmentProcessor> SkColorMatrixFilterRowMajor255::asFragmentProcessor(
    387                                                                   GrContext*, SkColorSpace*) const {
    388     return ColorMatrixEffect::Make(fMatrix);
    389 }
    390 
    391 #endif
    392 
    393 #ifndef SK_IGNORE_TO_STRING
    394 void 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 
    410 sk_sp<SkColorFilter> SkColorFilter::MakeMatrixFilterRowMajor255(const SkScalar array[20]) {
    411     return sk_sp<SkColorFilter>(new SkColorMatrixFilterRowMajor255(array));
    412 }
    413 
    414 ///////////////////////////////////////////////////////////////////////////////
    415 
    416 sk_sp<SkColorFilter>
    417 SkColorMatrixFilterRowMajor255::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