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     fShaderA = sA;  sA->ref();
     27     fShaderB = sB;  sB->ref();
     28     // mode may be null
     29     fMode = mode;
     30     SkSafeRef(mode);
     31 }
     32 
     33 SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
     34     INHERITED(buffer) {
     35     fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
     36     fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
     37     fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
     38 }
     39 
     40 SkComposeShader::~SkComposeShader() {
     41     SkSafeUnref(fMode);
     42     fShaderB->unref();
     43     fShaderA->unref();
     44 }
     45 
     46 void SkComposeShader::beginSession() {
     47     this->INHERITED::beginSession();
     48     fShaderA->beginSession();
     49     fShaderB->beginSession();
     50 }
     51 
     52 void SkComposeShader::endSession() {
     53     fShaderA->endSession();
     54     fShaderB->endSession();
     55     this->INHERITED::endSession();
     56 }
     57 
     58 class SkAutoAlphaRestore {
     59 public:
     60     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
     61         fAlpha = paint->getAlpha();
     62         fPaint = paint;
     63         paint->setAlpha(newAlpha);
     64     }
     65 
     66     ~SkAutoAlphaRestore() {
     67         fPaint->setAlpha(fAlpha);
     68     }
     69 private:
     70     SkPaint*    fPaint;
     71     uint8_t     fAlpha;
     72 };
     73 
     74 void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) {
     75     this->INHERITED::flatten(buffer);
     76     buffer.writeFlattenable(fShaderA);
     77     buffer.writeFlattenable(fShaderB);
     78     buffer.writeFlattenable(fMode);
     79 }
     80 
     81 /*  We call setContext on our two worker shaders. However, we
     82     always let them see opaque alpha, and if the paint really
     83     is translucent, then we apply that after the fact.
     84 */
     85 bool SkComposeShader::setContext(const SkBitmap& device,
     86                                  const SkPaint& paint,
     87                                  const SkMatrix& matrix) {
     88     if (!this->INHERITED::setContext(device, paint, matrix)) {
     89         return false;
     90     }
     91 
     92     // we preconcat our localMatrix (if any) with the device matrix
     93     // before calling our sub-shaders
     94 
     95     SkMatrix tmpM;
     96 
     97     (void)this->getLocalMatrix(&tmpM);
     98     tmpM.setConcat(matrix, tmpM);
     99 
    100     SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
    101 
    102     return  fShaderA->setContext(device, paint, tmpM) &&
    103             fShaderB->setContext(device, paint, tmpM);
    104 }
    105 
    106 // larger is better (fewer times we have to loop), but we shouldn't
    107 // take up too much stack-space (each element is 4 bytes)
    108 #define TMP_COLOR_COUNT     64
    109 
    110 void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
    111     SkShader*   shaderA = fShaderA;
    112     SkShader*   shaderB = fShaderB;
    113     SkXfermode* mode = fMode;
    114     unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
    115 
    116     SkPMColor   tmp[TMP_COLOR_COUNT];
    117 
    118     if (NULL == mode) {   // implied SRC_OVER
    119         // TODO: when we have a good test-case, should use SkBlitRow::Proc32
    120         // for these loops
    121         do {
    122             int n = count;
    123             if (n > TMP_COLOR_COUNT) {
    124                 n = TMP_COLOR_COUNT;
    125             }
    126 
    127             shaderA->shadeSpan(x, y, result, n);
    128             shaderB->shadeSpan(x, y, tmp, n);
    129 
    130             if (256 == scale) {
    131                 for (int i = 0; i < n; i++) {
    132                     result[i] = SkPMSrcOver(tmp[i], result[i]);
    133                 }
    134             } else {
    135                 for (int i = 0; i < n; i++) {
    136                     result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
    137                                             scale);
    138                 }
    139             }
    140 
    141             result += n;
    142             x += n;
    143             count -= n;
    144         } while (count > 0);
    145     } else {    // use mode for the composition
    146         do {
    147             int n = count;
    148             if (n > TMP_COLOR_COUNT) {
    149                 n = TMP_COLOR_COUNT;
    150             }
    151 
    152             shaderA->shadeSpan(x, y, result, n);
    153             shaderB->shadeSpan(x, y, tmp, n);
    154             mode->xfer32(result, tmp, n, NULL);
    155 
    156             if (256 == scale) {
    157                 for (int i = 0; i < n; i++) {
    158                     result[i] = SkAlphaMulQ(result[i], scale);
    159                 }
    160             }
    161 
    162             result += n;
    163             x += n;
    164             count -= n;
    165         } while (count > 0);
    166     }
    167 }
    168 
    169