blob: e928fee74e9c68fce237e27047b72cb0f37a492c [file] [log] [blame]
// 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/Cmds.h"
#include "experimental/sorttoy/Fake.h"
#include "experimental/sorttoy/SortKey.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
#include "include/effects/SkGradientShader.h"
//------------------------------------------------------------------------------------------------
SortKey SaveCmd::getKey() {
SkASSERT(0);
return {};
}
void SaveCmd::execute(FakeCanvas* f) const {
f->save();
}
void SaveCmd::execute(SkCanvas* c) const {
c->save();
}
//------------------------------------------------------------------------------------------------
SortKey RestoreCmd::getKey() {
SkASSERT(0);
return {};
}
void RestoreCmd::execute(FakeCanvas* f) const {
f->restore();
}
void RestoreCmd::execute(SkCanvas* c) const {
c->restore();
}
//------------------------------------------------------------------------------------------------
DrawCmd::DrawCmd(ID id,
Shape shape,
SkIRect r,
const FakePaint& p)
: Cmd(id)
, fShape(shape)
, fRect(r)
, fPaint(p) {
}
DrawCmd::DrawCmd(ID id,
PaintersOrder paintersOrder,
Shape shape,
SkIRect r,
const FakePaint& p,
sk_sp<FakeMCBlob> state)
: Cmd(id)
, fPaintersOrder(paintersOrder)
, fShape(shape)
, fRect(r)
, fPaint(p)
, fMCState(std::move(state)) {
}
static bool shared_contains(int x, int y, Shape s, SkIRect r) {
if (s == Shape::kRect) {
return r.contains(x, y);
} else {
float a = r.width() / 2.0f; // horizontal radius
float b = r.height() / 2.0f; // vertical radius
float h = 0.5f * (r.fLeft + r.fRight); // center X
float k = 0.5f * (r.fTop + r.fBottom); // center Y
float xTerm = x + 0.5f - h;
float yTerm = y + 0.5f - k;
return (xTerm * xTerm) / (a * a) + (yTerm * yTerm) / (b * b) < 1.0f;
}
}
bool DrawCmd::contains(int x, int y) const {
return shared_contains(x, y, fShape, fRect);
}
uint32_t DrawCmd::getSortZ() const {
return fPaintersOrder.toUInt();
}
// Opaque and transparent draws both write their painter's index to the depth buffer
uint32_t DrawCmd::getDrawZ() const {
return fPaintersOrder.toUInt();
}
SortKey DrawCmd::getKey() {
return SortKey(fPaint.isTransparent(), this->getSortZ(), fPaint.toID());
}
void DrawCmd::execute(FakeCanvas* c) const {
c->drawShape(fID, fShape, fRect, fPaint);
}
void DrawCmd::execute(SkCanvas* c) const {
SkColor4f colors[2] = {
SkColor4f::FromColor(fPaint.c0()),
SkColor4f::FromColor(fPaint.c1())
};
SkPaint p;
if (fPaint.toID() == kSolidMat) {
p.setColor(fPaint.c0());
} else if (fPaint.toID() == kLinearMat) {
SkPoint pts[] = { { 0.0f, 0.0f, }, { 256.0f, 256.0f } };
p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, nullptr, 2,
SkTileMode::kClamp));
} else {
SkASSERT(fPaint.toID() == kRadialMat);
auto shader = SkGradientShader::MakeRadial(SkPoint::Make(128.0f, 128.0f),
128.0f,
colors,
nullptr,
nullptr,
2,
SkTileMode::kRepeat);
p.setShader(std::move(shader));
}
if (fShape == Shape::kRect) {
c->drawRect(SkRect::Make(fRect), p);
} else {
c->drawOval(SkRect::Make(fRect), p);
}
}
static bool is_opaque(SkColor c) {
return 0xFF == SkColorGetA(c);
}
void DrawCmd::rasterize(uint32_t zBuffer[256][256], SkBitmap* dstBM) const {
uint32_t z = this->getDrawZ();
SkIRect scissor = fMCState->scissor();
for (int y = fRect.fTop; y < fRect.fBottom; ++y) {
for (int x = fRect.fLeft; x < fRect.fRight; ++x) {
if (!scissor.contains(x, y)) {
continue;
}
if (!this->contains(x, y)) {
continue;
}
if (z > zBuffer[x][y]) {
zBuffer[x][y] = z;
SkColor c = fPaint.evalColor(x, y);
if (is_opaque(c)) {
*dstBM->getAddr32(x, y) = c;
} else {
SkColor4f bot = SkColor4f::FromColor(*dstBM->getAddr32(x, y));
SkColor4f top = SkColor4f::FromColor(c);
SkColor4f result = {
top.fA * top.fR + (1.0f - top.fA) * bot.fR,
top.fA * top.fG + (1.0f - top.fA) * bot.fG,
top.fA * top.fB + (1.0f - top.fA) * bot.fB,
top.fA + (1.0f - top.fA) * bot.fA
};
*dstBM->getAddr32(x, y) = result.toSkColor();
}
}
}
}
}
//------------------------------------------------------------------------------------------------
ClipCmd::ClipCmd(ID id, Shape shape, SkIRect r)
: Cmd(id)
, fShape(shape)
, fRect(r) {
}
ClipCmd::ClipCmd(ID id, PaintersOrder paintersOrderWhenAdded, Shape shape, SkIRect r)
: Cmd(id)
, fShape(shape)
, fRect(r)
, fPaintersOrderWhenAdded(paintersOrderWhenAdded) {
}
ClipCmd::~ClipCmd() {}
bool ClipCmd::contains(int x, int y) const {
return shared_contains(x, y, fShape, fRect);
}
uint32_t ClipCmd::getSortZ() const {
SkASSERT(fPaintersOrderWhenAdded.isValid());
return fPaintersOrderWhenAdded.toUInt();
}
// A clip writes the painter's index corresponding to when it's "popped" off the clip stack
uint32_t ClipCmd::getDrawZ() const {
SkASSERT(fPaintersOrderWhenPopped.isValid());
return fPaintersOrderWhenPopped.toUInt();
}
SortKey ClipCmd::getKey() {
return SortKey(false, this->getSortZ(), kInvalidMat);
}
void ClipCmd::onAboutToBePopped(PaintersOrder paintersOrderWhenPopped) {
SkASSERT(!fPaintersOrderWhenPopped.isValid() && paintersOrderWhenPopped.isValid());
fPaintersOrderWhenPopped = paintersOrderWhenPopped;
}
void ClipCmd::execute(FakeCanvas* c) const {
// This call is creating the 'real' ClipCmd for the "actual" case
SkASSERT(!fPaintersOrderWhenAdded.isValid() && !fPaintersOrderWhenPopped.isValid());
c->clipShape(fID, fShape, fRect);
}
void ClipCmd::execute(SkCanvas* c) const {
if (fShape == Shape::kRect) {
c->clipRect(SkRect::Make(fRect));
} else {
c->clipRRect(SkRRect::MakeOval(SkRect::Make(fRect)));
}
}
void ClipCmd::rasterize(uint32_t zBuffer[256][256], SkBitmap* /* dstBM */) const {
uint32_t drawZ = this->getDrawZ();
// TODO: limit this via the scissor!
for (int y = 0; y < 256; ++y) {
for (int x = 0; x < 256; ++x) {
if (!this->contains(x, y) && drawZ > zBuffer[x][y]) {
zBuffer[x][y] = drawZ;
}
}
}
}
//------------------------------------------------------------------------------------------------