try resolver pattern

Change-Id: I32c5dbf4cacce9112df42a6f29800859625dc4a9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/218965
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
Auto-Submit: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2019-06-10 16:41:41 -04:00 committed by Skia Commit-Bot
parent bba5aa761c
commit a716809d5a
7 changed files with 650 additions and 0 deletions

View File

@ -21,6 +21,7 @@ declare_args() {
skia_use_angle = false
skia_use_egl = false
skia_use_expat = true
skia_use_experimental_xform = false
skia_use_ffmpeg = false
skia_use_fontconfig = is_linux
skia_use_fonthost_mac = is_mac
@ -1749,6 +1750,17 @@ if (skia_enable_tools) {
}
}
test_lib("experimental_xform") {
sources = [
"experimental/xform/SkShape.cpp",
"experimental/xform/SkXform.cpp",
"experimental/xform/XContext.cpp",
]
deps = [
":skia",
]
}
if (skia_use_lua) {
test_lib("lua") {
sources = [
@ -2377,6 +2389,10 @@ if (skia_enable_tools) {
"modules/sksg:samples",
"//third_party/imgui",
]
if (skia_use_experimental_xform) {
deps += [ ":experimental_xform" ]
sources += [ "gm/xform.cpp" ]
}
}
if (!skia_use_angle && (is_linux || is_win || is_mac)) {

View File

@ -0,0 +1,26 @@
/*
* 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"
void GeoShape::draw(XContext* ctx) {
ctx->drawRect(fRect, fPaint, this->xform());
}
void GroupShape::draw(XContext* ctx) {
if (fArray.count() == 0) {
return;
}
ctx->push(this->xform());
for (auto s : fArray) {
s->draw(ctx);
}
ctx->pop();
}

View File

@ -0,0 +1,104 @@
/*
* Copyright 2019 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkShape_DEFINED
#define SkShape_DEFINED
#include "experimental/xform/SkXform.h"
#include "include/core/SkPaint.h"
class SkCanvas;
class XContext {
public:
virtual ~XContext() {}
void push(Xform* parentXform) { this->onPush(parentXform); }
void pop() { this->onPop(); }
void drawRect(const SkRect&, const SkPaint&, Xform* localXform);
static std::unique_ptr<XContext> Make(SkCanvas*);
protected:
virtual void onPush(Xform*) = 0;
virtual void onPop() = 0;
virtual void onDrawRect(const SkRect&, const SkPaint&, Xform*) = 0;
};
class Shape : public SkRefCnt {
sk_sp<Xform> fXform;
public:
Shape(sk_sp<Xform> x = nullptr) : fXform(std::move(x)) {}
Xform* xform() const { return fXform.get(); }
void setXform(sk_sp<Xform> x) {
fXform = std::move(x);
}
virtual void draw(XContext*) {}
};
class GeoShape : public Shape {
SkRect fRect;
SkPaint fPaint;
GeoShape(sk_sp<Xform> x, const SkRect& r, SkColor c) : Shape(std::move(x)), fRect(r) {
fPaint.setColor(c);
}
public:
static sk_sp<Shape> Make(sk_sp<Xform> x, const SkRect& r, SkColor c) {
return sk_sp<Shape>(new GeoShape(std::move(x), r, c));
}
void draw(XContext*) override;
};
class GroupShape : public Shape {
SkTDArray<Shape*> fArray;
GroupShape(sk_sp<Xform> x) : Shape(std::move(x)) {}
public:
static sk_sp<GroupShape> Make(sk_sp<Xform> x = nullptr) {
return sk_sp<GroupShape>(new GroupShape(std::move(x)));
}
static sk_sp<GroupShape> Make(sk_sp<Xform> x, sk_sp<Shape> s) {
auto g = sk_sp<GroupShape>(new GroupShape(std::move(x)));
g->append(std::move(s));
return g;
}
~GroupShape() override {
fArray.unrefAll();
}
int count() const { return fArray.count(); }
Shape* get(int index) const { return fArray[index]; }
void set(int index, sk_sp<Shape> s) {
fArray[index] = s.release();
}
void append(sk_sp<Shape> s) {
*fArray.append() = s.release();
}
void insert(int index, sk_sp<Shape> s) {
*fArray.insert(index) = s.release();
}
void remove(int index) {
SkSafeUnref(fArray[index]);
fArray.remove(index);
}
void draw(XContext*) override ;
};
#endif

View File

@ -0,0 +1,87 @@
/*
* 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/SkXform.h"
static std::atomic<uint32_t> gGenID{1};
Xform::GenID Xform::NextGenID() {
return gGenID++;
}
#ifdef SK_DEBUG
void Xform::debugValidate() const {
if (this->isCached() && fParent) {
SkASSERT(fParent->isCached());
}
for (auto c : fChildren) {
SkASSERT(c->parent() == this);
c->debugValidate();
}
}
#endif
void Xform::setParent(sk_sp<Xform> parent) {
if (parent == fParent) {
return;
}
if (fParent) {
fParent->internalRemoveChild(this);
}
if (parent) {
parent->internalAddChild(this);
}
fParent = std::move(parent);
// Potentially we could skip this if knew that our old and new parents
// were both cached, and they started us in the same state...
// For now, we conservatively always inval
this->invalidateCaches();
this->debugValidate();
}
void Xform::internalAddChild(Xform* child) {
SkASSERT(fChildren.find(child) < 0);
fChildren.push_back(child);
}
void Xform::internalRemoveChild(Xform* child) {
int index = fChildren.find(child);
SkASSERT(index >= 0);
fChildren.removeShuffle(index);
}
void Xform::invalidateCaches() {
fGenID = 0;
if (this->isCached()) {
this->internalInvalidateCaches();
for (auto c : fChildren) {
c->invalidateCaches();
}
}
}
void Xform::visit(XformResolver* resolver) {
this->onVisit(resolver);
}
void Xform::setCache(const SkMatrix& ctm, sk_sp<ClipCache> clip) {
fCTM = ctm;
fClip = std::move(clip);
fGenID = NextGenID();
}
//////////////////////////////////////////////////////////////////////////////////////////////////
void MatrixXF::onVisit(XformResolver* resolver) {
resolver->concat(fLocalMatrix);
}
void ClipXF::onVisit(XformResolver* resolver) {
resolver->clipRect(fRect, fOp);
}

View File

@ -0,0 +1,142 @@
/*
* Copyright 2019 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkXform_DEFINED
#define SkXform_DEFINED
#include "include/core/SkRefCnt.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkClipOp.h"
#include "include/core/SkRect.h"
#include "include/core/SkRRect.h"
#include "include/core/SkPath.h"
#include "include/private/SkTDArray.h"
class XformResolver {
public:
virtual ~XformResolver() {}
virtual void concat(const SkMatrix&) = 0;
virtual void clipRect(const SkRect&, SkClipOp) = 0;
virtual void clipRRect(const SkRRect&, SkClipOp) = 0;
virtual void clipPath(const SkPath&, SkClipOp) = 0;
};
class ClipCache : public SkRefCnt {
public:
ClipCache() {}
};
class Xform : public SkRefCnt {
public:
typedef uint32_t GenID;
Xform* parent() const { return fParent.get(); }
void setParent(sk_sp<Xform> p);
void visit(XformResolver* resolver);
GenID genID() const { return fGenID; }
bool isCached() const { return !!fClip; }
void invalidateCaches();
const SkMatrix& ctm() const { return fCTM; }
ClipCache* clip() const { return fClip.get(); }
void setCache(const SkMatrix&, sk_sp<ClipCache>);
protected:
Xform(sk_sp<Xform> parent = nullptr) {
if (parent) {
this->setParent(std::move(parent));
}
}
virtual void onVisit(XformResolver*) {}
private:
sk_sp<Xform> fParent;
// unowned bare pointers
SkTDArray<Xform*> fChildren;
// cache
SkMatrix fCTM;
sk_sp<ClipCache> fClip;
uint32_t fGenID = 0;
static GenID NextGenID();
void internalInvalidateCaches() { fClip = nullptr; }
void internalAddChild(Xform*);
void internalRemoveChild(Xform*);
#ifdef SK_DEBUG
void debugValidate() const;
#else
void debugValidate() const {}
#endif
};
///////////////////////////////////////////////////////////////////////////////////////////////
class MatrixXF : public Xform {
public:
static sk_sp<MatrixXF> Make(sk_sp<Xform> parent = nullptr) {
return sk_sp<MatrixXF>(new MatrixXF(std::move(parent)));
}
MatrixXF(sk_sp<Xform> parent) : Xform(std::move(parent)) {
fLocalMatrix.reset();
}
void setLocalMatrix(const SkMatrix& m) {
fLocalMatrix = m;
}
void setTranslate(SkScalar sx, SkScalar sy) {
fLocalMatrix.setTranslate(sx, sy);
}
void setScale(SkScalar sx, SkScalar sy) {
fLocalMatrix.setScale(sx, sy);
}
void setRotate(SkScalar degrees) {
fLocalMatrix.setRotate(degrees);
}
protected:
void onVisit(XformResolver* resolver) override;
private:
SkMatrix fLocalMatrix;
};
class ClipXF : public Xform {
public:
ClipXF(sk_sp<Xform> parent = nullptr) : Xform(std::move(parent)) {}
ClipXF(sk_sp<Xform> parent, const SkRect& r, SkClipOp op = SkClipOp::kIntersect)
: Xform(std::move(parent))
, fRect(r)
, fOp(op)
{}
void setRect(const SkRect& r, SkClipOp op = SkClipOp::kIntersect) {
fRect = r;
fOp = op;
}
protected:
void onVisit(XformResolver* resolver) override;
private:
SkRect fRect;
SkClipOp fOp;
};
#endif

View File

@ -0,0 +1,172 @@
/*
* 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 {
fRC.op(r, fCTM, fBounds, (SkRegion::Op)op, false);
fCache.reset(nullptr);
}
void clipRRect(const SkRRect& rr, SkClipOp op) override {
fRC.op(rr, fCTM, fBounds, (SkRegion::Op)op, false);
fCache.reset(nullptr);
}
void clipPath(const SkPath& p, SkClipOp op) override {
fRC.op(p, fCTM, fBounds, (SkRegion::Op)op, false);
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));
}

103
gm/xform.cpp Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "experimental/xform/SkShape.h"
#include "experimental/xform/SkXform.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "tools/timer/AnimTimer.h"
class XformGM : public skiagm::GM {
sk_sp<MatrixXF> fRoot, fRA, fRB, fA, fB;
sk_sp<Shape> fShape;
public:
XformGM() {
fRoot = MatrixXF::Make();
fRA = MatrixXF::Make(fRoot);
fRB = MatrixXF::Make(fRoot);
fA = MatrixXF::Make(fRA);
fB = MatrixXF::Make(fRB);
fRA->setRotate(30);
fA->setTranslate(100, 0);
fRB->setTranslate(100, 0);
fB->setRotate(30);
sk_sp<GroupShape> g = GroupShape::Make();
g->append(GeoShape::Make(fA, {0, 0, 100, 60}, SK_ColorRED));
g->append(GeoShape::Make(fB, {0, 0, 100, 60}, SK_ColorGREEN));
g->append(GeoShape::Make(fRA, {0, 0, 100, 60}, SK_ColorBLUE));
g->append(GeoShape::Make(fRB, {0, 0, 100, 60}, SK_ColorGRAY));
g->append(GeoShape::Make(fRoot, {0, 0, 100, 60}, 0xFFCC8844));
sk_sp<MatrixXF> sub = MatrixXF::Make();
SkMatrix m;
m.setScale(0.5, 0.5);
m.postTranslate(50, 50);
sub->setLocalMatrix(m);
sk_sp<GroupShape> parent = GroupShape::Make();
parent->append(g);
parent->append(GroupShape::Make(sub, g));
fShape = parent;
}
protected:
SkString onShortName() override { return SkString("exp_xform"); }
SkISize onISize() override { return SkISize::Make(520, 520); }
void onDraw(SkCanvas* canvas) override {
auto ctx = XContext::Make(canvas);
if (0) {
canvas->translate(2, 2);
SkRect rect{0, 0, 100, 60};
SkPaint paint; paint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(rect, paint);
canvas->save(); canvas->translate(10, 10);
paint.setColor(SK_ColorRED); canvas->drawRect(rect, paint); canvas->restore();
canvas->save(); canvas->scale(2, 2);
paint.setColor(SK_ColorBLUE); canvas->drawRect(rect, paint); canvas->restore();
canvas->save(); canvas->scale(2, 2); canvas->translate(10, 10);
paint.setColor(SK_ColorBLACK); canvas->drawRect(rect, paint); canvas->restore();
canvas->save(); canvas->translate(10, 10); canvas->scale(2, 2);
paint.setColor(SK_ColorBLACK); canvas->drawRect(rect, paint); canvas->restore();
auto x0 = MatrixXF::Make();
auto x1 = MatrixXF::Make(x0);
auto x2 = MatrixXF::Make(x1);
x1->setScale(2, 2);
x2->setTranslate(10, 10);
auto sh = GeoShape::Make(x2, {0, 0, 100, 60}, 0x8800FF00);
sh->draw(ctx.get());
return;
}
fShape->draw(ctx.get());
}
bool onAnimate(const AnimTimer& timer) override {
float scale = 3 + sinf(timer.scaled(1, 0)) * 2;
fRoot->setScale(scale, scale);
fRA->setRotate(timer.scaled(40, 0));
fB->setRotate(timer.scaled(40*sqrtf(2), 0));
return true;
}
private:
typedef skiagm::GM INHERITED;
};
DEF_GM( return new XformGM; )