|  | /* | 
|  | * 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)); | 
|  | } |