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