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