c29810e882
Adding ovals reveals the clipping failure. The "actual" results with the oval clips are currently incorrect. Bug: skia:11837 Change-Id: I0789d5f758457425250ff6cecc0a5bb44f6e82ef Reviewed-on: https://skia-review.googlesource.com/c/skia/+/420637 Commit-Queue: Robert Phillips <robertphillips@google.com> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
195 lines
5.4 KiB
C++
195 lines
5.4 KiB
C++
// 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/ngatoy/Fake.h"
|
|
|
|
#include "experimental/ngatoy/Cmds.h"
|
|
#include "experimental/ngatoy/SortKey.h"
|
|
|
|
#include "include/core/SkBitmap.h"
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
void FakeMCBlob::MCState::addClip(sk_sp<ClipCmd> 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<ClipCmd>& c : fCmds) {
|
|
c->onAboutToBePopped(paintersOrderWhenPopped);
|
|
}
|
|
}
|
|
|
|
|
|
FakeMCBlob::FakeMCBlob(const std::vector<MCState>& 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<ClipCmd>& 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<FakeMCBlob> state = fTracker.snapState();
|
|
SkASSERT(state);
|
|
|
|
sk_sp<Cmd> tmp = sk_make_sp<DrawCmd>(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<ClipCmd> tmp = sk_make_sp<ClipCmd>(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<Cmd>& c : fSortedCmds) {
|
|
c->rasterize(fZBuffer, &fBM);
|
|
}
|
|
}
|
|
|
|
void FakeDevice::getOrder(std::vector<ID>* ops) const {
|
|
SkASSERT(fFinalized);
|
|
|
|
for (const sk_sp<Cmd>& 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<Cmd>& a, const sk_sp<Cmd>& 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<ID> FakeCanvas::getOrder() const {
|
|
SkASSERT(fFinalized);
|
|
|
|
std::vector<ID> ops;
|
|
|
|
for (auto& d : fDeviceStack) {
|
|
d->getOrder(&ops);
|
|
}
|
|
|
|
return ops;
|
|
}
|