1 // Copyright 2021 Google LLC.
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
4 #include "experimental/sorttoy/Cmds.h"
5 #include "experimental/sorttoy/Fake.h"
6 #include "experimental/sorttoy/SortKey.h"
7
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkRRect.h"
13 #include "include/effects/SkGradientShader.h"
14
15
16 //------------------------------------------------------------------------------------------------
getKey()17 SortKey SaveCmd::getKey() {
18 SkASSERT(0);
19 return {};
20 }
21
execute(FakeCanvas * f) const22 void SaveCmd::execute(FakeCanvas* f) const {
23 f->save();
24 }
25
execute(SkCanvas * c) const26 void SaveCmd::execute(SkCanvas* c) const {
27 c->save();
28 }
29
30 //------------------------------------------------------------------------------------------------
getKey()31 SortKey RestoreCmd::getKey() {
32 SkASSERT(0);
33 return {};
34 }
35
execute(FakeCanvas * f) const36 void RestoreCmd::execute(FakeCanvas* f) const {
37 f->restore();
38 }
39
execute(SkCanvas * c) const40 void RestoreCmd::execute(SkCanvas* c) const {
41 c->restore();
42 }
43
44 //------------------------------------------------------------------------------------------------
DrawCmd(ID id,Shape shape,SkIRect r,const FakePaint & p)45 DrawCmd::DrawCmd(ID id,
46 Shape shape,
47 SkIRect r,
48 const FakePaint& p)
49 : Cmd(id)
50 , fShape(shape)
51 , fRect(r)
52 , fPaint(p) {
53 }
54
DrawCmd(ID id,PaintersOrder paintersOrder,Shape shape,SkIRect r,const FakePaint & p,sk_sp<FakeMCBlob> state)55 DrawCmd::DrawCmd(ID id,
56 PaintersOrder paintersOrder,
57 Shape shape,
58 SkIRect r,
59 const FakePaint& p,
60 sk_sp<FakeMCBlob> state)
61 : Cmd(id)
62 , fPaintersOrder(paintersOrder)
63 , fShape(shape)
64 , fRect(r)
65 , fPaint(p)
66 , fMCState(std::move(state)) {
67 }
68
shared_contains(int x,int y,Shape s,SkIRect r)69 static bool shared_contains(int x, int y, Shape s, SkIRect r) {
70 if (s == Shape::kRect) {
71 return r.contains(x, y);
72 } else {
73 float a = r.width() / 2.0f; // horizontal radius
74 float b = r.height() / 2.0f; // vertical radius
75 float h = 0.5f * (r.fLeft + r.fRight); // center X
76 float k = 0.5f * (r.fTop + r.fBottom); // center Y
77
78 float xTerm = x + 0.5f - h;
79 float yTerm = y + 0.5f - k;
80
81 return (xTerm * xTerm) / (a * a) + (yTerm * yTerm) / (b * b) < 1.0f;
82 }
83 }
84
contains(int x,int y) const85 bool DrawCmd::contains(int x, int y) const {
86 return shared_contains(x, y, fShape, fRect);
87 }
88
getSortZ() const89 uint32_t DrawCmd::getSortZ() const {
90 return fPaintersOrder.toUInt();
91 }
92
93 // Opaque and transparent draws both write their painter's index to the depth buffer
getDrawZ() const94 uint32_t DrawCmd::getDrawZ() const {
95 return fPaintersOrder.toUInt();
96 }
97
getKey()98 SortKey DrawCmd::getKey() {
99 return SortKey(fPaint.isTransparent(), this->getSortZ(), fPaint.toID());
100 }
101
execute(FakeCanvas * c) const102 void DrawCmd::execute(FakeCanvas* c) const {
103 c->drawShape(fID, fShape, fRect, fPaint);
104 }
105
execute(SkCanvas * c) const106 void DrawCmd::execute(SkCanvas* c) const {
107
108 SkColor4f colors[2] = {
109 SkColor4f::FromColor(fPaint.c0()),
110 SkColor4f::FromColor(fPaint.c1())
111 };
112
113 SkPaint p;
114 if (fPaint.toID() == kSolidMat) {
115 p.setColor(fPaint.c0());
116 } else if (fPaint.toID() == kLinearMat) {
117 SkPoint pts[] = { { 0.0f, 0.0f, }, { 256.0f, 256.0f } };
118 p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, nullptr, 2,
119 SkTileMode::kClamp));
120 } else {
121 SkASSERT(fPaint.toID() == kRadialMat);
122
123 auto shader = SkGradientShader::MakeRadial(SkPoint::Make(128.0f, 128.0f),
124 128.0f,
125 colors,
126 nullptr,
127 nullptr,
128 2,
129 SkTileMode::kRepeat);
130 p.setShader(std::move(shader));
131 }
132
133 if (fShape == Shape::kRect) {
134 c->drawRect(SkRect::Make(fRect), p);
135 } else {
136 c->drawOval(SkRect::Make(fRect), p);
137 }
138 }
139
is_opaque(SkColor c)140 static bool is_opaque(SkColor c) {
141 return 0xFF == SkColorGetA(c);
142 }
143
rasterize(uint32_t zBuffer[256][256],SkBitmap * dstBM) const144 void DrawCmd::rasterize(uint32_t zBuffer[256][256], SkBitmap* dstBM) const {
145
146 uint32_t z = this->getDrawZ();
147 SkIRect scissor = fMCState->scissor();
148
149 for (int y = fRect.fTop; y < fRect.fBottom; ++y) {
150 for (int x = fRect.fLeft; x < fRect.fRight; ++x) {
151 if (!scissor.contains(x, y)) {
152 continue;
153 }
154
155 if (!this->contains(x, y)) {
156 continue;
157 }
158
159 if (z > zBuffer[x][y]) {
160 zBuffer[x][y] = z;
161
162 SkColor c = fPaint.evalColor(x, y);
163
164 if (is_opaque(c)) {
165 *dstBM->getAddr32(x, y) = c;
166 } else {
167 SkColor4f bot = SkColor4f::FromColor(*dstBM->getAddr32(x, y));
168 SkColor4f top = SkColor4f::FromColor(c);
169 SkColor4f result = {
170 top.fA * top.fR + (1.0f - top.fA) * bot.fR,
171 top.fA * top.fG + (1.0f - top.fA) * bot.fG,
172 top.fA * top.fB + (1.0f - top.fA) * bot.fB,
173 top.fA + (1.0f - top.fA) * bot.fA
174 };
175 *dstBM->getAddr32(x, y) = result.toSkColor();
176 }
177 }
178 }
179 }
180 }
181
182 //------------------------------------------------------------------------------------------------
ClipCmd(ID id,Shape shape,SkIRect r)183 ClipCmd::ClipCmd(ID id, Shape shape, SkIRect r)
184 : Cmd(id)
185 , fShape(shape)
186 , fRect(r) {
187 }
188
ClipCmd(ID id,PaintersOrder paintersOrderWhenAdded,Shape shape,SkIRect r)189 ClipCmd::ClipCmd(ID id, PaintersOrder paintersOrderWhenAdded, Shape shape, SkIRect r)
190 : Cmd(id)
191 , fShape(shape)
192 , fRect(r)
193 , fPaintersOrderWhenAdded(paintersOrderWhenAdded) {
194 }
195
~ClipCmd()196 ClipCmd::~ClipCmd() {}
197
contains(int x,int y) const198 bool ClipCmd::contains(int x, int y) const {
199 return shared_contains(x, y, fShape, fRect);
200 }
201
getSortZ() const202 uint32_t ClipCmd::getSortZ() const {
203 SkASSERT(fPaintersOrderWhenAdded.isValid());
204
205 return fPaintersOrderWhenAdded.toUInt();
206 }
207
208 // A clip writes the painter's index corresponding to when it's "popped" off the clip stack
getDrawZ() const209 uint32_t ClipCmd::getDrawZ() const {
210 SkASSERT(fPaintersOrderWhenPopped.isValid());
211
212 return fPaintersOrderWhenPopped.toUInt();
213 }
214
getKey()215 SortKey ClipCmd::getKey() {
216 return SortKey(false, this->getSortZ(), kInvalidMat);
217 }
218
onAboutToBePopped(PaintersOrder paintersOrderWhenPopped)219 void ClipCmd::onAboutToBePopped(PaintersOrder paintersOrderWhenPopped) {
220 SkASSERT(!fPaintersOrderWhenPopped.isValid() && paintersOrderWhenPopped.isValid());
221 fPaintersOrderWhenPopped = paintersOrderWhenPopped;
222 }
223
execute(FakeCanvas * c) const224 void ClipCmd::execute(FakeCanvas* c) const {
225 // This call is creating the 'real' ClipCmd for the "actual" case
226 SkASSERT(!fPaintersOrderWhenAdded.isValid() && !fPaintersOrderWhenPopped.isValid());
227
228 c->clipShape(fID, fShape, fRect);
229 }
230
execute(SkCanvas * c) const231 void ClipCmd::execute(SkCanvas* c) const {
232 if (fShape == Shape::kRect) {
233 c->clipRect(SkRect::Make(fRect));
234 } else {
235 c->clipRRect(SkRRect::MakeOval(SkRect::Make(fRect)));
236 }
237 }
238
rasterize(uint32_t zBuffer[256][256],SkBitmap *) const239 void ClipCmd::rasterize(uint32_t zBuffer[256][256], SkBitmap* /* dstBM */) const {
240 uint32_t drawZ = this->getDrawZ();
241
242 // TODO: limit this via the scissor!
243 for (int y = 0; y < 256; ++y) {
244 for (int x = 0; x < 256; ++x) {
245 if (!this->contains(x, y) && drawZ > zBuffer[x][y]) {
246 zBuffer[x][y] = drawZ;
247 }
248 }
249 }
250 }
251
252 //------------------------------------------------------------------------------------------------
253