• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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