Home | History | Annotate | Download | only in effects
      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 
     14 static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
     15                           unsigned b, unsigned a) {
     16     return array[0] * r + array[1] * g  + array[2] * b + array[3] * a + array[4];
     17 }
     18 
     19 static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
     20                        unsigned b) {
     21     return array[0] * r + array[1] * g  + array[2] * b + array[4];
     22 }
     23 
     24 static void General(const SkColorMatrixFilter::State& state,
     25                     unsigned r, unsigned g, unsigned b, unsigned a,
     26                     int32_t* SK_RESTRICT result) {
     27     const int32_t* SK_RESTRICT array = state.fArray;
     28     const int shift = state.fShift;
     29 
     30     result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
     31     result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
     32     result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
     33     result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
     34 }
     35 
     36 static void General16(const SkColorMatrixFilter::State& state,
     37                       unsigned r, unsigned g, unsigned b, unsigned a,
     38                       int32_t* SK_RESTRICT result) {
     39     const int32_t* SK_RESTRICT array = state.fArray;
     40 
     41     result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
     42     result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
     43     result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
     44     result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
     45 }
     46 
     47 static void AffineAdd(const SkColorMatrixFilter::State& state,
     48                       unsigned r, unsigned g, unsigned b, unsigned a,
     49                       int32_t* SK_RESTRICT result) {
     50     const int32_t* SK_RESTRICT array = state.fArray;
     51     const int shift = state.fShift;
     52 
     53     result[0] = rowmul3(&array[0], r, g, b) >> shift;
     54     result[1] = rowmul3(&array[5], r, g, b) >> shift;
     55     result[2] = rowmul3(&array[10], r, g, b) >> shift;
     56     result[3] = a;
     57 }
     58 
     59 static void AffineAdd16(const SkColorMatrixFilter::State& state,
     60                         unsigned r, unsigned g, unsigned b, unsigned a,
     61                         int32_t* SK_RESTRICT result) {
     62     const int32_t* SK_RESTRICT array = state.fArray;
     63 
     64     result[0] = rowmul3(&array[0], r, g, b) >> 16;
     65     result[1] = rowmul3(&array[5], r, g, b) >> 16;
     66     result[2] = rowmul3(&array[10], r, g, b) >> 16;
     67     result[3] = a;
     68 }
     69 
     70 static void ScaleAdd(const SkColorMatrixFilter::State& state,
     71                      unsigned r, unsigned g, unsigned b, unsigned a,
     72                      int32_t* SK_RESTRICT result) {
     73     const int32_t* SK_RESTRICT array = state.fArray;
     74     const int shift = state.fShift;
     75 
     76     // cast to (int) to keep the expression signed for the shift
     77     result[0] = (array[0] * (int)r + array[4]) >> shift;
     78     result[1] = (array[6] * (int)g + array[9]) >> shift;
     79     result[2] = (array[12] * (int)b + array[14]) >> shift;
     80     result[3] = a;
     81 }
     82 
     83 static void ScaleAdd16(const SkColorMatrixFilter::State& state,
     84                        unsigned r, unsigned g, unsigned b, unsigned a,
     85                        int32_t* SK_RESTRICT result) {
     86     const int32_t* SK_RESTRICT array = state.fArray;
     87 
     88     // cast to (int) to keep the expression signed for the shift
     89     result[0] = (array[0] * (int)r + array[4]) >> 16;
     90     result[1] = (array[6] * (int)g + array[9]) >> 16;
     91     result[2] = (array[12] * (int)b + array[14]) >> 16;
     92     result[3] = a;
     93 }
     94 
     95 static void Add(const SkColorMatrixFilter::State& state,
     96                 unsigned r, unsigned g, unsigned b, unsigned a,
     97                 int32_t* SK_RESTRICT result) {
     98     const int32_t* SK_RESTRICT array = state.fArray;
     99     const int shift = state.fShift;
    100 
    101     result[0] = r + (array[4] >> shift);
    102     result[1] = g + (array[9] >> shift);
    103     result[2] = b + (array[14] >> shift);
    104     result[3] = a;
    105 }
    106 
    107 static void Add16(const SkColorMatrixFilter::State& state,
    108                   unsigned r, unsigned g, unsigned b, unsigned a,
    109                   int32_t* SK_RESTRICT result) {
    110     const int32_t* SK_RESTRICT array = state.fArray;
    111 
    112     result[0] = r + (array[4] >> 16);
    113     result[1] = g + (array[9] >> 16);
    114     result[2] = b + (array[14] >> 16);
    115     result[3] = a;
    116 }
    117 
    118 #define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag |  \
    119                          SkColorFilter::kHasFilter16_Flag)
    120 
    121 // src is [20] but some compilers won't accept __restrict__ on anything
    122 // but an raw pointer or reference
    123 void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
    124     int32_t* array = fState.fArray;
    125     SkFixed max = 0;
    126     for (int i = 0; i < 20; i++) {
    127         SkFixed value = SkScalarToFixed(src[i]);
    128         array[i] = value;
    129         value = SkAbs32(value);
    130         max = SkMax32(max, value);
    131     }
    132 
    133     /*  All of fArray[] values must fit in 23 bits, to safely allow me to
    134         multiply them by 8bit unsigned values, and get a signed answer without
    135         overflow. This means clz needs to be 9 or bigger
    136     */
    137     int bits = SkCLZ(max);
    138     int32_t one = SK_Fixed1;
    139 
    140     fState.fShift = 16; // we are starting out as fixed 16.16
    141     if (bits < 9) {
    142         bits = 9 - bits;
    143         fState.fShift -= bits;
    144         for (int i = 0; i < 20; i++) {
    145             array[i] >>= bits;
    146         }
    147         one >>= bits;
    148     }
    149 
    150     // check if we have to munge Alpha
    151     int32_t changesAlpha = (array[15] | array[16] | array[17] |
    152                             (array[18] - one) | array[19]);
    153     int32_t usesAlpha = (array[3] | array[8] | array[13]);
    154     bool shiftIs16 = (16 == fState.fShift);
    155 
    156     if (changesAlpha | usesAlpha) {
    157         fProc = shiftIs16 ? General16 : General;
    158         fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
    159     } else {
    160         fFlags = kNO_ALPHA_FLAGS;
    161 
    162         int32_t needsScale = (array[0] - one) |       // red axis
    163                              (array[6] - one) |       // green axis
    164                              (array[12] - one);       // blue axis
    165 
    166         int32_t needs3x3 =  array[1] | array[2] |     // red off-axis
    167                             array[5] | array[7] |     // green off-axis
    168                             array[10] | array[11];    // blue off-axis
    169 
    170         if (needs3x3) {
    171             fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
    172         } else if (needsScale) {
    173             fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
    174         } else if (array[4] | array[9] | array[14]) {   // needs add
    175             fProc = shiftIs16 ? Add16 : Add;
    176         } else {
    177             fProc = NULL;   // identity
    178         }
    179     }
    180 
    181     /*  preround our add values so we get a rounded shift. We do this after we
    182         analyze the array, so we don't miss the case where the caller has zeros
    183         which could make us accidentally take the General or Add case.
    184     */
    185     if (NULL != fProc) {
    186         int32_t add = 1 << (fState.fShift - 1);
    187         array[4] += add;
    188         array[9] += add;
    189         array[14] += add;
    190         array[19] += add;
    191     }
    192 }
    193 
    194 ///////////////////////////////////////////////////////////////////////////////
    195 
    196 static int32_t pin(int32_t value, int32_t max) {
    197     if (value < 0) {
    198         value = 0;
    199     }
    200     if (value > max) {
    201         value = max;
    202     }
    203     return value;
    204 }
    205 
    206 SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
    207     this->initState(cm.fMat);
    208 }
    209 
    210 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
    211     memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
    212     this->initState(array);
    213 }
    214 
    215 uint32_t SkColorMatrixFilter::getFlags() const {
    216     return this->INHERITED::getFlags() | fFlags;
    217 }
    218 
    219 void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
    220                                      SkPMColor dst[]) const {
    221     Proc proc = fProc;
    222     const State& state = fState;
    223     int32_t result[4];
    224 
    225     if (NULL == proc) {
    226         if (src != dst) {
    227             memcpy(dst, src, count * sizeof(SkPMColor));
    228         }
    229         return;
    230     }
    231 
    232     const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
    233 
    234     for (int i = 0; i < count; i++) {
    235         SkPMColor c = src[i];
    236 
    237         unsigned r = SkGetPackedR32(c);
    238         unsigned g = SkGetPackedG32(c);
    239         unsigned b = SkGetPackedB32(c);
    240         unsigned a = SkGetPackedA32(c);
    241 
    242         // need our components to be un-premultiplied
    243         if (255 != a) {
    244             SkUnPreMultiply::Scale scale = table[a];
    245             r = SkUnPreMultiply::ApplyScale(scale, r);
    246             g = SkUnPreMultiply::ApplyScale(scale, g);
    247             b = SkUnPreMultiply::ApplyScale(scale, b);
    248 
    249             SkASSERT(r <= 255);
    250             SkASSERT(g <= 255);
    251             SkASSERT(b <= 255);
    252         }
    253 
    254         proc(state, r, g, b, a, result);
    255 
    256         r = pin(result[0], SK_R32_MASK);
    257         g = pin(result[1], SK_G32_MASK);
    258         b = pin(result[2], SK_B32_MASK);
    259         a = pin(result[3], SK_A32_MASK);
    260         // re-prepremultiply if needed
    261         dst[i] = SkPremultiplyARGBInline(a, r, g, b);
    262     }
    263 }
    264 
    265 void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
    266                                        uint16_t dst[]) const {
    267     SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
    268 
    269     Proc   proc = fProc;
    270     const State& state = fState;
    271     int32_t result[4];
    272 
    273     if (NULL == proc) {
    274         if (src != dst) {
    275             memcpy(dst, src, count * sizeof(uint16_t));
    276         }
    277         return;
    278     }
    279 
    280     for (int i = 0; i < count; i++) {
    281         uint16_t c = src[i];
    282 
    283         // expand to 8bit components (since our matrix translate is 8bit biased
    284         unsigned r = SkPacked16ToR32(c);
    285         unsigned g = SkPacked16ToG32(c);
    286         unsigned b = SkPacked16ToB32(c);
    287 
    288         proc(state, r, g, b, 0, result);
    289 
    290         r = pin(result[0], SK_R32_MASK);
    291         g = pin(result[1], SK_G32_MASK);
    292         b = pin(result[2], SK_B32_MASK);
    293 
    294         // now packed it back down to 16bits (hmmm, could dither...)
    295         dst[i] = SkPack888ToRGB16(r, g, b);
    296     }
    297 }
    298 
    299 ///////////////////////////////////////////////////////////////////////////////
    300 
    301 void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    302     this->INHERITED::flatten(buffer);
    303     SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
    304     buffer.writeScalarArray(fMatrix.fMat, 20);
    305 }
    306 
    307 SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
    308         : INHERITED(buffer) {
    309     SkASSERT(buffer.getArrayCount() == 20);
    310     buffer.readScalarArray(fMatrix.fMat);
    311     this->initState(fMatrix.fMat);
    312 }
    313 
    314 bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
    315     if (matrix) {
    316         memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
    317     }
    318     return true;
    319 }
    320 
    321 #if SK_SUPPORT_GPU
    322 #include "GrEffect.h"
    323 #include "GrTBackendEffectFactory.h"
    324 #include "gl/GrGLEffect.h"
    325 
    326 class ColorMatrixEffect : public GrEffect {
    327 public:
    328     static GrEffectRef* Create(const SkColorMatrix& matrix) {
    329         AutoEffectUnref effect(SkNEW_ARGS(ColorMatrixEffect, (matrix)));
    330         return CreateEffectRef(effect);
    331     }
    332 
    333     static const char* Name() { return "Color Matrix"; }
    334 
    335     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
    336         return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
    337     }
    338 
    339     virtual void getConstantColorComponents(GrColor* color,
    340                                             uint32_t* validFlags) const SK_OVERRIDE {
    341         // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
    342         // type flags it might be worth checking the other components.
    343 
    344         // The matrix is defined such the 4th row determines the output alpha. The first four
    345         // columns of that row multiply the input r, g, b, and a, respectively, and the last column
    346         // is the "translation".
    347         static const ValidComponentFlags kRGBAFlags[] = {
    348             kR_ValidComponentFlag,
    349             kG_ValidComponentFlag,
    350             kB_ValidComponentFlag,
    351             kA_ValidComponentFlag
    352         };
    353         static const int kShifts[] = {
    354             GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
    355         };
    356         enum {
    357             kAlphaRowStartIdx = 15,
    358             kAlphaRowTranslateIdx = 19,
    359         };
    360 
    361         SkScalar outputA = 0;
    362         for (int i = 0; i < 4; ++i) {
    363             // If any relevant component of the color to be passed through the matrix is non-const
    364             // then we can't know the final result.
    365             if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
    366                 if (!(*validFlags & kRGBAFlags[i])) {
    367                     *validFlags = 0;
    368                     return;
    369                 } else {
    370                     uint32_t component = (*color >> kShifts[i]) & 0xFF;
    371                     outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
    372                 }
    373             }
    374         }
    375         outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
    376         *validFlags = kA_ValidComponentFlag;
    377         // We pin the color to [0,1]. This would happen to the *final* color output from the frag
    378         // shader but currently the effect does not pin its own output. So in the case of over/
    379         // underflow this may deviate from the actual result. Maybe the effect should pin its
    380         // result if the matrix could over/underflow for any component?
    381         *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
    382     }
    383 
    384     GR_DECLARE_EFFECT_TEST;
    385 
    386     class GLEffect : public GrGLEffect {
    387     public:
    388         // this class always generates the same code.
    389         static EffectKey GenKey(const GrEffectStage&, const GrGLCaps&) { return 0; }
    390 
    391         GLEffect(const GrBackendEffectFactory& factory,
    392                  const GrEffectRef& effect)
    393         : INHERITED(factory)
    394         , fMatrixHandle(GrGLUniformManager::kInvalidUniformHandle)
    395         , fVectorHandle(GrGLUniformManager::kInvalidUniformHandle) {}
    396 
    397         virtual void emitCode(GrGLShaderBuilder* builder,
    398                               const GrEffectStage&,
    399                               EffectKey,
    400                               const char* vertexCoords,
    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->fFSCode.appendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
    418             builder->fFSCode.appendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
    419                                      outputColor,
    420                                      builder->getUniformCStr(fMatrixHandle),
    421                                      inputColor,
    422                                      builder->getUniformCStr(fVectorHandle));
    423             builder->fFSCode.appendf("\t%s.rgb *= %s.a;\n", outputColor, outputColor);
    424         }
    425 
    426         virtual void setData(const GrGLUniformManager& uniManager,
    427                              const GrEffectStage& stage) SK_OVERRIDE {
    428             const ColorMatrixEffect& cme = GetEffectFromStage<ColorMatrixEffect>(stage);
    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 
    450 private:
    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 
    463 GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
    464 
    465 GrEffectRef* ColorMatrixEffect::TestCreate(SkRandom* random,
    466                                            GrContext*,
    467                                            GrTexture* dummyTextures[2]) {
    468     SkColorMatrix colorMatrix;
    469     for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
    470         colorMatrix.fMat[i] = random->nextSScalar1();
    471     }
    472     return ColorMatrixEffect::Create(colorMatrix);
    473 }
    474 
    475 GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
    476     return ColorMatrixEffect::Create(fMatrix);
    477 }
    478 
    479 #endif
    480