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