// Copyright 2021 Google LLC. // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. #include "experimental/sorttoy/Fake.h" #include "experimental/sorttoy/Cmds.h" #include "experimental/sorttoy/SortKey.h" #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" //------------------------------------------------------------------------------------------------- void FakeMCBlob::MCState::addClip(sk_sp clipCmd) { clipCmd->mutate(fTrans); fCmds.push_back(std::move(clipCmd)); fCached = nullptr; } bool FakeMCBlob::MCState::operator==(const MCState& other) const { if (fTrans != other.fTrans || fCmds.size() != other.fCmds.size()) { return false; } for (size_t i = 0; i < fCmds.size(); ++i) { if (fCmds[i]->rect() != other.fCmds[i]->rect()) { return false; } } return true; } void FakeMCBlob::MCState::aboutToBePopped(PaintersOrder paintersOrderWhenPopped) { for (sk_sp& c : fCmds) { c->onAboutToBePopped(paintersOrderWhenPopped); } } FakeMCBlob::FakeMCBlob(const std::vector& stack) : fID(NextID()), fStack(stack) { fScissor = SkIRect::MakeLTRB(-1000, -1000, 1000, 1000); for (MCState& s : fStack) { // xform the clip rects into device space to compute the scissor for (const sk_sp& c : s.fCmds) { SkASSERT(c->hasBeenMutated()); SkIRect r = c->rect(); r.offset(fCTM); if (!fScissor.intersect(r)) { fScissor.setEmpty(); } } fCTM += s.getTrans(); } } //------------------------------------------------------------------------------------------------- // Linearly blend between c0 & c1: // (t == 0) -> c0 // (t == 1) -> c1 static SkColor blend(float t, SkColor c0, SkColor c1) { SkASSERT(t >= 0.0f && t <= 1.0f); SkColor4f top = SkColor4f::FromColor(c0); SkColor4f bot = SkColor4f::FromColor(c1); SkColor4f result = { t * bot.fR + (1.0f - t) * top.fR, t * bot.fG + (1.0f - t) * top.fG, t * bot.fB + (1.0f - t) * top.fB, t * bot.fA + (1.0f - t) * top.fA }; return result.toSkColor(); } int FakePaint::toID() const { switch (fType) { case Type::kNormal: return kSolidMat; case Type::kLinear: return kLinearMat; case Type::kRadial: return kRadialMat; } SkUNREACHABLE; } SkColor FakePaint::evalColor(int x, int y) const { switch (fType) { case Type::kNormal: return fColor0; case Type::kLinear: { float t = SK_ScalarRoot2Over2 * x + SK_ScalarRoot2Over2 * y; t /= SK_ScalarSqrt2 * 256.0f; return blend(t, fColor0, fColor1); } case Type::kRadial: { x -= 128; y -= 128; float dist = sqrt(x*x + y*y) / 128.0f; if (dist > 1.0f) { return fColor0; } else { return blend(dist, fColor0, fColor1); } } } SkUNREACHABLE; } //------------------------------------------------------------------------------------------------- void FakeDevice::save() { fTracker.push(); } void FakeDevice::drawShape(ID id, PaintersOrder paintersOrder, Shape shape, SkIRect r, FakePaint p) { sk_sp state = fTracker.snapState(); SkASSERT(state); sk_sp tmp = sk_make_sp(id, paintersOrder, shape, r, p, std::move(state)); fSortedCmds.push_back(std::move(tmp)); } void FakeDevice::clipShape(ID id, PaintersOrder paintersOrder, Shape shape, SkIRect r) { sk_sp tmp = sk_make_sp(id, paintersOrder, shape, r); fTracker.clip(std::move(tmp)); } void FakeDevice::restore(PaintersOrder paintersOrderWhenPopped) { fTracker.pop(paintersOrderWhenPopped); } void FakeDevice::finalize() { SkASSERT(!fFinalized); fFinalized = true; this->sort(); for (const sk_sp& c : fSortedCmds) { c->rasterize(fZBuffer, &fBM); } } void FakeDevice::getOrder(std::vector* ops) const { SkASSERT(fFinalized); for (const sk_sp& c : fSortedCmds) { ops->push_back(c->id()); } } void FakeDevice::sort() { // In general we want: // opaque draws to occur front to back (i.e., in reverse painter's order) while minimizing // state changes due to materials // transparent draws to occur back to front (i.e., in painter's order) // // In both scenarios we would like to batch as much as possible. std::sort(fSortedCmds.begin(), fSortedCmds.end(), [](const sk_sp& a, const sk_sp& b) { return a->getKey() < b->getKey(); }); } //------------------------------------------------------------------------------------------------- void FakeCanvas::drawShape(ID id, Shape shape, SkIRect r, FakePaint p) { SkASSERT(!fFinalized); fDeviceStack.back()->drawShape(id, this->nextPaintersOrder(), shape, r, p); } void FakeCanvas::clipShape(ID id, Shape shape, SkIRect r) { SkASSERT(!fFinalized); fDeviceStack.back()->clipShape(id, this->nextPaintersOrder(), shape, r); } void FakeCanvas::finalize() { SkASSERT(!fFinalized); fFinalized = true; for (auto& d : fDeviceStack) { d->finalize(); } } std::vector FakeCanvas::getOrder() const { SkASSERT(fFinalized); std::vector ops; for (auto& d : fDeviceStack) { d->getOrder(&ops); } return ops; }