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