Changeset - r20428:f2a9c31f2342
[Not reviewed]
master
0 1 0
rubidium - 11 years ago 2013-06-23 15:35:06
rubidium@openttd.org
(svn r25443) -Codechange: move the glyph to sprite cache into the FreeTypeFontCache instance
1 file changed with 67 insertions and 79 deletions:
0 comments (0 inline, 0 general)
src/fontcache.cpp
Show inline comments
 
@@ -130,24 +130,50 @@ bool SpriteFontCache::GetDrawGlyphShadow
 
#ifdef WITH_FREETYPE
 
#include <ft2build.h>
 
#include FT_FREETYPE_H
 
#include FT_GLYPH_H
 
#include FT_TRUETYPE_TABLES_H
 

	
 
/** Font cache for fonts that are based on a freetype font. */
 
class FreeTypeFontCache : public FontCache {
 
private:
 
	FT_Face face;  ///< The font face associated with this font.
 
	int ascender;  ///< The ascender value of this font.
 
	int descender; ///< The descender value of this font.
 

	
 
	/** Container for information about a glyph. */
 
	struct GlyphEntry {
 
		Sprite *sprite; ///< The loaded sprite.
 
		byte width;     ///< The width of the glyph.
 
		bool duplicate; ///< Whether this glyph entry is a duplicate, i.e. may this be freed?
 
	};
 

	
 
	/**
 
	 * The glyph cache. This is structured to reduce memory consumption.
 
	 * 1) There is a 'segment' table for each font size.
 
	 * 2) Each segment table is a discrete block of characters.
 
	 * 3) Each block contains 256 (aligned) characters sequential characters.
 
	 *
 
	 * The cache is accessed in the following way:
 
	 * For character 0x0041  ('A'): glyph_to_sprite[0x00][0x41]
 
	 * For character 0x20AC (Euro): glyph_to_sprite[0x20][0xAC]
 
	 *
 
	 * Currently only 256 segments are allocated, "limiting" us to 65536 characters.
 
	 * This can be simply changed in the two functions Get & SetGlyphPtr.
 
	 */
 
	GlyphEntry **glyph_to_sprite;
 

	
 
	GlyphEntry *GetGlyphPtr(WChar key);
 
	void SetGlyphPtr(WChar key, const GlyphEntry *glyph, bool duplicate = false);
 

	
 
public:
 
	FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
 
	~FreeTypeFontCache();
 
	virtual SpriteID GetUnicodeGlyph(uint32 key) { return this->parent->GetUnicodeGlyph(key); }
 
	virtual void SetUnicodeGlyph(uint32 key, SpriteID sprite) { this->parent->SetUnicodeGlyph(key, sprite); }
 
	virtual void InitializeUnicodeGlyphMap() { this->parent->InitializeUnicodeGlyphMap(); }
 
	virtual void ClearFontCache();
 
	virtual const Sprite *GetGlyph(uint32 key);
 
	virtual uint GetGlyphWidth(uint32 key);
 
	virtual bool GetDrawGlyphShadow();
 
};
 

	
 
@@ -155,25 +181,25 @@ FT_Library _library = NULL;
 

	
 
FreeTypeSettings _freetype;
 

	
 
static const byte FACE_COLOUR   = 1;
 
static const byte SHADOW_COLOUR = 2;
 

	
 
/**
 
 * Create a new FreeTypeFontCache.
 
 * @param fs     The font size that is going to be cached.
 
 * @param face   The font that has to be loaded.
 
 * @param pixels The number of pixels this font should be high.
 
 */
 
FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face)
 
FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), glyph_to_sprite(NULL)
 
{
 
	if (pixels == 0) {
 
		/* Try to determine a good height based on the minimal height recommended by the font. */
 
		pixels = _default_font_height[this->fs];
 

	
 
		TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
 
		if (head != NULL) {
 
			/* Font height is minimum height plus the difference between the default
 
			 * height for this font size and the small size. */
 
			int diff = _default_font_height[this->fs] - _default_font_height[FS_SMALL];
 
			pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, _default_font_height[this->fs], MAX_FONT_SIZE);
 
		}
 
@@ -240,42 +266,40 @@ static void LoadFreeTypeFont(const char 
 
				if (error == FT_Err_Ok) return;
 
			}
 
		}
 
	}
 

	
 
	FT_Done_Face(*face);
 
	*face = NULL;
 

	
 
	ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
 
}
 

	
 

	
 
static void ResetGlyphCache(bool monospace);
 

	
 
/**
 
 * Unload the face
 
 * Free everything that was allocated for this font cache.
 
 */
 
FreeTypeFontCache::~FreeTypeFontCache()
 
{
 
	if (this->face != NULL) FT_Done_Face(this->face);
 

	
 
	this->ClearFontCache();
 
}
 

	
 
/**
 
 * (Re)initialize the freetype related things, i.e. load the non-sprite fonts.
 
 * @param monospace Whether to initialise the monospace or regular fonts.
 
 */
 
void InitFreeType(bool monospace)
 
{
 
	ResetGlyphCache(monospace);
 

	
 
	for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
 
		FontCache *fc = FontCache::Get(fs);
 
		if (fc->HasParent()) delete fc;
 
	}
 

	
 
	if (StrEmpty(_freetype.small.font) && StrEmpty(_freetype.medium.font) && StrEmpty(_freetype.large.font) && StrEmpty(_freetype.mono.font)) {
 
		DEBUG(freetype, 1, "No font faces specified, using sprite fonts instead");
 
		return;
 
	}
 

	
 
	if (_library == NULL) {
 
		if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
 
@@ -304,115 +328,79 @@ void InitFreeType(bool monospace)
 
		/* Set each font size */
 
		if (small != NULL)  new FreeTypeFontCache(FS_SMALL,  small,  _freetype.small.size);
 
		if (medium != NULL) new FreeTypeFontCache(FS_NORMAL, medium, _freetype.medium.size);
 
		if (large != NULL)  new FreeTypeFontCache(FS_LARGE,  large,  _freetype.large.size);
 
	}
 
}
 

	
 
/**
 
 * Free everything allocated w.r.t. fonts.
 
 */
 
void UninitFreeType()
 
{
 
	ClearFontCache();
 

	
 
	for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
 
		FontCache *fc = FontCache::Get(fs);
 
		if (fc->HasParent()) delete fc;
 
	}
 

	
 
	FT_Done_FreeType(_library);
 
	_library = NULL;
 
}
 

	
 
/**
 
 * Reset cached glyphs.
 
 */
 
void FreeTypeFontCache::ClearFontCache()
 
{
 
	ResetGlyphCache(true);
 
	ResetGlyphCache(false);
 
	if (this->glyph_to_sprite == NULL) return;
 

	
 
	for (int i = 0; i < 256; i++) {
 
		if (this->glyph_to_sprite[i] == NULL) continue;
 

	
 
		for (int j = 0; j < 256; j++) {
 
			if (this->glyph_to_sprite[i][j].duplicate) continue;
 
			free(this->glyph_to_sprite[i][j].sprite);
 
		}
 

	
 
		free(this->glyph_to_sprite[i]);
 
	}
 

	
 
	free(this->glyph_to_sprite);
 
	this->glyph_to_sprite = NULL;
 
}
 

	
 
struct GlyphEntry {
 
	Sprite *sprite;
 
	byte width;
 
	bool duplicate;
 
};
 

	
 

	
 
/* The glyph cache. This is structured to reduce memory consumption.
 
 * 1) There is a 'segment' table for each font size.
 
 * 2) Each segment table is a discrete block of characters.
 
 * 3) Each block contains 256 (aligned) characters sequential characters.
 
 *
 
 * The cache is accessed in the following way:
 
 * For character 0x0041  ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
 
 * For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
 
 *
 
 * Currently only 256 segments are allocated, "limiting" us to 65536 characters.
 
 * This can be simply changed in the two functions Get & SetGlyphPtr.
 
 */
 
static GlyphEntry **_glyph_ptr[FS_END];
 

	
 
/**
 
 * Clear the complete cache
 
 * @param monospace Whether to reset the monospace or regular font.
 
 */
 
static void ResetGlyphCache(bool monospace)
 
FreeTypeFontCache::GlyphEntry *FreeTypeFontCache::GetGlyphPtr(WChar key)
 
{
 
	for (FontSize i = FS_BEGIN; i < FS_END; i++) {
 
		if (monospace != (i == FS_MONO)) continue;
 
		if (_glyph_ptr[i] == NULL) continue;
 

	
 
		for (int j = 0; j < 256; j++) {
 
			if (_glyph_ptr[i][j] == NULL) continue;
 

	
 
			for (int k = 0; k < 256; k++) {
 
				if (_glyph_ptr[i][j][k].duplicate) continue;
 
				free(_glyph_ptr[i][j][k].sprite);
 
			}
 

	
 
			free(_glyph_ptr[i][j]);
 
		}
 

	
 
		free(_glyph_ptr[i]);
 
		_glyph_ptr[i] = NULL;
 
	}
 
}
 

	
 
static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
 
{
 
	if (_glyph_ptr[size] == NULL) return NULL;
 
	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
 
	return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
 
	if (this->glyph_to_sprite == NULL) return NULL;
 
	if (this->glyph_to_sprite[GB(key, 8, 8)] == NULL) return NULL;
 
	return &this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)];
 
}
 

	
 

	
 
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph, bool duplicate = false)
 
void FreeTypeFontCache::SetGlyphPtr(WChar key, const GlyphEntry *glyph, bool duplicate)
 
{
 
	if (_glyph_ptr[size] == NULL) {
 
		DEBUG(freetype, 3, "Allocating root glyph cache for size %u", size);
 
		_glyph_ptr[size] = CallocT<GlyphEntry*>(256);
 
	if (this->glyph_to_sprite == NULL) {
 
		DEBUG(freetype, 3, "Allocating root glyph cache for size %u", this->fs);
 
		this->glyph_to_sprite = CallocT<GlyphEntry*>(256);
 
	}
 

	
 
	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
 
		DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
 
		_glyph_ptr[size][GB(key, 8, 8)] = CallocT<GlyphEntry>(256);
 
	if (this->glyph_to_sprite[GB(key, 8, 8)] == NULL) {
 
		DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), this->fs);
 
		this->glyph_to_sprite[GB(key, 8, 8)] = CallocT<GlyphEntry>(256);
 
	}
 

	
 
	DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, size);
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite    = glyph->sprite;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width     = glyph->width;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate;
 
	DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, this->fs);
 
	this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].sprite    = glyph->sprite;
 
	this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].width     = glyph->width;
 
	this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate;
 
}
 

	
 
static void *AllocateFont(size_t size)
 
{
 
	return MallocT<byte>(size);
 
}
 

	
 

	
 
/* Check if a glyph should be rendered with antialiasing */
 
static bool GetFontAAState(FontSize size)
 
{
 
	/* AA is only supported for 32 bpp */
 
@@ -448,47 +436,47 @@ const Sprite *FreeTypeFontCache::GetGlyp
 

	
 
		/* Load the sprite if it's known. */
 
		if (sprite != 0) return GetSprite(sprite, ST_FONT);
 

	
 
		/* For the 'rare' case there is no font available at all. */
 
		if (this->face == NULL) error("No sprite font and no real font either... bailing!");
 

	
 
		/* Use the '?' from the freetype font. */
 
		key = '?';
 
	}
 

	
 
	/* Check for the glyph in our cache */
 
	glyph = GetGlyphPtr(this->fs, key);
 
	glyph = this->GetGlyphPtr(key);
 
	if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
 

	
 
	slot = this->face->glyph;
 

	
 
	bool aa = GetFontAAState(this->fs);
 

	
 
	FT_UInt glyph_index = FT_Get_Char_Index(this->face, key);
 
	if (glyph_index == 0) {
 
		if (key == '?') {
 
			/* The font misses the '?' character. Use sprite font. */
 
			SpriteID sprite = this->GetUnicodeGlyph(key);
 
			Sprite *spr = (Sprite*)GetRawSprite(sprite, ST_FONT, AllocateFont);
 
			assert(spr != NULL);
 
			new_glyph.sprite = spr;
 
			new_glyph.width  = spr->width + (this->fs != FS_NORMAL);
 
			SetGlyphPtr(this->fs, key, &new_glyph, false);
 
			this->SetGlyphPtr(key, &new_glyph, false);
 
			return new_glyph.sprite;
 
		} else {
 
			/* Use '?' for missing characters. */
 
			this->GetGlyph('?');
 
			glyph = GetGlyphPtr(this->fs, '?');
 
			SetGlyphPtr(this->fs, key, glyph, true);
 
			glyph = this->GetGlyphPtr('?');
 
			this->SetGlyphPtr(key, glyph, true);
 
			return glyph->sprite;
 
		}
 
	}
 
	FT_Load_Glyph(this->face, glyph_index, FT_LOAD_DEFAULT);
 
	FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
 

	
 
	/* Despite requesting a normal glyph, FreeType may have returned a bitmap */
 
	aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
 

	
 
	/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
 
	width  = max(1, slot->bitmap.width + (this->fs == FS_NORMAL));
 
	height = max(1, slot->bitmap.rows  + (this->fs == FS_NORMAL));
 
@@ -519,50 +507,50 @@ const Sprite *FreeTypeFontCache::GetGlyp
 
	for (y = 0; y < slot->bitmap.rows; y++) {
 
		for (x = 0; x < slot->bitmap.width; x++) {
 
			if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
 
				sprite.data[x + y * sprite.width].m = FACE_COLOUR;
 
				sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
 
			}
 
		}
 
	}
 

	
 
	new_glyph.sprite = BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, AllocateFont);
 
	new_glyph.width  = slot->advance.x >> 6;
 

	
 
	SetGlyphPtr(this->fs, key, &new_glyph);
 
	this->SetGlyphPtr(key, &new_glyph);
 

	
 
	return new_glyph.sprite;
 
}
 

	
 

	
 
bool FreeTypeFontCache::GetDrawGlyphShadow()
 
{
 
	return GetFontAAState(FS_NORMAL);
 
}
 

	
 

	
 
uint FreeTypeFontCache::GetGlyphWidth(WChar key)
 
{
 
	GlyphEntry *glyph;
 

	
 
	if (this->face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
 
		SpriteID sprite = this->GetUnicodeGlyph(key);
 
		if (sprite == 0) sprite = this->GetUnicodeGlyph('?');
 
		return SpriteExists(sprite) ? GetSprite(sprite, ST_FONT)->width + (this->fs != FS_NORMAL && this->fs != FS_MONO) : 0;
 
	}
 

	
 
	glyph = GetGlyphPtr(this->fs, key);
 
	glyph = this->GetGlyphPtr(key);
 
	if (glyph == NULL || glyph->sprite == NULL) {
 
		this->GetGlyph(key);
 
		glyph = GetGlyphPtr(this->fs, key);
 
		glyph = this->GetGlyphPtr(key);
 
	}
 

	
 
	return glyph->width;
 
}
 

	
 

	
 
#endif /* WITH_FREETYPE */
 

	
 
/* Sprite based glyph mapping */
 

	
 
#include "table/unicode.h"
 

	
0 comments (0 inline, 0 general)