blob: 6ee39d0c47ff035d3485f25248e84f38abe2105e [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrTextStrike_DEFINED
#define GrTextStrike_DEFINED
#include "GrAtlas.h"
#include "GrDrawTarget.h"
#include "GrFontScaler.h"
#include "GrGlyph.h"
#include "SkTDynamicHash.h"
#include "SkVarAlloc.h"
class GrFontCache;
class GrGpu;
class GrFontPurgeListener;
/**
* The textstrike maps a hostfontscaler instance to a dictionary of
* glyphid->strike
*/
class GrTextStrike {
public:
GrTextStrike(GrFontCache*, const GrFontDescKey* fontScalerKey);
~GrTextStrike();
const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
GrFontCache* getFontCache() const { return fFontCache; }
inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
GrGlyph* glyph = fCache.find(packed);
if (NULL == glyph) {
glyph = this->generateGlyph(packed, scaler);
}
return glyph;
}
// returns true if glyph (or glyph+padding for distance field)
// is too large to ever fit in texture atlas subregions (GrPlots)
bool glyphTooLargeForAtlas(GrGlyph*);
// returns true if glyph successfully added to texture atlas, false otherwise
bool addGlyphToAtlas(GrGlyph*, GrFontScaler*);
// testing
int countGlyphs() const { return fCache.count(); }
// remove any references to this plot
void removePlot(const GrPlot* plot);
static const GrFontDescKey& GetKey(const GrTextStrike& ts) {
return *(ts.fFontScalerKey);
}
static uint32_t Hash(const GrFontDescKey& key) {
return key.getHash();
}
public:
// for easy removal from list
GrTextStrike* fPrev;
GrTextStrike* fNext;
private:
SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
const GrFontDescKey* fFontScalerKey;
SkVarAlloc fPool;
GrFontCache* fFontCache;
bool fUseDistanceField;
GrAtlas::ClientPlotUsage fPlotUsage;
GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
friend class GrFontCache;
};
class GrFontCache {
public:
GrFontCache(GrGpu*);
~GrFontCache();
inline GrTextStrike* getStrike(GrFontScaler* scaler, bool useDistanceField) {
this->validate();
GrTextStrike* strike = fCache.find(*(scaler->getKey()));
if (NULL == strike) {
strike = this->generateStrike(scaler);
} else if (strike->fPrev) {
// Need to put the strike at the head of its dllist, since that is how
// we age the strikes for purging (we purge from the back of the list)
this->detachStrikeFromList(strike);
// attach at the head
fHead->fPrev = strike;
strike->fNext = fHead;
strike->fPrev = NULL;
fHead = strike;
}
strike->fUseDistanceField = useDistanceField;
this->validate();
return strike;
}
// add to texture atlas that matches this format
GrPlot* addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
int width, int height, const void* image,
SkIPoint16* loc);
void freeAll();
// make an unused plot available for this glyph
bool freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph);
// testing
int countStrikes() const { return fCache.count(); }
GrTextStrike* getHeadStrike() const { return fHead; }
void updateTextures() {
for (int i = 0; i < kAtlasCount; ++i) {
if (fAtlases[i]) {
fAtlases[i]->uploadPlotsToTexture();
}
}
}
#ifdef SK_DEBUG
void validate() const;
#else
void validate() const {}
#endif
void dump() const;
enum AtlasType {
kA8_AtlasType, //!< 1-byte per pixel
k565_AtlasType, //!< 2-bytes per pixel
k8888_AtlasType, //!< 4-bytes per pixel
kLast_AtlasType = k8888_AtlasType
};
static const int kAtlasCount = kLast_AtlasType + 1;
private:
friend class GrFontPurgeListener;
SkTDynamicHash<GrTextStrike, GrFontDescKey> fCache;
// for LRU
GrTextStrike* fHead;
GrTextStrike* fTail;
GrGpu* fGpu;
GrAtlas* fAtlases[kAtlasCount];
GrTextStrike* generateStrike(GrFontScaler*);
inline void detachStrikeFromList(GrTextStrike* strike) {
if (strike->fPrev) {
SkASSERT(fHead != strike);
strike->fPrev->fNext = strike->fNext;
} else {
SkASSERT(fHead == strike);
fHead = strike->fNext;
}
if (strike->fNext) {
SkASSERT(fTail != strike);
strike->fNext->fPrev = strike->fPrev;
} else {
SkASSERT(fTail == strike);
fTail = strike->fPrev;
}
}
void purgeStrike(GrTextStrike* strike);
};
#endif