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
17sk_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
33class SkAutoAlphaRestore {
34public:
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    }
44private:
45    SkPaint*    fPaint;
46    uint8_t     fAlpha;
47};
48#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
49
50sk_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
66void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
67    buffer.writeFlattenable(fShaderA.get());
68    buffer.writeFlattenable(fShaderB.get());
69    buffer.write32((int)fMode);
70}
71
72SkShader::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
99SkComposeShader::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
106bool 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
120void 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
187sk_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
215void 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