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