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/SkVM.h"
16#include "src/core/SkWriteBuffer.h"
17#include "src/shaders/SkColorShader.h"
18#include "src/shaders/SkComposeShader.h"
19
20namespace {
21
22struct LocalMatrixStageRec final : public SkStageRec {
23 LocalMatrixStageRec(const SkStageRec& rec, const SkMatrix& lm)
24 : INHERITED(rec) {
25 if (!lm.isIdentity()) {
26 if (fLocalM) {
27 fStorage.setConcat(lm, *fLocalM);
28 fLocalM = fStorage.isIdentity() ? nullptr : &fStorage;
29 } else {
30 fLocalM = &lm;
31 }
32 }
33 }
34
35private:
36 SkMatrix fStorage;
37
38 using INHERITED = SkStageRec;
39};
40
41} // namespace
42
43sk_sp<SkShader> SkShaders::Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
44 switch (mode) {
45 case SkBlendMode::kClear: return Color(0);
46 case SkBlendMode::kDst: return dst;
47 case SkBlendMode::kSrc: return src;
48 default: break;
49 }
50 return sk_sp<SkShader>(new SkShader_Blend(mode, std::move(dst), std::move(src)));
51}
52
53sk_sp<SkShader> SkShaders::Lerp(float weight, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
54 if (SkScalarIsNaN(weight)) {
55 return nullptr;
56 }
57 if (dst == src || weight <= 0) {
58 return dst;
59 }
60 if (weight >= 1) {
61 return src;
62 }
63 return sk_sp<SkShader>(new SkShader_Lerp(weight, std::move(dst), std::move(src)));
64}
65
66///////////////////////////////////////////////////////////////////////////////
67
68static bool append_shader_or_paint(const SkStageRec& rec, SkShader* shader) {
69 if (shader) {
70 if (!as_SB(shader)->appendStages(rec)) {
71 return false;
72 }
73 } else {
74 rec.fPipeline->append_constant_color(rec.fAlloc, rec.fPaint.getColor4f().premul().vec());
75 }
76 return true;
77}
78
79// Returns the output of e0, and leaves the output of e1 in r,g,b,a
80static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* s1) {
81 struct Storage {
82 float fRes0[4 * SkRasterPipeline_kMaxStride];
83 };
84 auto storage = rec.fAlloc->make<Storage>();
85
86 if (!append_shader_or_paint(rec, s0)) {
87 return nullptr;
88 }
89 rec.fPipeline->append(SkRasterPipeline::store_src, storage->fRes0);
90
91 if (!append_shader_or_paint(rec, s1)) {
92 return nullptr;
93 }
94 return storage->fRes0;
95}
96
97///////////////////////////////////////////////////////////////////////////////
98
99sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
100 sk_sp<SkShader> dst(buffer.readShader());
101 sk_sp<SkShader> src(buffer.readShader());
102 unsigned mode = buffer.read32();
103
104 // check for valid mode before we cast to the enum type
105 if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
106 return nullptr;
107 }
108 return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
109}
110
111void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
112 buffer.writeFlattenable(fDst.get());
113 buffer.writeFlattenable(fSrc.get());
114 buffer.write32((int)fMode);
115}
116
117bool SkShader_Blend::onAppendStages(const SkStageRec& orig_rec) const {
118 const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix());
119
120 float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get());
121 if (!res0) {
122 return false;
123 }
124
125 rec.fPipeline->append(SkRasterPipeline::load_dst, res0);
126 SkBlendMode_AppendStages(fMode, rec.fPipeline);
127 return true;
128}
129
130skvm::Color SkShader_Blend::onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
131 const SkMatrix& ctm, const SkMatrix* localM,
132 SkFilterQuality q, const SkColorInfo& dst,
133 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
134 skvm::Color d,s;
135 if ((d = as_SB(fDst)->program(p, x,y, paint, ctm,localM, q, dst, uniforms, alloc)) &&
136 (s = as_SB(fSrc)->program(p, x,y, paint, ctm,localM, q, dst, uniforms, alloc)))
137 {
138 return p->blend(fMode, s,d);
139 }
140 return {};
141}
142
143
144sk_sp<SkFlattenable> SkShader_Lerp::CreateProc(SkReadBuffer& buffer) {
145 sk_sp<SkShader> dst(buffer.readShader());
146 sk_sp<SkShader> src(buffer.readShader());
147 float t = buffer.readScalar();
148 return buffer.isValid() ? SkShaders::Lerp(t, std::move(dst), std::move(src)) : nullptr;
149}
150
151void SkShader_Lerp::flatten(SkWriteBuffer& buffer) const {
152 buffer.writeFlattenable(fDst.get());
153 buffer.writeFlattenable(fSrc.get());
154 buffer.writeScalar(fWeight);
155}
156
157bool SkShader_Lerp::onAppendStages(const SkStageRec& orig_rec) const {
158 const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix());
159
160 float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get());
161 if (!res0) {
162 return false;
163 }
164
165 rec.fPipeline->append(SkRasterPipeline::load_dst, res0);
166 rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fWeight);
167 return true;
168}
169
170skvm::Color SkShader_Lerp::onProgram(skvm::Builder* p, skvm::F32 x, skvm::F32 y, skvm::Color paint,
171 const SkMatrix& ctm, const SkMatrix* localM,
172 SkFilterQuality q, const SkColorInfo& dst,
173 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
174 skvm::Color d,s;
175 if ((d = as_SB(fDst)->program(p, x,y, paint, ctm,localM, q, dst, uniforms, alloc)) &&
176 (s = as_SB(fSrc)->program(p, x,y, paint, ctm,localM, q, dst, uniforms, alloc)))
177 {
178 auto t = p->uniformF(uniforms->pushF(fWeight));
179 return {
180 p->lerp(d.r, s.r, t),
181 p->lerp(d.g, s.g, t),
182 p->lerp(d.b, s.b, t),
183 p->lerp(d.a, s.a, t),
184 };
185 }
186 return {};
187}
188
189#if SK_SUPPORT_GPU
190
191#include "include/private/GrRecordingContext.h"
192#include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
193#include "src/gpu/effects/generated/GrComposeLerpEffect.h"
194#include "src/gpu/effects/generated/GrConstColorProcessor.h"
195
196static std::unique_ptr<GrFragmentProcessor> as_fp(const GrFPArgs& args, SkShader* shader) {
197 return shader ? as_SB(shader)->asFragmentProcessor(args) : nullptr;
198}
199
200std::unique_ptr<GrFragmentProcessor> SkShader_Blend::asFragmentProcessor(
201 const GrFPArgs& orig_args) const {
202 const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix());
203 auto fpA = as_fp(args, fDst.get());
204 auto fpB = as_fp(args, fSrc.get());
205 if (!fpA) {
206 return GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(fpB), fMode);
207 }
208 if (!fpB) {
209 return GrXfermodeFragmentProcessor::MakeFromDstProcessor(std::move(fpA), fMode);
210 }
211 return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB),
212 std::move(fpA), fMode);
213}
214
215std::unique_ptr<GrFragmentProcessor> SkShader_Lerp::asFragmentProcessor(
216 const GrFPArgs& orig_args) const {
217 const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix());
218 auto fpA = as_fp(args, fDst.get());
219 auto fpB = as_fp(args, fSrc.get());
220 return GrComposeLerpEffect::Make(std::move(fpA), std::move(fpB), fWeight);
221}
222#endif
223