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
130static skvm::Color program_or_paint(const sk_sp<SkShader>& sh, skvm::Builder* p,
131 skvm::Coord device, skvm::Coord local, skvm::Color paint,
132 const SkMatrixProvider& mats, const SkMatrix* localM,
133 SkFilterQuality q, const SkColorInfo& dst,
134 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) {
135 return sh ? as_SB(sh)->program(p, device,local, paint, mats,localM, q,dst, uniforms,alloc)
136 : p->premul(paint);
137}
138
139skvm::Color SkShader_Blend::onProgram(skvm::Builder* p,
140 skvm::Coord device, skvm::Coord local, skvm::Color paint,
141 const SkMatrixProvider& mats, const SkMatrix* localM,
142 SkFilterQuality q, const SkColorInfo& dst,
143 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
144 skvm::Color d,s;
145 if ((d = program_or_paint(fDst, p, device,local, paint, mats,localM, q,dst, uniforms,alloc)) &&
146 (s = program_or_paint(fSrc, p, device,local, paint, mats,localM, q,dst, uniforms,alloc)))
147 {
148 return p->blend(fMode, s,d);
149 }
150 return {};
151}
152
153
154sk_sp<SkFlattenable> SkShader_Lerp::CreateProc(SkReadBuffer& buffer) {
155 sk_sp<SkShader> dst(buffer.readShader());
156 sk_sp<SkShader> src(buffer.readShader());
157 float t = buffer.readScalar();
158 return buffer.isValid() ? SkShaders::Lerp(t, std::move(dst), std::move(src)) : nullptr;
159}
160
161void SkShader_Lerp::flatten(SkWriteBuffer& buffer) const {
162 buffer.writeFlattenable(fDst.get());
163 buffer.writeFlattenable(fSrc.get());
164 buffer.writeScalar(fWeight);
165}
166
167bool SkShader_Lerp::onAppendStages(const SkStageRec& orig_rec) const {
168 const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix());
169
170 float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get());
171 if (!res0) {
172 return false;
173 }
174
175 rec.fPipeline->append(SkRasterPipeline::load_dst, res0);
176 rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fWeight);
177 return true;
178}
179
180skvm::Color SkShader_Lerp::onProgram(skvm::Builder* p,
181 skvm::Coord device, skvm::Coord local, skvm::Color paint,
182 const SkMatrixProvider& mats, const SkMatrix* localM,
183 SkFilterQuality q, const SkColorInfo& dst,
184 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
185 skvm::Color d,s;
186 if ((d = program_or_paint(fDst, p, device,local, paint, mats,localM, q,dst, uniforms,alloc)) &&
187 (s = program_or_paint(fSrc, p, device,local, paint, mats,localM, q,dst, uniforms,alloc)))
188 {
189 auto t = p->uniformF(uniforms->pushF(fWeight));
190 return {
191 p->lerp(d.r, s.r, t),
192 p->lerp(d.g, s.g, t),
193 p->lerp(d.b, s.b, t),
194 p->lerp(d.a, s.a, t),
195 };
196 }
197 return {};
198}
199
200#if SK_SUPPORT_GPU
201
202#include "include/gpu/GrRecordingContext.h"
203#include "src/gpu/effects/GrBlendFragmentProcessor.h"
204#include "src/gpu/effects/generated/GrComposeLerpEffect.h"
205#include "src/gpu/effects/generated/GrConstColorProcessor.h"
206
207static std::unique_ptr<GrFragmentProcessor> as_fp(const GrFPArgs& args, SkShader* shader) {
208 return shader ? as_SB(shader)->asFragmentProcessor(args) : nullptr;
209}
210
211std::unique_ptr<GrFragmentProcessor> SkShader_Blend::asFragmentProcessor(
212 const GrFPArgs& orig_args) const {
213 const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix());
214 auto fpA = as_fp(args, fDst.get());
215 auto fpB = as_fp(args, fSrc.get());
216 return GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
217}
218
219std::unique_ptr<GrFragmentProcessor> SkShader_Lerp::asFragmentProcessor(
220 const GrFPArgs& orig_args) const {
221 const GrFPArgs::WithPreLocalMatrix args(orig_args, this->getLocalMatrix());
222 auto fpA = as_fp(args, fDst.get());
223 auto fpB = as_fp(args, fSrc.get());
224 return GrComposeLerpEffect::Make(std::move(fpA), std::move(fpB), fWeight);
225}
226#endif
227