Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      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 
      9 
     10 #include "SkComposeShader.h"
     11 #include "SkColorFilter.h"
     12 #include "SkColorPriv.h"
     13 #include "SkColorShader.h"
     14 #include "SkFlattenableBuffers.h"
     15 #include "SkXfermode.h"
     16 #include "SkString.h"
     17 
     18 ///////////////////////////////////////////////////////////////////////////////
     19 
     20 SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
     21     fShaderA = sA;  sA->ref();
     22     fShaderB = sB;  sB->ref();
     23     // mode may be null
     24     fMode = mode;
     25     SkSafeRef(mode);
     26 }
     27 
     28 SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
     29     INHERITED(buffer) {
     30     fShaderA = buffer.readFlattenableT<SkShader>();
     31     if (NULL == fShaderA) {
     32         fShaderA = SkNEW_ARGS(SkColorShader, (0));
     33     }
     34     fShaderB = buffer.readFlattenableT<SkShader>();
     35     if (NULL == fShaderB) {
     36         fShaderB = SkNEW_ARGS(SkColorShader, (0));
     37     }
     38     fMode = buffer.readFlattenableT<SkXfermode>();
     39 }
     40 
     41 SkComposeShader::~SkComposeShader() {
     42     SkSafeUnref(fMode);
     43     fShaderB->unref();
     44     fShaderA->unref();
     45 }
     46 
     47 class SkAutoAlphaRestore {
     48 public:
     49     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
     50         fAlpha = paint->getAlpha();
     51         fPaint = paint;
     52         paint->setAlpha(newAlpha);
     53     }
     54 
     55     ~SkAutoAlphaRestore() {
     56         fPaint->setAlpha(fAlpha);
     57     }
     58 private:
     59     SkPaint*    fPaint;
     60     uint8_t     fAlpha;
     61 };
     62 
     63 void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) const {
     64     this->INHERITED::flatten(buffer);
     65     buffer.writeFlattenable(fShaderA);
     66     buffer.writeFlattenable(fShaderB);
     67     buffer.writeFlattenable(fMode);
     68 }
     69 
     70 /*  We call setContext on our two worker shaders. However, we
     71     always let them see opaque alpha, and if the paint really
     72     is translucent, then we apply that after the fact.
     73 
     74     We need to keep the calls to setContext/endContext balanced, since if we
     75     return false, our endContext() will not be called.
     76  */
     77 bool SkComposeShader::setContext(const SkBitmap& device,
     78                                  const SkPaint& paint,
     79                                  const SkMatrix& matrix) {
     80     if (!this->INHERITED::setContext(device, paint, matrix)) {
     81         return false;
     82     }
     83 
     84     // we preconcat our localMatrix (if any) with the device matrix
     85     // before calling our sub-shaders
     86 
     87     SkMatrix tmpM;
     88 
     89     tmpM.setConcat(matrix, this->getLocalMatrix());
     90 
     91     SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
     92 
     93     bool setContextA = fShaderA->setContext(device, paint, tmpM);
     94     bool setContextB = fShaderB->setContext(device, paint, tmpM);
     95     if (!setContextA || !setContextB) {
     96         if (setContextB) {
     97             fShaderB->endContext();
     98         }
     99         else if (setContextA) {
    100             fShaderA->endContext();
    101         }
    102         this->INHERITED::endContext();
    103         return false;
    104     }
    105     return true;
    106 }
    107 
    108 void SkComposeShader::endContext() {
    109     fShaderB->endContext();
    110     fShaderA->endContext();
    111     this->INHERITED::endContext();
    112 }
    113 
    114 // larger is better (fewer times we have to loop), but we shouldn't
    115 // take up too much stack-space (each element is 4 bytes)
    116 #define TMP_COLOR_COUNT     64
    117 
    118 void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
    119     SkShader*   shaderA = fShaderA;
    120     SkShader*   shaderB = fShaderB;
    121     SkXfermode* mode = fMode;
    122     unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
    123 
    124     SkPMColor   tmp[TMP_COLOR_COUNT];
    125 
    126     if (NULL == mode) {   // implied SRC_OVER
    127         // TODO: when we have a good test-case, should use SkBlitRow::Proc32
    128         // for these loops
    129         do {
    130             int n = count;
    131             if (n > TMP_COLOR_COUNT) {
    132                 n = TMP_COLOR_COUNT;
    133             }
    134 
    135             shaderA->shadeSpan(x, y, result, n);
    136             shaderB->shadeSpan(x, y, tmp, n);
    137 
    138             if (256 == scale) {
    139                 for (int i = 0; i < n; i++) {
    140                     result[i] = SkPMSrcOver(tmp[i], result[i]);
    141                 }
    142             } else {
    143                 for (int i = 0; i < n; i++) {
    144                     result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
    145                                             scale);
    146                 }
    147             }
    148 
    149             result += n;
    150             x += n;
    151             count -= n;
    152         } while (count > 0);
    153     } else {    // use mode for the composition
    154         do {
    155             int n = count;
    156             if (n > TMP_COLOR_COUNT) {
    157                 n = TMP_COLOR_COUNT;
    158             }
    159 
    160             shaderA->shadeSpan(x, y, result, n);
    161             shaderB->shadeSpan(x, y, tmp, n);
    162             mode->xfer32(result, tmp, n, NULL);
    163 
    164             if (256 == scale) {
    165                 for (int i = 0; i < n; i++) {
    166                     result[i] = SkAlphaMulQ(result[i], scale);
    167                 }
    168             }
    169 
    170             result += n;
    171             x += n;
    172             count -= n;
    173         } while (count > 0);
    174     }
    175 }
    176 
    177 #ifdef SK_DEVELOPER
    178 void SkComposeShader::toString(SkString* str) const {
    179     str->append("SkComposeShader: (");
    180 
    181     str->append("ShaderA: ");
    182     fShaderA->toString(str);
    183     str->append(" ShaderB: ");
    184     fShaderB->toString(str);
    185     str->append(" Xfermode: ");
    186     // TODO: add "fMode->toString(str);" once SkXfermode::toString is added
    187 
    188     this->INHERITED::toString(str);
    189 
    190     str->append(")");
    191 }
    192 #endif
    193