blob: 4f942b264cd0a9cbd01fc0458325ff8fee40aa99 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "modules/particles/include/SkParticleDrawable.h"
Brian Osman543d2e22019-02-15 14:29:38 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#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"
Brian Osman543d2e22019-02-15 14:29:38 -050019
20static 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
Brian Osman125daa42019-02-20 12:25:20 -050030struct DrawAtlasArrays {
31 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
Brian Osman543d2e22019-02-15 14:29:38 -050046class SkCircleDrawable : public SkParticleDrawable {
47public:
48 SkCircleDrawable(int radius = 1)
49 : fRadius(radius) {
50 this->rebuild();
51 }
52
53 REFLECTED(SkCircleDrawable, SkParticleDrawable)
54
Brian Osman125daa42019-02-20 12:25:20 -050055 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);
Brian Osman543d2e22019-02-15 14:29:38 -050059 for (int i = 0; i < count; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -050060 arrays.fRects[i].set(0.0f, 0.0f, fImage->width(), fImage->height());
Brian Osman543d2e22019-02-15 14:29:38 -050061 }
Brian Osman125daa42019-02-20 12:25:20 -050062 canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
63 count, SkBlendMode::kModulate, nullptr, paint);
Brian Osman543d2e22019-02-15 14:29:38 -050064 }
65
66 void visitFields(SkFieldVisitor* v) override {
67 v->visit("Radius", fRadius);
68 this->rebuild();
69 }
70
71private:
72 int fRadius;
73
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
85class SkImageDrawable : public SkParticleDrawable {
86public:
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
94 REFLECTED(SkImageDrawable, SkParticleDrawable)
95
Brian Osman125daa42019-02-20 12:25:20 -050096 void draw(SkCanvas* canvas, const SkParticleState particles[], int count,
97 const SkPaint* paint) override {
Brian Osman543d2e22019-02-15 14:29:38 -050098 SkRect baseRect = getBaseRect();
Brian Osman125daa42019-02-20 12:25:20 -050099 SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
100 DrawAtlasArrays arrays(particles, count, center);
Brian Osman543d2e22019-02-15 14:29:38 -0500101
Brian Osman125daa42019-02-20 12:25:20 -0500102 int frameCount = fCols * fRows;
Brian Osman543d2e22019-02-15 14:29:38 -0500103 for (int i = 0; i < count; ++i) {
Brian Osman125daa42019-02-20 12:25:20 -0500104 int frame = static_cast<int>(particles[i].fFrame * frameCount + 0.5f);
Brian Osman543d2e22019-02-15 14:29:38 -0500105 frame = SkTPin(frame, 0, frameCount - 1);
106 int row = frame / fCols;
107 int col = frame % fCols;
Brian Osman125daa42019-02-20 12:25:20 -0500108 arrays.fRects[i] = baseRect.makeOffset(col * baseRect.width(), row * baseRect.height());
Brian Osman543d2e22019-02-15 14:29:38 -0500109 }
Brian Osman125daa42019-02-20 12:25:20 -0500110 canvas->drawAtlas(fImage, arrays.fXforms.get(), arrays.fRects.get(), arrays.fColors.get(),
111 count, SkBlendMode::kModulate, nullptr, paint);
Brian Osman543d2e22019-02-15 14:29:38 -0500112 }
113
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
128private:
129 SkString fPath;
130 int fCols;
131 int fRows;
132
133 SkRect getBaseRect() const {
134 return SkRect::MakeWH(static_cast<float>(fImage->width()) / fCols,
135 static_cast<float>(fImage->height() / fRows));
136 }
137
138 void rebuild() {
Kevin Lubick96634842019-03-05 14:09:24 -0500139 fImage = SkImage::MakeFromEncoded(SkData::MakeFromFileName(fPath.c_str()));
Brian Osman543d2e22019-02-15 14:29:38 -0500140 if (!fImage) {
Kevin Lubick96634842019-03-05 14:09:24 -0500141 if (!fPath.isEmpty()) {
142 SkDebugf("Could not load image \"%s\"\n", fPath.c_str());
143 }
Brian Osman543d2e22019-02-15 14:29:38 -0500144 fImage = make_circle_image(1);
145 }
146 }
147
148 // Cached
149 sk_sp<SkImage> fImage;
150};
151
152void SkParticleDrawable::RegisterDrawableTypes() {
153 REGISTER_REFLECTED(SkParticleDrawable);
154 REGISTER_REFLECTED(SkCircleDrawable);
155 REGISTER_REFLECTED(SkImageDrawable);
156}
157
158sk_sp<SkParticleDrawable> SkParticleDrawable::MakeCircle(int radius) {
159 return sk_sp<SkParticleDrawable>(new SkCircleDrawable(radius));
160}
161
162sk_sp<SkParticleDrawable> SkParticleDrawable::MakeImage(const SkString& path, int cols, int rows) {
163 return sk_sp<SkParticleDrawable>(new SkImageDrawable(path, cols, rows));
164}