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
SkComposeShader(SkShader * sA,SkShader * sB,SkXfermode * mode)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 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
SkComposeShader(SkReadBuffer & buffer)30 SkComposeShader::SkComposeShader(SkReadBuffer& buffer) : INHERITED(buffer) {
31 fShaderA = buffer.readShader();
32 if (NULL == fShaderA) {
33 fShaderA = SkNEW_ARGS(SkColorShader, ((SkColor)0));
34 }
35 fShaderB = buffer.readShader();
36 if (NULL == fShaderB) {
37 fShaderB = SkNEW_ARGS(SkColorShader, ((SkColor)0));
38 }
39 fMode = buffer.readXfermode();
40 }
41 #endif
42
~SkComposeShader()43 SkComposeShader::~SkComposeShader() {
44 SkSafeUnref(fMode);
45 fShaderB->unref();
46 fShaderA->unref();
47 }
48
contextSize() const49 size_t SkComposeShader::contextSize() const {
50 return sizeof(ComposeShaderContext) + fShaderA->contextSize() + fShaderB->contextSize();
51 }
52
53 class SkAutoAlphaRestore {
54 public:
SkAutoAlphaRestore(SkPaint * paint,uint8_t newAlpha)55 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
56 fAlpha = paint->getAlpha();
57 fPaint = paint;
58 paint->setAlpha(newAlpha);
59 }
60
~SkAutoAlphaRestore()61 ~SkAutoAlphaRestore() {
62 fPaint->setAlpha(fAlpha);
63 }
64 private:
65 SkPaint* fPaint;
66 uint8_t fAlpha;
67 };
68 #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
69
CreateProc(SkReadBuffer & buffer)70 SkFlattenable* SkComposeShader::CreateProc(SkReadBuffer& buffer) {
71 SkAutoTUnref<SkShader> shaderA(buffer.readShader());
72 SkAutoTUnref<SkShader> shaderB(buffer.readShader());
73 SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
74 if (!shaderA.get() || !shaderB.get()) {
75 return NULL;
76 }
77 return SkNEW_ARGS(SkComposeShader, (shaderA, shaderB, mode));
78 }
79
flatten(SkWriteBuffer & buffer) const80 void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
81 buffer.writeFlattenable(fShaderA);
82 buffer.writeFlattenable(fShaderB);
83 buffer.writeFlattenable(fMode);
84 }
85
safe_call_destructor(T * obj)86 template <typename T> void safe_call_destructor(T* obj) {
87 if (obj) {
88 obj->~T();
89 }
90 }
91
onCreateContext(const ContextRec & rec,void * storage) const92 SkShader::Context* SkComposeShader::onCreateContext(const ContextRec& rec, void* storage) const {
93 char* aStorage = (char*) storage + sizeof(ComposeShaderContext);
94 char* bStorage = aStorage + fShaderA->contextSize();
95
96 // we preconcat our localMatrix (if any) with the device matrix
97 // before calling our sub-shaders
98 SkMatrix tmpM;
99 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
100
101 // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
102 // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
103 // sub-shaders.
104 SkPaint opaquePaint(*rec.fPaint);
105 opaquePaint.setAlpha(0xFF);
106
107 ContextRec newRec(rec);
108 newRec.fMatrix = &tmpM;
109 newRec.fPaint = &opaquePaint;
110
111 SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage);
112 SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage);
113 if (!contextA || !contextB) {
114 safe_call_destructor(contextA);
115 safe_call_destructor(contextB);
116 return NULL;
117 }
118
119 return SkNEW_PLACEMENT_ARGS(storage, ComposeShaderContext, (*this, rec, contextA, contextB));
120 }
121
ComposeShaderContext(const SkComposeShader & shader,const ContextRec & rec,SkShader::Context * contextA,SkShader::Context * contextB)122 SkComposeShader::ComposeShaderContext::ComposeShaderContext(
123 const SkComposeShader& shader, const ContextRec& rec,
124 SkShader::Context* contextA, SkShader::Context* contextB)
125 : INHERITED(shader, rec)
126 , fShaderContextA(contextA)
127 , fShaderContextB(contextB) {}
128
~ComposeShaderContext()129 SkComposeShader::ComposeShaderContext::~ComposeShaderContext() {
130 fShaderContextA->~Context();
131 fShaderContextB->~Context();
132 }
133
asACompose(ComposeRec * rec) const134 bool SkComposeShader::asACompose(ComposeRec* rec) const {
135 if (rec) {
136 rec->fShaderA = fShaderA;
137 rec->fShaderB = fShaderB;
138 rec->fMode = fMode;
139 }
140 return true;
141 }
142
143
144 // larger is better (fewer times we have to loop), but we shouldn't
145 // take up too much stack-space (each element is 4 bytes)
146 #define TMP_COLOR_COUNT 64
147
shadeSpan(int x,int y,SkPMColor result[],int count)148 void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
149 SkShader::Context* shaderContextA = fShaderContextA;
150 SkShader::Context* shaderContextB = fShaderContextB;
151 SkXfermode* mode = static_cast<const SkComposeShader&>(fShader).fMode;
152 unsigned scale = SkAlpha255To256(this->getPaintAlpha());
153
154 #ifdef SK_BUILD_FOR_ANDROID
155 scale = 256; // ugh -- maintain old bug/behavior for now
156 #endif
157
158 SkPMColor tmp[TMP_COLOR_COUNT];
159
160 if (NULL == mode) { // implied SRC_OVER
161 // TODO: when we have a good test-case, should use SkBlitRow::Proc32
162 // for these loops
163 do {
164 int n = count;
165 if (n > TMP_COLOR_COUNT) {
166 n = TMP_COLOR_COUNT;
167 }
168
169 shaderContextA->shadeSpan(x, y, result, n);
170 shaderContextB->shadeSpan(x, y, tmp, n);
171
172 if (256 == scale) {
173 for (int i = 0; i < n; i++) {
174 result[i] = SkPMSrcOver(tmp[i], result[i]);
175 }
176 } else {
177 for (int i = 0; i < n; i++) {
178 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
179 scale);
180 }
181 }
182
183 result += n;
184 x += n;
185 count -= n;
186 } while (count > 0);
187 } else { // use mode for the composition
188 do {
189 int n = count;
190 if (n > TMP_COLOR_COUNT) {
191 n = TMP_COLOR_COUNT;
192 }
193
194 shaderContextA->shadeSpan(x, y, result, n);
195 shaderContextB->shadeSpan(x, y, tmp, n);
196 mode->xfer32(result, tmp, n, NULL);
197
198 if (256 != scale) {
199 for (int i = 0; i < n; i++) {
200 result[i] = SkAlphaMulQ(result[i], scale);
201 }
202 }
203
204 result += n;
205 x += n;
206 count -= n;
207 } while (count > 0);
208 }
209 }
210
211 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const212 void SkComposeShader::toString(SkString* str) const {
213 str->append("SkComposeShader: (");
214
215 str->append("ShaderA: ");
216 fShaderA->toString(str);
217 str->append(" ShaderB: ");
218 fShaderB->toString(str);
219 str->append(" Xfermode: ");
220 fMode->toString(str);
221
222 this->INHERITED::toString(str);
223
224 str->append(")");
225 }
226 #endif
227