• 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 "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