cache fontmetrics in picture-record
Review URL: https://codereview.appspot.com/6908049

git-svn-id: http://skia.googlecode.com/svn/trunk@6706 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index 8f6e089..e104a40 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -120,6 +120,7 @@
     SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize);
 
     result->fIndex = index;
+    result->fTopBot[0] = result->fTopBot[1] = 0; // initial to sentinel values
     result->fFlatSize = size;
 
     // put the serialized contents into the data section of the new allocation
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index a00596f..4292361 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -329,9 +329,34 @@
         return !memcmp(a.dataToCompare(), b.dataToCompare(), N);
     }
 
+    // returns true if fTopBot[] has been recorded
+    bool isTopBotValid() const {
+        return fTopBot[0] < fTopBot[1];
+    }
+
+    // Returns fTopBot array, so it can be passed to a routine to compute them.
+    // For efficiency, we assert that fTopBot have not been recorded yet.
+    SkScalar* writableTopBot() {
+        SkASSERT(!this->isTopBotValid());
+        return fTopBot;
+    }
+
+    // return the topbot[] after it has been recorded
+    const SkScalar* topBot() const {
+        SkASSERT(this->isTopBotValid());
+        return fTopBot;
+    }
+
 private:
+    // This is *not* part of the key for search/sort
     int fIndex;
 
+    // Cache of paint's FontMetrics fTop,fBottom
+    // initialied to [0,0] as a sentinel that they have not been recorded yet
+    //
+    // This is *not* part of the key for search/sort
+    SkScalar fTopBot[2];
+
     // From here down is the data we look at in the search/sort. We always begin
     // with the checksum and then length.
     uint32_t fChecksum;
@@ -621,6 +646,10 @@
         fFlattenProc = &SkFlattenObjectProc<SkPaint>;
         fUnflattenProc = &SkUnflattenObjectProc<SkPaint>;
     }
+
+    SkFlatData* writableFlatData(int index) {
+        return const_cast<SkFlatData*>((*this)[index]);
+    }
 };
 
 class SkRegionDictionary : public SkFlatDictionary<SkRegion> {
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 270eee5..b62b5b9 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -473,19 +473,30 @@
     validate();
 }
 
-void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
-                                              SkScalar minY, SkScalar maxY) {
+// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
+// tweaked by paint.computeFastBounds().
+//
+static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
     SkPaint::FontMetrics metrics;
     paint.getFontMetrics(&metrics);
     SkRect bounds;
     // construct a rect so we can see any adjustments from the paint.
     // we use 0,1 for left,right, just so the rect isn't empty
-    bounds.set(0, metrics.fTop + minY,
-               SK_Scalar1, metrics.fBottom + maxY);
+    bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
     (void)paint.computeFastBounds(bounds, &bounds);
-    // now record the top and bottom
-    addScalar(bounds.fTop);
-    addScalar(bounds.fBottom);
+    topbot[0] = bounds.fTop;
+    topbot[1] = bounds.fBottom;
+}
+
+void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, int index,
+                                              SkScalar minY, SkScalar maxY) {
+    SkFlatData* flat = fPaints.writableFlatData(index);
+    if (!flat->isTopBotValid()) {
+        computeFontMetricsTopBottom(paint, flat->writableTopBot());
+        SkASSERT(flat->isTopBotValid());
+    }
+    addScalar(flat->topBot()[0] + minY);
+    addScalar(flat->topBot()[1] + maxY);
 }
 
 void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
@@ -493,12 +504,12 @@
     bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
 
     addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
-    addPaint(paint);
+    int paintIndex = addPaint(paint);
     addText(text, byteLength);
     addScalar(x);
     addScalar(y);
     if (fast) {
-        addFontMetricsTopBottom(paint, y, y);
+        addFontMetricsTopBottom(paint, paintIndex - 1, y, y);
     }
     validate();
 }
@@ -539,7 +550,7 @@
     } else {
         addDraw(DRAW_POS_TEXT);
     }
-    addPaint(paint);
+    int paintIndex = addPaint(paint);
     addText(text, byteLength);
     addInt(points);
 
@@ -548,7 +559,7 @@
 #endif
     if (canUseDrawH) {
         if (fast) {
-            addFontMetricsTopBottom(paint, pos[0].fY, pos[0].fY);
+            addFontMetricsTopBottom(paint, paintIndex - 1, pos[0].fY, pos[0].fY);
         }
         addScalar(pos[0].fY);
         SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
@@ -558,7 +569,7 @@
     else {
         fWriter.writeMul4(pos, points * sizeof(SkPoint));
         if (fastBounds) {
-            addFontMetricsTopBottom(paint, minY, maxY);
+            addFontMetricsTopBottom(paint, paintIndex - 1, minY, maxY);
         }
     }
 #ifdef SK_DEBUG_SIZE
@@ -578,7 +589,7 @@
     bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
 
     addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
-    addPaint(paint);
+    int paintIndex = this->addPaint(paint);
     addText(text, byteLength);
     addInt(points);
 
@@ -586,7 +597,7 @@
     size_t start = fWriter.size();
 #endif
     if (fast) {
-        addFontMetricsTopBottom(paint, constY, constY);
+        addFontMetricsTopBottom(paint, paintIndex - 1, constY, constY);
     }
     addScalar(constY);
     fWriter.writeMul4(xpos, points * sizeof(SkScalar));
@@ -673,12 +684,10 @@
     this->addInt(matrix ? fMatrices.find(*matrix) : 0);
 }
 
-void SkPictureRecord::addPaint(const SkPaint& paint) {
-    addPaintPtr(&paint);
-}
-
-void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
-    this->addInt(paint ? fPaints.find(*paint) : 0);
+int SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+    int index = paint ? fPaints.find(*paint) : 0;
+    this->addInt(index);
+    return index;
 }
 
 void SkPictureRecord::addPath(const SkPath& path) {
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 77e0114..77a6b04 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -72,7 +72,8 @@
     virtual void drawData(const void*, size_t) SK_OVERRIDE;
     virtual bool isDrawingToLayer() const SK_OVERRIDE;
 
-    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar minY, SkScalar maxY);
+    void addFontMetricsTopBottom(const SkPaint& paint, int paintIndex,
+                                 SkScalar minY, SkScalar maxY);
 
     const SkTDArray<SkPicture* >& getPictureRefs() const {
         return fPictureRefs;
@@ -118,8 +119,8 @@
     void addBitmap(const SkBitmap& bitmap);
     void addMatrix(const SkMatrix& matrix);
     void addMatrixPtr(const SkMatrix* matrix);
-    void addPaint(const SkPaint& paint);
-    void addPaintPtr(const SkPaint* paint);
+    int  addPaint(const SkPaint& paint) { return this->addPaintPtr(&paint); }
+    int  addPaintPtr(const SkPaint* paint);
     void addPath(const SkPath& path);
     void addPicture(SkPicture& picture);
     void addPoint(const SkPoint& point);