1 /*
2 * Copyright 2019 Google LLC
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 "modules/particles/include/SkParticleBinding.h"
9
10 #include "include/core/SkContourMeasure.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkPath.h"
13 #include "include/private/SkTPin.h"
14 #include "include/utils/SkParsePath.h"
15 #include "include/utils/SkTextUtils.h"
16 #include "modules/particles/include/SkReflected.h"
17 #include "modules/skresources/include/SkResources.h"
18 #include "src/core/SkMatrixProvider.h"
19 #include "src/core/SkVM.h"
20 #include "src/shaders/SkShaderBase.h"
21 #include "src/sksl/SkSLCompiler.h"
22
visitFields(SkFieldVisitor * v)23 void SkParticleBinding::visitFields(SkFieldVisitor* v) {
24 v->visit("Name", fName);
25 }
26
27 namespace {
28 struct PosNrm { SkPoint pos; SkVector nrm; };
29 using LinearizedPath = std::vector<PosNrm>;
30 } // namespace
31
linearize_path(const SkPath & path)32 static LinearizedPath linearize_path(const SkPath& path) {
33 LinearizedPath lin;
34 SkContourMeasureIter iter(path, false);
35 while (auto contour = iter.next()) {
36 for (SkScalar x = 0; x < contour->length(); x++) {
37 SkPoint pos;
38 SkVector tan;
39 SkAssertResult(contour->getPosTan(x, &pos, &tan));
40 lin.push_back({pos, {tan.fY, -tan.fX}});
41 }
42 }
43 return lin;
44 }
45
46 // Exposes an SkPath as an external, callable function. p(x) returns a float4 { pos.xy, normal.xy }
47 class SkPathExternalFunction : public SkParticleExternalFunction {
48 public:
SkPathExternalFunction(const char * name,SkSL::Compiler & compiler,const LinearizedPath & path,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)49 SkPathExternalFunction(const char* name,
50 SkSL::Compiler& compiler,
51 const LinearizedPath& path,
52 skvm::Uniforms* uniforms,
53 SkArenaAlloc* alloc)
54 : SkParticleExternalFunction(
55 name, compiler, *compiler.context().fTypes.fFloat4, uniforms, alloc)
56 , fPath(path) {}
57
callParameterCount() const58 int callParameterCount() const override { return 1; }
getCallParameterTypes(const SkSL::Type ** outTypes) const59 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
60 outTypes[0] = fCompiler.context().fTypes.fFloat.get();
61 }
62
call(skvm::Builder * builder,skvm::F32 * arguments,skvm::F32 * outResult,skvm::I32 mask) const63 void call(skvm::Builder* builder,
64 skvm::F32* arguments,
65 skvm::F32* outResult,
66 skvm::I32 mask) const override {
67 if (fPath.empty()) {
68 return;
69 }
70
71 skvm::Uniform ptr = fUniforms->pushPtr(fPath.data());
72 skvm::I32 index = trunc(clamp(arguments[0] * fPath.size(), 0, fPath.size() - 1));
73
74 outResult[0] = builder->gatherF(ptr, (index<<2)+0);
75 outResult[1] = builder->gatherF(ptr, (index<<2)+1);
76 outResult[2] = builder->gatherF(ptr, (index<<2)+2);
77 outResult[3] = builder->gatherF(ptr, (index<<2)+3);
78 }
79
80 private:
81 const LinearizedPath& fPath;
82 };
83
84 class SkPathBinding : public SkParticleBinding {
85 public:
SkPathBinding(const char * name="",const char * pathPath="",const char * pathName="")86 SkPathBinding(const char* name = "", const char* pathPath = "", const char* pathName = "")
87 : SkParticleBinding(name)
88 , fPathPath(pathPath)
89 , fPathName(pathName) {}
90
REFLECTED(SkPathBinding,SkParticleBinding)91 REFLECTED(SkPathBinding, SkParticleBinding)
92
93 void visitFields(SkFieldVisitor* v) override {
94 SkParticleBinding::visitFields(v);
95 v->visit("PathPath", fPathPath);
96 v->visit("PathName", fPathName);
97 }
98
toFunction(SkSL::Compiler & compiler,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)99 std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
100 skvm::Uniforms* uniforms,
101 SkArenaAlloc* alloc) override {
102 return std::make_unique<SkPathExternalFunction>(fName.c_str(), compiler, fData, uniforms,
103 alloc);
104 }
105
prepare(const skresources::ResourceProvider * resourceProvider)106 void prepare(const skresources::ResourceProvider* resourceProvider) override {
107 if (auto pathData = resourceProvider->load(fPathPath.c_str(), fPathName.c_str())) {
108 SkPath path;
109 if (0 != path.readFromMemory(pathData->data(), pathData->size())) {
110 fData = linearize_path(path);
111 }
112 }
113 }
114
115 private:
116 SkString fPathPath;
117 SkString fPathName;
118
119 // Cached
120 LinearizedPath fData;
121 };
122
123 class SkTextBinding : public SkParticleBinding {
124 public:
SkTextBinding(const char * name="",const char * text="",SkScalar fontSize=96)125 SkTextBinding(const char* name = "", const char* text = "", SkScalar fontSize = 96)
126 : SkParticleBinding(name)
127 , fText(text)
128 , fFontSize(fontSize) {}
129
REFLECTED(SkTextBinding,SkParticleBinding)130 REFLECTED(SkTextBinding, SkParticleBinding)
131
132 void visitFields(SkFieldVisitor* v) override {
133 SkParticleBinding::visitFields(v);
134 v->visit("Text", fText);
135 v->visit("FontSize", fFontSize);
136 }
137
toFunction(SkSL::Compiler & compiler,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)138 std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
139 skvm::Uniforms* uniforms,
140 SkArenaAlloc* alloc) override {
141 return std::make_unique<SkPathExternalFunction>(fName.c_str(), compiler, fData, uniforms,
142 alloc);
143 }
144
prepare(const skresources::ResourceProvider *)145 void prepare(const skresources::ResourceProvider*) override {
146 if (fText.isEmpty()) {
147 return;
148 }
149
150 SkFont font(nullptr, fFontSize);
151 SkPath path;
152 SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8, 0, 0, font, &path);
153 fData = linearize_path(path);
154 }
155
156 private:
157 SkString fText;
158 SkScalar fFontSize;
159
160 // Cached
161 LinearizedPath fData;
162 };
163
164 // Exposes an SkShader as an external, callable function. p(xy) returns a float4
165 class SkShaderExternalFunction : public SkParticleExternalFunction {
166 public:
SkShaderExternalFunction(const char * name,SkSL::Compiler & compiler,sk_sp<SkShader> shader,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)167 SkShaderExternalFunction(const char* name,
168 SkSL::Compiler& compiler,
169 sk_sp<SkShader> shader,
170 skvm::Uniforms* uniforms,
171 SkArenaAlloc* alloc)
172 : SkParticleExternalFunction(
173 name, compiler, *compiler.context().fTypes.fFloat4, uniforms, alloc)
174 , fShader(std::move(shader)) {}
175
callParameterCount() const176 int callParameterCount() const override { return 1; }
getCallParameterTypes(const SkSL::Type ** outTypes) const177 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
178 outTypes[0] = fCompiler.context().fTypes.fFloat2.get();
179 }
180
call(skvm::Builder * builder,skvm::F32 * arguments,skvm::F32 * outResult,skvm::I32 mask) const181 void call(skvm::Builder* builder,
182 skvm::F32* arguments,
183 skvm::F32* outResult,
184 skvm::I32 mask) const override {
185 skvm::Coord coord = {arguments[0], arguments[1]};
186 skvm::F32 zero = builder->splat(0.0f);
187 SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
188 SkColorInfo colorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType, /*cs=*/nullptr);
189
190 skvm::Color result = as_SB(fShader)->program(
191 builder, /*device=*/coord, /*local=*/coord, /*paint=*/{zero, zero, zero, zero},
192 matrixProvider, /*localM=*/nullptr, colorInfo, fUniforms,
193 fAlloc);
194 SkASSERT(result);
195 outResult[0] = result.r;
196 outResult[1] = result.g;
197 outResult[2] = result.b;
198 outResult[3] = result.a;
199 }
200
201 private:
202 sk_sp<SkShader> fShader;
203 };
204
205 class SkImageBinding : public SkParticleBinding {
206 public:
SkImageBinding(const char * name="",const char * imagePath="",const char * imageName="")207 SkImageBinding(const char* name = "", const char* imagePath = "", const char* imageName = "")
208 : SkParticleBinding(name)
209 , fImagePath(imagePath)
210 , fImageName(imageName) {}
211
REFLECTED(SkImageBinding,SkParticleBinding)212 REFLECTED(SkImageBinding, SkParticleBinding)
213
214 void visitFields(SkFieldVisitor* v) override {
215 SkParticleBinding::visitFields(v);
216 v->visit("ImagePath", fImagePath);
217 v->visit("ImageName", fImageName);
218 }
219
toFunction(SkSL::Compiler & compiler,skvm::Uniforms * uniforms,SkArenaAlloc * alloc)220 std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
221 skvm::Uniforms* uniforms,
222 SkArenaAlloc* alloc) override {
223 return std::make_unique<SkShaderExternalFunction>(fName.c_str(), compiler, fShader,
224 uniforms, alloc);
225 }
226
prepare(const skresources::ResourceProvider * resourceProvider)227 void prepare(const skresources::ResourceProvider* resourceProvider) override {
228 if (auto asset = resourceProvider->loadImageAsset(fImagePath.c_str(), fImageName.c_str(),
229 nullptr)) {
230 if (auto image = asset->getFrame(0)) {
231 SkMatrix normalize = SkMatrix::Scale(1.0f / image->width(), 1.0f / image->height());
232 fShader = image->makeShader(SkSamplingOptions(SkFilterMode::kLinear), &normalize);
233 return;
234 }
235 }
236
237 fShader = SkShaders::Color(SK_ColorWHITE);
238 }
239
240 private:
241 SkString fImagePath;
242 SkString fImageName;
243
244 // Cached
245 sk_sp<SkShader> fShader;
246 };
247
MakeImage(const char * name,const char * imagePath,const char * imageName)248 sk_sp<SkParticleBinding> SkParticleBinding::MakeImage(const char* name, const char* imagePath,
249 const char* imageName) {
250 return sk_sp<SkParticleBinding>(new SkImageBinding(name, imagePath, imageName));
251 }
252
MakePath(const char * name,const char * pathPath,const char * pathName)253 sk_sp<SkParticleBinding> SkParticleBinding::MakePath(const char* name, const char* pathPath,
254 const char* pathName) {
255 return sk_sp<SkParticleBinding>(new SkPathBinding(name, pathPath, pathName));
256 }
257
RegisterBindingTypes()258 void SkParticleBinding::RegisterBindingTypes() {
259 REGISTER_REFLECTED(SkParticleBinding);
260 REGISTER_REFLECTED(SkImageBinding);
261 REGISTER_REFLECTED(SkPathBinding);
262 REGISTER_REFLECTED(SkTextBinding);
263 }
264