/* * 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 snapCache() { if (!fCache) { fCache = sk_sp(new RasterClipCache(fRC)); } return fCache; } private: SkIRect fBounds; SkMatrix fCTM; SkRasterClip fRC; sk_sp 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 fStack; SkTDArray fCounts; SkCanvas* fCanvas; // bare pointer SkIRect fBounds; Xform* parentOrNull() { return fStack.count() > 0 ? fStack.top() : nullptr; } }; std::unique_ptr XContext::Make(SkCanvas* canvas) { return std::unique_ptr(new CanvasXContext(canvas)); }