• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkComposeShader.h"
9 #include "SkColorFilter.h"
10 #include "SkColorPriv.h"
11 #include "SkColorShader.h"
12 #include "SkReadBuffer.h"
13 #include "SkWriteBuffer.h"
14 #include "SkXfermode.h"
15 #include "SkString.h"
16 
17 ///////////////////////////////////////////////////////////////////////////////
18 
SkComposeShader(SkShader * sA,SkShader * sB,SkXfermode * mode)19 SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
20     fShaderA = sA;  sA->ref();
21     fShaderB = sB;  sB->ref();
22     // mode may be null
23     fMode = mode;
24     SkSafeRef(mode);
25 }
26 
~SkComposeShader()27 SkComposeShader::~SkComposeShader() {
28     SkSafeUnref(fMode);
29     fShaderB->unref();
30     fShaderA->unref();
31 }
32 
contextSize(const ContextRec & rec) const33 size_t SkComposeShader::contextSize(const ContextRec& rec) const {
34     return sizeof(ComposeShaderContext)
35         + fShaderA->contextSize(rec)
36         + fShaderB->contextSize(rec);
37 }
38 
39 class SkAutoAlphaRestore {
40 public:
SkAutoAlphaRestore(SkPaint * paint,uint8_t newAlpha)41     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
42         fAlpha = paint->getAlpha();
43         fPaint = paint;
44         paint->setAlpha(newAlpha);
45     }
46 
~SkAutoAlphaRestore()47     ~SkAutoAlphaRestore() {
48         fPaint->setAlpha(fAlpha);
49     }
50 private:
51     SkPaint*    fPaint;
52     uint8_t     fAlpha;
53 };
54 #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
55 
CreateProc(SkReadBuffer & buffer)56 SkFlattenable* SkComposeShader::CreateProc(SkReadBuffer& buffer) {
57     SkAutoTUnref<SkShader> shaderA(buffer.readShader());
58     SkAutoTUnref<SkShader> shaderB(buffer.readShader());
59     SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
60     if (!shaderA.get() || !shaderB.get()) {
61         return nullptr;
62     }
63     return new SkComposeShader(shaderA, shaderB, mode);
64 }
65 
flatten(SkWriteBuffer & buffer) const66 void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
67     buffer.writeFlattenable(fShaderA);
68     buffer.writeFlattenable(fShaderB);
69     buffer.writeFlattenable(fMode);
70 }
71 
safe_call_destructor(T * obj)72 template <typename T> void safe_call_destructor(T* obj) {
73     if (obj) {
74         obj->~T();
75     }
76 }
77 
onCreateContext(const ContextRec & rec,void * storage) const78 SkShader::Context* SkComposeShader::onCreateContext(const ContextRec& rec, void* storage) const {
79     char* aStorage = (char*) storage + sizeof(ComposeShaderContext);
80     char* bStorage = aStorage + fShaderA->contextSize(rec);
81 
82     // we preconcat our localMatrix (if any) with the device matrix
83     // before calling our sub-shaders
84     SkMatrix tmpM;
85     tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix());
86 
87     // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the
88     // result. ComposeShader itself will respect the alpha, and post-apply it after calling the
89     // sub-shaders.
90     SkPaint opaquePaint(*rec.fPaint);
91     opaquePaint.setAlpha(0xFF);
92 
93     ContextRec newRec(rec);
94     newRec.fMatrix = &tmpM;
95     newRec.fPaint = &opaquePaint;
96 
97     SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage);
98     SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage);
99     if (!contextA || !contextB) {
100         safe_call_destructor(contextA);
101         safe_call_destructor(contextB);
102         return nullptr;
103     }
104 
105     return new (storage) ComposeShaderContext(*this, rec, contextA, contextB);
106 }
107 
ComposeShaderContext(const SkComposeShader & shader,const ContextRec & rec,SkShader::Context * contextA,SkShader::Context * contextB)108 SkComposeShader::ComposeShaderContext::ComposeShaderContext(
109         const SkComposeShader& shader, const ContextRec& rec,
110         SkShader::Context* contextA, SkShader::Context* contextB)
111     : INHERITED(shader, rec)
112     , fShaderContextA(contextA)
113     , fShaderContextB(contextB) {}
114 
~ComposeShaderContext()115 SkComposeShader::ComposeShaderContext::~ComposeShaderContext() {
116     fShaderContextA->~Context();
117     fShaderContextB->~Context();
118 }
119 
asACompose(ComposeRec * rec) const120 bool SkComposeShader::asACompose(ComposeRec* rec) const {
121     if (rec) {
122         rec->fShaderA = fShaderA;
123         rec->fShaderB = fShaderB;
124         rec->fMode = fMode;
125     }
126     return true;
127 }
128 
129 
130 // larger is better (fewer times we have to loop), but we shouldn't
131 // take up too much stack-space (each element is 4 bytes)
132 #define TMP_COLOR_COUNT     64
133 
shadeSpan(int x,int y,SkPMColor result[],int count)134 void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) {
135     SkShader::Context* shaderContextA = fShaderContextA;
136     SkShader::Context* shaderContextB = fShaderContextB;
137     SkXfermode*        mode = static_cast<const SkComposeShader&>(fShader).fMode;
138     unsigned           scale = SkAlpha255To256(this->getPaintAlpha());
139 
140     SkPMColor   tmp[TMP_COLOR_COUNT];
141 
142     if (nullptr == mode) {   // implied SRC_OVER
143         // TODO: when we have a good test-case, should use SkBlitRow::Proc32
144         // for these loops
145         do {
146             int n = count;
147             if (n > TMP_COLOR_COUNT) {
148                 n = TMP_COLOR_COUNT;
149             }
150 
151             shaderContextA->shadeSpan(x, y, result, n);
152             shaderContextB->shadeSpan(x, y, tmp, n);
153 
154             if (256 == scale) {
155                 for (int i = 0; i < n; i++) {
156                     result[i] = SkPMSrcOver(tmp[i], result[i]);
157                 }
158             } else {
159                 for (int i = 0; i < n; i++) {
160                     result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
161                                             scale);
162                 }
163             }
164 
165             result += n;
166             x += n;
167             count -= n;
168         } while (count > 0);
169     } else {    // use mode for the composition
170         do {
171             int n = count;
172             if (n > TMP_COLOR_COUNT) {
173                 n = TMP_COLOR_COUNT;
174             }
175 
176             shaderContextA->shadeSpan(x, y, result, n);
177             shaderContextB->shadeSpan(x, y, tmp, n);
178             mode->xfer32(result, tmp, n, nullptr);
179 
180             if (256 != scale) {
181                 for (int i = 0; i < n; i++) {
182                     result[i] = SkAlphaMulQ(result[i], scale);
183                 }
184             }
185 
186             result += n;
187             x += n;
188             count -= n;
189         } while (count > 0);
190     }
191 }
192 
193 #if SK_SUPPORT_GPU
194 
195 #include "effects/GrConstColorProcessor.h"
196 #include "effects/GrXfermodeFragmentProcessor.h"
197 
198 /////////////////////////////////////////////////////////////////////
199 
asFragmentProcessor(GrContext * context,const SkMatrix & viewM,const SkMatrix * localMatrix,SkFilterQuality fq) const200 const GrFragmentProcessor* SkComposeShader::asFragmentProcessor(GrContext* context,
201                                                             const SkMatrix& viewM,
202                                                             const SkMatrix* localMatrix,
203                                                             SkFilterQuality fq) const {
204     // Fragment processor will only support SkXfermode::Mode modes currently.
205     SkXfermode::Mode mode;
206     if (!(SkXfermode::AsMode(fMode, &mode))) {
207         return nullptr;
208     }
209 
210     switch (mode) {
211         case SkXfermode::kClear_Mode:
212             return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
213                                                  GrConstColorProcessor::kIgnore_InputMode);
214             break;
215         case SkXfermode::kSrc_Mode:
216             return fShaderB->asFragmentProcessor(context, viewM, localMatrix, fq);
217             break;
218         case SkXfermode::kDst_Mode:
219             return fShaderA->asFragmentProcessor(context, viewM, localMatrix, fq);
220             break;
221         default:
222             SkAutoTUnref<const GrFragmentProcessor> fpA(fShaderA->asFragmentProcessor(context,
223                                                         viewM, localMatrix, fq));
224             if (!fpA.get()) {
225                 return nullptr;
226             }
227             SkAutoTUnref<const GrFragmentProcessor> fpB(fShaderB->asFragmentProcessor(context,
228                                                         viewM, localMatrix, fq));
229             if (!fpB.get()) {
230                 return nullptr;
231             }
232             return GrXfermodeFragmentProcessor::CreateFromTwoProcessors(fpB, fpA, mode);
233     }
234 }
235 #endif
236 
237 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const238 void SkComposeShader::toString(SkString* str) const {
239     str->append("SkComposeShader: (");
240 
241     str->append("ShaderA: ");
242     fShaderA->toString(str);
243     str->append(" ShaderB: ");
244     fShaderB->toString(str);
245     if (fMode) {
246         str->append(" Xfermode: ");
247         fMode->toString(str);
248     }
249 
250     this->INHERITED::toString(str);
251 
252     str->append(")");
253 }
254 #endif
255 
256 ///////////////////////////////////////////////////////////////////////////////////////////////////
257 
CreateComposeShader(SkShader * dst,SkShader * src,SkXfermode * xfer)258 SkShader* SkShader::CreateComposeShader(SkShader* dst, SkShader* src, SkXfermode* xfer) {
259     if (!dst || !src) {
260         return nullptr;
261     }
262     return new SkComposeShader(dst, src, xfer);
263 }
264 
CreateComposeShader(SkShader * dst,SkShader * src,SkXfermode::Mode mode)265 SkShader* SkShader::CreateComposeShader(SkShader* dst, SkShader* src, SkXfermode::Mode mode) {
266     SkAutoTUnref<SkXfermode> xfer(SkXfermode::Create(mode));
267     return CreateComposeShader(dst, src, xfer);
268 }
269