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 "SkParticleDrawable.h"
9
10 #include "SkAutoMalloc.h"
11 #include "SkCanvas.h"
12 #include "SkImage.h"
13 #include "SkPaint.h"
14 #include "SkParticleData.h"
15 #include "SkRect.h"
16 #include "SkSurface.h"
17 #include "SkString.h"
18 #include "SkRSXform.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
30 struct DrawAtlasArrays {
DrawAtlasArraysDrawAtlasArrays31 DrawAtlasArrays(const SkParticleState particles[], int count, SkPoint center)
32 : fXforms(count)
33 , fRects(count)
34 , fColors(count) {
35 for (int i = 0; i < count; ++i) {
36 fXforms[i] = particles[i].fPose.asRSXform(center);
37 fColors[i] = particles[i].fColor.toSkColor();
38 }
39 }
40
41 SkAutoTMalloc<SkRSXform> fXforms;
42 SkAutoTMalloc<SkRect> fRects;
43 SkAutoTMalloc<SkColor> fColors;
44 };
45
46 class SkCircleDrawable : public SkParticleDrawable {
47 public:
SkCircleDrawable(int radius=1)48 SkCircleDrawable(int radius = 1)
49 : fRadius(radius) {
50 this->rebuild();
51 }
52
REFLECTED(SkCircleDrawable,SkParticleDrawable)53 REFLECTED(SkCircleDrawable, SkParticleDrawable)
54
55 void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
56 const SkPaint* paint) override {
57 SkPoint center = { SkIntToScalar(fRadius), SkIntToScalar(fRadius) };
58 DrawAtlasArrays arrays(particles, count, center);
59 for (int i = 0; i < count; ++i) {
60 arrays.fRects[i].set(0.0f, 0.0f, fImage->width(), fImage->height());
61 }
62 canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
63 count, SkBlendMode::kModulate, nullptr, paint);
64 }
65
visitFields(SkFieldVisitor * v)66 void visitFields(SkFieldVisitor* v) override {
67 v->visit("Radius", fRadius);
68 this->rebuild();
69 }
70
71 private:
72 int fRadius;
73
rebuild()74 void rebuild() {
75 fRadius = SkTMax(fRadius, 1);
76 if (!fImage || fImage->width() != 2 * fRadius) {
77 fImage = make_circle_image(fRadius);
78 }
79 }
80
81 // Cached
82 sk_sp<SkImage> fImage;
83 };
84
85 class SkImageDrawable : public SkParticleDrawable {
86 public:
SkImageDrawable(const SkString & path=SkString (),int cols=1,int rows=1)87 SkImageDrawable(const SkString& path = SkString(), int cols = 1, int rows = 1)
88 : fPath(path)
89 , fCols(cols)
90 , fRows(rows) {
91 this->rebuild();
92 }
93
REFLECTED(SkImageDrawable,SkParticleDrawable)94 REFLECTED(SkImageDrawable, SkParticleDrawable)
95
96 void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
97 const SkPaint* paint) override {
98 SkRect baseRect = getBaseRect();
99 SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
100 DrawAtlasArrays arrays(particles, count, center);
101
102 int frameCount = fCols * fRows;
103 for (int i = 0; i < count; ++i) {
104 int frame = static_cast<int>(particles[i].fFrame * frameCount + 0.5f);
105 frame = SkTPin(frame, 0, frameCount - 1);
106 int row = frame / fCols;
107 int col = frame % fCols;
108 arrays.fRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
109 }
110 canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
111 count, SkBlendMode::kModulate, nullptr, paint);
112 }
113
visitFields(SkFieldVisitor * v)114 void visitFields(SkFieldVisitor* v) override {
115 SkString oldPath = fPath;
116
117 v->visit("Path", fPath);
118 v->visit("Columns", fCols);
119 v->visit("Rows", fRows);
120
121 fCols = SkTMax(fCols, 1);
122 fRows = SkTMax(fRows, 1);
123 if (oldPath != fPath) {
124 this->rebuild();
125 }
126 }
127
128 private:
129 SkString fPath;
130 int fCols;
131 int fRows;
132
getBaseRect() const133 SkRect getBaseRect() const {
134 return SkRect::MakeWH(static_cast<float>(fImage->width()) / fCols,
135 static_cast<float>(fImage->height() / fRows));
136 }
137
rebuild()138 void rebuild() {
139 fImage = SkImage::MakeFromEncoded(SkData::MakeFromFileName(fPath.c_str()));
140 if (!fImage) {
141 if (!fPath.isEmpty()) {
142 SkDebugf("Could not load image \"%s\"\n", fPath.c_str());
143 }
144 fImage = make_circle_image(1);
145 }
146 }
147
148 // Cached
149 sk_sp<SkImage> fImage;
150 };
151
RegisterDrawableTypes()152 void SkParticleDrawable::RegisterDrawableTypes() {
153 REGISTER_REFLECTED(SkParticleDrawable);
154 REGISTER_REFLECTED(SkCircleDrawable);
155 REGISTER_REFLECTED(SkImageDrawable);
156 }
157
MakeCircle(int radius)158 sk_sp<SkParticleDrawable> SkParticleDrawable::MakeCircle(int radius) {
159 return sk_sp<SkParticleDrawable>(new SkCircleDrawable(radius));
160 }
161
MakeImage(const SkString & path,int cols,int rows)162 sk_sp<SkParticleDrawable> SkParticleDrawable::MakeImage(const SkString& path, int cols, int rows) {
163 return sk_sp<SkParticleDrawable>(new SkImageDrawable(path, cols, rows));
164 }
165