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 #include "SkString.h"
     14 
     15 static 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 
     20 static 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 
     25 static 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 
     37 static 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 
     48 static 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 
     60 static 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 
     71 static 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 
     84 static 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 
     96 static 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 
    108 static 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
    124 void 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 
    197 static 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 
    207 SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
    208     this->initState(cm.fMat);
    209 }
    210 
    211 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
    212     memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
    213     this->initState(array);
    214 }
    215 
    216 uint32_t SkColorMatrixFilter::getFlags() const {
    217     return this->INHERITED::getFlags() | fFlags;
    218 }
    219 
    220 void 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 
    266 void 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 
    302 void 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 
    308 SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
    309         : INHERITED(buffer) {
    310     SkASSERT(buffer.getArrayCount() == 20);
    311     buffer.readScalarArray(fMatrix.fMat);
    312     this->initState(fMatrix.fMat);
    313 }
    314 
    315 bool 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 
    327 class ColorMatrixEffect : public GrEffect {
    328 public:
    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 
    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(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 
    476 GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
    477     return ColorMatrixEffect::Create(fMatrix);
    478 }
    479 
    480 #endif
    481 
    482 #ifdef SK_DEVELOPER
    483 void 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