/*
* 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 "GrAtlas.h"
#include "GrGpu.h"
#include "GrRectanizer.h"
#include "GrTextStrike.h"
#include "GrTextStrike_impl.h"
#include "GrRect.h"
SK_DEFINE_INST_COUNT(GrFontScaler)
SK_DEFINE_INST_COUNT(GrKey)
///////////////////////////////////////////////////////////////////////////////
GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
gpu->ref();
fAtlasMgr = NULL;
fHead = fTail = NULL;
}
GrFontCache::~GrFontCache() {
fCache.deleteAll();
delete fAtlasMgr;
fGpu->unref();
}
GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
const Key& key) {
if (NULL == fAtlasMgr) {
fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu));
}
GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
(this, scaler->getKey(),
scaler->getMaskFormat(), fAtlasMgr));
fCache.insert(key, strike);
if (fHead) {
fHead->fPrev = strike;
} else {
GrAssert(NULL == fTail);
fTail = strike;
}
strike->fPrev = NULL;
strike->fNext = fHead;
fHead = strike;
return strike;
}
void GrFontCache::freeAll() {
fCache.deleteAll();
delete fAtlasMgr;
fAtlasMgr = NULL;
fHead = NULL;
fTail = NULL;
}
void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
GrTextStrike* strike = fTail;
while (strike) {
if (strike == preserveStrike) {
strike = strike->fPrev;
continue;
}
GrTextStrike* strikeToPurge = strike;
// keep going if we won't free up any atlases with this strike.
strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
int index = fCache.slowFindIndex(strikeToPurge);
GrAssert(index >= 0);
fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
this->detachStrikeFromList(strikeToPurge);
delete strikeToPurge;
}
}
#if GR_DEBUG
void GrFontCache::validate() const {
int count = fCache.count();
if (0 == count) {
GrAssert(!fHead);
GrAssert(!fTail);
} else if (1 == count) {
GrAssert(fHead == fTail);
} else {
GrAssert(fHead != fTail);
}
int count2 = 0;
const GrTextStrike* strike = fHead;
while (strike) {
count2 += 1;
strike = strike->fNext;
}
GrAssert(count == count2);
count2 = 0;
strike = fTail;
while (strike) {
count2 += 1;
strike = strike->fPrev;
}
GrAssert(count == count2);
}
#endif
///////////////////////////////////////////////////////////////////////////////
#if GR_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 GrKey* key,
GrMaskFormat format,
GrAtlasMgr* atlasMgr) : fPool(64) {
fFontScalerKey = key;
fFontScalerKey->ref();
fFontCache = cache; // no need to ref, it won't go away before we do
fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
fAtlas = NULL;
fMaskFormat = format;
#if GR_DEBUG
// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
gCounter += 1;
#endif
}
static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
GrTextStrike::~GrTextStrike() {
GrAtlas::FreeLList(fAtlas);
fFontScalerKey->unref();
fCache.getArray().visitAll(FreeGlyph);
#if GR_DEBUG
gCounter -= 1;
// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
#endif
}
GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
GrFontScaler* scaler) {
GrIRect bounds;
if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
return NULL;
}
GrGlyph* glyph = fPool.alloc();
glyph->init(packed, bounds);
fCache.insert(packed, glyph);
return glyph;
}
bool GrTextStrike::getGlyphAtlas(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
GrAssert(glyph);
GrAssert(scaler);
GrAssert(fCache.contains(glyph));
if (glyph->fAtlas) {
return true;
}
GrAutoRef ar(scaler);
int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
size_t size = glyph->fBounds.area() * bytesPerPixel;
SkAutoSMalloc<1024> storage(size);
if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
glyph->height(),
glyph->width() * bytesPerPixel,
storage.get())) {
return false;
}
GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
glyph->height(), storage.get(),
fMaskFormat,
&glyph->fAtlasLocation);
if (NULL == atlas) {
return false;
}
// update fAtlas as well, since they may be chained in a linklist
glyph->fAtlas = fAtlas = atlas;
return true;
}