Remove the call to getFontMetrics from SkBBoxRecord

R=reed@google.com, caryclark@google.com

Author: sglez@google.com

Review URL: https://chromiumcodereview.appspot.com/23001007

git-svn-id: http://skia.googlecode.com/svn/trunk/src@10876 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/core/SkBBoxRecord.cpp b/core/SkBBoxRecord.cpp
index 3668087..1e6c69b 100644
--- a/core/SkBBoxRecord.cpp
+++ b/core/SkBBoxRecord.cpp
@@ -179,35 +179,45 @@
 
 void SkBBoxRecord::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
                                 SkScalar constY, const SkPaint& paint) {
-    SkRect bbox;
     size_t numChars = paint.countText(text, byteLength);
-    if (numChars > 0) {
-        bbox.fLeft  = xpos[0];
-        bbox.fRight = xpos[numChars - 1];
-        // if we had a guarantee that these will be monotonically increasing, this could be sped up
-        for (size_t i = 1; i < numChars; ++i) {
-            if (xpos[i] < bbox.fLeft) {
-                bbox.fLeft = xpos[i];
-            }
-            if (xpos[i] > bbox.fRight) {
-                bbox.fRight = xpos[i];
-            }
+    if (numChars == 0) {
+        return;
+    }
+
+    const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
+    WriteTopBot(paint, *flatPaintData);
+
+    SkScalar top = flatPaintData->topBot()[0];
+    SkScalar bottom = flatPaintData->topBot()[1];
+    SkScalar pad = top - bottom;
+
+    SkRect bbox;
+    bbox.fLeft = SK_ScalarMax;
+    bbox.fRight = SK_ScalarMin;
+
+    for (size_t i = 0; i < numChars; ++i) {
+        if (xpos[i] < bbox.fLeft) {
+            bbox.fLeft = xpos[i];
         }
-        SkPaint::FontMetrics metrics;
-        paint.getFontMetrics(&metrics);
-
-        // pad horizontally by max glyph height
-        SkScalar pad = (metrics.fTop - metrics.fBottom);
-        bbox.fLeft  += pad;
-        bbox.fRight -= pad;
-
-        bbox.fTop    = metrics.fTop + constY;
-        bbox.fBottom = metrics.fBottom + constY;
-        if (!this->transformBounds(bbox, &paint)) {
-            return;
+        if (xpos[i] > bbox.fRight) {
+            bbox.fRight = xpos[i];
         }
     }
-    INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+
+    // pad horizontally by max glyph height
+    bbox.fLeft  += pad;
+    bbox.fRight -= pad;
+
+    bbox.fTop    = top + constY;
+    bbox.fBottom = bottom + constY;
+
+    if (!this->transformBounds(bbox, &paint)) {
+        return;
+    }
+    // This is the equivalent of calling:
+    //  INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+    // but we filled our flat paint beforehand so that we could get font metrics.
+    drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
 }
 
 void SkBBoxRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
diff --git a/core/SkPictureRecord.cpp b/core/SkPictureRecord.cpp
index 309b8a8..01478bb 100644
--- a/core/SkPictureRecord.cpp
+++ b/core/SkPictureRecord.cpp
@@ -401,13 +401,16 @@
                                    SkColorGetA(saveLayerPaint->getColor()));
     dbmPaint->setColor(newColor);
 
-    const int paintIndex = paintDict->find(*dbmPaint);
+    const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
+    if (NULL == data) {
+        return false;
+    }
 
     // kill the saveLayer and alter the DBMR2R's paint to be the modified one
     convert_command_to_noop(writer, saveLayerInfo.fOffset);
     uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
     SkASSERT(dbmPaintId == *ptr);
-    *ptr = paintIndex;
+    *ptr = data->index();
     return true;
 }
 
@@ -967,10 +970,7 @@
     validate(initialOffset, size);
 }
 
-// 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]) {
+void SkPictureRecord::ComputeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
     SkPaint::FontMetrics metrics;
     paint.getFontMetrics(&metrics);
     SkRect bounds;
@@ -984,10 +984,7 @@
 
 void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
                                               SkScalar minY, SkScalar maxY) {
-    if (!flat.isTopBotWritten()) {
-        computeFontMetricsTopBottom(paint, flat.writableTopBot());
-        SkASSERT(flat.isTopBotWritten());
-    }
+    WriteTopBot(paint, flat);
     addScalar(flat.topBot()[0] + minY);
     addScalar(flat.topBot()[1] + maxY);
 }
@@ -1103,6 +1100,14 @@
 void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
                           const SkScalar xpos[], SkScalar constY,
                           const SkPaint& paint) {
+
+    const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
+    drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
+}
+
+void SkPictureRecord::drawPosTextHImpl(const void* text, size_t byteLength,
+                          const SkScalar xpos[], SkScalar constY,
+                          const SkPaint& paint, const SkFlatData* flatPaintData) {
     size_t points = paint.countText(text, byteLength);
     if (0 == points)
         return;
@@ -1116,11 +1121,11 @@
     }
     // + y + the actual points
     size += 1 * kUInt32Size + points * sizeof(SkScalar);
-
     uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
                                            &size);
-    const SkFlatData* flatPaintData = addPaint(paint);
     SkASSERT(flatPaintData);
+    addFlatPaint(flatPaintData);
+
     addText(text, byteLength);
     addInt(points);
 
@@ -1265,13 +1270,21 @@
     this->addInt(matrix ? fMatrices.find(*matrix) : 0);
 }
 
+const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
+    return fPaints.findAndReturnFlat(paint);
+}
+
 const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
-    const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
-    int index = data ? data->index() : 0;
-    this->addInt(index);
+    const SkFlatData* data = paint ? getFlatPaintData(*paint) : NULL;
+    this->addFlatPaint(data);
     return data;
 }
 
+void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
+    int index = flatPaint ? flatPaint->index() : 0;
+    this->addInt(index);
+}
+
 void SkPictureRecord::addPath(const SkPath& path) {
     if (NULL == fPathHeap) {
         fPathHeap = SkNEW(SkPathHeap);
diff --git a/core/SkPictureRecord.h b/core/SkPictureRecord.h
index 51547a4..b900f4f 100644
--- a/core/SkPictureRecord.h
+++ b/core/SkPictureRecord.h
@@ -165,6 +165,7 @@
     void addMatrixPtr(const SkMatrix* matrix);
     const SkFlatData* addPaint(const SkPaint& paint) { return this->addPaintPtr(&paint); }
     const SkFlatData* addPaintPtr(const SkPaint* paint);
+    void addFlatPaint(const SkFlatData* flatPaint);
     void addPath(const SkPath& path);
     void addPicture(SkPicture& picture);
     void addPoint(const SkPoint& point);
@@ -216,6 +217,26 @@
 #endif
 
 protected:
+    // 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]);
+
+    // Make sure that flat has fTopBot written.
+    static void WriteTopBot(const SkPaint& paint, const SkFlatData& flat) {
+        if (!flat.isTopBotWritten()) {
+            ComputeFontMetricsTopBottom(paint, flat.writableTopBot());
+            SkASSERT(flat.isTopBotWritten());
+        }
+    }
+    // Will return a cached version when possible.
+    const SkFlatData* getFlatPaintData(const SkPaint& paint);
+    /**
+     * SkBBoxRecord::drawPosTextH gets a flat paint and uses it,
+     * then it calls this, using the extra parameter, to avoid duplication.
+     */
+    void drawPosTextHImpl(const void* text, size_t byteLength,
+                          const SkScalar xpos[], SkScalar constY,
+                          const SkPaint& paint, const SkFlatData* flatPaintData);
 
     // These are set to NULL in our constructor, but may be changed by
     // subclasses, in which case they will be SkSafeUnref'd in our destructor.