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/Fake.h"
5
6 #include "experimental/sorttoy/Cmds.h"
7 #include "experimental/sorttoy/SortKey.h"
8
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11
12 //-------------------------------------------------------------------------------------------------
addClip(sk_sp<ClipCmd> clipCmd)13 void FakeMCBlob::MCState::addClip(sk_sp<ClipCmd> clipCmd) {
14 clipCmd->mutate(fTrans);
15 fCmds.push_back(std::move(clipCmd));
16 fCached = nullptr;
17 }
18
operator ==(const MCState & other) const19 bool FakeMCBlob::MCState::operator==(const MCState& other) const {
20 if (fTrans != other.fTrans || fCmds.size() != other.fCmds.size()) {
21 return false;
22 }
23
24 for (size_t i = 0; i < fCmds.size(); ++i) {
25 if (fCmds[i]->rect() != other.fCmds[i]->rect()) {
26 return false;
27 }
28 }
29
30 return true;
31 }
32
aboutToBePopped(PaintersOrder paintersOrderWhenPopped)33 void FakeMCBlob::MCState::aboutToBePopped(PaintersOrder paintersOrderWhenPopped) {
34 for (sk_sp<ClipCmd>& c : fCmds) {
35 c->onAboutToBePopped(paintersOrderWhenPopped);
36 }
37 }
38
39
FakeMCBlob(const std::vector<MCState> & stack)40 FakeMCBlob::FakeMCBlob(const std::vector<MCState>& stack) : fID(NextID()), fStack(stack) {
41 fScissor = SkIRect::MakeLTRB(-1000, -1000, 1000, 1000);
42
43 for (MCState& s : fStack) {
44 // xform the clip rects into device space to compute the scissor
45 for (const sk_sp<ClipCmd>& c : s.fCmds) {
46 SkASSERT(c->hasBeenMutated());
47 SkIRect r = c->rect();
48 r.offset(fCTM);
49 if (!fScissor.intersect(r)) {
50 fScissor.setEmpty();
51 }
52 }
53 fCTM += s.getTrans();
54 }
55 }
56
57 //-------------------------------------------------------------------------------------------------
58 // Linearly blend between c0 & c1:
59 // (t == 0) -> c0
60 // (t == 1) -> c1
blend(float t,SkColor c0,SkColor c1)61 static SkColor blend(float t, SkColor c0, SkColor c1) {
62 SkASSERT(t >= 0.0f && t <= 1.0f);
63
64 SkColor4f top = SkColor4f::FromColor(c0);
65 SkColor4f bot = SkColor4f::FromColor(c1);
66
67 SkColor4f result = {
68 t * bot.fR + (1.0f - t) * top.fR,
69 t * bot.fG + (1.0f - t) * top.fG,
70 t * bot.fB + (1.0f - t) * top.fB,
71 t * bot.fA + (1.0f - t) * top.fA
72 };
73 return result.toSkColor();
74 }
75
toID() const76 int FakePaint::toID() const {
77 switch (fType) {
78 case Type::kNormal: return kSolidMat;
79 case Type::kLinear: return kLinearMat;
80 case Type::kRadial: return kRadialMat;
81 }
82 SkUNREACHABLE;
83 }
84
evalColor(int x,int y) const85 SkColor FakePaint::evalColor(int x, int y) const {
86 switch (fType) {
87 case Type::kNormal: return fColor0;
88 case Type::kLinear: {
89 float t = SK_ScalarRoot2Over2 * x + SK_ScalarRoot2Over2 * y;
90 t /= SK_ScalarSqrt2 * 256.0f;
91 return blend(t, fColor0, fColor1);
92 }
93 case Type::kRadial: {
94 x -= 128;
95 y -= 128;
96 float dist = sqrt(x*x + y*y) / 128.0f;
97 if (dist > 1.0f) {
98 return fColor0;
99 } else {
100 return blend(dist, fColor0, fColor1);
101 }
102 }
103 }
104 SkUNREACHABLE;
105 }
106
107 //-------------------------------------------------------------------------------------------------
save()108 void FakeDevice::save() {
109 fTracker.push();
110 }
111
drawShape(ID id,PaintersOrder paintersOrder,Shape shape,SkIRect r,FakePaint p)112 void FakeDevice::drawShape(ID id, PaintersOrder paintersOrder, Shape shape, SkIRect r, FakePaint p) {
113 sk_sp<FakeMCBlob> state = fTracker.snapState();
114 SkASSERT(state);
115
116 sk_sp<Cmd> tmp = sk_make_sp<DrawCmd>(id, paintersOrder, shape, r, p, std::move(state));
117
118 fSortedCmds.push_back(std::move(tmp));
119 }
120
clipShape(ID id,PaintersOrder paintersOrder,Shape shape,SkIRect r)121 void FakeDevice::clipShape(ID id, PaintersOrder paintersOrder, Shape shape, SkIRect r) {
122 sk_sp<ClipCmd> tmp = sk_make_sp<ClipCmd>(id, paintersOrder, shape, r);
123
124 fTracker.clip(std::move(tmp));
125 }
126
restore(PaintersOrder paintersOrderWhenPopped)127 void FakeDevice::restore(PaintersOrder paintersOrderWhenPopped) {
128 fTracker.pop(paintersOrderWhenPopped);
129 }
130
finalize()131 void FakeDevice::finalize() {
132 SkASSERT(!fFinalized);
133 fFinalized = true;
134
135 this->sort();
136 for (const sk_sp<Cmd>& c : fSortedCmds) {
137 c->rasterize(fZBuffer, &fBM);
138 }
139 }
140
getOrder(std::vector<ID> * ops) const141 void FakeDevice::getOrder(std::vector<ID>* ops) const {
142 SkASSERT(fFinalized);
143
144 for (const sk_sp<Cmd>& c : fSortedCmds) {
145 ops->push_back(c->id());
146 }
147 }
148
sort()149 void FakeDevice::sort() {
150 // In general we want:
151 // opaque draws to occur front to back (i.e., in reverse painter's order) while minimizing
152 // state changes due to materials
153 // transparent draws to occur back to front (i.e., in painter's order)
154 //
155 // In both scenarios we would like to batch as much as possible.
156 std::sort(fSortedCmds.begin(), fSortedCmds.end(),
157 [](const sk_sp<Cmd>& a, const sk_sp<Cmd>& b) {
158 return a->getKey() < b->getKey();
159 });
160 }
161
162 //-------------------------------------------------------------------------------------------------
drawShape(ID id,Shape shape,SkIRect r,FakePaint p)163 void FakeCanvas::drawShape(ID id, Shape shape, SkIRect r, FakePaint p) {
164 SkASSERT(!fFinalized);
165
166 fDeviceStack.back()->drawShape(id, this->nextPaintersOrder(), shape, r, p);
167 }
168
clipShape(ID id,Shape shape,SkIRect r)169 void FakeCanvas::clipShape(ID id, Shape shape, SkIRect r) {
170 SkASSERT(!fFinalized);
171
172 fDeviceStack.back()->clipShape(id, this->nextPaintersOrder(), shape, r);
173 }
174
finalize()175 void FakeCanvas::finalize() {
176 SkASSERT(!fFinalized);
177 fFinalized = true;
178
179 for (auto& d : fDeviceStack) {
180 d->finalize();
181 }
182 }
183
getOrder() const184 std::vector<ID> FakeCanvas::getOrder() const {
185 SkASSERT(fFinalized);
186
187 std::vector<ID> ops;
188
189 for (auto& d : fDeviceStack) {
190 d->getOrder(&ops);
191 }
192
193 return ops;
194 }
195