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