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