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 "SkArenaAlloc.h"
      9 #include "SkComposeShader.h"
     10 #include "SkColorFilter.h"
     11 #include "SkColorPriv.h"
     12 #include "SkColorShader.h"
     13 #include "SkReadBuffer.h"
     14 #include "SkWriteBuffer.h"
     15 #include "SkString.h"
     16 
     17 sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
     18                                             SkBlendMode mode) {
     19     if (!src || !dst) {
     20         return nullptr;
     21     }
     22     if (SkBlendMode::kSrc == mode) {
     23         return src;
     24     }
     25     if (SkBlendMode::kDst == mode) {
     26         return dst;
     27     }
     28     return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
     29 }
     30 
     31 ///////////////////////////////////////////////////////////////////////////////
     32 
     33 class SkAutoAlphaRestore {
     34 public:
     35     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
     36         fAlpha = paint->getAlpha();
     37         fPaint = paint;
     38         paint->setAlpha(newAlpha);
     39     }
     40 
     41     ~SkAutoAlphaRestore() {
     42         fPaint->setAlpha(fAlpha);
     43     }
     44 private:
     45     SkPaint*    fPaint;
     46     uint8_t     fAlpha;
     47 };
     48 #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
     49 
     50 sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
     51     sk_sp<SkShader> shaderA(buffer.readShader());
     52     sk_sp<SkShader> shaderB(buffer.readShader());
     53     SkBlendMode mode;
     54     if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) {
     55         sk_sp<SkXfermode> xfer = buffer.readXfermode();
     56         mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver;
     57     } else {
     58         mode = (SkBlendMode)buffer.read32();
     59     }
     60     if (!shaderA || !shaderB) {
     61         return nullptr;
     62     }
     63     return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
     64 }
     65 
     66 void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
     67     buffer.writeFlattenable(fShaderA.get());
     68     buffer.writeFlattenable(fShaderB.get());
     69     buffer.write32((int)fMode);
     70 }
     71 
     72 SkShader::Context* SkComposeShader::onMakeContext(
     73     const ContextRec& rec, SkArenaAlloc* alloc) const
     74 {
     75     // we preconcat our localMatrix (if any) with the device matrix
     76     // before calling our sub-shaders
     77     SkMatrix tmpM;
     78     tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
     79 
     80     // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
     81     // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
     82     // sub-shaders.
     83     SkPaint opaquePaint(*rec.fPaint);
     84     opaquePaint.setAlpha(0xFF);
     85 
     86     ContextRec newRec(rec);
     87     newRec.fMatrix = &tmpM;
     88     newRec.fPaint = &opaquePaint;
     89 
     90     SkShader::Context* contextA = fShaderA->makeContext(newRec, alloc);
     91     SkShader::Context* contextB = fShaderB->makeContext(newRec, alloc);
     92     if (!contextA || !contextB) {
     93         return nullptr;
     94     }
     95 
     96     return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB);
     97 }
     98 
     99 SkComposeShader::ComposeShaderContext::ComposeShaderContext(
    100         const SkComposeShader& shader, const ContextRec& rec,
    101         SkShader::Context* contextA, SkShader::Context* contextB)
    102     : INHERITED(shader, rec)
    103     , fShaderContextA(contextA)
    104     , fShaderContextB(contextB) {}
    105 
    106 bool SkComposeShader::asACompose(ComposeRec* rec) const {
    107     if (rec) {
    108         rec->fShaderA   = fShaderA.get();
    109         rec->fShaderB   = fShaderB.get();
    110         rec->fBlendMode = fMode;
    111     }
    112     return true;
    113 }
    114 
    115 
    116 // larger is better (fewer times we have to loop), but we shouldn't
    117 // take up too much stack-space (each element is 4 bytes)
    118 #define TMP_COLOR_COUNT     64
    119 
    120 void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
    121     SkShader::Context* shaderContextA = fShaderContextA;
    122     SkShader::Context* shaderContextB = fShaderContextB;
    123     SkBlendMode        mode = static_cast<const SkComposeShader&>(fShader).fMode;
    124     unsigned           scale = SkAlpha255To256(this->getPaintAlpha());
    125 
    126     SkPMColor   tmp[TMP_COLOR_COUNT];
    127 
    128     SkXfermode* xfer = SkXfermode::Peek(mode);
    129     if (nullptr == xfer) {   // implied SRC_OVER
    130         // TODO: when we have a good test-case, should use SkBlitRow::Proc32
    131         // for these loops
    132         do {
    133             int n = count;
    134             if (n > TMP_COLOR_COUNT) {
    135                 n = TMP_COLOR_COUNT;
    136             }
    137 
    138             shaderContextA->shadeSpan(x, y, result, n);
    139             shaderContextB->shadeSpan(x, y, tmp, n);
    140 
    141             if (256 == scale) {
    142                 for (int i = 0; i < n; i++) {
    143                     result[i] = SkPMSrcOver(tmp[i], result[i]);
    144                 }
    145             } else {
    146                 for (int i = 0; i < n; i++) {
    147                     result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
    148                                             scale);
    149                 }
    150             }
    151 
    152             result += n;
    153             x += n;
    154             count -= n;
    155         } while (count > 0);
    156     } else {    // use mode for the composition
    157         do {
    158             int n = count;
    159             if (n > TMP_COLOR_COUNT) {
    160                 n = TMP_COLOR_COUNT;
    161             }
    162 
    163             shaderContextA->shadeSpan(x, y, result, n);
    164             shaderContextB->shadeSpan(x, y, tmp, n);
    165             xfer->xfer32(result, tmp, n, nullptr);
    166 
    167             if (256 != scale) {
    168                 for (int i = 0; i < n; i++) {
    169                     result[i] = SkAlphaMulQ(result[i], scale);
    170                 }
    171             }
    172 
    173             result += n;
    174             x += n;
    175             count -= n;
    176         } while (count > 0);
    177     }
    178 }
    179 
    180 #if SK_SUPPORT_GPU
    181 
    182 #include "effects/GrConstColorProcessor.h"
    183 #include "effects/GrXfermodeFragmentProcessor.h"
    184 
    185 /////////////////////////////////////////////////////////////////////
    186 
    187 sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
    188     switch (fMode) {
    189         case SkBlendMode::kClear:
    190             return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
    191                                                GrConstColorProcessor::kIgnore_InputMode);
    192             break;
    193         case SkBlendMode::kSrc:
    194             return fShaderB->asFragmentProcessor(args);
    195             break;
    196         case SkBlendMode::kDst:
    197             return fShaderA->asFragmentProcessor(args);
    198             break;
    199         default:
    200             sk_sp<GrFragmentProcessor> fpA(fShaderA->asFragmentProcessor(args));
    201             if (!fpA) {
    202                 return nullptr;
    203             }
    204             sk_sp<GrFragmentProcessor> fpB(fShaderB->asFragmentProcessor(args));
    205             if (!fpB) {
    206                 return nullptr;
    207             }
    208             return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
    209                                                                       std::move(fpA), fMode);
    210     }
    211 }
    212 #endif
    213 
    214 #ifndef SK_IGNORE_TO_STRING
    215 void SkComposeShader::toString(SkString* str) const {
    216     str->append("SkComposeShader: (");
    217 
    218     str->append("ShaderA: ");
    219     fShaderA->toString(str);
    220     str->append(" ShaderB: ");
    221     fShaderB->toString(str);
    222     if (SkBlendMode::kSrcOver != fMode) {
    223         str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode));
    224     }
    225 
    226     this->INHERITED::toString(str);
    227 
    228     str->append(")");
    229 }
    230 #endif
    231