void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
    GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);

    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
    for (; !iter.done(); ++iter) {
        GrBatchTextStrike* strike = &*iter;
        strike->removeID(id);

        // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
        // triggered the eviction
        if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
            fontCache->fCache.remove(*(strike->fFontScalerKey));
            SkDELETE(strike);
        }
    }
}
void GrAtlasTextBlob::regenInBatch(GrDrawBatch::Target* target,
                                   GrBatchFontCache* fontCache,
                                   GrBlobRegenHelper *helper,
                                   Run* run,
                                   Run::SubRunInfo* info, SkGlyphCache** cache,
                                   SkTypeface** typeface, GrFontScaler** scaler,
                                   const SkDescriptor** desc,
                                   int glyphCount, size_t vertexStride,
                                   GrColor color, SkScalar transX,
                                   SkScalar transY) const {
    static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
    GrBatchTextStrike* strike = nullptr;
    if (regenTexCoords) {
        info->resetBulkUseToken();

        // We can reuse if we have a valid strike and our descriptors / typeface are the
        // same.  The override descriptor is only for the non distance field text within
        // a run
        const SkDescriptor* newDesc = (run->fOverrideDescriptor && !info->drawAsDistanceFields()) ?
                                      run->fOverrideDescriptor->getDesc() :
                                      run->fDescriptor.getDesc();
        if (!*cache || !SkTypeface::Equal(*typeface, run->fTypeface) ||
            !((*desc)->equals(*newDesc))) {
            if (*cache) {
                SkGlyphCache::AttachCache(*cache);
            }
            *desc = newDesc;
            *cache = SkGlyphCache::DetachCache(run->fTypeface, run->fEffects, *desc);
            *scaler = GrTextUtils::GetGrFontScaler(*cache);
            *typeface = run->fTypeface;
        }

        if (regenGlyphs) {
            strike = fontCache->getStrike(*scaler);
        } else {
            strike = info->strike();
        }
    }

    bool brokenRun = false;
    for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
        GrGlyph* glyph = nullptr;
        int log2Width = 0, log2Height = 0;
        if (regenTexCoords) {
            size_t glyphOffset = glyphIdx + info->glyphStartIndex();

            if (regenGlyphs) {
                // Get the id from the old glyph, and use the new strike to lookup
                // the glyph.
                GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID;
                fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), *scaler);
                SkASSERT(id == fGlyphs[glyphOffset]->fPackedID);
            }
            glyph = fGlyphs[glyphOffset];
            SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat());

            if (!fontCache->hasGlyph(glyph) &&
                !strike->addGlyphToAtlas(target, glyph, *scaler, info->maskFormat())) {
                helper->flush();
                brokenRun = glyphIdx > 0;

                SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
                                                                    glyph,
                                                                    *scaler,
                                                                    info->maskFormat());
                SkASSERT(success);
            }
            fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
                                                    target->nextDrawToken());
            log2Width = fontCache->log2Width(info->maskFormat());
            log2Height = fontCache->log2Height(info->maskFormat());
        }

        intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
        vertex += info->vertexStartIndex();
        vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph;
        regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
                                                           info->drawAsDistanceFields(), transX,
                                                           transY, log2Width, log2Height, color);
        helper->incGlyphCount();
    }
inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Blob* blob, Run* run,
                                        TextInfo* info, SkGlyphCache** cache,
                                        SkTypeface** typeface, GrFontScaler** scaler,
                                        const SkDescriptor** desc, const GrGeometryProcessor* gp,
                                        int glyphCount, size_t vertexStride,
                                        GrColor color, SkScalar transX, SkScalar transY) const {
    static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
    GrBatchTextStrike* strike = nullptr;
    if (regenTexCoords) {
        info->fBulkUseToken.reset();

        // We can reuse if we have a valid strike and our descriptors / typeface are the
        // same.  The override descriptor is only for the non distance field text within
        // a run
        const SkDescriptor* newDesc = (run->fOverrideDescriptor && !this->usesDistanceFields()) ?
                                      run->fOverrideDescriptor->getDesc() :
                                      run->fDescriptor.getDesc();
        if (!*cache || !SkTypeface::Equal(*typeface, run->fTypeface) ||
                       !((*desc)->equals(*newDesc))) {
            if (*cache) {
                SkGlyphCache::AttachCache(*cache);
            }
            *desc = newDesc;
            *cache = SkGlyphCache::DetachCache(run->fTypeface, *desc);
            *scaler = GrTextContext::GetGrFontScaler(*cache);
            strike = info->fStrike;
            *typeface = run->fTypeface;
        }

        if (regenGlyphs) {
            strike = fFontCache->getStrike(*scaler);
        } else {
            strike = info->fStrike;
        }
    }

    bool brokenRun = false;
    for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
        GrGlyph* glyph = nullptr;
        if (regenTexCoords) {
            size_t glyphOffset = glyphIdx + info->fGlyphStartIndex;

            if (regenGlyphs) {
                // Get the id from the old glyph, and use the new strike to lookup
                // the glyph.
                GrGlyph::PackedID id = blob->fGlyphs[glyphOffset]->fPackedID;
                blob->fGlyphs[glyphOffset] = strike->getGlyph(id, this->maskFormat(), *scaler);
                SkASSERT(id == blob->fGlyphs[glyphOffset]->fPackedID);
            }
            glyph = blob->fGlyphs[glyphOffset];
            SkASSERT(glyph && glyph->fMaskFormat == this->maskFormat());

            if (!fFontCache->hasGlyph(glyph) &&
                !strike->addGlyphToAtlas(target, glyph, *scaler, this->maskFormat())) {
                this->flush(target, flushInfo);
                target->initDraw(gp, this->pipeline());
                brokenRun = glyphIdx > 0;

                SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
                                                                    glyph,
                                                                    *scaler,
                                                                    this->maskFormat());
                SkASSERT(success);
            }
            fFontCache->addGlyphToBulkAndSetUseToken(&info->fBulkUseToken, glyph,
                                                     target->currentToken());
        }

        intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
        vertex += info->fVertexStartIndex;
        vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph;
        regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
                                                           this->usesDistanceFields(), transX,
                                                           transY, color);
        flushInfo->fGlyphsToFlush++;
    }
void GrAtlasTextBlob::regenInBatch(GrDrawBatch::Target* target,
                                   GrBatchFontCache* fontCache,
                                   GrBlobRegenHelper *helper,
                                   Run* run,
                                   Run::SubRunInfo* info,
                                   SkAutoGlyphCache* lazyCache,
                                   int glyphCount, size_t vertexStride,
                                   GrColor color, SkScalar transX,
                                   SkScalar transY) const {
    SkASSERT(lazyCache);
    static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
    GrBatchTextStrike* strike = nullptr;
    if (regenTexCoords) {
        info->resetBulkUseToken();

        const SkDescriptor* desc = (run->fOverrideDescriptor && !info->drawAsDistanceFields())
                                      ? run->fOverrideDescriptor->getDesc()
                                      : run->fDescriptor.getDesc();

        if (!*lazyCache || (*lazyCache)->getDescriptor() != *desc) {
            SkScalerContextEffects effects;
            effects.fPathEffect = run->fPathEffect.get();
            effects.fRasterizer = run->fRasterizer.get();
            effects.fMaskFilter = run->fMaskFilter.get();
            lazyCache->reset(SkGlyphCache::DetachCache(run->fTypeface, effects, desc));
        }

        if (regenGlyphs) {
            strike = fontCache->getStrike(lazyCache->get());
        } else {
            strike = info->strike();
        }
    }

    bool brokenRun = false;
    for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
        GrGlyph* glyph = nullptr;
        int log2Width = 0, log2Height = 0;
        if (regenTexCoords) {
            size_t glyphOffset = glyphIdx + info->glyphStartIndex();

            if (regenGlyphs) {
                // Get the id from the old glyph, and use the new strike to lookup
                // the glyph.
                GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID;
                fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), lazyCache->get());
                SkASSERT(id == fGlyphs[glyphOffset]->fPackedID);
            }
            glyph = fGlyphs[glyphOffset];
            SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat());

            if (!fontCache->hasGlyph(glyph) &&
                !strike->addGlyphToAtlas(target, glyph, lazyCache->get(), info->maskFormat())) {
                helper->flush();
                brokenRun = glyphIdx > 0;

                SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
                                                                    glyph,
                                                                    lazyCache->get(),
                                                                    info->maskFormat());
                SkASSERT(success);
            }
            fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
                                                    target->nextDrawToken());
            log2Width = fontCache->log2Width(info->maskFormat());
            log2Height = fontCache->log2Height(info->maskFormat());
        }

        intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
        vertex += info->vertexStartIndex();
        vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph;
        regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
                                                           info->drawAsDistanceFields(), transX,
                                                           transY, log2Width, log2Height, color);
        helper->incGlyphCount();
    }