blob: 0adc87dd0e07447a8351dc4e2986232d56ba810a [file] [log] [blame]
Brian Osman543d2e22019-02-15 14:29:38 -05001/*
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 "Resources.h"
11#include "SkAutoMalloc.h"
12#include "SkCanvas.h"
13#include "SkImage.h"
14#include "SkPaint.h"
Brian Osman125daa42019-02-20 12:25:20 -050015#include "SkParticleData.h"
Brian Osman543d2e22019-02-15 14:29:38 -050016#include "SkRect.h"
17#include "SkSurface.h"
18#include "SkString.h"
19#include "SkRSXform.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
Brian Osman125daa42019-02-20 12:25:20 -050031struct DrawAtlasArrays {
32 DrawAtlasArrays(const SkParticleState particles[], int count, SkPoint center)
33 : fXforms(count)
34 , fRects(count)
35 , fColors(count) {
36 for (int i = 0; i < count; ++i) {
37 fXforms[i] = particles[i].fPose.asRSXform(center);
38 fColors[i] = particles[i].fColor.toSkColor();
39 }
40 }
41
42 SkAutoTMalloc<SkRSXform> fXforms;
43 SkAutoTMalloc<SkRect> fRects;
44 SkAutoTMalloc<SkColor> fColors;
45};
46
Brian Osman543d2e22019-02-15 14:29:38 -050047class SkCircleDrawable : public SkParticleDrawable {
48public:
49 SkCircleDrawable(int radius = 1)
50 : fRadius(radius) {
51 this->rebuild();
52 }
53
54 REFLECTED(SkCircleDrawable, SkParticleDrawable)
55
Brian Osman125daa42019-02-20 12:25:20 -050056 void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
57 const SkPaint* paint) override {
58 SkPoint center = { SkIntToScalar(fRadius), SkIntToScalar(fRadius) };
59 DrawAtlasArrays arrays(particles, count, center);
Brian Osman543d2e22019-02-15 14:29:38 -050060 for (int i = 0; i < count; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -050061 arrays.fRects[i].set(0.0f, 0.0f, fImage->width(), fImage->height());
Brian Osman543d2e22019-02-15 14:29:38 -050062 }
Brian Osman125daa42019-02-20 12:25:20 -050063 canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
64 count, SkBlendMode::kModulate, nullptr, paint);
Brian Osman543d2e22019-02-15 14:29:38 -050065 }
66
67 void visitFields(SkFieldVisitor* v) override {
68 v->visit("Radius", fRadius);
69 this->rebuild();
70 }
71
72private:
73 int fRadius;
74
75 void rebuild() {
76 fRadius = SkTMax(fRadius, 1);
77 if (!fImage || fImage->width() != 2 * fRadius) {
78 fImage = make_circle_image(fRadius);
79 }
80 }
81
82 // Cached
83 sk_sp<SkImage> fImage;
84};
85
86class SkImageDrawable : public SkParticleDrawable {
87public:
88 SkImageDrawable(const SkString& path = SkString(), int cols = 1, int rows = 1)
89 : fPath(path)
90 , fCols(cols)
91 , fRows(rows) {
92 this->rebuild();
93 }
94
95 REFLECTED(SkImageDrawable, SkParticleDrawable)
96
Brian Osman125daa42019-02-20 12:25:20 -050097 void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
98 const SkPaint* paint) override {
Brian Osman543d2e22019-02-15 14:29:38 -050099 SkRect baseRect = getBaseRect();
Brian Osman125daa42019-02-20 12:25:20 -0500100 SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
101 DrawAtlasArrays arrays(particles, count, center);
Brian Osman543d2e22019-02-15 14:29:38 -0500102
Brian Osman125daa42019-02-20 12:25:20 -0500103 int frameCount = fCols * fRows;
Brian Osman543d2e22019-02-15 14:29:38 -0500104 for (int i = 0; i < count; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -0500105 int frame = static_cast<int>(particles[i].fFrame * frameCount + 0.5f);
Brian Osman543d2e22019-02-15 14:29:38 -0500106 frame = SkTPin(frame, 0, frameCount - 1);
107 int row = frame / fCols;
108 int col = frame % fCols;
Brian Osman125daa42019-02-20 12:25:20 -0500109 arrays.fRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
Brian Osman543d2e22019-02-15 14:29:38 -0500110 }
Brian Osman125daa42019-02-20 12:25:20 -0500111 canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
112 count, SkBlendMode::kModulate, nullptr, paint);
Brian Osman543d2e22019-02-15 14:29:38 -0500113 }
114
115 void visitFields(SkFieldVisitor* v) override {
116 SkString oldPath = fPath;
117
118 v->visit("Path", fPath);
119 v->visit("Columns", fCols);
120 v->visit("Rows", fRows);
121
122 fCols = SkTMax(fCols, 1);
123 fRows = SkTMax(fRows, 1);
124 if (oldPath != fPath) {
125 this->rebuild();
126 }
127 }
128
129private:
130 SkString fPath;
131 int fCols;
132 int fRows;
133
134 SkRect getBaseRect() const {
135 return SkRect::MakeWH(static_cast<float>(fImage->width()) / fCols,
136 static_cast<float>(fImage->height() / fRows));
137 }
138
139 void rebuild() {
140 fImage = GetResourceAsImage(fPath.c_str());
141 if (!fImage) {
142 fImage = make_circle_image(1);
143 }
144 }
145
146 // Cached
147 sk_sp<SkImage> fImage;
148};
149
150void SkParticleDrawable::RegisterDrawableTypes() {
151 REGISTER_REFLECTED(SkParticleDrawable);
152 REGISTER_REFLECTED(SkCircleDrawable);
153 REGISTER_REFLECTED(SkImageDrawable);
154}
155
156sk_sp<SkParticleDrawable> SkParticleDrawable::MakeCircle(int radius) {
157 return sk_sp<SkParticleDrawable>(new SkCircleDrawable(radius));
158}
159
160sk_sp<SkParticleDrawable> SkParticleDrawable::MakeImage(const SkString& path, int cols, int rows) {
161 return sk_sp<SkParticleDrawable>(new SkImageDrawable(path, cols, rows));
162}