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:
parent
bba5aa761c
commit
a716809d5a
16
BUILD.gn
16
BUILD.gn
@ -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)) {
|
||||
|
26
experimental/xform/SkShape.cpp
Normal file
26
experimental/xform/SkShape.cpp
Normal 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();
|
||||
}
|
104
experimental/xform/SkShape.h
Normal file
104
experimental/xform/SkShape.h
Normal 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
|
87
experimental/xform/SkXform.cpp
Normal file
87
experimental/xform/SkXform.cpp
Normal 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);
|
||||
}
|
142
experimental/xform/SkXform.h
Normal file
142
experimental/xform/SkXform.h
Normal 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
|
172
experimental/xform/XContext.cpp
Normal file
172
experimental/xform/XContext.cpp
Normal 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
103
gm/xform.cpp
Normal 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; )
|
||||
|
Loading…
Reference in New Issue
Block a user