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