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