Ganesh text rendering cleanup.

Rename GrTextStrike.{cpp.h} to GrFontCache.{cpp,h}
Move contents of GrTextStrike_impl.h to GrFontCache.h
Move glyph uploading to a separate function and remove harmful gotos
Add assert on glyph upload failure (shouldn't happen)

Review URL: https://codereview.chromium.org/780923002
diff --git a/src/gpu/GrFontCache.cpp b/src/gpu/GrFontCache.cpp
new file mode 100644
index 0000000..cd1bada
--- /dev/null
+++ b/src/gpu/GrFontCache.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrFontCache.h"
+#include "GrGpu.h"
+#include "GrRectanizer.h"
+#include "GrSurfacePriv.h"
+#include "SkString.h"
+
+#include "SkDistanceFieldGen.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define GR_ATLAS_TEXTURE_WIDTH 1024
+#define GR_ATLAS_TEXTURE_HEIGHT 2048
+
+#define GR_PLOT_WIDTH  256
+#define GR_PLOT_HEIGHT 256
+
+#define GR_NUM_PLOTS_X   (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
+#define GR_NUM_PLOTS_Y   (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
+
+#define FONT_CACHE_STATS 0
+#if FONT_CACHE_STATS
+static int g_PurgeCount = 0;
+#endif
+
+GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
+    gpu->ref();
+    for (int i = 0; i < kAtlasCount; ++i) {
+        fAtlases[i] = NULL;
+    }
+
+    fHead = fTail = NULL;
+}
+
+GrFontCache::~GrFontCache() {
+    SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
+    while (!iter.done()) {
+        SkDELETE(&(*iter));
+        ++iter;
+    }
+    for (int i = 0; i < kAtlasCount; ++i) {
+        delete fAtlases[i];
+    }
+    fGpu->unref();
+#if FONT_CACHE_STATS
+      SkDebugf("Num purges: %d\n", g_PurgeCount);
+#endif
+}
+
+static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
+    static const GrPixelConfig sPixelConfigs[] = {
+        kAlpha_8_GrPixelConfig,
+        kRGB_565_GrPixelConfig,
+        kSkia8888_GrPixelConfig
+    };
+    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
+
+    return sPixelConfigs[format];
+}
+
+static int mask_format_to_atlas_index(GrMaskFormat format) {
+    static const int sAtlasIndices[] = {
+        GrFontCache::kA8_AtlasType,
+        GrFontCache::k565_AtlasType,
+        GrFontCache::k8888_AtlasType
+    };
+    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
+
+    SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
+    return sAtlasIndices[format];
+}
+
+GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
+    GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
+    fCache.add(strike);
+
+    if (fHead) {
+        fHead->fPrev = strike;
+    } else {
+        SkASSERT(NULL == fTail);
+        fTail = strike;
+    }
+    strike->fPrev = NULL;
+    strike->fNext = fHead;
+    fHead = strike;
+
+    return strike;
+}
+
+void GrFontCache::freeAll() {
+    SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
+    while (!iter.done()) {
+        SkDELETE(&(*iter));
+        ++iter;
+    }
+    fCache.rewind();
+    for (int i = 0; i < kAtlasCount; ++i) {
+        delete fAtlases[i];
+        fAtlases[i] = NULL;
+    }
+    fHead = NULL;
+    fTail = NULL;
+}
+
+void GrFontCache::purgeStrike(GrTextStrike* strike) {
+    fCache.remove(*(strike->fFontScalerKey));
+    this->detachStrikeFromList(strike);
+    delete strike;
+}
+
+
+GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
+                                int width, int height, const void* image,
+                                SkIPoint16* loc) {
+    GrPixelConfig config = mask_format_to_pixel_config(format);
+    int atlasIndex = mask_format_to_atlas_index(format);
+    if (NULL == fAtlases[atlasIndex]) {
+        SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
+                                            GR_ATLAS_TEXTURE_HEIGHT);
+        fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
+                                                    textureSize,
+                                                    GR_NUM_PLOTS_X,
+                                                    GR_NUM_PLOTS_Y,
+                                                    true));
+    }
+    return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc);
+}
+
+
+bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
+    SkASSERT(preserveStrike);
+
+    int index = mask_format_to_atlas_index(glyph->fMaskFormat);
+    GrAtlas* atlas = fAtlases[index];
+    GrPlot* plot = atlas->getUnusedPlot();
+    if (NULL == plot) {
+        return false;
+    }
+    plot->resetRects();
+
+    GrTextStrike* strike = fHead;
+    while (strike) {
+        GrTextStrike* strikeToPurge = strike;
+        strike = strikeToPurge->fNext;
+        strikeToPurge->removePlot(plot);
+
+        // clear out any empty strikes (except this one)
+        if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
+            this->purgeStrike(strikeToPurge);
+        }
+    }
+
+#if FONT_CACHE_STATS
+    ++g_PurgeCount;
+#endif
+
+    return true;
+}
+
+#ifdef SK_DEBUG
+void GrFontCache::validate() const {
+    int count = fCache.count();
+    if (0 == count) {
+        SkASSERT(!fHead);
+        SkASSERT(!fTail);
+    } else if (1 == count) {
+        SkASSERT(fHead == fTail);
+    } else {
+        SkASSERT(fHead != fTail);
+    }
+
+    int count2 = 0;
+    const GrTextStrike* strike = fHead;
+    while (strike) {
+        count2 += 1;
+        strike = strike->fNext;
+    }
+    SkASSERT(count == count2);
+
+    count2 = 0;
+    strike = fTail;
+    while (strike) {
+        count2 += 1;
+        strike = strike->fPrev;
+    }
+    SkASSERT(count == count2);
+}
+#endif
+
+void GrFontCache::dump() const {
+    static int gDumpCount = 0;
+    for (int i = 0; i < kAtlasCount; ++i) {
+        if (fAtlases[i]) {
+            GrTexture* texture = fAtlases[i]->getTexture();
+            if (texture) {
+                SkString filename;
+#ifdef SK_BUILD_FOR_ANDROID
+                filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
+#else
+                filename.printf("fontcache_%d%d.png", gDumpCount, i);
+#endif
+                texture->surfacePriv().savePixels(filename.c_str());
+            }
+        }
+    }
+    ++gDumpCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+    static int gCounter;
+#endif
+
+/*
+    The text strike is specific to a given font/style/matrix setup, which is
+    represented by the GrHostFontScaler object we are given in getGlyph().
+
+    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
+    atlas and a position within that texture.
+ */
+
+GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key) {
+    fFontScalerKey = key;
+    fFontScalerKey->ref();
+
+    fFontCache = cache;     // no need to ref, it won't go away before we do
+
+#ifdef SK_DEBUG
+//    SkDebugf(" GrTextStrike %p %d\n", this, gCounter);
+    gCounter += 1;
+#endif
+}
+
+GrTextStrike::~GrTextStrike() {
+    fFontScalerKey->unref();
+    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
+    while (!iter.done()) {
+        (*iter).free();
+        ++iter;
+    }
+
+#ifdef SK_DEBUG
+    gCounter -= 1;
+//    SkDebugf("~GrTextStrike %p %d\n", this, gCounter);
+#endif
+}
+
+GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
+                                     GrFontScaler* scaler) {
+    SkIRect bounds;
+    if (fUseDistanceField) {
+        if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
+            return NULL;
+        }
+    } else {
+        if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
+            return NULL;
+        }
+    }
+    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
+    
+    GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
+    glyph->init(packed, bounds, format);
+    fCache.add(glyph);
+    return glyph;
+}
+
+void GrTextStrike::removePlot(const GrPlot* plot) {
+    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
+    while (!iter.done()) {
+        if (plot == (*iter).fPlot) {
+            (*iter).fPlot = NULL;
+        }
+        ++iter;
+    }
+
+    GrAtlas::RemovePlot(&fPlotUsage, plot);
+}
+
+bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
+    int width = glyph->fBounds.width();
+    int height = glyph->fBounds.height();
+    int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
+    if (width + pad > GR_PLOT_WIDTH) {
+        return true;
+    }
+    if (height + pad > GR_PLOT_HEIGHT) {
+        return true;
+    }
+
+    return false;
+}
+
+bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
+#if 0   // testing hack to force us to flush our cache often
+    static int gCounter;
+    if ((++gCounter % 10) == 0) return false;
+#endif
+
+    SkASSERT(glyph);
+    SkASSERT(scaler);
+    SkASSERT(fCache.find(glyph->fPackedID));
+    SkASSERT(NULL == glyph->fPlot);
+
+    SkAutoUnref ar(SkSafeRef(scaler));
+
+    int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
+
+    size_t size = glyph->fBounds.area() * bytesPerPixel;
+    GrAutoMalloc<1024> storage(size);
+
+    if (fUseDistanceField) {
+        if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
+                                           glyph->height(),
+                                           storage.get())) {
+            return false;
+        }
+    } else {
+        if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
+                                         glyph->height(),
+                                         glyph->width() * bytesPerPixel,
+                                         storage.get())) {
+            return false;
+        }
+    }
+
+    GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
+                                          glyph->width(), glyph->height(),
+                                          storage.get(), &glyph->fAtlasLocation);
+
+    if (NULL == plot) {
+        return false;
+    }
+
+    glyph->fPlot = plot;
+    return true;
+}