SkComposeShader.cpp revision 791f12e6cf558e0dd1689fbb0a6b5eddac9f42ad
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 "SkFlattenableBuffers.h"
15#include "SkXfermode.h"
16#include "SkString.h"
17
18///////////////////////////////////////////////////////////////////////////////
19
20SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
21    fShaderA = sA;  sA->ref();
22    fShaderB = sB;  sB->ref();
23    // mode may be null
24    fMode = mode;
25    SkSafeRef(mode);
26}
27
28SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
29    INHERITED(buffer) {
30    fShaderA = buffer.readFlattenableT<SkShader>();
31    if (NULL == fShaderA) {
32        fShaderA = SkNEW_ARGS(SkColorShader, (0));
33    }
34    fShaderB = buffer.readFlattenableT<SkShader>();
35    if (NULL == fShaderB) {
36        fShaderB = SkNEW_ARGS(SkColorShader, (0));
37    }
38    fMode = buffer.readFlattenableT<SkXfermode>();
39}
40
41SkComposeShader::~SkComposeShader() {
42    SkSafeUnref(fMode);
43    fShaderB->unref();
44    fShaderA->unref();
45}
46
47class SkAutoAlphaRestore {
48public:
49    SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
50        fAlpha = paint->getAlpha();
51        fPaint = paint;
52        paint->setAlpha(newAlpha);
53    }
54
55    ~SkAutoAlphaRestore() {
56        fPaint->setAlpha(fAlpha);
57    }
58private:
59    SkPaint*    fPaint;
60    uint8_t     fAlpha;
61};
62
63void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) const {
64    this->INHERITED::flatten(buffer);
65    buffer.writeFlattenable(fShaderA);
66    buffer.writeFlattenable(fShaderB);
67    buffer.writeFlattenable(fMode);
68}
69
70/*  We call setContext on our two worker shaders. However, we
71    always let them see opaque alpha, and if the paint really
72    is translucent, then we apply that after the fact.
73
74    We need to keep the calls to setContext/endContext balanced, since if we
75    return false, our endContext() will not be called.
76 */
77bool SkComposeShader::setContext(const SkBitmap& device,
78                                 const SkPaint& paint,
79                                 const SkMatrix& matrix) {
80    if (!this->INHERITED::setContext(device, paint, matrix)) {
81        return false;
82    }
83
84    // we preconcat our localMatrix (if any) with the device matrix
85    // before calling our sub-shaders
86
87    SkMatrix tmpM;
88
89    tmpM.setConcat(matrix, this->getLocalMatrix());
90
91    SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
92
93    bool setContextA = fShaderA->setContext(device, paint, tmpM);
94    bool setContextB = fShaderB->setContext(device, paint, tmpM);
95    if (!setContextA || !setContextB) {
96        if (setContextB) {
97            fShaderB->endContext();
98        }
99        else if (setContextA) {
100            fShaderA->endContext();
101        }
102        this->INHERITED::endContext();
103        return false;
104    }
105    return true;
106}
107
108void SkComposeShader::endContext() {
109    fShaderB->endContext();
110    fShaderA->endContext();
111    this->INHERITED::endContext();
112}
113
114// larger is better (fewer times we have to loop), but we shouldn't
115// take up too much stack-space (each element is 4 bytes)
116#define TMP_COLOR_COUNT     64
117
118void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
119    SkShader*   shaderA = fShaderA;
120    SkShader*   shaderB = fShaderB;
121    SkXfermode* mode = fMode;
122    unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
123
124    SkPMColor   tmp[TMP_COLOR_COUNT];
125
126    if (NULL == mode) {   // implied SRC_OVER
127        // TODO: when we have a good test-case, should use SkBlitRow::Proc32
128        // for these loops
129        do {
130            int n = count;
131            if (n > TMP_COLOR_COUNT) {
132                n = TMP_COLOR_COUNT;
133            }
134
135            shaderA->shadeSpan(x, y, result, n);
136            shaderB->shadeSpan(x, y, tmp, n);
137
138            if (256 == scale) {
139                for (int i = 0; i < n; i++) {
140                    result[i] = SkPMSrcOver(tmp[i], result[i]);
141                }
142            } else {
143                for (int i = 0; i < n; i++) {
144                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
145                                            scale);
146                }
147            }
148
149            result += n;
150            x += n;
151            count -= n;
152        } while (count > 0);
153    } else {    // use mode for the composition
154        do {
155            int n = count;
156            if (n > TMP_COLOR_COUNT) {
157                n = TMP_COLOR_COUNT;
158            }
159
160            shaderA->shadeSpan(x, y, result, n);
161            shaderB->shadeSpan(x, y, tmp, n);
162            mode->xfer32(result, tmp, n, NULL);
163
164            if (256 == scale) {
165                for (int i = 0; i < n; i++) {
166                    result[i] = SkAlphaMulQ(result[i], scale);
167                }
168            }
169
170            result += n;
171            x += n;
172            count -= n;
173        } while (count > 0);
174    }
175}
176
177#ifdef SK_DEVELOPER
178void SkComposeShader::toString(SkString* str) const {
179    str->append("SkComposeShader: (");
180
181    str->append("ShaderA: ");
182    fShaderA->toString(str);
183    str->append(" ShaderB: ");
184    fShaderB->toString(str);
185    str->append(" Xfermode: ");
186    fMode->toString(str);
187
188    this->INHERITED::toString(str);
189
190    str->append(")");
191}
192#endif
193