SkComposeShader.cpp revision e901b6de3ef8dea842008a08fc81e92fb1478d61
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    // TODO : must fix this to not "cheat" and modify fPaint
104    SkAutoAlphaRestore  restore(const_cast<SkPaint*>(rec.fPaint), 0xFF);
105
106    char* aStorage = (char*) storage + sizeof(ComposeShaderContext);
107    char* bStorage = aStorage + fShaderA->contextSize();
108
109    // we preconcat our localMatrix (if any) with the device matrix
110    // before calling our sub-shaders
111
112    SkMatrix tmpM;
113    tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
114
115    ContextRec newRec(rec);
116    newRec.fMatrix = &tmpM;
117
118    SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage);
119    SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage);
120
121    // Both functions must succeed; otherwise validContext should have returned
122    // false.
123    SkASSERT(contextA);
124    SkASSERT(contextB);
125
126    return SkNEW_PLACEMENT_ARGS(storage, ComposeShaderContext, (*this, rec, contextA, contextB));
127}
128
129SkComposeShader::ComposeShaderContext::ComposeShaderContext(
130        const SkComposeShader& shader, const ContextRec& rec,
131        SkShader::Context* contextA, SkShader::Context* contextB)
132    : INHERITED(shader, rec)
133    , fShaderContextA(contextA)
134    , fShaderContextB(contextB) {}
135
136SkComposeShader::ComposeShaderContext::~ComposeShaderContext() {
137    fShaderContextA->~Context();
138    fShaderContextB->~Context();
139}
140
141// larger is better (fewer times we have to loop), but we shouldn't
142// take up too much stack-space (each element is 4 bytes)
143#define TMP_COLOR_COUNT     64
144
145void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
146    SkShader::Context* shaderContextA = fShaderContextA;
147    SkShader::Context* shaderContextB = fShaderContextB;
148    SkXfermode*        mode = static_cast<const SkComposeShader&>(fShader).fMode;
149    unsigned           scale = SkAlpha255To256(this->getPaintAlpha());
150
151    SkPMColor   tmp[TMP_COLOR_COUNT];
152
153    if (NULL == mode) {   // implied SRC_OVER
154        // TODO: when we have a good test-case, should use SkBlitRow::Proc32
155        // for these loops
156        do {
157            int n = count;
158            if (n > TMP_COLOR_COUNT) {
159                n = TMP_COLOR_COUNT;
160            }
161
162            shaderContextA->shadeSpan(x, y, result, n);
163            shaderContextB->shadeSpan(x, y, tmp, n);
164
165            if (256 == scale) {
166                for (int i = 0; i < n; i++) {
167                    result[i] = SkPMSrcOver(tmp[i], result[i]);
168                }
169            } else {
170                for (int i = 0; i < n; i++) {
171                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
172                                            scale);
173                }
174            }
175
176            result += n;
177            x += n;
178            count -= n;
179        } while (count > 0);
180    } else {    // use mode for the composition
181        do {
182            int n = count;
183            if (n > TMP_COLOR_COUNT) {
184                n = TMP_COLOR_COUNT;
185            }
186
187            shaderContextA->shadeSpan(x, y, result, n);
188            shaderContextB->shadeSpan(x, y, tmp, n);
189            mode->xfer32(result, tmp, n, NULL);
190
191            if (256 == scale) {
192                for (int i = 0; i < n; i++) {
193                    result[i] = SkAlphaMulQ(result[i], scale);
194                }
195            }
196
197            result += n;
198            x += n;
199            count -= n;
200        } while (count > 0);
201    }
202}
203
204#ifndef SK_IGNORE_TO_STRING
205void SkComposeShader::toString(SkString* str) const {
206    str->append("SkComposeShader: (");
207
208    str->append("ShaderA: ");
209    fShaderA->toString(str);
210    str->append(" ShaderB: ");
211    fShaderB->toString(str);
212    str->append(" Xfermode: ");
213    fMode->toString(str);
214
215    this->INHERITED::toString(str);
216
217    str->append(")");
218}
219#endif
220