Apply filterTextFlags so the fonts are the same.

In order to apply filterTextFlags correctly, teach
TrackLayerDevice how to process save and restore layers.

At this point, I don't see any other traffic than the
cache warming traffic.

This code has a performance between 82% and 105% of just
drawing the picture.

BUG=skia:7515

Change-Id: I44736be46884f18b6d120d4b5ca582f34dbdff0f
Reviewed-on: https://skia-review.googlesource.com/114641
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index a426d1e..e90408d 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -2445,6 +2445,7 @@
 
     void private_draw_shadow_rec(const SkPath&, const SkDrawShadowRec&);
 
+
 protected:
     // default impl defers to getDevice()->newSurface(info)
     virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props);
@@ -2559,6 +2560,8 @@
     bool clipRectBounds(const SkRect* bounds, SaveLayerFlags flags, SkIRect* intersection,
                         const SkImageFilter* imageFilter = nullptr);
 
+    SkBaseDevice* getTopDevice() const;
+
 private:
     /** After calling saveLayer(), there can be any number of devices that make
      up the top-most drawing area. LayerIter can be used to iterate through
@@ -2620,7 +2623,6 @@
     }
 
     SkBaseDevice* getDevice() const;
-    SkBaseDevice* getTopDevice() const;
 
     class MCRec;
 
diff --git a/include/utils/SkNoDrawCanvas.h b/include/utils/SkNoDrawCanvas.h
index 7475956..4c67226 100644
--- a/include/utils/SkNoDrawCanvas.h
+++ b/include/utils/SkNoDrawCanvas.h
@@ -29,6 +29,8 @@
     // TODO: investigate the users of this ctor.
     SkNoDrawCanvas(const SkIRect&);
 
+    explicit SkNoDrawCanvas(SkBaseDevice* device);
+
     // Optimization to reset state to be the same as after construction.
     void resetCanvas(int width, int height) {
         resetForNextPicture(SkIRect::MakeWH(width, height));
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 2f31335..24a6d4a 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -52,58 +52,6 @@
 #define RETURN_ON_NULL(ptr)     do { if (nullptr == (ptr)) return; } while (0)
 #define RETURN_ON_FALSE(pred)   do { if (!(pred)) return; } while (0)
 
-class SkNoPixelsDevice : public SkBaseDevice {
-public:
-    SkNoPixelsDevice(const SkIRect& bounds, const SkSurfaceProps& props)
-        : SkBaseDevice(SkImageInfo::MakeUnknown(bounds.width(), bounds.height()), props)
-    {
-        // this fails if we enable this assert: DiscardableImageMapTest.GetDiscardableImagesInRectMaxImage
-        //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0);
-    }
-
-    void resetForNextPicture(const SkIRect& bounds) {
-        //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0);
-        this->privateResize(bounds.width(), bounds.height());
-    }
-
-protected:
-    // We don't track the clip at all (for performance), but we have to respond to some queries.
-    // We pretend to be wide-open. We could pretend to always be empty, but that *seems* worse.
-    void onSave() override {}
-    void onRestore() override {}
-    void onClipRect(const SkRect& rect, SkClipOp, bool aa) override {}
-    void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) override {}
-    void onClipPath(const SkPath& path, SkClipOp, bool aa) override {}
-    void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override {}
-    void onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) override {}
-    bool onClipIsAA() const override { return false; }
-    void onAsRgnClip(SkRegion* rgn) const override {
-        rgn->setRect(SkIRect::MakeWH(this->width(), this->height()));
-    }
-    ClipType onGetClipType() const override {
-        return kRect_ClipType;
-    }
-
-    void drawPaint(const SkPaint& paint) override {}
-    void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&) override {}
-    void drawRect(const SkRect&, const SkPaint&) override {}
-    void drawOval(const SkRect&, const SkPaint&) override {}
-    void drawRRect(const SkRRect&, const SkPaint&) override {}
-    void drawPath(const SkPath&, const SkPaint&, const SkMatrix*, bool) override {}
-    void drawBitmap(const SkBitmap&, SkScalar x, SkScalar y, const SkPaint&) override {}
-    void drawSprite(const SkBitmap&, int, int, const SkPaint&) override {}
-    void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint&,
-                        SkCanvas::SrcRectConstraint) override {}
-    void drawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {}
-    void drawPosText(const void*, size_t, const SkScalar[], int, const SkPoint&,
-                     const SkPaint&) override {}
-    void drawDevice(SkBaseDevice*, int, int, const SkPaint&) override {}
-    void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override {}
-
-private:
-    typedef SkBaseDevice INHERITED;
-};
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /*
@@ -974,7 +922,6 @@
     return true;
 }
 
-
 int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
     return this->saveLayer(SaveLayerRec(bounds, paint, 0));
 }
@@ -2907,6 +2854,9 @@
 SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
     : INHERITED(bounds, kConservativeRasterClip_InitFlag) {}
 
+SkNoDrawCanvas::SkNoDrawCanvas(SkBaseDevice *device)
+    : INHERITED(device) {}
+
 SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
     (void)this->INHERITED::getSaveLayerStrategy(rec);
     return kNoLayer_SaveLayerStrategy;
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index c7b993c..6739230 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -11,6 +11,7 @@
 #include "SkRefCnt.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
+#include "SkRegion.h"
 #include "SkSurfaceProps.h"
 
 class SkBitmap;
@@ -20,7 +21,6 @@
 struct SkIRect;
 class SkMatrix;
 class SkRasterHandleAllocator;
-class SkRegion;
 class SkSpecialImage;
 
 class SkBaseDevice : public SkRefCnt {
@@ -125,6 +125,12 @@
     void setGlobalCTM(const SkMatrix& ctm);
     virtual void validateDevBounds(const SkIRect&) {}
 
+    /**
+     * Returns the text-related flags, possibly modified based on the state of the
+     * device (e.g. support for LCD).
+     */
+    uint32_t filterTextFlags(const SkPaint&) const;
+
 protected:
     enum TileUsage {
         kPossible_TileUsage,    //!< the created device may be drawn tiled
@@ -135,12 +141,6 @@
         uint32_t    fFlags;     // SkPaint::getFlags()
     };
 
-    /**
-     * Returns the text-related flags, possibly modified based on the state of the
-     * device (e.g. support for LCD).
-     */
-    uint32_t filterTextFlags(const SkPaint&) const;
-
     virtual bool onShouldDisableLCD(const SkPaint&) const { return false; }
 
     virtual void onSave() {}
@@ -390,6 +390,58 @@
     typedef SkRefCnt INHERITED;
 };
 
+class SkNoPixelsDevice : public SkBaseDevice {
+public:
+    SkNoPixelsDevice(const SkIRect& bounds, const SkSurfaceProps& props)
+            : SkBaseDevice(SkImageInfo::MakeUnknown(bounds.width(), bounds.height()), props)
+    {
+        // this fails if we enable this assert: DiscardableImageMapTest.GetDiscardableImagesInRectMaxImage
+        //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0);
+    }
+
+    void resetForNextPicture(const SkIRect& bounds) {
+        //SkASSERT(bounds.width() >= 0 && bounds.height() >= 0);
+        this->privateResize(bounds.width(), bounds.height());
+    }
+
+protected:
+    // We don't track the clip at all (for performance), but we have to respond to some queries.
+    // We pretend to be wide-open. We could pretend to always be empty, but that *seems* worse.
+    void onSave() override {}
+    void onRestore() override {}
+    void onClipRect(const SkRect& rect, SkClipOp, bool aa) override {}
+    void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) override {}
+    void onClipPath(const SkPath& path, SkClipOp, bool aa) override {}
+    void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override {}
+    void onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) override {}
+    bool onClipIsAA() const override { return false; }
+    void onAsRgnClip(SkRegion* rgn) const override {
+        rgn->setRect(SkIRect::MakeWH(this->width(), this->height()));
+    }
+    ClipType onGetClipType() const override {
+        return kRect_ClipType;
+    }
+
+    void drawPaint(const SkPaint& paint) override {}
+    void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&) override {}
+    void drawRect(const SkRect&, const SkPaint&) override {}
+    void drawOval(const SkRect&, const SkPaint&) override {}
+    void drawRRect(const SkRRect&, const SkPaint&) override {}
+    void drawPath(const SkPath&, const SkPaint&, const SkMatrix*, bool) override {}
+    void drawBitmap(const SkBitmap&, SkScalar x, SkScalar y, const SkPaint&) override {}
+    void drawSprite(const SkBitmap&, int, int, const SkPaint&) override {}
+    void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint&,
+                        SkCanvas::SrcRectConstraint) override {}
+    void drawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {}
+    void drawPosText(const void*, size_t, const SkScalar[], int, const SkPoint&,
+                     const SkPaint&) override {}
+    void drawDevice(SkBaseDevice*, int, int, const SkPaint&) override {}
+    void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override {}
+
+private:
+    typedef SkBaseDevice INHERITED;
+};
+
 class SkAutoDeviceCTMRestore : SkNoncopyable {
 public:
     SkAutoDeviceCTMRestore(SkBaseDevice* device, const SkMatrix& ctm)
diff --git a/tools/remote_demo.cpp b/tools/remote_demo.cpp
index b8cfdc7..8ff7d29 100644
--- a/tools/remote_demo.cpp
+++ b/tools/remote_demo.cpp
@@ -21,6 +21,7 @@
 #include "SkTextBlobRunIterator.h"
 #include "SkGlyphCache.h"
 #include "SkDrawFilter.h"
+#include "SkDevice.h"
 
 #include <type_traits>
 #include <chrono>
@@ -51,8 +52,13 @@
 static bool gPurgeFontCaches = true;
 static bool gUseProcess = true;
 
+static int gFontMetrics;
+static int gMetricsImage;
+static int gPath;
+
 enum direction : int {kRead = 0, kWrite = 1};
 
+#define INSTRUMENT 0
 
 template <typename T>
 class SkArraySlice : public std::tuple<const T*, size_t> {
@@ -300,6 +306,18 @@
     };
 };
 
+
+class TrackLayerDevice : public SkNoPixelsDevice {
+public:
+    TrackLayerDevice(const SkIRect& bounds, const SkSurfaceProps& props)
+            : SkNoPixelsDevice(bounds, props) { }
+    SkBaseDevice* onCreateDevice(const CreateInfo& cinfo, const SkPaint*) override {
+        const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
+        return new TrackLayerDevice(this->getGlobalBounds(), surfaceProps);
+    }
+};
+
+
 class TextBlobFilterCanvas : public SkNoDrawCanvas {
 public:
     struct StrikeSpec {
@@ -323,7 +341,7 @@
                          const SkMatrix& deviceMatrix,
                          const SkSurfaceProps& props,
                          SkScalerContextFlags flags)
-        : SkNoDrawCanvas(width, height)
+        : SkNoDrawCanvas{new TrackLayerDevice{SkIRect::MakeWH(width, height), props}}
         , fDeviceMatrix{deviceMatrix}
         , fSurfaceProps{props}
         , fScalerContextFlags{flags} { }
@@ -422,6 +440,10 @@
 
 
 protected:
+    SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
+        return kFullLayer_SaveLayerStrategy;
+    }
+
     void onDrawTextBlob(
         const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) override
     {
@@ -433,6 +455,7 @@
             // applyFontToPaint() always overwrites the exact same attributes,
             // so it is safe to not re-seed the paint for this reason.
             it.applyFontToPaint(&runPaint);
+            runPaint.setFlags(this->getTopDevice()->filterTextFlags(runPaint));
             if (auto looper = runPaint.getLooper()) {
                 this->processLooper(position, it, runPaint, looper, this);
             } else {
@@ -441,6 +464,16 @@
         }
     }
 
+    void onDrawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {
+        SK_ABORT("DrawText");
+    }
+    void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override {
+        SK_ABORT("DrawPosText");
+    }
+    void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override {
+        SK_ABORT("DrawPosTextH");
+    }
+
 private:
     using PosFn = SkPoint(*)(int index, const SkScalar* pos);
     using MapFn = SkPoint(*)(const SkMatrix& m, SkPoint pt);
@@ -633,13 +666,14 @@
     void generateFontMetrics(const SkTypefaceProxy& tf,
                              const SkScalerContextRec& rec,
                              SkPaint::FontMetrics* metrics) override {
+        gFontMetrics += 1;
 
         //SK_ABORT("generateFontMetrics should not be called.");
         // Send generateFontMetrics
         Op* op = this->startOpWrite(OpCode::kFontMetrics, tf, rec);
         fTransport->endWrite();
 
-#if 1
+#if INSTRUMENT
         SkScalerContextRecDescriptor rd{rec};
         std::cout << " metrics font op rec tf: " << rec.fFontID
                   << " tf id: " << tf.fontID()
@@ -656,15 +690,16 @@
                                  const SkScalerContextRec& rec,
                                  SkArenaAlloc* alloc,
                                  SkGlyph* glyph) override {
+        gMetricsImage += 1;
         //SK_ABORT("generateMetricsAndImage should not be called.");
         // Send generateMetricsAndImage
         SkScalerContextRecDescriptor rd{rec};
 
-#if 1
+#if INSTRUMENT
         std::cout << " metrics image op rec tf: " << rec.fFontID
                   << " tf id: " << tf.fontID()
                   << " rec: " << rd.desc().getChecksum()
-                  << " glyphid: " << glyph->getPackedID().getPackedID()
+                  << " glyphid: " << glyph->getPackedID().getPackedID() << "\n"
                   << rec.dump().c_str() << std::endl;
 #endif
         Op* op = this->startOpWrite(OpCode::kGlyphMetricsAndImage, tf, rec);
@@ -690,6 +725,7 @@
     void generatePath(const SkTypefaceProxy& tf,
                       const SkScalerContextRec& rec,
                       SkGlyphID glyph, SkPath* path) override {
+        gPath += 1;
         // Send generatePath
         SkScalerContextRecDescriptor rd{rec};
 
@@ -722,7 +758,7 @@
     sk_sp<SkPicture> pic,
     TextBlobFilterCanvas* filter) {
 
-    filter->drawPicture(pic);
+    pic->playback(filter);
 
     transport->startEmplace<Op>(OpCode::kPrepopulateCache, SkFontID{0},
                                 SkScalerContextRec{});
@@ -747,7 +783,7 @@
             };
             strike = SkGlyphCache::CreateStrikeExclusive(*desc,creator);
         }
-#if 1
+#if INSTRUMENT
         std::cout << std::hex << "prepop cache " << (intptr_t)cache
                   << " desc: " << desc->getChecksum()
                   << " typeface id: " << tf->uniqueID()
@@ -771,7 +807,9 @@
 
     // needed for font metrics mistake.
     Transport in = Transport::DoubleBuffer(*transport);
+#if INSTRUMENT
     SkDebugf("========= Sending prep cache ========\n");
+#endif
 
     in.startRead();
     filter->readDataFromTransport(&in, perStrike, perGlyph, finishStrike);
@@ -797,7 +835,8 @@
     SkMatrix deviceMatrix = SkMatrix::I();
     // kFakeGammaAndBoostContrast
     TextBlobFilterCanvas filter(
-        r.width(), r.height(), deviceMatrix, s->props(), SkScalerContextFlags::kFakeGammaAndBoostContrast);
+        r.width(), r.height(), deviceMatrix, s->props(),
+        SkScalerContextFlags::kFakeGammaAndBoostContrast);
 
     if (cache != nullptr) {
         for (int i = 0; i < 0; i++) {
@@ -815,7 +854,7 @@
     }
 
     std::chrono::duration<double> total_seconds{0.0};
-    for (int i = 0; i < 1; i++) { // 20
+    for (int i = 0; i < 100; i++) { // 20
         if (gPurgeFontCaches) {
             SkGraphics::PurgeFontCache();
         }
@@ -860,6 +899,10 @@
     rc.prepareDeserializeProcs(&procs);
 
     final_draw("test.png", &transport, &procs, picData.get(), &rc);
+
+    if (gFontMetrics + gMetricsImage + gPath > 0) {
+        fprintf(stderr, "exceptions - fm: %d mi: %d p: %d\n", gFontMetrics, gMetricsImage, gPath);
+    }
 }
 
 static int renderer(