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/SkBitmap.h"
11#include "include/core/SkContourMeasure.h"
12#include "include/core/SkImage.h"
13#include "include/core/SkPath.h"
14#include "include/utils/SkParsePath.h"
15#include "include/utils/SkTextUtils.h"
16#include "modules/particles/include/SkParticleEffect.h"
17#include "modules/particles/include/SkReflected.h"
18#include "modules/skresources/include/SkResources.h"
19#include "src/sksl/SkSLCompiler.h"
20
21void SkParticleBinding::visitFields(SkFieldVisitor* v) {
22 v->visit("Name", fName);
23}
24
25class SkEffectExternalValue : public SkParticleExternalValue {
26public:
27 SkEffectExternalValue(const char* name, SkSL::Compiler& compiler,
28 sk_sp<SkParticleEffectParams> params)
29 : SkParticleExternalValue(name, compiler, *compiler.context().fVoid_Type)
30 , fParams(std::move(params)) {}
31
32 bool canCall() const override { return true; }
33 int callParameterCount() const override { return 1; }
34 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
35 outTypes[0] = fCompiler.context().fBool_Type.get();
36 }
37
38 void call(int index, float* arguments, float* outReturn) override {
39 bool loop = ((int*)arguments)[0] != 0;
40 fEffect->addSpawnRequest(index, loop, fParams);
41 }
42
43private:
44 sk_sp<SkParticleEffectParams> fParams;
45};
46
47class SkEffectBinding : public SkParticleBinding {
48public:
49 SkEffectBinding(const char* name = "", sk_sp<SkParticleEffectParams> params = nullptr)
50 : SkParticleBinding(name)
51 , fParams(std::move(params)) {
52 if (!fParams) {
53 fParams.reset(new SkParticleEffectParams());
54 }
55 }
56
57 REFLECTED(SkEffectBinding, SkParticleBinding)
58
59 void visitFields(SkFieldVisitor* v) override {
60 SkParticleBinding::visitFields(v);
61 fParams->visitFields(v);
62 }
63
64 std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
65 return std::unique_ptr<SkParticleExternalValue>(
66 new SkEffectExternalValue(fName.c_str(), compiler, fParams));
67 }
68
69 void prepare(const skresources::ResourceProvider* resourceProvider) override {
70 fParams->prepare(resourceProvider);
71 }
72
73private:
74 sk_sp<SkParticleEffectParams> fParams;
75};
76
77struct SkPathContours {
78 SkScalar fTotalLength;
79 SkTArray<sk_sp<SkContourMeasure>> fContours;
80
81 void rebuild(const SkPath& path) {
82 fTotalLength = 0;
83 fContours.reset();
84
85 SkContourMeasureIter iter(path, false);
86 while (auto contour = iter.next()) {
87 fContours.push_back(contour);
88 fTotalLength += contour->length();
89 }
90 }
91};
92
93// Exposes an SkPath as an external, callable value. p(x) returns a float4 { pos.xy, normal.xy }
94class SkPathExternalValue : public SkParticleExternalValue {
95public:
96 SkPathExternalValue(const char* name, SkSL::Compiler& compiler, const SkPathContours* path)
97 : SkParticleExternalValue(name, compiler, *compiler.context().fFloat4_Type)
98 , fPath(path) { }
99
100 bool canCall() const override { return true; }
101 int callParameterCount() const override { return 1; }
102 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
103 outTypes[0] = fCompiler.context().fFloat_Type.get();
104 }
105
106 void call(int index, float* arguments, float* outReturn) override {
107 SkScalar len = fPath->fTotalLength * arguments[0];
108 int idx = 0;
109 while (idx < fPath->fContours.count() - 1 && len > fPath->fContours[idx]->length()) {
110 len -= fPath->fContours[idx++]->length();
111 }
112 SkVector localXAxis;
113 if (idx >= fPath->fContours.count() ||
114 !fPath->fContours[idx]->getPosTan(len, (SkPoint*)outReturn, &localXAxis)) {
115 outReturn[0] = outReturn[1] = 0.0f;
116 localXAxis = { 1, 0 };
117 }
118 outReturn[2] = localXAxis.fY;
119 outReturn[3] = -localXAxis.fX;
120 }
121
122private:
123 const SkPathContours* fPath;
124};
125
126class SkPathBinding : public SkParticleBinding {
127public:
128 SkPathBinding(const char* name = "", const char* pathPath = "", const char* pathName = "")
129 : SkParticleBinding(name)
130 , fPathPath(pathPath)
131 , fPathName(pathName) {}
132
133 REFLECTED(SkPathBinding, SkParticleBinding)
134
135 void visitFields(SkFieldVisitor* v) override {
136 SkParticleBinding::visitFields(v);
137 v->visit("PathPath", fPathPath);
138 v->visit("PathName", fPathName);
139 }
140
141 std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
142 return std::unique_ptr<SkParticleExternalValue>(
143 new SkPathExternalValue(fName.c_str(), compiler, &fContours));
144 }
145
146 void prepare(const skresources::ResourceProvider* resourceProvider) override {
147 if (auto pathData = resourceProvider->load(fPathPath.c_str(), fPathName.c_str())) {
148 SkPath path;
149 if (0 != path.readFromMemory(pathData->data(), pathData->size())) {
150 fContours.rebuild(path);
151 }
152 }
153 }
154
155private:
156 SkString fPathPath;
157 SkString fPathName;
158
159 // Cached
160 SkPathContours fContours;
161};
162
163class SkTextBinding : public SkParticleBinding {
164public:
165 SkTextBinding(const char* name = "", const char* text = "", SkScalar fontSize = 96)
166 : SkParticleBinding(name)
167 , fText(text)
168 , fFontSize(fontSize) {}
169
170 REFLECTED(SkTextBinding, SkParticleBinding)
171
172 void visitFields(SkFieldVisitor* v) override {
173 SkParticleBinding::visitFields(v);
174 v->visit("Text", fText);
175 v->visit("FontSize", fFontSize);
176 }
177
178 std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
179 return std::unique_ptr<SkParticleExternalValue>(
180 new SkPathExternalValue(fName.c_str(), compiler, &fContours));
181 }
182
183 void prepare(const skresources::ResourceProvider*) override {
184 if (fText.isEmpty()) {
185 return;
186 }
187
188 SkFont font(nullptr, fFontSize);
189 SkPath path;
190 SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8, 0, 0, font, &path);
191 fContours.rebuild(path);
192 }
193
194private:
195 SkString fText;
196 SkScalar fFontSize;
197
198 // Cached
199 SkPathContours fContours;
200};
201
202// Exposes an SkBitmap as an external, callable value. p(xy) returns a float4
203class SkBitmapExternalValue : public SkParticleExternalValue {
204public:
205 SkBitmapExternalValue(const char* name, SkSL::Compiler& compiler, const SkBitmap& bitmap)
206 : SkParticleExternalValue(name, compiler, *compiler.context().fFloat4_Type)
207 , fBitmap(bitmap) {
208 SkASSERT(bitmap.colorType() == kRGBA_F32_SkColorType);
209 }
210
211 bool canCall() const override { return true; }
212 int callParameterCount() const override { return 1; }
213 void getCallParameterTypes(const SkSL::Type** outTypes) const override {
214 outTypes[0] = fCompiler.context().fFloat2_Type.get();
215 }
216
217 void call(int index, float* arguments, float* outReturn) override {
218 int x = SkTPin(static_cast<int>(arguments[0] * fBitmap.width()), 0, fBitmap.width() - 1);
219 int y = SkTPin(static_cast<int>(arguments[1] * fBitmap.height()), 0, fBitmap.height() - 1);
220 float* p = static_cast<float*>(fBitmap.getAddr(x, y));
221 memcpy(outReturn, p, 4 * sizeof(float));
222 }
223
224private:
225 SkBitmap fBitmap;
226};
227
228class SkImageBinding : public SkParticleBinding {
229public:
230 SkImageBinding(const char* name = "", const char* imagePath = "", const char* imageName = "")
231 : SkParticleBinding(name)
232 , fImagePath(imagePath)
233 , fImageName(imageName) {}
234
235 REFLECTED(SkImageBinding, SkParticleBinding)
236
237 void visitFields(SkFieldVisitor* v) override {
238 SkParticleBinding::visitFields(v);
239 v->visit("ImagePath", fImagePath);
240 v->visit("ImageName", fImageName);
241 }
242
243 std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
244 return std::unique_ptr<SkParticleExternalValue>(
245 new SkBitmapExternalValue(fName.c_str(), compiler, fBitmap));
246 }
247
248 void prepare(const skresources::ResourceProvider* resourceProvider) override {
249 if (auto asset = resourceProvider->loadImageAsset(fImagePath.c_str(), fImageName.c_str(),
250 nullptr)) {
251 if (auto image = asset->getFrame(0)) {
252 fBitmap.allocPixels(image->imageInfo().makeColorType(kRGBA_F32_SkColorType));
253 image->readPixels(fBitmap.pixmap(), 0, 0);
254 return;
255 }
256 }
257
258 fBitmap.allocPixels(SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, kPremul_SkAlphaType));
259 fBitmap.eraseColor(SK_ColorWHITE);
260 }
261
262private:
263 SkString fImagePath;
264 SkString fImageName;
265
266 // Cached
267 SkBitmap fBitmap;
268};
269
270sk_sp<SkParticleBinding> SkParticleBinding::MakeEffect(const char* name,
271 sk_sp<SkParticleEffectParams> params) {
272 return sk_sp<SkParticleBinding>(new SkEffectBinding(name, std::move(params)));
273}
274
275sk_sp<SkParticleBinding> SkParticleBinding::MakeImage(const char* name, const char* imagePath,
276 const char* imageName) {
277 return sk_sp<SkParticleBinding>(new SkImageBinding(name, imagePath, imageName));
278}
279
280sk_sp<SkParticleBinding> SkParticleBinding::MakePath(const char* name, const char* pathPath,
281 const char* pathName) {
282 return sk_sp<SkParticleBinding>(new SkPathBinding(name, pathPath, pathName));
283}
284
285void SkParticleBinding::RegisterBindingTypes() {
286 REGISTER_REFLECTED(SkParticleBinding);
287 REGISTER_REFLECTED(SkEffectBinding);
288 REGISTER_REFLECTED(SkImageBinding);
289 REGISTER_REFLECTED(SkPathBinding);
290 REGISTER_REFLECTED(SkTextBinding);
291}
292