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