Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkComposeShader.h"
      9 #include "SkColorFilter.h"
     10 #include "SkColorPriv.h"
     11 #include "SkColorShader.h"
     12 #include "SkReadBuffer.h"
     13 #include "SkWriteBuffer.h"
     14 #include "SkXfermode.h"
     15 #include "SkString.h"
     16 
     17 ///////////////////////////////////////////////////////////////////////////////
     18 
     19 SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
     20     fShaderA = sA;  sA->ref();
     21     fShaderB = sB;  sB->ref();
     22     // mode may be null
     23     fMode = mode;
     24     SkSafeRef(mode);
     25 }
     26 
     27 SkComposeShader::~SkComposeShader() {
     28     SkSafeUnref(fMode);
     29     fShaderB->unref();
     30     fShaderA->unref();
     31 }
     32 
     33 size_t SkComposeShader::contextSize(const ContextRec& rec) const {
     34     return sizeof(ComposeShaderContext)
     35         + fShaderA->contextSize(rec)
     36         + fShaderB->contextSize(rec);
     37 }
     38 
     39 class SkAutoAlphaRestore {
     40 public:
     41     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
     42         fAlpha = paint->getAlpha();
     43         fPaint = paint;
     44         paint->setAlpha(newAlpha);
     45     }
     46 
     47     ~SkAutoAlphaRestore() {
     48         fPaint->setAlpha(fAlpha);
     49     }
     50 private:
     51     SkPaint*    fPaint;
     52     uint8_t     fAlpha;
     53 };
     54 #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
     55 
     56 SkFlattenable* SkComposeShader::CreateProc(SkReadBuffer& buffer) {
     57     SkAutoTUnref<SkShader> shaderA(buffer.readShader());
     58     SkAutoTUnref<SkShader> shaderB(buffer.readShader());
     59     SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
     60     if (!shaderA.get() || !shaderB.get()) {
     61         return nullptr;
     62     }
     63     return new SkComposeShader(shaderA, shaderB, mode);
     64 }
     65 
     66 void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
     67     buffer.writeFlattenable(fShaderA);
     68     buffer.writeFlattenable(fShaderB);
     69     buffer.writeFlattenable(fMode);
     70 }
     71 
     72 template <typename T> void safe_call_destructor(T* obj) {
     73     if (obj) {
     74         obj->~T();
     75     }
     76 }
     77 
     78 SkShader::Context* SkComposeShader::onCreateContext(const ContextRec& rec, void* storage) const {
     79     char* aStorage = (char*) storage + sizeof(ComposeShaderContext);
     80     char* bStorage = aStorage + fShaderA->contextSize(rec);
     81 
     82     // we preconcat our localMatrix (if any) with the device matrix
     83     // before calling our sub-shaders
     84     SkMatrix tmpM;
     85     tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
     86 
     87     // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
     88     // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
     89     // sub-shaders.
     90     SkPaint opaquePaint(*rec.fPaint);
     91     opaquePaint.setAlpha(0xFF);
     92 
     93     ContextRec newRec(rec);
     94     newRec.fMatrix = &tmpM;
     95     newRec.fPaint = &opaquePaint;
     96 
     97     SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage);
     98     SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage);
     99     if (!contextA || !contextB) {
    100         safe_call_destructor(contextA);
    101         safe_call_destructor(contextB);
    102         return nullptr;
    103     }
    104 
    105     return new (storage) ComposeShaderContext(*this, rec, contextA, contextB);
    106 }
    107 
    108 SkComposeShader::ComposeShaderContext::ComposeShaderContext(
    109         const SkComposeShader& shader, const ContextRec& rec,
    110         SkShader::Context* contextA, SkShader::Context* contextB)
    111     : INHERITED(shader, rec)
    112     , fShaderContextA(contextA)
    113     , fShaderContextB(contextB) {}
    114 
    115 SkComposeShader::ComposeShaderContext::~ComposeShaderContext() {
    116     fShaderContextA->~Context();
    117     fShaderContextB->~Context();
    118 }
    119 
    120 bool SkComposeShader::asACompose(ComposeRec* rec) const {
    121     if (rec) {
    122         rec->fShaderA = fShaderA;
    123         rec->fShaderB = fShaderB;
    124         rec->fMode = fMode;
    125     }
    126     return true;
    127 }
    128 
    129 
    130 // larger is better (fewer times we have to loop), but we shouldn't
    131 // take up too much stack-space (each element is 4 bytes)
    132 #define TMP_COLOR_COUNT     64
    133 
    134 void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
    135     SkShader::Context* shaderContextA = fShaderContextA;
    136     SkShader::Context* shaderContextB = fShaderContextB;
    137     SkXfermode*        mode = static_cast<const SkComposeShader&>(fShader).fMode;
    138     unsigned           scale = SkAlpha255To256(this->getPaintAlpha());
    139 
    140     SkPMColor   tmp[TMP_COLOR_COUNT];
    141 
    142     if (nullptr == mode) {   // implied SRC_OVER
    143         // TODO: when we have a good test-case, should use SkBlitRow::Proc32
    144         // for these loops
    145         do {
    146             int n = count;
    147             if (n > TMP_COLOR_COUNT) {
    148                 n = TMP_COLOR_COUNT;
    149             }
    150 
    151             shaderContextA->shadeSpan(x, y, result, n);
    152             shaderContextB->shadeSpan(x, y, tmp, n);
    153 
    154             if (256 == scale) {
    155                 for (int i = 0; i < n; i++) {
    156                     result[i] = SkPMSrcOver(tmp[i], result[i]);
    157                 }
    158             } else {
    159                 for (int i = 0; i < n; i++) {
    160                     result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
    161                                             scale);
    162                 }
    163             }
    164 
    165             result += n;
    166             x += n;
    167             count -= n;
    168         } while (count > 0);
    169     } else {    // use mode for the composition
    170         do {
    171             int n = count;
    172             if (n > TMP_COLOR_COUNT) {
    173                 n = TMP_COLOR_COUNT;
    174             }
    175 
    176             shaderContextA->shadeSpan(x, y, result, n);
    177             shaderContextB->shadeSpan(x, y, tmp, n);
    178             mode->xfer32(result, tmp, n, nullptr);
    179 
    180             if (256 != scale) {
    181                 for (int i = 0; i < n; i++) {
    182                     result[i] = SkAlphaMulQ(result[i], scale);
    183                 }
    184             }
    185 
    186             result += n;
    187             x += n;
    188             count -= n;
    189         } while (count > 0);
    190     }
    191 }
    192 
    193 #if SK_SUPPORT_GPU
    194 
    195 #include "effects/GrConstColorProcessor.h"
    196 #include "effects/GrXfermodeFragmentProcessor.h"
    197 
    198 /////////////////////////////////////////////////////////////////////
    199 
    200 const GrFragmentProcessor* SkComposeShader::asFragmentProcessor(GrContext* context,
    201                                                             const SkMatrix& viewM,
    202                                                             const SkMatrix* localMatrix,
    203                                                             SkFilterQuality fq) const {
    204     // Fragment processor will only support SkXfermode::Mode modes currently.
    205     SkXfermode::Mode mode;
    206     if (!(SkXfermode::AsMode(fMode, &mode))) {
    207         return nullptr;
    208     }
    209 
    210     switch (mode) {
    211         case SkXfermode::kClear_Mode:
    212             return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
    213                                                  GrConstColorProcessor::kIgnore_InputMode);
    214             break;
    215         case SkXfermode::kSrc_Mode:
    216             return fShaderB->asFragmentProcessor(context, viewM, localMatrix, fq);
    217             break;
    218         case SkXfermode::kDst_Mode:
    219             return fShaderA->asFragmentProcessor(context, viewM, localMatrix, fq);
    220             break;
    221         default:
    222             SkAutoTUnref<const GrFragmentProcessor> fpA(fShaderA->asFragmentProcessor(context,
    223                                                         viewM, localMatrix, fq));
    224             if (!fpA.get()) {
    225                 return nullptr;
    226             }
    227             SkAutoTUnref<const GrFragmentProcessor> fpB(fShaderB->asFragmentProcessor(context,
    228                                                         viewM, localMatrix, fq));
    229             if (!fpB.get()) {
    230                 return nullptr;
    231             }
    232             return GrXfermodeFragmentProcessor::CreateFromTwoProcessors(fpB, fpA, mode);
    233     }
    234 }
    235 #endif
    236 
    237 #ifndef SK_IGNORE_TO_STRING
    238 void SkComposeShader::toString(SkString* str) const {
    239     str->append("SkComposeShader: (");
    240 
    241     str->append("ShaderA: ");
    242     fShaderA->toString(str);
    243     str->append(" ShaderB: ");
    244     fShaderB->toString(str);
    245     if (fMode) {
    246         str->append(" Xfermode: ");
    247         fMode->toString(str);
    248     }
    249 
    250     this->INHERITED::toString(str);
    251 
    252     str->append(")");
    253 }
    254 #endif
    255 
    256 ///////////////////////////////////////////////////////////////////////////////////////////////////
    257 
    258 SkShader* SkShader::CreateComposeShader(SkShader* dst, SkShader* src, SkXfermode* xfer) {
    259     if (!dst || !src) {
    260         return nullptr;
    261     }
    262     return new SkComposeShader(dst, src, xfer);
    263 }
    264 
    265 SkShader* SkShader::CreateComposeShader(SkShader* dst, SkShader* src, SkXfermode::Mode mode) {
    266     SkAutoTUnref<SkXfermode> xfer(SkXfermode::Create(mode));
    267     return CreateComposeShader(dst, src, xfer);
    268 }
    269