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
SkComposeShader(SkShader * sA,SkShader * sB,SkXfermode * mode)20 SkComposeShader::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
SkComposeShader(SkFlattenableReadBuffer & buffer)28 SkComposeShader::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
~SkComposeShader()41 SkComposeShader::~SkComposeShader() {
42 SkSafeUnref(fMode);
43 fShaderB->unref();
44 fShaderA->unref();
45 }
46
47 class SkAutoAlphaRestore {
48 public:
SkAutoAlphaRestore(SkPaint * paint,uint8_t newAlpha)49 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
50 fAlpha = paint->getAlpha();
51 fPaint = paint;
52 paint->setAlpha(newAlpha);
53 }
54
~SkAutoAlphaRestore()55 ~SkAutoAlphaRestore() {
56 fPaint->setAlpha(fAlpha);
57 }
58 private:
59 SkPaint* fPaint;
60 uint8_t fAlpha;
61 };
62
flatten(SkFlattenableWriteBuffer & buffer) const63 void 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 */
setContext(const SkBitmap & device,const SkPaint & paint,const SkMatrix & matrix)77 bool 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
endContext()108 void 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
shadeSpan(int x,int y,SkPMColor result[],int count)118 void 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
toString(SkString * str) const178 void 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 // TODO: add "fMode->toString(str);" once SkXfermode::toString is added
187
188 this->INHERITED::toString(str);
189
190 str->append(")");
191 }
192 #endif
193