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