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 "include/core/SkColorFilter.h"
9 #include "include/core/SkString.h"
10 #include "include/private/SkColorData.h"
11 #include "src/core/SkArenaAlloc.h"
12 #include "src/core/SkBlendModePriv.h"
13 #include "src/core/SkRasterPipeline.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkRuntimeEffectPriv.h"
16 #include "src/core/SkVM.h"
17 #include "src/core/SkWriteBuffer.h"
18 #include "src/shaders/SkColorShader.h"
19 #include "src/shaders/SkComposeShader.h"
20 
21 namespace {
22 
23 struct LocalMatrixStageRec final : public SkStageRec {
LocalMatrixStageRec__anona974dc740111::LocalMatrixStageRec24     LocalMatrixStageRec(const SkStageRec& rec, const SkMatrix& lm)
25         : INHERITED(rec) {
26         if (!lm.isIdentity()) {
27             if (fLocalM) {
28                 fStorage.setConcat(lm, *fLocalM);
29                 fLocalM = fStorage.isIdentity() ? nullptr : &fStorage;
30             } else {
31                 fLocalM = &lm;
32             }
33         }
34     }
35 
36 private:
37     SkMatrix fStorage;
38 
39     using INHERITED = SkStageRec;
40 };
41 
42 } // namespace
43 
Blend(SkBlendMode mode,sk_sp<SkShader> dst,sk_sp<SkShader> src)44 sk_sp<SkShader> SkShaders::Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
45     switch (mode) {
46         case SkBlendMode::kClear: return Color(0);
47         case SkBlendMode::kDst:   return dst;
48         case SkBlendMode::kSrc:   return src;
49         default: break;
50     }
51     return sk_sp<SkShader>(new SkShader_Blend(mode, std::move(dst), std::move(src)));
52 }
53 
Lerp(float weight,sk_sp<SkShader> dst,sk_sp<SkShader> src)54 sk_sp<SkShader> SkShaders::Lerp(float weight, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
55     if (SkScalarIsNaN(weight)) {
56         return nullptr;
57     }
58     if (dst == src || weight <= 0) {
59         return dst;
60     }
61     if (weight >= 1) {
62         return src;
63     }
64 
65     sk_sp<SkRuntimeEffect> effect = SkMakeCachedRuntimeEffect(
66         SkRuntimeEffect::MakeForShader,
67         "uniform shader a;"
68         "uniform shader b;"
69         "uniform half w;"
70         "half4 main(float2 xy) { return mix(sample(a, xy), sample(b, xy), w); }"
71     );
72     SkASSERT(effect);
73 
74     sk_sp<SkShader> inputs[] = {dst, src};
75     return effect->makeShader(SkData::MakeWithCopy(&weight, sizeof(weight)),
76                               inputs,
77                               SK_ARRAY_COUNT(inputs),
78                               nullptr,
79                               false);
80 }
81 
82 ///////////////////////////////////////////////////////////////////////////////
83 
append_shader_or_paint(const SkStageRec & rec,SkShader * shader)84 static bool append_shader_or_paint(const SkStageRec& rec, SkShader* shader) {
85     if (shader) {
86         if (!as_SB(shader)->appendStages(rec)) {
87             return false;
88         }
89     } else {
90         rec.fPipeline->append_constant_color(rec.fAlloc, rec.fPaint.getColor4f().premul().vec());
91     }
92     return true;
93 }
94 
95 // Returns the output of e0, and leaves the output of e1 in r,g,b,a
append_two_shaders(const SkStageRec & rec,SkShader * s0,SkShader * s1)96 static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* s1) {
97     struct Storage {
98         float   fRes0[4 * SkRasterPipeline_kMaxStride];
99     };
100     auto storage = rec.fAlloc->make<Storage>();
101 
102     if (!append_shader_or_paint(rec, s0)) {
103         return nullptr;
104     }
105     rec.fPipeline->append(SkRasterPipeline::store_src, storage->fRes0);
106 
107     if (!append_shader_or_paint(rec, s1)) {
108         return nullptr;
109     }
110     return storage->fRes0;
111 }
112 
113 ///////////////////////////////////////////////////////////////////////////////
114 
CreateProc(SkReadBuffer & buffer)115 sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
116     sk_sp<SkShader> dst(buffer.readShader());
117     sk_sp<SkShader> src(buffer.readShader());
118     unsigned        mode = buffer.read32();
119 
120     // check for valid mode before we cast to the enum type
121     if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
122         return nullptr;
123     }
124     return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
125 }
126 
flatten(SkWriteBuffer & buffer) const127 void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
128     buffer.writeFlattenable(fDst.get());
129     buffer.writeFlattenable(fSrc.get());
130     buffer.write32((int)fMode);
131 }
132 
onAppendStages(const SkStageRec & orig_rec) const133 bool SkShader_Blend::onAppendStages(const SkStageRec& orig_rec) const {
134     const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix());
135 
136     float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get());
137     if (!res0) {
138         return false;
139     }
140 
141     rec.fPipeline->append(SkRasterPipeline::load_dst, res0);
142     SkBlendMode_AppendStages(fMode, rec.fPipeline);
143     return true;
144 }
145 
program_or_paint(const sk_sp<SkShader> & sh,skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & mats,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)146 static skvm::Color program_or_paint(const sk_sp<SkShader>& sh, skvm::Builder* p,
147                                     skvm::Coord device, skvm::Coord local, skvm::Color paint,
148                                     const SkMatrixProvider& mats, const SkMatrix* localM,
149                                     const SkColorInfo& dst,
150                                     skvm::Uniforms* uniforms, SkArenaAlloc* alloc) {
151     return sh ? as_SB(sh)->program(p, device,local, paint, mats,localM, dst, uniforms,alloc)
152               : p->premul(paint);
153 }
154 
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & mats,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const155 skvm::Color SkShader_Blend::onProgram(skvm::Builder* p,
156                                       skvm::Coord device, skvm::Coord local, skvm::Color paint,
157                                       const SkMatrixProvider& mats, const SkMatrix* localM,
158                                       const SkColorInfo& dst,
159                                       skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
160     skvm::Color d,s;
161     if ((d = program_or_paint(fDst, p, device,local, paint, mats,localM, dst, uniforms,alloc)) &&
162         (s = program_or_paint(fSrc, p, device,local, paint, mats,localM, dst, uniforms,alloc)))
163     {
164         return p->blend(fMode, s,d);
165     }
166     return {};
167 }
168 
169 #if SK_SUPPORT_GPU
170 
171 #include "include/gpu/GrRecordingContext.h"
172 #include "src/gpu/GrFragmentProcessor.h"
173 #include "src/gpu/effects/GrBlendFragmentProcessor.h"
174 
as_fp(const GrFPArgs & args,SkShader * shader)175 static std::unique_ptr<GrFragmentProcessor> as_fp(const GrFPArgs& args, SkShader* shader) {
176     return shader ? as_SB(shader)->asFragmentProcessor(args) : nullptr;
177 }
178 
asFragmentProcessor(const GrFPArgs & orig_args) const179 std::unique_ptr<GrFragmentProcessor> SkShader_Blend::asFragmentProcessor(
180         const GrFPArgs& orig_args) const {
181     const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix());
182     auto fpA = as_fp(args, fDst.get());
183     auto fpB = as_fp(args, fSrc.get());
184     return GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
185 }
186 #endif
187