1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkArenaAlloc.h"
9 #include "SkComposeShader.h"
10 #include "SkColorFilter.h"
11 #include "SkColorPriv.h"
12 #include "SkColorShader.h"
13 #include "SkReadBuffer.h"
14 #include "SkWriteBuffer.h"
15 #include "SkString.h"
16
MakeComposeShader(sk_sp<SkShader> dst,sk_sp<SkShader> src,SkBlendMode mode)17 sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src,
18 SkBlendMode mode) {
19 if (!src || !dst) {
20 return nullptr;
21 }
22 if (SkBlendMode::kSrc == mode) {
23 return src;
24 }
25 if (SkBlendMode::kDst == mode) {
26 return dst;
27 }
28 return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode));
29 }
30
31 ///////////////////////////////////////////////////////////////////////////////
32
33 class SkAutoAlphaRestore {
34 public:
SkAutoAlphaRestore(SkPaint * paint,uint8_t newAlpha)35 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
36 fAlpha = paint->getAlpha();
37 fPaint = paint;
38 paint->setAlpha(newAlpha);
39 }
40
~SkAutoAlphaRestore()41 ~SkAutoAlphaRestore() {
42 fPaint->setAlpha(fAlpha);
43 }
44 private:
45 SkPaint* fPaint;
46 uint8_t fAlpha;
47 };
48 #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
49
CreateProc(SkReadBuffer & buffer)50 sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
51 sk_sp<SkShader> shaderA(buffer.readShader());
52 sk_sp<SkShader> shaderB(buffer.readShader());
53 SkBlendMode mode;
54 if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) {
55 sk_sp<SkXfermode> xfer = buffer.readXfermode();
56 mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver;
57 } else {
58 mode = (SkBlendMode)buffer.read32();
59 }
60 if (!shaderA || !shaderB) {
61 return nullptr;
62 }
63 return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode);
64 }
65
flatten(SkWriteBuffer & buffer) const66 void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
67 buffer.writeFlattenable(fShaderA.get());
68 buffer.writeFlattenable(fShaderB.get());
69 buffer.write32((int)fMode);
70 }
71
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const72 SkShader::Context* SkComposeShader::onMakeContext(
73 const ContextRec& rec, SkArenaAlloc* alloc) const
74 {
75 // we preconcat our localMatrix (if any) with the device matrix
76 // before calling our sub-shaders
77 SkMatrix tmpM;
78 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
79
80 // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
81 // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
82 // sub-shaders.
83 SkPaint opaquePaint(*rec.fPaint);
84 opaquePaint.setAlpha(0xFF);
85
86 ContextRec newRec(rec);
87 newRec.fMatrix = &tmpM;
88 newRec.fPaint = &opaquePaint;
89
90 SkShader::Context* contextA = fShaderA->makeContext(newRec, alloc);
91 SkShader::Context* contextB = fShaderB->makeContext(newRec, alloc);
92 if (!contextA || !contextB) {
93 return nullptr;
94 }
95
96 return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB);
97 }
98
ComposeShaderContext(const SkComposeShader & shader,const ContextRec & rec,SkShader::Context * contextA,SkShader::Context * contextB)99 SkComposeShader::ComposeShaderContext::ComposeShaderContext(
100 const SkComposeShader& shader, const ContextRec& rec,
101 SkShader::Context* contextA, SkShader::Context* contextB)
102 : INHERITED(shader, rec)
103 , fShaderContextA(contextA)
104 , fShaderContextB(contextB) {}
105
asACompose(ComposeRec * rec) const106 bool SkComposeShader::asACompose(ComposeRec* rec) const {
107 if (rec) {
108 rec->fShaderA = fShaderA.get();
109 rec->fShaderB = fShaderB.get();
110 rec->fBlendMode = fMode;
111 }
112 return true;
113 }
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
shadeSpan(int x,int y,SkPMColor result[],int count)120 void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
121 SkShader::Context* shaderContextA = fShaderContextA;
122 SkShader::Context* shaderContextB = fShaderContextB;
123 SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode;
124 unsigned scale = SkAlpha255To256(this->getPaintAlpha());
125
126 SkPMColor tmp[TMP_COLOR_COUNT];
127
128 SkXfermode* xfer = SkXfermode::Peek(mode);
129 if (nullptr == xfer) { // implied SRC_OVER
130 // TODO: when we have a good test-case, should use SkBlitRow::Proc32
131 // for these loops
132 do {
133 int n = count;
134 if (n > TMP_COLOR_COUNT) {
135 n = TMP_COLOR_COUNT;
136 }
137
138 shaderContextA->shadeSpan(x, y, result, n);
139 shaderContextB->shadeSpan(x, y, tmp, n);
140
141 if (256 == scale) {
142 for (int i = 0; i < n; i++) {
143 result[i] = SkPMSrcOver(tmp[i], result[i]);
144 }
145 } else {
146 for (int i = 0; i < n; i++) {
147 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
148 scale);
149 }
150 }
151
152 result += n;
153 x += n;
154 count -= n;
155 } while (count > 0);
156 } else { // use mode for the composition
157 do {
158 int n = count;
159 if (n > TMP_COLOR_COUNT) {
160 n = TMP_COLOR_COUNT;
161 }
162
163 shaderContextA->shadeSpan(x, y, result, n);
164 shaderContextB->shadeSpan(x, y, tmp, n);
165 xfer->xfer32(result, tmp, n, nullptr);
166
167 if (256 != scale) {
168 for (int i = 0; i < n; i++) {
169 result[i] = SkAlphaMulQ(result[i], scale);
170 }
171 }
172
173 result += n;
174 x += n;
175 count -= n;
176 } while (count > 0);
177 }
178 }
179
180 #if SK_SUPPORT_GPU
181
182 #include "effects/GrConstColorProcessor.h"
183 #include "effects/GrXfermodeFragmentProcessor.h"
184
185 /////////////////////////////////////////////////////////////////////
186
asFragmentProcessor(const AsFPArgs & args) const187 sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const {
188 switch (fMode) {
189 case SkBlendMode::kClear:
190 return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
191 GrConstColorProcessor::kIgnore_InputMode);
192 break;
193 case SkBlendMode::kSrc:
194 return fShaderB->asFragmentProcessor(args);
195 break;
196 case SkBlendMode::kDst:
197 return fShaderA->asFragmentProcessor(args);
198 break;
199 default:
200 sk_sp<GrFragmentProcessor> fpA(fShaderA->asFragmentProcessor(args));
201 if (!fpA) {
202 return nullptr;
203 }
204 sk_sp<GrFragmentProcessor> fpB(fShaderB->asFragmentProcessor(args));
205 if (!fpB) {
206 return nullptr;
207 }
208 return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
209 std::move(fpA), fMode);
210 }
211 }
212 #endif
213
214 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const215 void SkComposeShader::toString(SkString* str) const {
216 str->append("SkComposeShader: (");
217
218 str->append("ShaderA: ");
219 fShaderA->toString(str);
220 str->append(" ShaderB: ");
221 fShaderB->toString(str);
222 if (SkBlendMode::kSrcOver != fMode) {
223 str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode));
224 }
225
226 this->INHERITED::toString(str);
227
228 str->append(")");
229 }
230 #endif
231