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 "SkBlendModePriv.h"
10 #include "SkComposeShader.h"
11 #include "SkColorFilter.h"
12 #include "SkColorData.h"
13 #include "SkColorShader.h"
14 #include "SkRasterPipeline.h"
15 #include "SkReadBuffer.h"
16 #include "SkWriteBuffer.h"
17 #include "SkString.h"
18 #include "../jumper/SkJumper.h"
19 
MakeCompose(sk_sp<SkShader> dst,sk_sp<SkShader> src,SkBlendMode mode,float lerpT)20 sk_sp<SkShader> SkShader::MakeCompose(sk_sp<SkShader> dst, sk_sp<SkShader> src, SkBlendMode mode,
21                                       float lerpT) {
22     if (!src || !dst || SkScalarIsNaN(lerpT)) {
23         return nullptr;
24     }
25     lerpT = SkScalarPin(lerpT, 0, 1);
26 
27     if (lerpT == 0) {
28         return dst;
29     } else if (lerpT == 1) {
30         if (mode == SkBlendMode::kSrc) {
31             return src;
32         }
33         if (mode == SkBlendMode::kDst) {
34             return dst;
35         }
36     }
37     return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode, lerpT));
38 }
39 
40 ///////////////////////////////////////////////////////////////////////////////
41 
CreateProc(SkReadBuffer & buffer)42 sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) {
43     sk_sp<SkShader> dst(buffer.readShader());
44     sk_sp<SkShader> src(buffer.readShader());
45     unsigned        mode = buffer.read32();
46     float           lerp = buffer.readScalar();
47 
48     // check for valid mode before we cast to the enum type
49     if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
50         return nullptr;
51     }
52     return MakeCompose(std::move(dst), std::move(src), static_cast<SkBlendMode>(mode), lerp);
53 }
54 
flatten(SkWriteBuffer & buffer) const55 void SkComposeShader::flatten(SkWriteBuffer& buffer) const {
56     buffer.writeFlattenable(fDst.get());
57     buffer.writeFlattenable(fSrc.get());
58     buffer.write32((int)fMode);
59     buffer.writeScalar(fLerpT);
60 }
61 
onMakeColorSpace(SkColorSpaceXformer * xformer) const62 sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
63     return MakeCompose(xformer->apply(fDst.get()), xformer->apply(fSrc.get()),
64                        fMode, fLerpT);
65 }
66 
67 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
asACompose(ComposeRec * rec) const68 bool SkComposeShader::asACompose(ComposeRec* rec) const {
69     if (!this->isJustMode()) {
70         return false;
71     }
72 
73     if (rec) {
74         rec->fShaderA   = fDst.get();
75         rec->fShaderB   = fSrc.get();
76         rec->fBlendMode = fMode;
77     }
78     return true;
79 }
80 #endif
81 
onAppendStages(const StageRec & rec) const82 bool SkComposeShader::onAppendStages(const StageRec& rec) const {
83     struct Storage {
84         float   fRGBA[4 * SkJumper_kMaxStride];
85         float   fAlpha;
86     };
87     auto storage = rec.fAlloc->make<Storage>();
88 
89     if (!as_SB(fSrc)->appendStages(rec)) {
90         return false;
91     }
92     // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now
93     // since fShaderB will overwrite them.
94     rec.fPipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA);
95 
96     if (!as_SB(fDst)->appendStages(rec)) {
97         return false;
98     }
99     // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode/lerp
100     // so we have to shuttle them. If we had a stage the would load_into_dst, then we could
101     // reverse the two shader invocations, and avoid this move...
102     rec.fPipeline->append(SkRasterPipeline::move_src_dst);
103     rec.fPipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA);
104 
105     if (!this->isJustLerp()) {
106         SkBlendMode_AppendStages(fMode, rec.fPipeline);
107     }
108     if (!this->isJustMode()) {
109         rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fLerpT);
110     }
111     return true;
112 }
113 
114 #if SK_SUPPORT_GPU
115 
116 #include "effects/GrConstColorProcessor.h"
117 #include "effects/GrXfermodeFragmentProcessor.h"
118 
119 /////////////////////////////////////////////////////////////////////
120 
asFragmentProcessor(const GrFPArgs & args) const121 std::unique_ptr<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(
122         const GrFPArgs& args) const {
123     if (this->isJustMode()) {
124         SkASSERT(fMode != SkBlendMode::kSrc && fMode != SkBlendMode::kDst); // caught in factory
125         if (fMode == SkBlendMode::kClear) {
126             return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
127                                                GrConstColorProcessor::InputMode::kIgnore);
128         }
129     }
130 
131     std::unique_ptr<GrFragmentProcessor> fpA(as_SB(fDst)->asFragmentProcessor(args));
132     if (!fpA) {
133         return nullptr;
134     }
135     std::unique_ptr<GrFragmentProcessor> fpB(as_SB(fSrc)->asFragmentProcessor(args));
136     if (!fpB) {
137         return nullptr;
138     }
139     // TODO: account for fLerpT when it is < 1
140     return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
141                                                               std::move(fpA), fMode);
142 }
143 #endif
144 
145 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const146 void SkComposeShader::toString(SkString* str) const {
147     str->append("SkComposeShader: (");
148 
149     str->append("dst: ");
150     as_SB(fDst)->toString(str);
151     str->append(" src: ");
152     as_SB(fSrc)->toString(str);
153     str->appendf(" mode: %s", SkBlendMode_Name(fMode));
154     str->appendf(" lerpT: %g", fLerpT);
155 
156     this->INHERITED::toString(str);
157 
158     str->append(")");
159 }
160 #endif
161