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/SkParticleDrawable.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkRSXform.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkString.h"
16 #include "include/core/SkSurface.h"
17 #include "include/private/SkTPin.h"
18 #include "modules/particles/include/SkParticleData.h"
19 #include "modules/skresources/include/SkResources.h"
20 #include "src/core/SkAutoMalloc.h"
21 
make_circle_image(int radius)22 static sk_sp<SkImage> make_circle_image(int radius) {
23     auto surface = SkSurface::MakeRasterN32Premul(radius * 2, radius * 2);
24     surface->getCanvas()->clear(SK_ColorTRANSPARENT);
25     SkPaint paint;
26     paint.setAntiAlias(true);
27     paint.setColor(SK_ColorWHITE);
28     surface->getCanvas()->drawCircle(radius, radius, radius, paint);
29     return surface->makeImageSnapshot();
30 }
31 
make_rsxform(SkPoint ofs,float posX,float posY,float dirX,float dirY,float scale)32 static inline SkRSXform make_rsxform(SkPoint ofs,
33                                      float posX, float posY, float dirX, float dirY, float scale) {
34     const float s = dirX * scale;
35     const float c = -dirY * scale;
36     return SkRSXform::Make(c, s,
37                            posX + -c * ofs.fX + s * ofs.fY,
38                            posY + -s * ofs.fX + -c * ofs.fY);
39 }
40 
41 struct DrawAtlasArrays {
DrawAtlasArraysDrawAtlasArrays42     DrawAtlasArrays(const SkParticles& particles, int count, SkPoint center)
43             : fXforms(count)
44             , fRects(count)
45             , fColors(count) {
46         float* c[] = {
47             particles.fData[SkParticles::kColorR].get(),
48             particles.fData[SkParticles::kColorG].get(),
49             particles.fData[SkParticles::kColorB].get(),
50             particles.fData[SkParticles::kColorA].get(),
51         };
52 
53         float* pos[] = {
54             particles.fData[SkParticles::kPositionX].get(),
55             particles.fData[SkParticles::kPositionY].get(),
56         };
57         float* dir[] = {
58             particles.fData[SkParticles::kHeadingX].get(),
59             particles.fData[SkParticles::kHeadingY].get(),
60         };
61         float* scale = particles.fData[SkParticles::kScale].get();
62 
63         for (int i = 0; i < count; ++i) {
64             fXforms[i] = make_rsxform(center, pos[0][i], pos[1][i], dir[0][i], dir[1][i], scale[i]);
65             fColors[i] = SkColor4f{ c[0][i], c[1][i], c[2][i], c[3][i] }.toSkColor();
66         }
67     }
68 
69     SkAutoTMalloc<SkRSXform> fXforms;
70     SkAutoTMalloc<SkRect>    fRects;
71     SkAutoTMalloc<SkColor>   fColors;
72 };
73 
74 class SkCircleDrawable : public SkParticleDrawable {
75 public:
SkCircleDrawable(int radius=1)76     SkCircleDrawable(int radius = 1) : fRadius(radius) {}
77 
REFLECTED(SkCircleDrawable,SkParticleDrawable)78     REFLECTED(SkCircleDrawable, SkParticleDrawable)
79 
80     void draw(SkCanvas* canvas, const SkParticles& particles, int count,
81               const SkPaint& paint) override {
82         int r = std::max(fRadius, 1);
83         SkPoint center = { SkIntToScalar(r), SkIntToScalar(r) };
84         DrawAtlasArrays arrays(particles, count, center);
85         for (int i = 0; i < count; ++i) {
86             arrays.fRects[i].setIWH(fImage->width(), fImage->height());
87         }
88         SkSamplingOptions sampling(SkFilterMode::kLinear);
89         canvas->drawAtlas(fImage.get(), arrays.fXforms.get(), arrays.fRects.get(),
90                           arrays.fColors.get(), count, SkBlendMode::kModulate, sampling,
91                           nullptr, &paint);
92     }
93 
prepare(const skresources::ResourceProvider *)94     void prepare(const skresources::ResourceProvider*) override {
95         int r = std::max(fRadius, 1);
96         if (!fImage || fImage->width() != 2 * r) {
97             fImage = make_circle_image(r);
98         }
99     }
100 
visitFields(SkFieldVisitor * v)101     void visitFields(SkFieldVisitor* v) override {
102         v->visit("Radius", fRadius);
103     }
104 
105 private:
106     int fRadius;
107 
108     // Cached
109     sk_sp<SkImage> fImage;
110 };
111 
112 class SkImageDrawable : public SkParticleDrawable {
113 public:
SkImageDrawable(const char * imagePath="",const char * imageName="",int cols=1,int rows=1)114     SkImageDrawable(const char* imagePath = "", const char* imageName = "",
115                     int cols = 1, int rows = 1)
116             : fPath(imagePath)
117             , fName(imageName)
118             , fCols(cols)
119             , fRows(rows) {}
120 
REFLECTED(SkImageDrawable,SkParticleDrawable)121     REFLECTED(SkImageDrawable, SkParticleDrawable)
122 
123     void draw(SkCanvas* canvas, const SkParticles& particles, int count,
124               const SkPaint& paint) override {
125         int cols = std::max(fCols, 1),
126             rows = std::max(fRows, 1);
127         SkRect baseRect = SkRect::MakeWH(static_cast<float>(fImage->width()) / cols,
128                                          static_cast<float>(fImage->height()) / rows);
129         SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
130         DrawAtlasArrays arrays(particles, count, center);
131 
132         int frameCount = cols * rows;
133         float* spriteFrames = particles.fData[SkParticles::kSpriteFrame].get();
134         for (int i = 0; i < count; ++i) {
135             int frame = static_cast<int>(spriteFrames[i] * frameCount + 0.5f);
136             frame = SkTPin(frame, 0, frameCount - 1);
137             int row = frame / cols;
138             int col = frame % cols;
139             arrays.fRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
140         }
141         canvas->drawAtlas(fImage.get(), arrays.fXforms.get(), arrays.fRects.get(),
142                           arrays.fColors.get(), count, SkBlendMode::kModulate,
143                           SkSamplingOptions(SkFilterMode::kLinear), nullptr, &paint);
144     }
145 
prepare(const skresources::ResourceProvider * resourceProvider)146     void prepare(const skresources::ResourceProvider* resourceProvider) override {
147         fImage.reset();
148         if (auto asset = resourceProvider->loadImageAsset(fPath.c_str(), fName.c_str(), nullptr)) {
149             fImage = asset->getFrame(0);
150         }
151         if (!fImage) {
152             SkDebugf("Could not load image \"%s:%s\"\n", fPath.c_str(), fName.c_str());
153             fImage = make_circle_image(1);
154         }
155     }
156 
visitFields(SkFieldVisitor * v)157     void visitFields(SkFieldVisitor* v) override {
158         v->visit("Path", fPath);
159         v->visit("Name", fName);
160         v->visit("Columns", fCols);
161         v->visit("Rows", fRows);
162     }
163 
164 private:
165     SkString fPath;
166     SkString fName;
167     int      fCols;
168     int      fRows;
169 
170     // Cached
171     sk_sp<SkImage> fImage;
172 };
173 
RegisterDrawableTypes()174 void SkParticleDrawable::RegisterDrawableTypes() {
175     REGISTER_REFLECTED(SkParticleDrawable);
176     REGISTER_REFLECTED(SkCircleDrawable);
177     REGISTER_REFLECTED(SkImageDrawable);
178 }
179 
MakeCircle(int radius)180 sk_sp<SkParticleDrawable> SkParticleDrawable::MakeCircle(int radius) {
181     return sk_sp<SkParticleDrawable>(new SkCircleDrawable(radius));
182 }
183 
MakeImage(const char * imagePath,const char * imageName,int cols,int rows)184 sk_sp<SkParticleDrawable> SkParticleDrawable::MakeImage(const char* imagePath,
185                                                         const char* imageName,
186                                                         int cols, int rows) {
187     return sk_sp<SkParticleDrawable>(new SkImageDrawable(imagePath, imageName, cols, rows));
188 }
189