[TextBlob] Fall back to TightRunBounds when the font bounds are empty
authorfmalita <fmalita@chromium.org>
Fri, 9 Oct 2015 21:46:28 +0000 (14:46 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 9 Oct 2015 21:46:28 +0000 (14:46 -0700)
Empty font bounds are likely an indication of a font bug.  As a best
effort, we can use TightRunBounds in this easily detectable case.

Since TightRunBounds only supports kDefault_Positioning currently, the
CL also reinstates handling of kFull_Positioning and
kHorizontal_Positioning (removed in http://crrev.com/858153007).

BUG=507022
R=reed@google.com,bungeman@google.com

Review URL: https://codereview.chromium.org/1399123002

src/core/SkTextBlob.cpp

index a49ff25..2ccf26b 100644 (file)
@@ -367,12 +367,44 @@ SkTextBlobBuilder::~SkTextBlobBuilder() {
 }
 
 SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
-    SkASSERT(SkTextBlob::kDefault_Positioning == run.positioning());
-
     SkRect bounds;
     SkPaint paint;
     run.font().applyToPaint(&paint);
-    paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
+
+    if (SkTextBlob::kDefault_Positioning == run.positioning()) {
+        paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
+        return bounds.makeOffset(run.offset().x(), run.offset().y());
+    }
+
+    SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
+    paint.getTextWidths(run.glyphBuffer(),
+                        run.glyphCount() * sizeof(uint16_t),
+                        NULL,
+                        glyphBounds.get());
+
+    SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
+             SkTextBlob::kHorizontal_Positioning == run.positioning());
+    // kFull_Positioning       => [ x, y, x, y... ]
+    // kHorizontal_Positioning => [ x, x, x... ]
+    //                            (const y applied by runBounds.offset(run->offset()) later)
+    const SkScalar horizontalConstY = 0;
+    const SkScalar* glyphPosX = run.posBuffer();
+    const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
+                                                      glyphPosX + 1 : &horizontalConstY;
+    const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
+    const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
+                                                   posXInc : 0;
+
+    bounds.setEmpty();
+    for (unsigned i = 0; i < run.glyphCount(); ++i) {
+        bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
+        glyphPosX += posXInc;
+        glyphPosY += posYInc;
+    }
+
+    SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
+    SkASSERT(run.positioning() == SkTextBlob::kHorizontal_Positioning ||
+             (void*)glyphPosY <= SkTextBlob::RunRecord::Next(&run));
 
     return bounds.makeOffset(run.offset().x(), run.offset().y());
 }
@@ -382,7 +414,16 @@ SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run
     SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
              SkTextBlob::kHorizontal_Positioning == run.positioning());
 
-    // First, compute the glyph position bbox.
+    SkPaint paint;
+    run.font().applyToPaint(&paint);
+    const SkRect fontBounds = paint.getFontBounds();
+    if (fontBounds.isEmpty()) {
+        // Empty font bounds are likely a font bug.  TightBounds has a better chance of
+        // producing useful results in this case.
+        return TightRunBounds(run);
+    }
+
+    // Compute the glyph position bbox.
     SkRect bounds;
     switch (run.positioning()) {
     case SkTextBlob::kHorizontal_Positioning: {
@@ -410,9 +451,6 @@ SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run
     }
 
     // Expand by typeface glyph bounds.
-    SkPaint paint;
-    run.font().applyToPaint(&paint);
-    const SkRect fontBounds = paint.getFontBounds();
     bounds.fLeft   += fontBounds.left();
     bounds.fTop    += fontBounds.top();
     bounds.fRight  += fontBounds.right();