2 * Copyright 2019 Google LLC
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "modules/particles/include/SkParticleBinding.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkContourMeasure.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkPath.h"
15 #include "include/private/SkTPin.h"
16 #include "include/utils/SkParsePath.h"
17 #include "include/utils/SkTextUtils.h"
18 #include "modules/particles/include/SkReflected.h"
19 #include "modules/skresources/include/SkResources.h"
20 #include "src/core/SkMatrixProvider.h"
21 #include "src/core/SkVM.h"
22 #include "src/shaders/SkShaderBase.h"
23 #include "src/sksl/SkSLCompiler.h"
25 void SkParticleBinding::visitFields(SkFieldVisitor* v) {
26 v->visit("Name", fName);
30 struct PosNrm { SkPoint pos; SkVector nrm; };
31 using LinearizedPath = std::vector<PosNrm>;
34 static LinearizedPath linearize_path(const SkPath& path) {
36 SkContourMeasureIter iter(path, false);
37 while (auto contour = iter.next()) {
38 for (SkScalar x = 0; x < contour->length(); x++) {
41 SkAssertResult(contour->getPosTan(x, &pos, &tan));
42 lin.push_back({pos, {tan.fY, -tan.fX}});
48 // Exposes an SkPath as an external, callable function. p(x) returns a float4 { pos.xy, normal.xy }
49 class SkPathExternalFunction : public SkParticleExternalFunction {
51 SkPathExternalFunction(const char* name,
52 SkSL::Compiler& compiler,
53 const LinearizedPath& path,
54 skvm::Uniforms* uniforms,
56 : SkParticleExternalFunction(
57 name, compiler, *compiler.context().fTypes.fFloat4, uniforms, alloc)
60 int callParameterCount() const override { return 1; }
61 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
62 outTypes[0] = fCompiler.context().fTypes.fFloat.get();
65 void call(skvm::Builder* builder,
68 skvm::I32 mask) const override {
73 skvm::Uniform ptr = fUniforms->pushPtr(fPath.data());
74 skvm::I32 index = trunc(clamp(arguments[0] * fPath.size(), 0, fPath.size() - 1));
76 outResult[0] = builder->gatherF(ptr, (index<<2)+0);
77 outResult[1] = builder->gatherF(ptr, (index<<2)+1);
78 outResult[2] = builder->gatherF(ptr, (index<<2)+2);
79 outResult[3] = builder->gatherF(ptr, (index<<2)+3);
83 const LinearizedPath& fPath;
86 class SkPathBinding : public SkParticleBinding {
88 SkPathBinding(const char* name = "", const char* pathPath = "", const char* pathName = "")
89 : SkParticleBinding(name)
91 , fPathName(pathName) {}
93 REFLECTED(SkPathBinding, SkParticleBinding)
95 void visitFields(SkFieldVisitor* v) override {
96 SkParticleBinding::visitFields(v);
97 v->visit("PathPath", fPathPath);
98 v->visit("PathName", fPathName);
101 std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
102 skvm::Uniforms* uniforms,
103 SkArenaAlloc* alloc) override {
104 return std::make_unique<SkPathExternalFunction>(fName.c_str(), compiler, fData, uniforms,
108 void prepare(const skresources::ResourceProvider* resourceProvider) override {
109 if (auto pathData = resourceProvider->load(fPathPath.c_str(), fPathName.c_str())) {
111 if (0 != path.readFromMemory(pathData->data(), pathData->size())) {
112 fData = linearize_path(path);
122 LinearizedPath fData;
125 class SkTextBinding : public SkParticleBinding {
127 SkTextBinding(const char* name = "", const char* text = "", SkScalar fontSize = 96)
128 : SkParticleBinding(name)
130 , fFontSize(fontSize) {}
132 REFLECTED(SkTextBinding, SkParticleBinding)
134 void visitFields(SkFieldVisitor* v) override {
135 SkParticleBinding::visitFields(v);
136 v->visit("Text", fText);
137 v->visit("FontSize", fFontSize);
140 std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
141 skvm::Uniforms* uniforms,
142 SkArenaAlloc* alloc) override {
143 return std::make_unique<SkPathExternalFunction>(fName.c_str(), compiler, fData, uniforms,
147 void prepare(const skresources::ResourceProvider*) override {
148 if (fText.isEmpty()) {
152 SkFont font(nullptr, fFontSize);
154 SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8, 0, 0, font, &path);
155 fData = linearize_path(path);
163 LinearizedPath fData;
166 // Exposes an SkShader as an external, callable function. p(xy) returns a float4
167 class SkShaderExternalFunction : public SkParticleExternalFunction {
169 SkShaderExternalFunction(const char* name,
170 SkSL::Compiler& compiler,
171 sk_sp<SkShader> shader,
172 skvm::Uniforms* uniforms,
174 : SkParticleExternalFunction(
175 name, compiler, *compiler.context().fTypes.fFloat4, uniforms, alloc)
176 , fShader(std::move(shader)) {}
178 int callParameterCount() const override { return 1; }
179 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
180 outTypes[0] = fCompiler.context().fTypes.fFloat2.get();
183 void call(skvm::Builder* builder,
184 skvm::F32* arguments,
185 skvm::F32* outResult,
186 skvm::I32 mask) const override {
187 skvm::Coord coord = {arguments[0], arguments[1]};
188 skvm::F32 zero = builder->splat(0.0f);
189 SkOverrideDeviceMatrixProvider matrixProvider(SkMatrix::I());
190 SkColorInfo colorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType, /*cs=*/nullptr);
192 skvm::Color result = as_SB(fShader)->program(
193 builder, /*device=*/coord, /*local=*/coord, /*paint=*/{zero, zero, zero, zero},
194 matrixProvider, /*localM=*/nullptr, colorInfo, fUniforms,
197 outResult[0] = result.r;
198 outResult[1] = result.g;
199 outResult[2] = result.b;
200 outResult[3] = result.a;
204 sk_sp<SkShader> fShader;
207 class SkImageBinding : public SkParticleBinding {
209 SkImageBinding(const char* name = "", const char* imagePath = "", const char* imageName = "")
210 : SkParticleBinding(name)
211 , fImagePath(imagePath)
212 , fImageName(imageName) {}
214 REFLECTED(SkImageBinding, SkParticleBinding)
216 void visitFields(SkFieldVisitor* v) override {
217 SkParticleBinding::visitFields(v);
218 v->visit("ImagePath", fImagePath);
219 v->visit("ImageName", fImageName);
222 std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
223 skvm::Uniforms* uniforms,
224 SkArenaAlloc* alloc) override {
225 return std::make_unique<SkShaderExternalFunction>(fName.c_str(), compiler, fShader,
229 void prepare(const skresources::ResourceProvider* resourceProvider) override {
230 SkASSERT(resourceProvider);
231 if (auto asset = resourceProvider->loadImageAsset(fImagePath.c_str(), fImageName.c_str(),
234 if (auto image = asset->getFrame(0)) {
235 SkMatrix normalize = SkMatrix::Scale(1.0f / image->width(), 1.0f / image->height());
236 fShader = image->makeShader(SkSamplingOptions(SkFilterMode::kLinear), &normalize);
240 fShader = SkShaders::Color(SK_ColorWHITE);
248 sk_sp<SkShader> fShader;
251 sk_sp<SkParticleBinding> SkParticleBinding::MakeImage(const char* name, const char* imagePath,
252 const char* imageName) {
253 return sk_sp<SkParticleBinding>(new SkImageBinding(name, imagePath, imageName));
256 sk_sp<SkParticleBinding> SkParticleBinding::MakePath(const char* name, const char* pathPath,
257 const char* pathName) {
258 return sk_sp<SkParticleBinding>(new SkPathBinding(name, pathPath, pathName));
261 void SkParticleBinding::RegisterBindingTypes() {
262 REGISTER_REFLECTED(SkParticleBinding);
263 REGISTER_REFLECTED(SkImageBinding);
264 REGISTER_REFLECTED(SkPathBinding);
265 REGISTER_REFLECTED(SkTextBinding);