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