2011-07-28 14:26:00 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2010 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
2010-12-22 21:39:39 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "GrGpu.h"
|
|
|
|
#include "GrRectanizer.h"
|
|
|
|
#include "GrTextStrike.h"
|
|
|
|
#include "GrTextStrike_impl.h"
|
2013-10-02 18:19:17 +00:00
|
|
|
#include "SkString.h"
|
2010-12-22 21:39:39 +00:00
|
|
|
|
2014-03-11 15:57:40 +00:00
|
|
|
#include "SkDistanceFieldGen.h"
|
2013-11-11 20:54:09 +00:00
|
|
|
|
2012-06-26 20:16:17 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2014-04-01 16:25:11 +00:00
|
|
|
#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)
|
|
|
|
|
2013-08-05 19:42:56 +00:00
|
|
|
#define FONT_CACHE_STATS 0
|
|
|
|
#if FONT_CACHE_STATS
|
|
|
|
static int g_PurgeCount = 0;
|
|
|
|
#endif
|
|
|
|
|
2010-12-22 21:39:39 +00:00
|
|
|
GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
|
|
|
|
gpu->ref();
|
2013-12-03 19:45:22 +00:00
|
|
|
for (int i = 0; i < kAtlasCount; ++i) {
|
2013-09-26 12:57:19 +00:00
|
|
|
fAtlasMgr[i] = NULL;
|
|
|
|
}
|
2010-12-22 21:39:39 +00:00
|
|
|
|
|
|
|
fHead = fTail = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GrFontCache::~GrFontCache() {
|
|
|
|
fCache.deleteAll();
|
2013-12-03 19:45:22 +00:00
|
|
|
for (int i = 0; i < kAtlasCount; ++i) {
|
2013-09-26 12:57:19 +00:00
|
|
|
delete fAtlasMgr[i];
|
|
|
|
}
|
2010-12-22 21:39:39 +00:00
|
|
|
fGpu->unref();
|
2013-08-05 19:42:56 +00:00
|
|
|
#if FONT_CACHE_STATS
|
|
|
|
GrPrintf("Num purges: %d\n", g_PurgeCount);
|
|
|
|
#endif
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 15:28:40 +00:00
|
|
|
static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
|
2013-12-04 07:02:26 +00:00
|
|
|
static const GrPixelConfig sPixelConfigs[] = {
|
|
|
|
kAlpha_8_GrPixelConfig,
|
|
|
|
kRGB_565_GrPixelConfig,
|
2013-12-03 19:45:22 +00:00
|
|
|
kSkia8888_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) {
|
2013-12-04 07:02:26 +00:00
|
|
|
static const int sAtlasIndices[] = {
|
|
|
|
GrFontCache::kA8_AtlasType,
|
|
|
|
GrFontCache::k565_AtlasType,
|
|
|
|
GrFontCache::k8888_AtlasType,
|
|
|
|
GrFontCache::k8888_AtlasType
|
2013-12-03 19:45:22 +00:00
|
|
|
};
|
|
|
|
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
|
|
|
|
|
|
|
|
SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
|
|
|
|
return sAtlasIndices[format];
|
2013-09-26 15:28:40 +00:00
|
|
|
}
|
|
|
|
|
2010-12-22 21:39:39 +00:00
|
|
|
GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
|
|
|
|
const Key& key) {
|
2013-09-26 12:57:19 +00:00
|
|
|
GrMaskFormat format = scaler->getMaskFormat();
|
2013-09-26 15:28:40 +00:00
|
|
|
GrPixelConfig config = mask_format_to_pixel_config(format);
|
2013-12-03 19:45:22 +00:00
|
|
|
int atlasIndex = mask_format_to_atlas_index(format);
|
|
|
|
if (NULL == fAtlasMgr[atlasIndex]) {
|
2014-04-01 16:25:11 +00:00
|
|
|
SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
|
|
|
|
GR_ATLAS_TEXTURE_HEIGHT);
|
|
|
|
fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config,
|
|
|
|
textureSize,
|
|
|
|
GR_NUM_PLOTS_X,
|
2014-05-14 15:14:51 +00:00
|
|
|
GR_NUM_PLOTS_Y,
|
|
|
|
true));
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
2012-07-09 20:17:56 +00:00
|
|
|
GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
|
2013-12-03 19:45:22 +00:00
|
|
|
(this, scaler->getKey(), format, fAtlasMgr[atlasIndex]));
|
2010-12-22 21:39:39 +00:00
|
|
|
fCache.insert(key, strike);
|
|
|
|
|
|
|
|
if (fHead) {
|
|
|
|
fHead->fPrev = strike;
|
|
|
|
} else {
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(NULL == fTail);
|
2010-12-22 21:39:39 +00:00
|
|
|
fTail = strike;
|
|
|
|
}
|
|
|
|
strike->fPrev = NULL;
|
|
|
|
strike->fNext = fHead;
|
|
|
|
fHead = strike;
|
|
|
|
|
|
|
|
return strike;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GrFontCache::freeAll() {
|
|
|
|
fCache.deleteAll();
|
2013-12-03 19:45:22 +00:00
|
|
|
for (int i = 0; i < kAtlasCount; ++i) {
|
2013-09-26 12:57:19 +00:00
|
|
|
delete fAtlasMgr[i];
|
|
|
|
fAtlasMgr[i] = NULL;
|
|
|
|
}
|
2011-03-30 21:26:44 +00:00
|
|
|
fHead = NULL;
|
|
|
|
fTail = NULL;
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
|
2013-10-27 20:50:23 +00:00
|
|
|
void GrFontCache::purgeStrike(GrTextStrike* strike) {
|
|
|
|
const GrFontCache::Key key(strike->fFontScalerKey);
|
|
|
|
fCache.remove(key, strike);
|
|
|
|
this->detachStrikeFromList(strike);
|
|
|
|
delete strike;
|
|
|
|
}
|
|
|
|
|
2014-03-03 14:30:25 +00:00
|
|
|
bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
|
2013-09-16 20:28:37 +00:00
|
|
|
SkASSERT(NULL != preserveStrike);
|
2014-03-03 14:30:25 +00:00
|
|
|
|
|
|
|
GrAtlasMgr* atlasMgr = preserveStrike->fAtlasMgr;
|
|
|
|
GrPlot* plot = atlasMgr->getUnusedPlot();
|
|
|
|
if (NULL == plot) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
plot->resetRects();
|
|
|
|
|
|
|
|
GrTextStrike* strike = fHead;
|
2013-09-16 20:28:37 +00:00
|
|
|
GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
|
2013-08-05 19:42:56 +00:00
|
|
|
while (strike) {
|
2014-03-03 14:30:25 +00:00
|
|
|
if (maskFormat != strike->fMaskFormat) {
|
|
|
|
strike = strike->fNext;
|
2013-08-05 19:42:56 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-03-03 14:30:25 +00:00
|
|
|
|
2013-08-05 19:42:56 +00:00
|
|
|
GrTextStrike* strikeToPurge = strike;
|
2014-03-03 14:30:25 +00:00
|
|
|
strike = strikeToPurge->fNext;
|
|
|
|
strikeToPurge->removePlot(plot);
|
|
|
|
|
|
|
|
// clear out any empty strikes (except this one)
|
|
|
|
if (strikeToPurge != preserveStrike && strikeToPurge->fAtlas.isEmpty()) {
|
2013-10-27 20:50:23 +00:00
|
|
|
this->purgeStrike(strikeToPurge);
|
2013-08-05 19:42:56 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-03 14:30:25 +00:00
|
|
|
|
2013-08-05 19:42:56 +00:00
|
|
|
#if FONT_CACHE_STATS
|
|
|
|
++g_PurgeCount;
|
|
|
|
#endif
|
|
|
|
|
2014-03-03 14:30:25 +00:00
|
|
|
return true;
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
|
2013-08-28 14:17:03 +00:00
|
|
|
#ifdef SK_DEBUG
|
2010-12-22 21:39:39 +00:00
|
|
|
void GrFontCache::validate() const {
|
|
|
|
int count = fCache.count();
|
|
|
|
if (0 == count) {
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(!fHead);
|
|
|
|
SkASSERT(!fTail);
|
2010-12-22 21:39:39 +00:00
|
|
|
} else if (1 == count) {
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(fHead == fTail);
|
2010-12-22 21:39:39 +00:00
|
|
|
} else {
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(fHead != fTail);
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int count2 = 0;
|
|
|
|
const GrTextStrike* strike = fHead;
|
|
|
|
while (strike) {
|
|
|
|
count2 += 1;
|
|
|
|
strike = strike->fNext;
|
|
|
|
}
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(count == count2);
|
2010-12-22 21:39:39 +00:00
|
|
|
|
|
|
|
count2 = 0;
|
|
|
|
strike = fTail;
|
|
|
|
while (strike) {
|
|
|
|
count2 += 1;
|
|
|
|
strike = strike->fPrev;
|
|
|
|
}
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(count == count2);
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-10-02 18:19:17 +00:00
|
|
|
void GrFontCache::dump() const {
|
|
|
|
static int gDumpCount = 0;
|
2013-12-03 19:45:22 +00:00
|
|
|
for (int i = 0; i < kAtlasCount; ++i) {
|
2013-10-02 18:19:17 +00:00
|
|
|
if (NULL != fAtlasMgr[i]) {
|
|
|
|
GrTexture* texture = fAtlasMgr[i]->getTexture();
|
|
|
|
if (NULL != texture) {
|
|
|
|
SkString filename;
|
2014-03-26 19:49:03 +00:00
|
|
|
#ifdef SK_BUILD_FOR_ANDROID
|
|
|
|
filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
|
|
|
|
#else
|
2013-10-02 18:19:17 +00:00
|
|
|
filename.printf("fontcache_%d%d.png", gDumpCount, i);
|
2014-03-26 19:49:03 +00:00
|
|
|
#endif
|
2013-10-02 18:19:17 +00:00
|
|
|
texture->savePixels(filename.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++gDumpCount;
|
|
|
|
}
|
|
|
|
|
2010-12-22 21:39:39 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2013-08-28 14:17:03 +00:00
|
|
|
#ifdef SK_DEBUG
|
2010-12-22 21:39:39 +00:00
|
|
|
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,
|
2011-03-15 15:40:16 +00:00
|
|
|
GrMaskFormat format,
|
2014-03-03 14:30:25 +00:00
|
|
|
GrAtlasMgr* atlasMgr) : fPool(64) {
|
2010-12-22 21:39:39 +00:00
|
|
|
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
|
|
|
|
|
2011-03-15 15:40:16 +00:00
|
|
|
fMaskFormat = format;
|
|
|
|
|
2013-08-28 14:17:03 +00:00
|
|
|
#ifdef SK_DEBUG
|
2011-07-05 19:09:47 +00:00
|
|
|
// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
|
2010-12-22 21:39:39 +00:00
|
|
|
gCounter += 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-03-03 14:30:25 +00:00
|
|
|
// this signature is needed because it's used with
|
|
|
|
// SkTDArray::visitAll() (see destructor)
|
2013-08-05 19:42:56 +00:00
|
|
|
static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
|
|
|
|
|
2010-12-22 21:39:39 +00:00
|
|
|
GrTextStrike::~GrTextStrike() {
|
|
|
|
fFontScalerKey->unref();
|
2013-08-05 19:42:56 +00:00
|
|
|
fCache.getArray().visitAll(free_glyph);
|
2010-12-22 21:39:39 +00:00
|
|
|
|
2013-08-28 14:17:03 +00:00
|
|
|
#ifdef SK_DEBUG
|
2010-12-22 21:39:39 +00:00
|
|
|
gCounter -= 1;
|
2011-07-05 19:09:47 +00:00
|
|
|
// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
|
2010-12-22 21:39:39 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
|
|
|
|
GrFontScaler* scaler) {
|
2013-07-17 21:39:42 +00:00
|
|
|
SkIRect bounds;
|
2014-04-14 22:05:07 +00:00
|
|
|
if (fUseDistanceField) {
|
|
|
|
if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GrGlyph* glyph = fPool.alloc();
|
|
|
|
glyph->init(packed, bounds);
|
|
|
|
fCache.insert(packed, glyph);
|
|
|
|
return glyph;
|
|
|
|
}
|
|
|
|
|
2014-03-03 14:30:25 +00:00
|
|
|
void GrTextStrike::removePlot(const GrPlot* plot) {
|
|
|
|
SkTDArray<GrGlyph*>& glyphArray = fCache.getArray();
|
|
|
|
for (int i = 0; i < glyphArray.count(); ++i) {
|
|
|
|
if (plot == glyphArray[i]->fPlot) {
|
|
|
|
glyphArray[i]->fPlot = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fAtlasMgr->removePlot(&fAtlas, plot);
|
2013-08-05 19:42:56 +00:00
|
|
|
}
|
|
|
|
|
2013-11-11 20:54:09 +00:00
|
|
|
|
2014-03-03 14:30:25 +00:00
|
|
|
bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
|
2011-04-04 20:06:59 +00:00
|
|
|
#if 0 // testing hack to force us to flush our cache often
|
|
|
|
static int gCounter;
|
|
|
|
if ((++gCounter % 10) == 0) return false;
|
|
|
|
#endif
|
|
|
|
|
2013-08-17 00:02:59 +00:00
|
|
|
SkASSERT(glyph);
|
|
|
|
SkASSERT(scaler);
|
|
|
|
SkASSERT(fCache.contains(glyph));
|
2013-10-07 18:20:27 +00:00
|
|
|
SkASSERT(NULL == glyph->fPlot);
|
2010-12-22 21:39:39 +00:00
|
|
|
|
2013-09-09 13:38:37 +00:00
|
|
|
SkAutoRef ar(scaler);
|
2011-03-15 15:40:16 +00:00
|
|
|
|
|
|
|
int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
|
2013-11-11 20:54:09 +00:00
|
|
|
|
2014-04-14 22:05:07 +00:00
|
|
|
size_t size = glyph->fBounds.area() * bytesPerPixel;
|
|
|
|
SkAutoSMalloc<1024> storage(size);
|
2013-11-11 20:54:09 +00:00
|
|
|
if (fUseDistanceField) {
|
2014-04-14 22:05:07 +00:00
|
|
|
if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
|
|
|
|
glyph->height(),
|
|
|
|
storage.get())) {
|
2013-11-11 20:54:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
|
|
|
|
glyph->height(),
|
|
|
|
glyph->width() * bytesPerPixel,
|
|
|
|
storage.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-12-22 21:39:39 +00:00
|
|
|
}
|
|
|
|
|
2014-04-14 22:05:07 +00:00
|
|
|
GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
|
|
|
|
glyph->height(), storage.get(),
|
|
|
|
&glyph->fAtlasLocation);
|
|
|
|
|
2013-09-27 19:39:38 +00:00
|
|
|
if (NULL == plot) {
|
2010-12-22 21:39:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-09-27 19:39:38 +00:00
|
|
|
glyph->fPlot = plot;
|
2010-12-22 21:39:39 +00:00
|
|
|
return true;
|
|
|
|
}
|