Home | History | Annotate | Download | only in core
      1 /* libs/graphics/effects/SkShaderExtras.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #include "SkComposeShader.h"
     19 #include "SkColorFilter.h"
     20 #include "SkColorPriv.h"
     21 #include "SkXfermode.h"
     22 
     23 //////////////////////////////////////////////////////////////////////////////////////
     24 
     25 SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode)
     26 {
     27     fShaderA = sA;  sA->ref();
     28     fShaderB = sB;  sB->ref();
     29     // mode may be null
     30     fMode = mode;   mode->safeRef();
     31 }
     32 
     33 SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
     34     INHERITED(buffer)
     35 {
     36     fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
     37     fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
     38     fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
     39 }
     40 
     41 SkComposeShader::~SkComposeShader()
     42 {
     43     fMode->safeUnref(); // may be null
     44     fShaderB->unref();
     45     fShaderA->unref();
     46 }
     47 
     48 void SkComposeShader::beginSession()
     49 {
     50     this->INHERITED::beginSession();
     51     fShaderA->beginSession();
     52     fShaderB->beginSession();
     53 }
     54 
     55 void SkComposeShader::endSession()
     56 {
     57     fShaderA->endSession();
     58     fShaderB->endSession();
     59     this->INHERITED::endSession();
     60 }
     61 
     62 class SkAutoAlphaRestore {
     63 public:
     64     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha)
     65     {
     66         fAlpha = paint->getAlpha();
     67         fPaint = paint;
     68         paint->setAlpha(newAlpha);
     69     }
     70     ~SkAutoAlphaRestore()
     71     {
     72         fPaint->setAlpha(fAlpha);
     73     }
     74 private:
     75     SkPaint*    fPaint;
     76     uint8_t     fAlpha;
     77 };
     78 
     79 void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer)
     80 {
     81     this->INHERITED::flatten(buffer);
     82     buffer.writeFlattenable(fShaderA);
     83     buffer.writeFlattenable(fShaderB);
     84     buffer.writeFlattenable(fMode);
     85 }
     86 
     87 /*  We call setContext on our two worker shaders. However, we
     88     always let them see opaque alpha, and if the paint really
     89     is translucent, then we apply that after the fact.
     90 */
     91 bool SkComposeShader::setContext(const SkBitmap& device,
     92                                  const SkPaint& paint,
     93                                  const SkMatrix& matrix)
     94 {
     95     if (!this->INHERITED::setContext(device, paint, matrix))
     96         return false;
     97 
     98     // we preconcat our localMatrix (if any) with the device matrix
     99     // before calling our sub-shaders
    100 
    101     SkMatrix tmpM;
    102 
    103     (void)this->getLocalMatrix(&tmpM);
    104     tmpM.setConcat(matrix, tmpM);
    105 
    106     SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
    107 
    108     return  fShaderA->setContext(device, paint, tmpM) &&
    109             fShaderB->setContext(device, paint, tmpM);
    110 }
    111 
    112 // larger is better (fewer times we have to loop), but we shouldn't
    113 // take up too much stack-space (each element is 4 bytes)
    114 #define TMP_COLOR_COUNT     64
    115 
    116 void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count)
    117 {
    118     SkShader*   shaderA = fShaderA;
    119     SkShader*   shaderB = fShaderB;
    120     SkXfermode* mode = fMode;
    121     unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
    122 
    123     SkPMColor   tmp[TMP_COLOR_COUNT];
    124 
    125     if (NULL == mode)   // implied SRC_OVER
    126     {
    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             shaderA->shadeSpan(x, y, result, n);
    135             shaderB->shadeSpan(x, y, tmp, n);
    136 
    137             if (256 == scale)
    138             {
    139                 for (int i = 0; i < n; i++)
    140                     result[i] = SkPMSrcOver(tmp[i], result[i]);
    141             }
    142             else
    143             {
    144                 for (int i = 0; i < n; i++)
    145                     result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), scale);
    146             }
    147 
    148             result += n;
    149             x += n;
    150             count -= n;
    151         } while (count > 0);
    152     }
    153     else    // use mode for the composition
    154     {
    155         do {
    156             int n = count;
    157             if (n > TMP_COLOR_COUNT)
    158                 n = TMP_COLOR_COUNT;
    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             {
    166                 for (int i = 0; i < n; i++)
    167                     result[i] = SkAlphaMulQ(result[i], scale);
    168             }
    169 
    170             result += n;
    171             x += n;
    172             count -= n;
    173         } while (count > 0);
    174     }
    175 }
    176 
    177