Fix for 510931, merge to m44

BUG=510931
NOTREECHECKS=true
NOTRY=true
NOPRESUBMIT=true

Review URL: https://codereview.chromium.org/1275393003
diff --git a/gm/textblobrandomfont.cpp b/gm/textblobrandomfont.cpp
new file mode 100644
index 0000000..a95966e
--- /dev/null
+++ b/gm/textblobrandomfont.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2015 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 "Resources.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkStream.h"
+#include "SkSurface.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+#include "../src/fonts/SkRandomScalerContext.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+
+namespace skiagm {
+class TextBlobRandomFont : public GM {
+public:
+    // This gm tests that textblobs can be translated and scaled with a font that returns random
+    // but deterministic masks
+    TextBlobRandomFont() { }
+
+protected:
+    void onOnceBeforeDraw() override {
+        SkTextBlobBuilder builder;
+
+        const char* text = "The quick brown fox jumps over the lazy dog.";
+
+        // make textbloben
+        SkPaint paint;
+        paint.setTextSize(32);
+        paint.setLCDRenderText(true);
+
+        // Setup our random scaler context
+        SkAutoTUnref<SkTypeface> orig(sk_tool_utils::create_portable_typeface("sans-serif",
+                                                                              SkTypeface::kBold));
+        if (NULL == orig) {
+            orig.reset(SkTypeface::RefDefault());
+        }
+        SkAutoTUnref<SkTypeface> random(SkNEW_ARGS(SkRandomTypeface, (orig, paint, false)));
+        paint.setTypeface(random);
+
+        SkRect bounds;
+        paint.measureText(text, strlen(text), &bounds);
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, 0);
+
+        // A8
+        const char* bigtext1 = "The quick brown fox";
+        const char* bigtext2 = "jumps over the lazy dog.";
+        paint.setTextSize(160);
+        paint.setSubpixelText(false);
+        paint.setLCDRenderText(false);
+        paint.measureText(bigtext1, strlen(bigtext1), &bounds);
+        SkScalar offset = bounds.height();
+        sk_tool_utils::add_to_text_blob(&builder, bigtext1, paint, 0, offset);
+
+        paint.measureText(bigtext2, strlen(bigtext2), &bounds);
+        offset += bounds.height();
+        sk_tool_utils::add_to_text_blob(&builder, bigtext2, paint, 0, offset);
+
+        // build
+        fBlob.reset(builder.build());
+    }
+
+    SkString onShortName() override {
+        return SkString("textblobrandomfont");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        // This GM exists to test a specific feature of the GPU backend.
+        if (NULL == canvas->getGrContext()) {
+            this->drawGpuOnlyMessage(canvas);
+            return;
+        }
+
+        canvas->drawColor(sk_tool_utils::color_to_565(SK_ColorWHITE));
+
+        SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
+        SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+        SkAutoTUnref<SkSurface> surface(canvas->newSurface(info, &props));
+        if (surface) {
+            SkPaint paint;
+            paint.setAntiAlias(true);
+
+            SkCanvas* c = surface->getCanvas();
+
+            int stride = SkScalarCeilToInt(fBlob->bounds().height());
+            int yOffset = stride / 8;
+            for (int i = 0; i < 1; i++) {
+                // fiddle the canvas to force regen of textblobs
+                canvas->rotate(i % 2 ? 0.0f : -0.05f);
+                canvas->drawTextBlob(fBlob, 10.0f, SkIntToScalar(yOffset), paint);
+                yOffset += stride;
+
+                // This will draw as black boxes
+                c->drawTextBlob(fBlob, 10, SkIntToScalar(yOffset), paint);
+                surface->draw(canvas, 0, 0, nullptr);
+
+                // free gpu resources and verify
+                yOffset += stride;
+                canvas->getGrContext()->freeGpuResources();
+                canvas->drawTextBlob(fBlob, 10, SkIntToScalar(yOffset), paint);
+
+                yOffset += stride;
+            }
+
+        } else {
+            const char* text = "This test requires a surface";
+            size_t len = strlen(text);
+            SkPaint paint;
+            canvas->drawText(text, len, 10, 100, paint);
+        }
+    }
+
+private:
+    SkAutoTUnref<const SkTextBlob> fBlob;
+
+    static const int kWidth = 2000;
+    static const int kHeight = 1600;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(TextBlobRandomFont); )
+}
+#endif
diff --git a/gyp/utils.gypi b/gyp/utils.gypi
index f382e10..50a2a1a 100644
--- a/gyp/utils.gypi
+++ b/gyp/utils.gypi
@@ -133,6 +133,8 @@
         #testing
         '<(skia_src_path)/fonts/SkGScalerContext.cpp',
         '<(skia_src_path)/fonts/SkGScalerContext.h',
+        '<(skia_src_path)/fonts/SkRandomScalerContext.cpp',
+        '<(skia_src_path)/fonts/SkRandomScalerContext.cpp',
         '<(skia_src_path)/fonts/SkTestScalerContext.cpp',
         '<(skia_src_path)/fonts/SkTestScalerContext.h',
     ],
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 7d8b892..873c882 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -367,6 +367,7 @@
     friend class SkPDFCIDFont;
     friend class GrPathRendering;
     friend class GrGLPathRendering;
+    friend class SkRandomTypeface; // For debugging
 
     /** Retrieve detailed typeface metrics.  Used by the PDF backend.
      @param perGlyphInfo Indicate what glyph specific information (advances,
diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h
index 02b3a76..fe5235a 100644
--- a/src/core/SkScalerContext.h
+++ b/src/core/SkScalerContext.h
@@ -299,6 +299,8 @@
 
     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
 
+    void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; }
+
 private:
     // never null
     SkAutoTUnref<SkTypeface> fTypeface;
@@ -328,6 +330,8 @@
     // When there is a filter, previous steps must create a linear mask
     // and the pre-blend applied as a final step.
     const SkMaskGamma::PreBlend fPreBlendForFilter;
+
+    friend class SkRandomScalerContext; // For debugging
 };
 
 #define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
diff --git a/src/fonts/SkRandomScalerContext.cpp b/src/fonts/SkRandomScalerContext.cpp
new file mode 100644
index 0000000..799e51d
--- /dev/null
+++ b/src/fonts/SkRandomScalerContext.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRandomScalerContext.h"
+#include "SkGlyph.h"
+#include "SkPath.h"
+#include "SkCanvas.h"
+#include "SkRasterizer.h"
+
+class SkRandomScalerContext : public SkScalerContext {
+public:
+    SkRandomScalerContext(SkRandomTypeface*, const SkDescriptor*, bool fFakeIt);
+    virtual ~SkRandomScalerContext();
+
+protected:
+    unsigned generateGlyphCount() override;
+    uint16_t generateCharToGlyph(SkUnichar) override;
+    void generateAdvance(SkGlyph*) override;
+    void generateMetrics(SkGlyph*) override;
+    void generateImage(const SkGlyph&) override;
+    void generatePath(const SkGlyph&, SkPath*) override;
+    void generateFontMetrics(SkPaint::FontMetrics*) override;
+
+private:
+    SkRandomTypeface*     fFace;
+    SkScalerContext* fProxy;
+    bool fFakeIt;
+};
+
+#define STD_SIZE    1
+
+#include "SkDescriptor.h"
+
+SkRandomScalerContext::SkRandomScalerContext(SkRandomTypeface* face, const SkDescriptor* desc,
+                                             bool fakeIt)
+        : SkScalerContext(face, desc)
+        , fFace(face)
+        , fFakeIt(fakeIt) {
+    fProxy = face->proxy()->createScalerContext(desc);
+}
+
+SkRandomScalerContext::~SkRandomScalerContext() {
+    SkDELETE(fProxy);
+}
+
+unsigned SkRandomScalerContext::generateGlyphCount() {
+    return fProxy->getGlyphCount();
+}
+
+uint16_t SkRandomScalerContext::generateCharToGlyph(SkUnichar uni) {
+    return fProxy->charToGlyphID(uni);
+}
+
+void SkRandomScalerContext::generateAdvance(SkGlyph* glyph) {
+    fProxy->getAdvance(glyph);
+}
+
+void SkRandomScalerContext::generateMetrics(SkGlyph* glyph) {
+    // Here we will change the mask format of the glyph
+    // NOTE this is being overridden by the base class
+    SkMask::Format format;
+    switch (glyph->getGlyphID() % 4) {
+        case 0:
+            format = SkMask::kLCD16_Format;
+            break;
+        case 1:
+            format = SkMask::kA8_Format;
+            break;
+        case 2:
+            format = SkMask::kARGB32_Format;
+            break;
+        case 3:
+            format = SkMask::kBW_Format;
+            break;
+    }
+
+    fProxy->getMetrics(glyph);
+
+    glyph->fMaskFormat = format;
+    if (fFakeIt) {
+        return;
+    }
+    if (SkMask::kARGB32_Format == format) {
+        SkPath path;
+        fProxy->getPath(*glyph, &path);
+
+        SkRect storage;
+        const SkPaint& paint = fFace->paint();
+        const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(),
+                                                            &storage,
+                                                            SkPaint::kFill_Style);
+        SkIRect ibounds;
+        newBounds.roundOut(&ibounds);
+        glyph->fLeft = ibounds.fLeft;
+        glyph->fTop = ibounds.fTop;
+        glyph->fWidth = ibounds.width();
+        glyph->fHeight = ibounds.height();
+    } else {
+        SkPath      devPath, fillPath;
+        SkMatrix    fillToDevMatrix;
+
+        this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
+
+        // just use devPath
+        const SkIRect ir = devPath.getBounds().roundOut();
+
+        if (ir.isEmpty() || !ir.is16Bit()) {
+            glyph->fLeft    = 0;
+            glyph->fTop     = 0;
+            glyph->fWidth   = 0;
+            glyph->fHeight  = 0;
+            return;
+        }
+        glyph->fLeft    = ir.fLeft;
+        glyph->fTop     = ir.fTop;
+        glyph->fWidth   = SkToU16(ir.width());
+        glyph->fHeight  = SkToU16(ir.height());
+
+        if (glyph->fWidth > 0) {
+            switch (glyph->fMaskFormat) {
+            case SkMask::kLCD16_Format:
+                glyph->fWidth += 2;
+                glyph->fLeft -= 1;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void SkRandomScalerContext::generateImage(const SkGlyph& glyph) {
+    SkMask::Format format = (SkMask::Format)glyph.fMaskFormat;
+    switch (glyph.getGlyphID() % 4) {
+        case 0:
+            format = SkMask::kLCD16_Format;
+            break;
+        case 1:
+            format = SkMask::kA8_Format;
+            break;
+        case 2:
+            format = SkMask::kARGB32_Format;
+            break;
+        case 3:
+            format = SkMask::kBW_Format;
+            break;
+    }
+    const_cast<SkGlyph&>(glyph).fMaskFormat = format;
+
+    // if the format is ARGB, we just draw the glyph from path ourselves.  Otherwise, we force
+    // our proxy context to generate the image from paths.
+    if (!fFakeIt) {
+        if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
+            SkPath path;
+            fProxy->getPath(glyph, &path);
+
+            SkBitmap bm;
+            bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight),
+                             glyph.fImage, glyph.rowBytes());
+            bm.eraseColor(0);
+
+            SkCanvas canvas(bm);
+            canvas.translate(-SkIntToScalar(glyph.fLeft),
+                             -SkIntToScalar(glyph.fTop));
+            canvas.drawPath(path, fFace->paint());
+        } else {
+            fProxy->forceGenerateImageFromPath();
+            fProxy->getImage(glyph);
+            fProxy->forceOffGenerateImageFromPath();
+        }
+    } else {
+        sk_bzero(glyph.fImage, glyph.computeImageSize());
+    }
+}
+
+void SkRandomScalerContext::generatePath(const SkGlyph& glyph, SkPath* path) {
+    fProxy->getPath(glyph, path);
+}
+
+void SkRandomScalerContext::generateFontMetrics(SkPaint::FontMetrics* metrics) {
+    fProxy->getFontMetrics(metrics);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTypefaceCache.h"
+
+SkRandomTypeface::SkRandomTypeface(SkTypeface* proxy, const SkPaint& paint, bool fakeIt)
+    : SkTypeface(proxy->fontStyle(), SkTypefaceCache::NewFontID(), false)
+    , fProxy(SkRef(proxy))
+    , fPaint(paint)
+    , fFakeIt(fakeIt) {}
+
+SkRandomTypeface::~SkRandomTypeface() {
+    fProxy->unref();
+}
+
+SkScalerContext* SkRandomTypeface::onCreateScalerContext(
+                                            const SkDescriptor* desc) const {
+    return SkNEW_ARGS(SkRandomScalerContext, (const_cast<SkRandomTypeface*>(this), desc, fFakeIt));
+}
+
+void SkRandomTypeface::onFilterRec(SkScalerContextRec* rec) const {
+    fProxy->filterRec(rec);
+    rec->setHinting(SkPaint::kNo_Hinting);
+    rec->fMaskFormat = SkMask::kARGB32_Format;
+}
+
+SkAdvancedTypefaceMetrics* SkRandomTypeface::onGetAdvancedTypefaceMetrics(
+                                PerGlyphInfo info,
+                                const uint32_t* glyphIDs,
+                                uint32_t glyphIDsCount) const {
+    return fProxy->getAdvancedTypefaceMetrics(info, glyphIDs, glyphIDsCount);
+}
+
+SkStreamAsset* SkRandomTypeface::onOpenStream(int* ttcIndex) const {
+    return fProxy->openStream(ttcIndex);
+}
+
+void SkRandomTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
+                                      bool* isLocal) const {
+    fProxy->getFontDescriptor(desc, isLocal);
+}
+
+int SkRandomTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
+                                 uint16_t glyphs[], int glyphCount) const {
+    return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount);
+}
+
+int SkRandomTypeface::onCountGlyphs() const {
+    return fProxy->countGlyphs();
+}
+
+int SkRandomTypeface::onGetUPEM() const {
+    return fProxy->getUnitsPerEm();
+}
+
+void SkRandomTypeface::onGetFamilyName(SkString* familyName) const {
+    fProxy->getFamilyName(familyName);
+}
+
+SkTypeface::LocalizedStrings* SkRandomTypeface::onCreateFamilyNameIterator() const {
+    return fProxy->createFamilyNameIterator();
+}
+
+int SkRandomTypeface::onGetTableTags(SkFontTableTag tags[]) const {
+    return fProxy->getTableTags(tags);
+}
+
+size_t SkRandomTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
+                                    size_t length, void* data) const {
+    return fProxy->getTableData(tag, offset, length, data);
+}
+
diff --git a/src/fonts/SkRandomScalerContext.h b/src/fonts/SkRandomScalerContext.h
new file mode 100644
index 0000000..24b203f
--- /dev/null
+++ b/src/fonts/SkRandomScalerContext.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRandomScalerContext_DEFINED
+#define SkRandomScalerContext_DEFINED
+
+#include "SkScalerContext.h"
+#include "SkTypeface.h"
+
+/*
+ * This scaler context is for debug only purposes.  It will 'randomly' but deterministically return
+ * LCD / A8 / BW / RBGA masks based off of the Glyph ID
+ */
+
+class SkRandomTypeface : public SkTypeface {
+public:
+    SkRandomTypeface(SkTypeface* proxy, const SkPaint&, bool fakeit);
+    virtual ~SkRandomTypeface();
+
+    SkTypeface* proxy() const { return fProxy; }
+    const SkPaint& paint() const { return fPaint; }
+
+protected:
+    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
+        PerGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) const override;
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override;
+
+    int onCharsToGlyphs(const void* chars, Encoding encoding,
+                        uint16_t glyphs[], int glyphCount) const override;
+    int onCountGlyphs() const override;
+    int onGetUPEM() const override;
+
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
+
+    int onGetTableTags(SkFontTableTag tags[]) const override;
+    size_t onGetTableData(SkFontTableTag, size_t offset,
+                          size_t length, void* data) const override;
+
+private:
+    SkTypeface* fProxy;
+    SkPaint     fPaint;
+    bool        fFakeIt;
+};
+
+#endif
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp
index b562014..bc015ea 100644
--- a/src/gpu/GrAtlasTextContext.cpp
+++ b/src/gpu/GrAtlasTextContext.cpp
@@ -875,10 +875,7 @@
         if (glyph.fWidth) {
             this->bmpAppendGlyph(blob,
                                  runIndex,
-                                 GrGlyph::Pack(glyph.getGlyphID(),
-                                               glyph.getSubXFixed(),
-                                               glyph.getSubYFixed(),
-                                               GrGlyph::kCoverage_MaskStyle),
+                                 glyph,
                                  Sk48Dot16FloorToInt(fx),
                                  Sk48Dot16FloorToInt(fy),
                                  color,
@@ -945,10 +942,7 @@
                 if (glyph.fWidth) {
                     this->bmpAppendGlyph(blob,
                                          runIndex,
-                                         GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed(),
-                                                       GrGlyph::kCoverage_MaskStyle),
+                                         glyph,
                                          Sk48Dot16FloorToInt(fx),
                                          Sk48Dot16FloorToInt(fy),
                                          color,
@@ -983,10 +977,7 @@
 
                     this->bmpAppendGlyph(blob,
                                          runIndex,
-                                         GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed(),
-                                                       GrGlyph::kCoverage_MaskStyle),
+                                         glyph,
                                          Sk48Dot16FloorToInt(fx),
                                          Sk48Dot16FloorToInt(fy),
                                          color,
@@ -1011,10 +1002,7 @@
                     Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
                     this->bmpAppendGlyph(blob,
                                          runIndex,
-                                         GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed(),
-                                                       GrGlyph::kCoverage_MaskStyle),
+                                         glyph,
                                          Sk48Dot16FloorToInt(fx),
                                          Sk48Dot16FloorToInt(fy),
                                          color,
@@ -1039,10 +1027,7 @@
                     Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
                     this->bmpAppendGlyph(blob,
                                          runIndex,
-                                         GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed(),
-                                                       GrGlyph::kCoverage_MaskStyle),
+                                         glyph,
                                          Sk48Dot16FloorToInt(fx),
                                          Sk48Dot16FloorToInt(fy),
                                          color,
@@ -1168,10 +1153,7 @@
 
                 if (!this->dfAppendGlyph(blob,
                                          runIndex,
-                                         GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed(),
-                                                       GrGlyph::kDistance_MaskStyle),
+                                         glyph,
                                          x, y, color, fontScaler, clipRect,
                                          textRatio, viewMatrix)) {
                     // couldn't append, send to fallback
@@ -1201,10 +1183,7 @@
 
                 if (!this->dfAppendGlyph(blob,
                                          runIndex,
-                                         GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed(),
-                                                       GrGlyph::kDistance_MaskStyle),
+                                         glyph,
                                          x - advanceX, y - advanceY, color,
                                          fontScaler,
                                          clipRect,
@@ -1224,16 +1203,19 @@
 }
 
 void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
-                                        GrGlyph::PackedID packed,
+                                        const SkGlyph& skGlyph,
                                         int vx, int vy, GrColor color, GrFontScaler* scaler,
                                         const SkIRect& clipRect) {
     Run& run = blob->fRuns[runIndex];
     if (!fCurrStrike) {
         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
-        run.fStrike.reset(SkRef(fCurrStrike));
     }
 
-    GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
+    GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
+                                         skGlyph.getSubXFixed(),
+                                         skGlyph.getSubYFixed(),
+                                         GrGlyph::kCoverage_MaskStyle);
+    GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
     if (!glyph) {
         return;
     }
@@ -1259,7 +1241,7 @@
 
     // If the glyph is too large we fall back to paths
     if (glyph->fTooLargeForAtlas) {
-        this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
+        this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy));
         return;
     }
 
@@ -1267,7 +1249,10 @@
 
     PerSubRunInfo* subRun = &run.fSubRunInfo.back();
     if (run.fInitialized && subRun->fMaskFormat != format) {
-        subRun = &run.fSubRunInfo.push_back();
+        subRun = &run.push_back();
+        subRun->fStrike.reset(SkRef(fCurrStrike));
+    } else if (!run.fInitialized) {
+        subRun->fStrike.reset(SkRef(fCurrStrike));
     }
 
     run.fInitialized = true;
@@ -1285,7 +1270,7 @@
 }
 
 bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
-                                       GrGlyph::PackedID packed,
+                                       const SkGlyph& skGlyph,
                                        SkScalar sx, SkScalar sy, GrColor color,
                                        GrFontScaler* scaler,
                                        const SkIRect& clipRect,
@@ -1293,10 +1278,13 @@
     Run& run = blob->fRuns[runIndex];
     if (!fCurrStrike) {
         fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
-        run.fStrike.reset(SkRef(fCurrStrike));
     }
 
-    GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
+    GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
+                                         skGlyph.getSubXFixed(),
+                                         skGlyph.getSubYFixed(),
+                                         GrGlyph::kDistance_MaskStyle);
+    GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler);
     if (!glyph) {
         return true;
     }
@@ -1335,11 +1323,15 @@
     // TODO combine with the above
     // If the glyph is too large we fall back to paths
     if (glyph->fTooLargeForAtlas) {
-        this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
+        this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy);
         return true;
     }
 
     PerSubRunInfo* subRun = &run.fSubRunInfo.back();
+    if (!run.fInitialized) {
+        subRun->fStrike.reset(SkRef(fCurrStrike));
+    }
+    run.fInitialized = true;
     SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
     subRun->fMaskFormat = kA8_GrMaskFormat;
 
@@ -1352,17 +1344,16 @@
 }
 
 inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
-                                                GrFontScaler* scaler, SkScalar x, SkScalar y) {
+                                                GrFontScaler* scaler, const SkGlyph& skGlyph,
+                                                SkScalar x, SkScalar y) {
     if (NULL == glyph->fPath) {
-        SkPath* path = SkNEW(SkPath);
-        if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
-            // flag the glyph as being dead?
-            SkDELETE(path);
+        const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
+        if (!glyphPath) {
             return;
         }
-        glyph->fPath = path;
+
+        glyph->fPath = SkNEW_ARGS(SkPath, (*glyphPath));
     }
-    SkASSERT(glyph->fPath);
     blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
 }
 
@@ -1585,7 +1576,7 @@
 
             uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
             bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
-                                           run.fStrike->isAbandoned();
+                                           info.fStrike->isAbandoned();
             bool regenerateColors;
             if (fUseDistanceFields) {
                 regenerateColors = !fUseLCDText && run.fColor != args.fColor;
@@ -1633,41 +1624,46 @@
                         desc = newDesc;
                         cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
                         scaler = GrTextContext::GetGrFontScaler(cache);
-                        strike = run.fStrike;
+                        strike = info.fStrike;
                         typeface = run.fTypeface;
                     }
 
-                    if (run.fStrike->isAbandoned()) {
+                    if (info.fStrike->isAbandoned()) {
                         regenerateGlyphs = true;
                         strike = fFontCache->getStrike(scaler);
                     } else {
-                        strike = run.fStrike;
+                        strike = info.fStrike;
                     }
                 }
 
                 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
                     if (regenerateTextureCoords) {
                         size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
-                        GrGlyph* glyph;
+
+                        GrGlyph* glyph = blob->fGlyphs[glyphOffset];
+                        GrGlyph::PackedID id = glyph->fPackedID;
+                        const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
                         if (regenerateGlyphs) {
                             // Get the id from the old glyph, and use the new strike to lookup
                             // the glyph.
-                            glyph = blob->fGlyphs[glyphOffset];
-                            blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
+                            blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, fMaskFormat,
                                                                           scaler);
                         }
                         glyph = blob->fGlyphs[glyphOffset];
                         SkASSERT(glyph);
 
                         if (!fFontCache->hasGlyph(glyph) &&
-                            !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
+                            !strike->addGlyphToAtlas(batchTarget, glyph, scaler, skGlyph,
+                                                     fMaskFormat)) {
                             this->flush(batchTarget, &flushInfo);
                             this->initDraw(batchTarget, gp, pipeline);
                             brokenRun = glyphIdx > 0;
 
                             SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
                                                                                 glyph,
-                                                                                scaler);
+                                                                                scaler,
+                                                                                skGlyph,
+                                                                                fMaskFormat);
                             SkASSERT(success);
                         }
                         fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
@@ -1705,7 +1701,7 @@
                 run.fColor = args.fColor;
                 if (regenerateTextureCoords) {
                     if (regenerateGlyphs) {
-                        run.fStrike.reset(SkRef(strike));
+                        info.fStrike.reset(SkRef(strike));
                     }
                     info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
                                                         fFontCache->atlasGeneration(fMaskFormat);
diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h
index eb7ba47..83ffa0e 100644
--- a/src/gpu/GrAtlasTextContext.h
+++ b/src/gpu/GrAtlasTextContext.h
@@ -109,6 +109,19 @@
                     , fGlyphStartIndex(0)
                     , fGlyphEndIndex(0)
                     , fDrawAsDistanceFields(false) {}
+                SubRunInfo(const SubRunInfo& that)
+                    : fBulkUseToken(that.fBulkUseToken)
+                    , fStrike(SkSafeRef(that.fStrike.get()))
+                    , fAtlasGeneration(that.fAtlasGeneration)
+                    , fVertexStartIndex(that.fVertexStartIndex)
+                    , fVertexEndIndex(that.fVertexEndIndex)
+                    , fGlyphStartIndex(that.fGlyphStartIndex)
+                    , fGlyphEndIndex(that.fGlyphEndIndex)
+                    , fTextRatio(that.fTextRatio)
+                    , fMaskFormat(that.fMaskFormat)
+                    , fDrawAsDistanceFields(that.fDrawAsDistanceFields)
+                    , fUseLCDText(that.fUseLCDText) {
+                }
                 // Distance field text cannot draw coloremoji, and so has to fall back.  However,
                 // though the distance field text and the coloremoji may share the same run, they
                 // will have different descriptors.  If fOverrideDescriptor is non-NULL, then it
@@ -117,6 +130,7 @@
                 // significantly, and then the subrun could just have a refed pointer to the
                 // correct descriptor.
                 GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken;
+                SkAutoTUnref<GrBatchTextStrike> fStrike;
                 uint64_t fAtlasGeneration;
                 size_t fVertexStartIndex;
                 size_t fVertexEndIndex;
@@ -130,8 +144,9 @@
 
             SubRunInfo& push_back() {
                 // Forward glyph / vertex information to seed the new sub run
-                SubRunInfo& prevSubRun = fSubRunInfo.back();
                 SubRunInfo& newSubRun = fSubRunInfo.push_back();
+                SubRunInfo& prevSubRun = fSubRunInfo.fromBack(1);
+
                 newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex;
                 newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex;
 
@@ -140,7 +155,6 @@
                 return newSubRun;
             }
             static const int kMinSubRuns = 1;
-            SkAutoTUnref<GrBatchTextStrike> fStrike;
             SkAutoTUnref<SkTypeface> fTypeface;
             SkRect fVertexBounds;
             SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo;
@@ -259,13 +273,13 @@
     BitmapTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint,
                                 const SkMatrix& viewMatrix, SkGlyphCache** cache,
                                 SkPaint* dfPaint, SkScalar* textRatio);
-    void bmpAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
+    void bmpAppendGlyph(BitmapTextBlob*, int runIndex, const SkGlyph&, int left, int top,
                         GrColor color, GrFontScaler*, const SkIRect& clipRect);
-    bool dfAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, SkScalar sx, SkScalar sy,
+    bool dfAppendGlyph(BitmapTextBlob*, int runIndex, const SkGlyph&, SkScalar sx, SkScalar sy,
                        GrColor color, GrFontScaler*, const SkIRect& clipRect, SkScalar textRatio,
                        const SkMatrix& viewMatrix);
-    inline void appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
-                                GrFontScaler* scaler, SkScalar x, SkScalar y);
+    inline void appendGlyphPath(BitmapTextBlob*, GrGlyph*, GrFontScaler*, const SkGlyph&,
+                                SkScalar x, SkScalar y);
     inline void appendGlyphCommon(BitmapTextBlob*, Run*, Run::SubRunInfo*,
                                   const SkRect& positions, GrColor color,
                                   size_t vertexStride, bool useVertexColor,
diff --git a/src/gpu/GrBatchAtlas.cpp b/src/gpu/GrBatchAtlas.cpp
index 20e73e4..7d84757 100644
--- a/src/gpu/GrBatchAtlas.cpp
+++ b/src/gpu/GrBatchAtlas.cpp
@@ -17,9 +17,9 @@
     loc->fY += offset.fY;
 }
 
-static GrBatchAtlas::AtlasID create_id(int index, int generation) {
-    // Generation ID can roll over because we only check for equality
+static GrBatchAtlas::AtlasID create_id(uint32_t index, uint64_t generation) {
     SkASSERT(index < (1 << 16));
+    SkASSERT(generation < ((uint64_t)1 << 48));
     return generation << 16 | index;
 }
 
@@ -40,8 +40,8 @@
     // monotonically incrementing number which is bumped every time the cpu backing store is
     // wiped, or when the plot itself is evicted from the atlas(ie, there is continuity in genID()
     // across atlas spills)
-    int index() const { return fIndex; }
-    int genID() const { return fGenID; }
+    uint32_t index() const { return fIndex; }
+    uint64_t genID() const { return fGenID; }
     GrBatchAtlas::AtlasID id() { return fID; }
 
     GrTexture* texture() const { return fTexture; }
@@ -121,8 +121,8 @@
         SkDEBUGCODE(fDirty = false;)
     }
 
-    int x() const { return fX; }
-    int y() const { return fY; }
+    uint32_t x() const { return fX; }
+    uint32_t y() const { return fY; }
 
 private:
     BatchPlot()
@@ -153,7 +153,7 @@
         delete fRects;
     }
 
-    void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint32_t generation,
+    void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint64_t generation,
               int offX, int offY, int width, int height, size_t bpp) {
         fIndex = index;
         fGenID = generation;
@@ -176,13 +176,13 @@
     BatchToken fLastUse;
 
     uint32_t fIndex;
-    uint32_t fGenID;
+    uint64_t fGenID;
     GrBatchAtlas::AtlasID fID;
     unsigned char* fData;
-    int fWidth;
-    int fHeight;
-    int fX;
-    int fY;
+    uint32_t fWidth;
+    uint32_t fHeight;
+    uint32_t fX;
+    uint32_t fY;
     GrTexture* fTexture;
     GrRectanizer* fRects;
     GrBatchAtlas* fAtlas;
@@ -226,8 +226,8 @@
     , fPlotHeight(texture->height() / numPlotsY)
     , fAtlasGeneration(kInvalidAtlasGeneration + 1) {
     SkASSERT(fNumPlotsX * fNumPlotsY <= BulkUseTokenUpdater::kMaxPlots);
-    SkASSERT(fPlotWidth * fNumPlotsX == texture->width());
-    SkASSERT(fPlotHeight * fNumPlotsY == texture->height());
+    SkASSERT(fPlotWidth * fNumPlotsX == static_cast<uint32_t>(texture->width()));
+    SkASSERT(fPlotHeight * fNumPlotsY == static_cast<uint32_t>(texture->height()));
 
     // We currently do not support compressed atlases...
     SkASSERT(!GrPixelConfigIsCompressed(texture->desc().fConfig));
@@ -239,7 +239,7 @@
     SkAutoTUnref<BatchPlot>* currPlot = fPlotArray;
     for (int y = fNumPlotsY - 1, r = 0; y >= 0; --y, ++r) {
         for (int x = fNumPlotsX - 1, c = 0; x >= 0; --x, ++c) {
-            int id = r * fNumPlotsX + c;
+            uint32_t id = r * fNumPlotsX + c;
             currPlot->reset(SkNEW(BatchPlot));
             (*currPlot)->init(this, texture, id, 1, x, y, fPlotWidth, fPlotHeight, fBPP);
 
@@ -287,7 +287,9 @@
 bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget,
                               int width, int height, const void* image, SkIPoint16* loc) {
     // We should already have a texture, TODO clean this up
-    SkASSERT(fTexture && width <= fPlotWidth && height <= fPlotHeight);
+    SkASSERT(fTexture &&
+             static_cast<uint32_t>(width) <= fPlotWidth &&
+             static_cast<uint32_t>(height) <= fPlotHeight);
 
     // now look through all allocated plots for one we can share, in Most Recently Refed order
     GrBatchPlotList::Iter plotIter;
@@ -331,7 +333,7 @@
     int index = plot->index();
     int x = plot->x();
     int y = plot->y();
-    int generation = plot->genID();
+    uint64_t generation = plot->genID();
 
     this->processEviction(plot->id());
     fPlotList.remove(plot);
@@ -352,14 +354,14 @@
 }
 
 bool GrBatchAtlas::hasID(AtlasID id) {
-    int index = GetIndexFromID(id);
+    uint32_t index = GetIndexFromID(id);
     SkASSERT(index < fNumPlotsX * fNumPlotsY);
     return fPlotArray[index]->genID() == GetGenerationFromID(id);
 }
 
 void GrBatchAtlas::setLastUseToken(AtlasID id, BatchToken batchToken) {
     SkASSERT(this->hasID(id));
-    int index = GetIndexFromID(id);
+    uint32_t index = GetIndexFromID(id);
     SkASSERT(index < fNumPlotsX * fNumPlotsY);
     this->makeMRU(fPlotArray[index]);
     fPlotArray[index]->setLastUseToken(batchToken);
diff --git a/src/gpu/GrBatchAtlas.h b/src/gpu/GrBatchAtlas.h
index 83ef5ec..4d21394 100644
--- a/src/gpu/GrBatchAtlas.h
+++ b/src/gpu/GrBatchAtlas.h
@@ -24,7 +24,7 @@
     typedef uint64_t BatchToken;
     // An AtlasID is an opaque handle which callers can use to determine if the atlas contains
     // a specific piece of data
-    typedef uint32_t AtlasID;
+    typedef uint64_t AtlasID;
     static const uint32_t kInvalidAtlasID = 0;
     static const uint64_t kInvalidAtlasGeneration = 0;
 
@@ -67,6 +67,11 @@
     class BulkUseTokenUpdater {
     public:
         BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
+        BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
+        : fPlotsToUpdate(that.fPlotsToUpdate)
+        , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) {
+        }
+
         void add(AtlasID id) {
             int index = GrBatchAtlas::GetIndexFromID(id);
             if (!this->find(index)) {
@@ -107,12 +112,13 @@
     }
 
 private:
-    static int GetIndexFromID(AtlasID id) {
+    static uint32_t GetIndexFromID(AtlasID id) {
         return id & 0xffff;
     }
 
-    static int GetGenerationFromID(AtlasID id) {
-        return (id >> 16) & 0xffff;
+    // top 48 bits are reserved for the generation ID
+    static uint64_t GetGenerationFromID(AtlasID id) {
+        return (id >> 16) & 0xffffffffffff;
     }
 
     inline void updatePlot(GrBatchTarget*, AtlasID*, BatchPlot*);
@@ -122,10 +128,10 @@
     inline void processEviction(AtlasID);
 
     GrTexture* fTexture;
-    int fNumPlotsX;
-    int fNumPlotsY;
-    int fPlotWidth;
-    int fPlotHeight;
+    uint32_t fNumPlotsX;
+    uint32_t fNumPlotsY;
+    uint32_t fPlotWidth;
+    uint32_t fPlotHeight;
     size_t fBPP;
     uint64_t fAtlasGeneration;
 
diff --git a/src/gpu/GrBatchFontCache.cpp b/src/gpu/GrBatchFontCache.cpp
index 2d6213c..007f18a 100644
--- a/src/gpu/GrBatchFontCache.cpp
+++ b/src/gpu/GrBatchFontCache.cpp
@@ -173,20 +173,20 @@
     }
 }
 
-GrGlyph* GrBatchTextStrike::generateGlyph(GrGlyph::PackedID packed,
+GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
                                           GrFontScaler* scaler) {
     SkIRect bounds;
     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
-        if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
+        if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) {
             return NULL;
         }
     } else {
-        if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
+        if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) {
             return NULL;
         }
     }
-    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
-    
+    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph);
+
     GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
     glyph->init(packed, bounds, format);
     fCache.add(glyph);
@@ -206,7 +206,8 @@
 }
 
 bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
-                                        GrFontScaler* scaler) {
+                                        GrFontScaler* scaler, const SkGlyph& skGlyph,
+                                        GrMaskFormat expectedMaskFormat) {
     SkASSERT(glyph);
     SkASSERT(scaler);
     SkASSERT(fCache.find(glyph->fPackedID));
@@ -214,27 +215,25 @@
 
     SkAutoUnref ar(SkSafeRef(scaler));
 
-    int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
+    int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
 
     size_t size = glyph->fBounds.area() * bytesPerPixel;
     GrAutoMalloc<1024> storage(size);
 
     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
-        if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
-                                           glyph->height(),
+        if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(),
                                            storage.get())) {
             return false;
         }
     } else {
-        if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
-                                         glyph->height(),
-                                         glyph->width() * bytesPerPixel,
+        if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(),
+                                         glyph->width() * bytesPerPixel, expectedMaskFormat,
                                          storage.get())) {
             return false;
         }
     }
 
-    bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, glyph->fMaskFormat,
+    bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, expectedMaskFormat,
                                                glyph->width(), glyph->height(),
                                                storage.get(), &glyph->fAtlasLocation);
     if (success) {
diff --git a/src/gpu/GrBatchFontCache.h b/src/gpu/GrBatchFontCache.h
index f1c56ac..f6617d0 100644
--- a/src/gpu/GrBatchFontCache.h
+++ b/src/gpu/GrBatchFontCache.h
@@ -12,6 +12,7 @@
 #include "GrDrawTarget.h"
 #include "GrFontScaler.h"
 #include "GrGlyph.h"
+#include "SkGlyph.h"
 #include "SkTDynamicHash.h"
 #include "SkVarAlloc.h"
 
@@ -32,16 +33,36 @@
     const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
     GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
 
-    inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
+    inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
+                             GrFontScaler* scaler) {
         GrGlyph* glyph = fCache.find(packed);
         if (NULL == glyph) {
-            glyph = this->generateGlyph(packed, scaler);
+            glyph = this->generateGlyph(skGlyph, packed, scaler);
         }
         return glyph;
     }
 
-    // returns true if glyph successfully added to texture atlas, false otherwise
-    bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
+    // This variant of the above function is called by TextBatch.  At this point, it is possible
+    // that the maskformat of the glyph differs from what we expect.  In these cases we will just
+    // draw a clear square.
+    // skbug:4143 crbug:510931
+    inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
+                             GrMaskFormat expectedMaskFormat, GrFontScaler* scaler) {
+        GrGlyph* glyph = fCache.find(packed);
+        if (NULL == glyph) {
+            glyph = this->generateGlyph(skGlyph, packed, scaler);
+            glyph->fMaskFormat = expectedMaskFormat;
+        }
+        return glyph;
+    }
+
+    // returns true if glyph successfully added to texture atlas, false otherwise.  If the glyph's
+    // mask format has changed, then addGlyphToAtlas will draw a clear box.  This will almost never
+    // happen.
+    // TODO we can handle some of these cases if we really want to, but the long term solution is to
+    // get the actual glyph image itself when we get the glyph metrics.
+    bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*, const SkGlyph&,
+                         GrMaskFormat expectedMaskFormat);
 
     // testing
     int countGlyphs() const { return fCache.count(); }
@@ -68,7 +89,7 @@
     int fAtlasedGlyphs;
     bool fIsAbandoned;
 
-    GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
+    GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, GrFontScaler*);
 
     friend class GrBatchFontCache;
 };
diff --git a/src/gpu/GrFontScaler.cpp b/src/gpu/GrFontScaler.cpp
index ed1970e..1d228a4 100644
--- a/src/gpu/GrFontScaler.cpp
+++ b/src/gpu/GrFontScaler.cpp
@@ -83,10 +83,7 @@
     return fKey;
 }
 
-GrMaskFormat GrFontScaler::getPackedGlyphMaskFormat(GrGlyph::PackedID packed) const {
-    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
-                                                      GrGlyph::UnpackFixedX(packed),
-                                                      GrGlyph::UnpackFixedY(packed));
+GrMaskFormat GrFontScaler::getPackedGlyphMaskFormat(const SkGlyph& glyph) const {
     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
     switch (format) {
         case SkMask::kBW_Format:
@@ -103,19 +100,23 @@
     }
 }
 
-bool GrFontScaler::getPackedGlyphBounds(GrGlyph::PackedID packed, SkIRect* bounds) {
-    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
-                                                      GrGlyph::UnpackFixedX(packed),
-                                                      GrGlyph::UnpackFixedY(packed));
+bool GrFontScaler::getPackedGlyphBounds(const SkGlyph& glyph, SkIRect* bounds) {
+#if 1
+    // crbug:510931
+    // Retrieving the image from the cache can actually change the mask format.
+    fStrike->findImage(glyph);
+#endif
     bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
 
     return true;
 }
 
-bool GrFontScaler::getPackedGlyphDFBounds(GrGlyph::PackedID packed, SkIRect* bounds) {
-    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
-                                                      GrGlyph::UnpackFixedX(packed),
-                                                      GrGlyph::UnpackFixedY(packed));
+bool GrFontScaler::getPackedGlyphDFBounds(const SkGlyph& glyph, SkIRect* bounds) {
+#if 1
+    // crbug:510931
+    // Retrieving the image from the cache can actually change the mask format.
+    fStrike->findImage(glyph);
+#endif
     bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
     bounds->outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
 
@@ -148,12 +149,8 @@
 }
 }
 
-bool GrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed,
-                                         int width, int height,
-                                         int dstRB, void* dst) {
-    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
-                                                      GrGlyph::UnpackFixedX(packed),
-                                                      GrGlyph::UnpackFixedY(packed));
+bool GrFontScaler::getPackedGlyphImage(const SkGlyph& glyph, int width, int height, int dstRB,
+                                       GrMaskFormat expectedMaskFormat, void* dst) {
     SkASSERT(glyph.fWidth == width);
     SkASSERT(glyph.fHeight == height);
     const void* src = fStrike->findImage(glyph);
@@ -161,6 +158,18 @@
         return false;
     }
 
+    // crbug:510931
+    // Retrieving the image from the cache can actually change the mask format.  This case is very
+    // uncommon so for now we just draw a clear box for these glyphs.
+    if (getPackedGlyphMaskFormat(glyph) != expectedMaskFormat) {
+        const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
+        for (int y = 0; y < height; y++) {
+            sk_bzero(dst, width * bpp);
+            dst = (char*)dst + dstRB;
+        }
+        return true;
+    }
+
     int srcRB = glyph.rowBytes();
     // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to
     // check the glyph's format, not the strike's format, and to be able to convert to any of the
@@ -168,7 +177,7 @@
     if (SkMask::kBW_Format == glyph.fMaskFormat) {
         // expand bits to our mask type
         const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
-        switch (this->getMaskFormat()) {
+        switch (expectedMaskFormat) {
             case kA8_GrMaskFormat:{
                 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
                 expand_bits(bytes, bits, width, height, dstRB, srcRB);
@@ -185,7 +194,7 @@
     } else if (srcRB == dstRB) {
         memcpy(dst, src, dstRB * height);
     } else {
-        const int bbp = GrMaskFormatBytesPerPixel(this->getMaskFormat());
+        const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
         for (int y = 0; y < height; y++) {
             memcpy(dst, src, width * bbp);
             src = (const char*)src + srcRB;
@@ -195,12 +204,7 @@
     return true;
 }
 
-bool GrFontScaler::getPackedGlyphDFImage(GrGlyph::PackedID packed,
-                                         int width, int height,
-                                         void* dst) {
-    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
-                                                      GrGlyph::UnpackFixedX(packed),
-                                                      GrGlyph::UnpackFixedY(packed));
+bool GrFontScaler::getPackedGlyphDFImage(const SkGlyph& glyph, int width, int height, void* dst) {
     SkASSERT(glyph.fWidth + 2*SK_DistanceFieldPad == width);
     SkASSERT(glyph.fHeight + 2*SK_DistanceFieldPad == height);
     const void* image = fStrike->findImage(glyph);
@@ -229,14 +233,12 @@
     return true;
 }
 
-// we should just return const SkPath* (NULL means false)
-bool GrFontScaler::getGlyphPath(uint16_t glyphID, SkPath* path) {
+const SkPath* GrFontScaler::getGlyphPath(const SkGlyph& glyph) {
+    return fStrike->findPath(glyph);
+}
 
-    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID);
-    const SkPath* skPath = fStrike->findPath(glyph);
-    if (skPath) {
-        *path = *skPath;
-        return true;
-    }
-    return false;
+const SkGlyph& GrFontScaler::grToSkGlyph(GrGlyph::PackedID id) {
+    return fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(id),
+                                      GrGlyph::UnpackFixedX(id),
+                                      GrGlyph::UnpackFixedY(id));
 }
diff --git a/src/gpu/GrFontScaler.h b/src/gpu/GrFontScaler.h
index 54d1e3f..eb93d91 100644
--- a/src/gpu/GrFontScaler.h
+++ b/src/gpu/GrFontScaler.h
@@ -13,6 +13,7 @@
 
 #include "SkDescriptor.h"
 
+class SkGlyph;
 class SkPath;
 
 /*
@@ -67,14 +68,14 @@
     
     const GrFontDescKey* getKey();
     GrMaskFormat getMaskFormat() const;
-    GrMaskFormat getPackedGlyphMaskFormat(GrGlyph::PackedID) const;
-    bool getPackedGlyphBounds(GrGlyph::PackedID, SkIRect* bounds);
-    bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height,
-                                     int rowBytes, void* image);
-    bool getPackedGlyphDFBounds(GrGlyph::PackedID, SkIRect* bounds);
-    bool getPackedGlyphDFImage(GrGlyph::PackedID, int width, int height,
-                                       void* image);
-    bool getGlyphPath(uint16_t glyphID, SkPath*);
+    GrMaskFormat getPackedGlyphMaskFormat(const SkGlyph&) const;
+    bool getPackedGlyphBounds(const SkGlyph&, SkIRect* bounds);
+    bool getPackedGlyphImage(const SkGlyph&, int width, int height, int rowBytes,
+                             GrMaskFormat expectedMaskFormat, void* image);
+    bool getPackedGlyphDFBounds(const SkGlyph&, SkIRect* bounds);
+    bool getPackedGlyphDFImage(const SkGlyph&, int width, int height, void* image);
+    const SkPath* getGlyphPath(const SkGlyph&);
+    const SkGlyph& grToSkGlyph(GrGlyph::PackedID);
     
 private:
     SkGlyphCache*  fStrike;
diff --git a/tests/TextBlobCacheTest.cpp b/tests/TextBlobCacheTest.cpp
new file mode 100644
index 0000000..c063040
--- /dev/null
+++ b/tests/TextBlobCacheTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "sk_tool_utils.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+#include "SkTextBlob.h"
+#include "SkFontMgr.h"
+#include "SkGraphics.h"
+#include "SkSurface.h"
+#include "SkTypeface.h"
+#include "../src/fonts/SkRandomScalerContext.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    #include "SkTypeface_win.h"
+#endif
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+
+struct TextBlobWrapper {
+    // This class assumes it 'owns' the textblob it wraps, and thus does not need to take a ref
+    explicit TextBlobWrapper(const SkTextBlob* blob) : fBlob(blob) {}
+    TextBlobWrapper(const TextBlobWrapper& blob) : fBlob(SkRef(blob.fBlob.get())) {}
+
+    SkAutoTUnref<const SkTextBlob> fBlob;
+};
+
+static void draw(SkCanvas* canvas, int redraw, const SkTArray<TextBlobWrapper>& blobs) {
+    int yOffset = 0;
+    for (int r = 0; r < redraw; r++) {
+        for (int i = 0; i < blobs.count(); i++) {
+            const SkTextBlob* blob = blobs[i].fBlob.get();
+            const SkRect& bounds = blob->bounds();
+            yOffset += SkScalarCeilToInt(bounds.height());
+            SkPaint paint;
+            canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint);
+        }
+    }
+}
+
+static const int kWidth = 1024;
+static const int kHeight = 768;
+
+// This test hammers the GPU textblobcache and font atlas
+static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContextFactory* factory,
+                                  int maxTotalText, int maxGlyphID, int maxFamilies, bool normal) {
+    // setup surface
+    uint32_t flags = 0;
+    SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
+
+    // We don't typically actually draw with this unittest
+    GrContext* ctx = factory->get(GrContextFactory::kNull_GLContextType);
+    SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType);
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info,
+                                                               0, &props));
+    REPORTER_ASSERT(reporter, surface);
+    if (!surface) {
+        return;
+    }
+
+    SkCanvas* canvas = surface->getCanvas();
+
+    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+
+    int count = SkMin32(fm->countFamilies(), maxFamilies);
+
+    // make a ton of text
+    SkAutoTArray<uint16_t> text(maxTotalText);
+    for (int i = 0; i < maxTotalText; i++) {
+        text[i] = i % maxGlyphID;
+    }
+
+    // generate textblobs
+    SkTArray<TextBlobWrapper> blobs;
+    for (int i = 0; i < count; i++) {
+        SkPaint paint;
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        paint.setTextSize(48); // draw big glyphs to really stress the atlas
+
+        SkString familyName;
+        fm->getFamilyName(i, &familyName);
+        SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
+        for (int j = 0; j < set->count(); ++j) {
+            SkFontStyle fs;
+            set->getStyle(j, &fs, NULL);
+
+            // We use a typeface which randomy returns unexpected mask formats to fuzz
+            SkAutoTUnref<SkTypeface> orig(set->createTypeface(j));
+            if (normal) {
+                paint.setTypeface(orig);
+            } else {
+                SkAutoTUnref<SkTypeface> typeface(SkNEW_ARGS(SkRandomTypeface, (orig, paint, true)));
+                paint.setTypeface(typeface);
+            }
+
+            SkTextBlobBuilder builder;
+            for (int aa = 0; aa < 2; aa++) {
+                for (int subpixel = 0; subpixel < 2; subpixel++) {
+                    for (int lcd = 0; lcd < 2; lcd++) {
+                        paint.setAntiAlias(SkToBool(aa));
+                        paint.setSubpixelText(SkToBool(subpixel));
+                        paint.setLCDRenderText(SkToBool(lcd));
+                        if (!SkToBool(lcd)) {
+                            paint.setTextSize(160);
+                        }
+                        const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint,
+                                                                                   maxTotalText,
+                                                                                   0, 0,
+                                                                                   NULL);
+                        memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t));
+                    }
+                }
+            }
+            SkNEW_APPEND_TO_TARRAY(&blobs, TextBlobWrapper, (builder.build()));
+        }
+    }
+
+    // create surface where LCD is impossible
+    info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
+    SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry);
+    SkAutoTUnref<SkSurface> surfaceNoLCD(canvas->newSurface(info, &propsNoLCD));
+    REPORTER_ASSERT(reporter, surface);
+    if (!surface) {
+        return;
+    }
+
+    SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas();
+
+    // test redraw
+    draw(canvas, 2, blobs);
+    draw(canvasNoLCD, 2, blobs);
+
+    // test draw after free
+    ctx->freeGpuResources();
+    draw(canvas, 1, blobs);
+
+    ctx->freeGpuResources();
+    draw(canvasNoLCD, 1, blobs);
+
+    // test draw after abandon
+    ctx->abandonContext();
+    draw(canvas, 1, blobs);
+}
+
+DEF_GPUTEST(TextBlobCache, reporter, factory) {
+    text_blob_cache_inner(reporter, factory, 4096, 256, 30, true);
+}
+
+DEF_GPUTEST(TextBlobAbnormal, reporter, factory) {
+#ifdef SK_BUILD_FOR_ANDROID
+    text_blob_cache_inner(reporter, factory, 256, 256, 30, false);
+#else
+    text_blob_cache_inner(reporter, factory, 512, 256, 30, false);
+#endif
+}
+#endif