SkComposeShader.cpp revision 1ab536f16164795782a75b11efcb17541cbc2e26
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
76/*  We call validContext/createContext on our two worker shaders.
77    However, we always let them see opaque alpha, and if the paint
78    really is translucent, then we apply that after the fact.
79
80 */
81bool SkComposeShader::validContext(const ContextRec& rec, SkMatrix* totalInverse) const {
82    if (!this->INHERITED::validContext(rec, totalInverse)) {
83        return false;
84    }
85
86    // we preconcat our localMatrix (if any) with the device matrix
87    // before calling our sub-shaders
88
89    SkMatrix tmpM;
90    tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
91
92    ContextRec newRec(rec);
93    newRec.fMatrix = &tmpM;
94
95    return fShaderA->validContext(newRec) && fShaderB->validContext(newRec);
96}
97
98SkShader::Context* SkComposeShader::createContext(const ContextRec& rec, void* storage) const {
99    if (!this->validContext(rec)) {
100        return NULL;
101    }
102
103    char* aStorage = (char*) storage + sizeof(ComposeShaderContext);
104    char* bStorage = aStorage + fShaderA->contextSize();
105
106    // we preconcat our localMatrix (if any) with the device matrix
107    // before calling our sub-shaders
108    SkMatrix tmpM;
109    tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
110
111    // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
112    // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
113    // sub-shaders.
114    SkPaint opaquePaint(*rec.fPaint);
115    opaquePaint.setAlpha(0xFF);
116
117    ContextRec newRec(rec);
118    newRec.fMatrix = &tmpM;
119    newRec.fPaint = &opaquePaint;
120
121    SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage);
122    SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage);
123
124    // Both functions must succeed; otherwise validContext should have returned
125    // false.
126    SkASSERT(contextA);
127    SkASSERT(contextB);
128
129    return SkNEW_PLACEMENT_ARGS(storage, ComposeShaderContext, (*this, rec, contextA, contextB));
130}
131
132SkComposeShader::ComposeShaderContext::ComposeShaderContext(
133        const SkComposeShader& shader, const ContextRec& rec,
134        SkShader::Context* contextA, SkShader::Context* contextB)
135    : INHERITED(shader, rec)
136    , fShaderContextA(contextA)
137    , fShaderContextB(contextB) {}
138
139SkComposeShader::ComposeShaderContext::~ComposeShaderContext() {
140    fShaderContextA->~Context();
141    fShaderContextB->~Context();
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