Fix SkPaint::measureText for stroked hairline text

SkPaint::measureText and text drawing used different criteria for
determining whether text should be drawn as paths or not.

Adds tests glyph_pos_(h/n)_(s/f/b) to test the text rendering and the glyph
positioning in the rendering. Mainly added in order to define what is the
expected text rendering when hairline stroke is used with various transform
options.

The testcase also tries to note or highlight the fact that SkPaint::measureText
is not expected to produce intuitively matching results when compared to a
rendering, if the rendering is done so that the device ends up having a device
transform.

This fixes the glyph_pos_h_s (hairline, stroked) test-case.

Ignore shadertext2_pdf-poppler.png gm on
Test-Ubuntu13.10-ShuttleA-NoGPU-x86_64-Debug temporarily, as that fails.

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

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/335603003
diff --git a/expectations/gm/Test-Ubuntu13.10-ShuttleA-NoGPU-x86_64-Debug/expected-results.json b/expectations/gm/Test-Ubuntu13.10-ShuttleA-NoGPU-x86_64-Debug/expected-results.json
index 09d6c58..3055881 100644
--- a/expectations/gm/Test-Ubuntu13.10-ShuttleA-NoGPU-x86_64-Debug/expected-results.json
+++ b/expectations/gm/Test-Ubuntu13.10-ShuttleA-NoGPU-x86_64-Debug/expected-results.json
@@ -9210,6 +9210,7 @@
           5483462587049856153
         ]
       ], 
+      "ignore-failure": true,
       "reviewed-by-human": true
     }, 
     "shadertext3_565.png": {
@@ -10860,4 +10861,4 @@
       "reviewed-by-human": false
     }
   }
-}
\ No newline at end of file
+}
diff --git a/gm/glyph_pos.cpp b/gm/glyph_pos.cpp
new file mode 100644
index 0000000..b045101
--- /dev/null
+++ b/gm/glyph_pos.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkTypeface.h"
+
+/* This test tries to define the effect of using hairline strokes on text.
+ * Provides non-hairline images for reference and consistency checks.
+ * glyph_pos_(h/n)_(s/f/b)
+ *   -> test hairline/non-hairline stroke/fill/stroke+fill.
+ */
+static const SkScalar kTextHeight = 14.0f;
+static const char kText[] = "Proportional Hamburgefons #% fi";
+
+namespace skiagm {
+
+class GlyphPosGM : public GM {
+public:
+    GlyphPosGM(SkScalar strokeWidth, SkPaint::Style strokeStyle)
+        : fStrokeWidth(strokeWidth)
+        , fStrokeStyle(strokeStyle) {
+        }
+
+protected:
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        return kSkipTiled_Flag;
+    }
+
+    virtual SkString onShortName() SK_OVERRIDE {
+        SkString str("glyph_pos");
+        if (fStrokeWidth == 0.0f) {
+            str.append("_h"); // h == Hairline.
+        } else {
+            str.append("_n"); // n == Normal.
+        }
+        if (fStrokeStyle == SkPaint::kStroke_Style) {
+            str.append("_s");
+        } else if (fStrokeStyle == SkPaint::kFill_Style) {
+            str.append("_f");
+        } else {
+            str.append("_b"); // b == Both.
+        }
+        return str;
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(800, 600); }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        if (!fProp) {
+            fProp.reset(SkTypeface::CreateFromName("Helvetica", SkTypeface::kNormal));
+        }
+
+        // There's a black pixel at 40, 40 for reference.
+        canvas->drawPoint(40.0f, 40.0f, SK_ColorBLACK);
+
+        // Two reference images.
+        canvas->translate(50.0f, 50.0f);
+        drawTestCase(canvas, 1.0f);
+
+        canvas->translate(0.0f, 50.0f);
+        drawTestCase(canvas, 3.0f);
+
+        // Uniform scaling test.
+        canvas->translate(0.0f, 100.0f);
+        canvas->save();
+        canvas->scale(3.0f, 3.0f);
+        drawTestCase(canvas, 1.0f);
+        canvas->restore();
+
+        // Non-uniform scaling test.
+        canvas->translate(0.0f, 100.0f);
+        canvas->save();
+        canvas->scale(3.0f, 6.0f);
+        drawTestCase(canvas, 1.0f);
+        canvas->restore();
+
+        // Skew test.
+        canvas->translate(0.0f, 80.0f);
+        canvas->save();
+        canvas->scale(3.0f, 3.0f);
+        SkMatrix skew;
+        skew.setIdentity();
+        skew.setSkewX(SkScalarDiv(8.0f,
+                                  25.0f));
+        skew.setSkewY(SkScalarDiv(2.0f,
+                                  25.0f));
+        canvas->concat(skew);
+        drawTestCase(canvas, 1.0f);
+        canvas->restore();
+
+        // Perspective test.
+        canvas->translate(0.0f, 80.0f);
+        canvas->save();
+        SkMatrix perspective;
+        perspective.setIdentity();
+        perspective.setPerspX(-SkScalarDiv(SK_Scalar1, 340.0f));
+        perspective.setSkewX(SkScalarDiv(8.0f,
+                                         25.0f));
+        perspective.setSkewY(SkScalarDiv(2.0f,
+                                         25.0f));
+
+
+        canvas->concat(perspective);
+        drawTestCase(canvas, 1.0f);
+        canvas->restore();
+    }
+
+    void drawTestCase(SkCanvas* canvas, SkScalar textScale) {
+        SkPaint paint;
+        paint.setColor(SK_ColorBLACK);
+        paint.setAntiAlias(true);
+        paint.setTextSize(kTextHeight * textScale);
+        paint.setTypeface(fProp);
+        paint.setDevKernText(true);
+        paint.setStrokeWidth(fStrokeWidth);
+        paint.setStyle(fStrokeStyle);
+
+        // This demonstrates that we can not measure the text if there's a device transform. The
+        // canvas total matrix will end up being a device transform.
+        bool drawRef = !(canvas->getTotalMatrix().getType() &
+                         ~(SkMatrix::kIdentity_Mask | SkMatrix::kTranslate_Mask));
+
+        SkRect bounds;
+        if (drawRef) {
+            SkScalar advance = paint.measureText(kText, sizeof(kText) - 1, &bounds);
+
+            paint.setStrokeWidth(0.0f);
+            paint.setStyle(SkPaint::kStroke_Style);
+
+            // Green box is the measured text bounds.
+            paint.setColor(SK_ColorGREEN);
+            canvas->drawRect(bounds, paint);
+
+            // Red line is the measured advance from the 0,0 of the text position.
+            paint.setColor(SK_ColorRED);
+            canvas->drawLine(0.0f, 0.0f, advance, 0.0f, paint);
+        }
+
+        // Black text is the testcase, eg. the text.
+        paint.setColor(SK_ColorBLACK);
+        paint.setStrokeWidth(fStrokeWidth);
+        paint.setStyle(fStrokeStyle);
+        canvas->drawText(kText, sizeof(kText) - 1, 0.0f, 0.0f, paint);
+
+        if (drawRef) {
+            SkScalar widths[sizeof(kText) - 1];
+            paint.getTextWidths(kText, sizeof(kText) - 1, widths, NULL);
+
+            paint.setStrokeWidth(0.0f);
+            paint.setStyle(SkPaint::kStroke_Style);
+
+            // Magenta lines are the positions for the characters.
+            paint.setColor(SK_ColorMAGENTA);
+            SkScalar w = bounds.x();
+            for (size_t i = 0; i < sizeof(kText) - 1; ++i) {
+                canvas->drawLine(w, 0.0f, w, 5.0f, paint);
+                w += widths[i];
+            }
+        }
+    }
+
+private:
+    SkAutoTUnref<SkTypeface> fProp;
+    SkScalar fStrokeWidth;
+    SkPaint::Style fStrokeStyle;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* GlyphPosHairlineStrokeAndFillFactory(void*) {
+    return new GlyphPosGM(0.0f, SkPaint::kStrokeAndFill_Style);
+}
+static GM* GlyphPosStrokeAndFillFactory(void*) {
+    return new GlyphPosGM(1.2f, SkPaint::kStrokeAndFill_Style);
+}
+static GM* GlyphPosHairlineStrokeFactory(void*) {
+    return new GlyphPosGM(0.0f, SkPaint::kStroke_Style);
+}
+static GM* GlyphPosStrokeFactory(void*) {
+    return new GlyphPosGM(1.2f, SkPaint::kStroke_Style);
+}
+static GM* GlyphPosHairlineFillFactory(void*) {
+    return new GlyphPosGM(0.0f, SkPaint::kFill_Style);
+}
+static GM* GlyphPosFillFactory(void*) {
+    return new GlyphPosGM(1.2f, SkPaint::kFill_Style);
+}
+
+static GMRegistry reg1(GlyphPosHairlineStrokeAndFillFactory);
+static GMRegistry reg2(GlyphPosStrokeAndFillFactory);
+static GMRegistry reg3(GlyphPosHairlineStrokeFactory);
+static GMRegistry reg4(GlyphPosStrokeFactory);
+static GMRegistry reg5(GlyphPosHairlineFillFactory);
+static GMRegistry reg6(GlyphPosFillFactory);
+
+
+}
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 18e589b..694ec72 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -83,6 +83,7 @@
     '../gm/gammatext.cpp',
     '../gm/getpostextpath.cpp',
     '../gm/giantbitmap.cpp',
+    '../gm/glyph_pos.cpp',
     '../gm/gradients.cpp',
     '../gm/gradients_2pt_conical.cpp',
     '../gm/gradients_no_texture.cpp',
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index f766ca1..a73faec 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -1117,9 +1117,6 @@
 
     static bool TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM);
 
-    bool tooBigToUseCache() const;
-    bool tooBigToUseCache(const SkMatrix& ctm) const;
-
     // Set flags/hinting/textSize up to use for drawing text as paths.
     // Returns scale factor to restore the original textSize, since will will
     // have change it to kCanonicalTextSizeForPaths.
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 16d8bb2..86b54f2 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -11,6 +11,7 @@
 #include "SkColorFilter.h"
 #include "SkData.h"
 #include "SkDeviceProperties.h"
+#include "SkDraw.h"
 #include "SkFontDescriptor.h"
 #include "SkFontHost.h"
 #include "SkGlyphCache.h"
@@ -506,15 +507,6 @@
     return tooBig(matrix, MaxCacheSize2());
 }
 
-bool SkPaint::tooBigToUseCache(const SkMatrix& ctm) const {
-    SkMatrix textM;
-    return TooBigToUseCache(ctm, *this->setTextMatrix(&textM));
-}
-
-bool SkPaint::tooBigToUseCache() const {
-    SkMatrix textM;
-    return tooBig(*this->setTextMatrix(&textM), MaxCacheSize2());
-}
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -997,7 +989,7 @@
 class SkCanonicalizePaint {
 public:
     SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
-        if (paint.isLinearText() || paint.tooBigToUseCache()) {
+        if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
             SkPaint* p = fLazy.set(paint);
             fScale = p->setupForAsPaths();
             fPaint = p;