2019-06-12 14:20:44 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "experimental/xform/SkShape.h"
|
|
|
|
#include "experimental/xform/SkXform.h"
|
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
|
|
|
|
#include "src/core/SkRasterClip.h"
|
|
|
|
|
|
|
|
class RasterClipCache : public ClipCache {
|
|
|
|
public:
|
|
|
|
RasterClipCache(const SkRasterClip& rc) : fRC(std::move(rc)) {}
|
|
|
|
|
|
|
|
SkRasterClip fRC;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const SkRasterClip& peek_rasterclip(ClipCache* clip) {
|
|
|
|
return ((RasterClipCache*)clip)->fRC;
|
|
|
|
}
|
|
|
|
|
|
|
|
class RasterXformResolver : public XformResolver {
|
|
|
|
public:
|
|
|
|
RasterXformResolver(const SkIRect& bounds)
|
|
|
|
: fBounds(bounds)
|
|
|
|
, fCTM(SkMatrix::I())
|
|
|
|
, fRC(bounds)
|
|
|
|
{}
|
|
|
|
|
|
|
|
RasterXformResolver(Xform* parent) {
|
|
|
|
const SkRasterClip& rc = peek_rasterclip(parent->clip());
|
|
|
|
fBounds = rc.getBounds();
|
|
|
|
fCTM = parent->ctm();
|
|
|
|
fRC = rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void concat(const SkMatrix& m) override {
|
|
|
|
fCTM.preConcat(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clipRect(const SkRect& r, SkClipOp op) override {
|
2021-08-31 14:51:50 +00:00
|
|
|
fRC.op(r, fCTM, op, false);
|
2019-06-12 14:20:44 +00:00
|
|
|
fCache.reset(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clipRRect(const SkRRect& rr, SkClipOp op) override {
|
2021-08-31 14:51:50 +00:00
|
|
|
fRC.op(rr, fCTM, op, false);
|
2019-06-12 14:20:44 +00:00
|
|
|
fCache.reset(nullptr);
|
|
|
|
}
|
|
|
|
void clipPath(const SkPath& p, SkClipOp op) override {
|
2021-08-31 14:51:50 +00:00
|
|
|
fRC.op(p, fCTM, op, false);
|
2019-06-12 14:20:44 +00:00
|
|
|
fCache.reset(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
const SkMatrix& ctm() const { return fCTM; }
|
|
|
|
|
|
|
|
sk_sp<ClipCache> snapCache() {
|
|
|
|
if (!fCache) {
|
|
|
|
fCache = sk_sp<ClipCache>(new RasterClipCache(fRC));
|
|
|
|
}
|
|
|
|
return fCache;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SkIRect fBounds;
|
|
|
|
SkMatrix fCTM;
|
|
|
|
SkRasterClip fRC;
|
|
|
|
sk_sp<ClipCache> fCache;
|
|
|
|
};
|
|
|
|
|
|
|
|
void XContext::drawRect(const SkRect& r, const SkPaint& p, Xform* x) {
|
|
|
|
this->onDrawRect(r, p, x);
|
|
|
|
}
|
|
|
|
|
|
|
|
class CanvasXContext : public XContext {
|
|
|
|
public:
|
|
|
|
CanvasXContext(SkCanvas* canvas) : fCanvas(canvas) {
|
|
|
|
fBounds = {
|
|
|
|
0, 0, canvas->getBaseLayerSize().width(), canvas->getBaseLayerSize().height()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static int count_nodes(const Xform* x) {
|
|
|
|
int n = 0;
|
|
|
|
for (; x; x = x->parent()) {
|
|
|
|
n += 1;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
void onPush(Xform* x) override {
|
|
|
|
int n = count_nodes(x);
|
|
|
|
fCounts.push_back(n);
|
|
|
|
if (n) {
|
|
|
|
int prevCount = fStack.count();
|
|
|
|
// now push the x tree such that we get [... grandparent, parent, x] in the array
|
|
|
|
Xform** ptr = fStack.append(n) + n;
|
|
|
|
Xform* xx = x;
|
|
|
|
while (n --> 0) {
|
|
|
|
*--ptr = xx;
|
|
|
|
xx = xx->parent();
|
|
|
|
}
|
|
|
|
// init with the old tail
|
|
|
|
if (prevCount > 0) {
|
|
|
|
RasterXformResolver res(fStack[prevCount - 1]);
|
|
|
|
for (int i = prevCount; i < fStack.count(); ++i) {
|
|
|
|
fStack[i]->visit(&res);
|
|
|
|
fStack[i]->setCache(res.ctm(), res.snapCache());
|
|
|
|
}
|
|
|
|
} else if (!x->genID()) {
|
|
|
|
RasterXformResolver res(fBounds);
|
|
|
|
for (int i = 0; i < fStack.count(); ++i) {
|
|
|
|
fStack[i]->visit(&res);
|
|
|
|
fStack[i]->setCache(res.ctm(), res.snapCache());
|
|
|
|
}
|
|
|
|
SkASSERT(x->genID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void onPop() override {
|
|
|
|
int n = fCounts.top();
|
|
|
|
fCounts.pop();
|
|
|
|
if (n) {
|
|
|
|
fStack.setCount(fStack.count() - n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void onDrawRect(const SkRect& r, const SkPaint& p, Xform* x) override {
|
|
|
|
Xform* parent = this->parentOrNull();
|
|
|
|
Xform::GenID parentID = parent ? parent->genID() : 0;
|
|
|
|
SkASSERT(parent == nullptr || parentID != 0);
|
|
|
|
|
|
|
|
if (x) {
|
|
|
|
SkASSERT(x->genID() != parentID || (x->genID() == 0 && parentID == 0));
|
|
|
|
if (x->genID() <= parentID) { // x is out of date
|
|
|
|
this->push(x); // will update caches
|
|
|
|
this->pop();
|
|
|
|
}
|
|
|
|
SkASSERT(x->genID() > parentID);
|
|
|
|
} else {
|
|
|
|
x = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkAutoCanvasRestore acr(fCanvas, false);
|
|
|
|
if (x) {
|
|
|
|
fCanvas->save();
|
|
|
|
fCanvas->concat(x->ctm());
|
|
|
|
fCanvas->clipRegion(peek_rasterclip(x->clip()).bwRgn());
|
|
|
|
}
|
|
|
|
fCanvas->drawRect(r, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SkTDArray<Xform*> fStack;
|
|
|
|
SkTDArray<int> fCounts;
|
|
|
|
|
|
|
|
SkCanvas* fCanvas; // bare pointer
|
|
|
|
SkIRect fBounds;
|
|
|
|
|
|
|
|
Xform* parentOrNull() {
|
|
|
|
return fStack.count() > 0 ? fStack.top() : nullptr;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<XContext> XContext::Make(SkCanvas* canvas) {
|
|
|
|
return std::unique_ptr<XContext>(new CanvasXContext(canvas));
|
|
|
|
}
|