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