From: Wolfgang (Blub) Bumiller <blub@speed.at>
Date: Thu, 3 Nov 2011 11:51:23 +0000 (+0100)
Subject: font/glyph unloading now added, lots of cross-referencing lists to keep clean...
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=88f2eec4ef4fcb7a9e978e44e8deb85b784e34f7;p=xonotic%2Fdarkplaces.git

font/glyph unloading now added, lots of cross-referencing lists to keep clean...
---

diff --git a/ft2.c b/ft2.c
index 7c869c69..5463ff1b 100644
--- a/ft2.c
+++ b/ft2.c
@@ -322,9 +322,11 @@ void font_start(void)
 	font_glyphtex_start = font_glyphtex_end = NULL;
 }
 
+void Font_Free_GlyphTex(ft2_glyphtex_t *gt);
 void font_shutdown(void)
 {
 	int i;
+	ft2_glyphtex_t *gt, *gtnext;
 	for (i = 0; i < dp_fonts.maxsize; ++i)
 	{
 		if (dp_fonts.f[i].ft2)
@@ -333,6 +335,13 @@ void font_shutdown(void)
 			dp_fonts.f[i].ft2 = NULL;
 		}
 	}
+	gt = font_glyphtex_start;
+	for (gt = font_glyphtex_start; gt; gt = gtnext)
+	{
+		gtnext = gt->next;
+		Font_Free_GlyphTex(gt);
+	}
+	font_glyphtex_start = font_glyphtex_end = NULL;
 	Font_CloseLibrary();
 }
 
@@ -379,16 +388,19 @@ ft2_glyphtex_t *Font_New_GlyphTex(int width, int height)
 	int gtpitch;
 
 	ft2_glyphtex_t *gt = (ft2_glyphtex_t *)Mem_Alloc(font_mempool, sizeof(ft2_glyphtex_t));
+	gt->next = NULL;
+	gt->prev = NULL;
+
 	if (!font_glyphtex_start)
 		font_glyphtex_start = gt;
+
 	if (font_glyphtex_end) {
 		font_glyphtex_end->next = gt;
 		gt->prev = font_glyphtex_end;
-		font_glyphtex_end = gt;
-	} else {
-		font_glyphtex_end = gt;
 	}
 
+	font_glyphtex_end = gt;
+
 	if (r_font_use_alpha_textures.integer)
 		bytesPerPixel = 1;
 
@@ -398,9 +410,9 @@ ft2_glyphtex_t *Font_New_GlyphTex(int width, int height)
 	gt->bytesPerPixel = bytesPerPixel;
 	gtpitch = width * bytesPerPixel;
 	gt->tex = NULL;
-	gtdata = (unsigned char*)Mem_Alloc(font_mempool, width * height * bytesPerPixel);
-	//memset(gt->data, 0, width * height * bytesPerPixel);
+
 	// Initialize as white texture with zero alpha
+	gtdata = (unsigned char*)Mem_Alloc(font_mempool, width * height * bytesPerPixel);
 	tp = 0;
 	while (tp < height*gtpitch)
 	{
@@ -424,18 +436,24 @@ ft2_glyphtex_t *Font_New_GlyphTex(int width, int height)
 	gt->glyph_count = 0;
 	//gt->lastusedframe = 0;
 
-	gt->first_glyph = gt->last_glyph = NULL;
+	gt->first_glyph = NULL;
 
 	++font_glyphtex_counter;
 	Con_Printf("Glyphtextures: %i\n", font_glyphtex_counter);
 	return gt;
 }
 
-void Font_Free_GlyphTex(ft2_glyphtex_t *gt);
+static void Font_Free_Glyph(glyph_t *glyph);
 void Font_Free_GlyphTex(ft2_glyphtex_t *gt)
 {
 	ft2_glyphtex_t *prev = gt->prev;
 	ft2_glyphtex_t *next = gt->next;
+	glyph_t *glyph, *glnext;
+
+	// IMPORTANT: If we don't set this to 0, the deletion of
+	// the contained glyphs may trigger yet another Free_GlyphTex call
+	// due to the glyphtex suddenly be recognized as unused!
+	gt->glyph_count = 0;
 
 	if (prev)
 		prev->next = next;
@@ -446,6 +464,11 @@ void Font_Free_GlyphTex(ft2_glyphtex_t *gt)
 	Mod_AllocLightmap_Free(&gt->blockstate);
 
 	// FIXME: Go through glyphs now
+	for (glyph = gt->first_glyph; glyph; glyph = glnext)
+	{
+		glnext = glyph->next; // Important: glyph gets freed, and also removes itself from the list
+		Font_Free_Glyph(glyph);
+	}
 
 	R_FreeTexture(gt->tex);
 	gt->tex = NULL;
@@ -509,6 +532,8 @@ qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
 		return false;
 	}
 
+	memset(ft2, 0, sizeof(*ft2));
+
 	// check if a fallback font has been specified, if it has been, and the
 	// font fails to load, use the image font as main font
 	for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
@@ -899,98 +924,6 @@ static void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitc
 	}
 }
 
-static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
-//static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
-#if 0
-// DEPRECATED:
-static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
-{
-	int map_index;
-	ft2_font_map_t *fmap, temp;
-	int gpad_l, gpad_r, gpad_t, gpad_b;
-
-	if (!(size > 0.001f && size < 1000.0f))
-		size = 0;
-
-	if (!size)
-		size = 16;
-	if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
-		return false;
-
-	for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
-	{
-		if (!font->font_maps[map_index])
-			break;
-		// if a similar size has already been loaded, ignore this one
-		//abs(font->font_maps[map_index]->size - size) < 4
-		if (font->font_maps[map_index]->size == size)
-			return true;
-	}
-
-	if (map_index >= MAX_FONT_SIZES)
-		return false;
-
-	if (check_only) {
-		FT_Face fontface;
-		if (font->image_font)
-			fontface = (FT_Face)font->next->face;
-		else
-			fontface = (FT_Face)font->face;
-		return (Font_SearchSize(font, fontface, size) > 0);
-	}
-
-	Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
-
-	memset(&temp, 0, sizeof(temp));
-	temp.size = size;
-	temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
-	if (!(r_font_nonpoweroftwo.integer && vid.support.arb_texture_non_power_of_two))
-		temp.glyphSize = CeilPowerOf2(temp.glyphSize);
-	temp.sfx = (1.0/64.0)/(double)size;
-	temp.sfy = (1.0/64.0)/(double)size;
-	temp.intSize = -1; // negative value: LoadMap must search now :)
-	if (!Font_LoadMap(font, &temp, 0, &fmap))
-	{
-		Con_Printf("ERROR: can't load the first character map for %s\n"
-			   "This is fatal\n",
-			   font->name);
-		Font_UnloadFont(font);
-		return false;
-	}
-	font->font_maps[map_index] = temp.next;
-
-	fmap->sfx = temp.sfx;
-	fmap->sfy = temp.sfy;
-
-	// load the default kerning vector:
-	if (font->has_kerning)
-	{
-		Uchar l, r;
-		FT_Vector kernvec;
-		for (l = 0; l < 256; ++l)
-		{
-			for (r = 0; r < 256; ++r)
-			{
-				FT_ULong ul, ur;
-				ul = qFT_Get_Char_Index((FT_Face)font->face, l);
-				ur = qFT_Get_Char_Index((FT_Face)font->face, r);
-				if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
-				{
-					fmap->kerning.kerning[l][r][0] = 0;
-					fmap->kerning.kerning[l][r][1] = 0;
-				}
-				else
-				{
-					fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
-					fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
-				}
-			}
-		}
-	}
-	return true;
-}
-#endif
-
 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
 {
 	int match = -1;
@@ -1043,16 +976,6 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
 	return match;
 }
 
-#if 0
-// DEPRECATED:
-ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
-{
-	if (index < 0 || index >= MAX_FONT_SIZES)
-		return NULL;
-	return font->font_maps[index];
-}
-#endif
-
 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
 {
 	if (font->currenth == h &&
@@ -1153,20 +1076,7 @@ qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left,
 }
 #endif
 
-/*
-static void UnloadMapRec(ft2_font_map_t *map)
-{
-	if (map->pic)
-	{
-		//Draw_FreePic(map->pic); // FIXME: refcounting needed...
-		map->pic = NULL;
-	}
-	if (map->next)
-		UnloadMapRec(map->next);
-	Mem_Free(map);
-}
-*/
-
+static void Font_GlyphTree_Free(ft2_glyph_tree_t *tree);
 void Font_UnloadFont(ft2_font_t *font)
 {
 	int i;
@@ -1185,16 +1095,19 @@ void Font_UnloadFont(ft2_font_t *font)
 		font->attachmentcount = 0;
 		font->attachments = NULL;
 	}
-	/*
+
 	for (i = 0; i < MAX_FONT_SIZES; ++i)
 	{
-		if (font->font_maps[i])
-		{
-			UnloadMapRec(font->font_maps[i]);
-			font->font_maps[i] = NULL;
+		ft2_font_size_t *fsize = &font->font_sizes[i];
+		int ch;
+		for (ch = 0; ch < 256; ++ch) {
+			if (fsize->main_glyphs[ch]) {
+				Font_Free_Glyph(fsize->main_glyphs[ch]);
+			}
 		}
+		Font_GlyphTree_Free(&fsize->glyphtree);
 	}
-	*/
+
 	if (ft2_dll)
 	{
 		if (font->face)
@@ -1255,6 +1168,10 @@ static void Font_Glyph_Insert(ft2_font_size_t *fsize, Uchar ch, glyph_t *glyph)
 {
 	ft2_glyph_tree_t *tree;
 	unsigned int i, idx;
+
+	glyph->ftsize = fsize;
+	glyph->treech = ch;
+
 	if (ch < 256)
 	{
 		fsize->main_glyphs[ch] = glyph;
@@ -1275,25 +1192,90 @@ static void Font_Glyph_Insert(ft2_font_size_t *fsize, Uchar ch, glyph_t *glyph)
 	return;
 }
 
-static void Font_Glyph_Free(glyph_t *glyph);
-static void Font_GlyphTree_Free(ft2_glyph_tree_t *tree)
+static void Font_GlyphTree_Remove(ft2_font_size_t *fsize, Uchar ch)
+{
+	ft2_glyph_tree_t *tree;
+	unsigned int i, idx;
+
+	if (ch < 256)
+	{
+		fsize->main_glyphs[ch] = NULL;
+		return;
+	}
+
+	tree = &fsize->glyphtree;
+	for (i = 0; i < (2 * sizeof(Uchar)); ++i) {
+		idx = (ch & 0x0F);
+		if (!tree->next[idx]) {
+			Con_Print("ERROR: Glyph not in glyphtree...\n");
+			return;
+		}
+		tree = tree->next[idx];
+		ch >>= 4;
+	}
+	tree->endglyph = NULL;
+}
+
+static void Font_GlyphTree_Free_Int(ft2_glyph_tree_t *tree, qboolean memfree)
 {
 	unsigned int i;
 	for (i = 0; i < sizeof(tree->next) / sizeof(tree->next[0]); ++i)
 	{
 		if (tree->next[i])
-			Font_GlyphTree_Free(tree->next[i]);
+			Font_GlyphTree_Free_Int(tree->next[i], true);
 	}
 	if (tree->endglyph)
 	{
-		Font_Glyph_Free(tree->endglyph);
+		Font_Free_Glyph(tree->endglyph);
 	}
-	Mem_Free(tree);
+	if (memfree)
+		Mem_Free(tree);
 }
 
-static void Font_Glyph_Free(glyph_t *glyph)
+static void Font_GlyphTree_Free(ft2_glyph_tree_t *tree)
 {
-	// FIXME: tell the glyphtexture that this glyph is now gone...
+	Font_GlyphTree_Free_Int(tree, false);
+}
+
+static void Font_GlyphTex_Glyph_Freed(ft2_glyphtex_t *gtex)
+{
+	// glyph_count is zero when we're unloading the glyphtexture
+	// in this case we shouldn't free the glyphtex twice if we hit a
+	// too high glyph-freed count!
+	if (!gtex->glyph_count)
+		return;
+	gtex->glyphs_freed++;
+	// Delete the glyphtex if it has too many unused characters
+	// TODO
+}
+
+static void Font_Free_Glyph(glyph_t *glyph)
+{
+	// The glyphtex wants to know about unloaded glyphs
+	// glyph->glyphtex->glyphs_freed++;
+	Font_GlyphTex_Glyph_Freed(glyph->glyphtex);
+
+	if (!glyph->prev) {
+		if (glyph->glyphtex->first_glyph != glyph) {
+			// Consistency checking...
+			// Should never happen but in this case I'd like to check
+			// Because there are many linked lists around here...
+			Con_Print("ERROR: Glyph has no link to a previous glyph but is not the first glyph in the glyph texture either!\n");
+		} else
+			glyph->glyphtex->first_glyph = glyph->next;
+	} else
+		glyph->prev->next = glyph->next;
+
+	if (glyph->next)
+		glyph->next->prev = glyph->prev;
+
+	// Now the glyph is unlinked from the glyphtex
+	// Unlink it from the fontsize as well!
+
+	Font_GlyphTree_Remove(glyph->ftsize, glyph->treech);
+
+	// Good, now we free the glyph
+
 	Mem_Free(glyph);
 }
 
@@ -1521,6 +1503,7 @@ glyph_t* Font_GetGlyph(ft2_font_t *font, int sizeindex, float _w, float _h, Ucha
 
 	glyph->glyphtex = glyphtex;
 	glyph->tex = glyphtex->tex;
+	glyphtex->glyph_count++;
 
 	// Glyph is rendered and we have space allocated on a glyph-texture.
 	// We're good to go, ready for postprocessing and the rest.
@@ -1633,6 +1616,16 @@ glyph_t* Font_GetGlyph(ft2_font_t *font, int sizeindex, float _w, float _h, Ucha
 	R_UpdateTexture(glyphtex->tex, data, gC, gR, 0, allocw, alloch, 8 * bytesPerPixel);
 	Mem_Free(data);
 
+	if (!glyphtex->first_glyph) {
+		glyphtex->first_glyph = glyph;
+		glyph->next = glyph->prev = NULL;
+	} else {
+		glyphtex->first_glyph->prev = glyph;
+		glyph->next = glyphtex->first_glyph;
+		glyph->prev = NULL;
+		glyphtex->first_glyph = glyph;
+	}
+
 	R_SaveTextureTGAFile(glyphtex->tex, "font-texture.tga", true);
 
 	glyph->image = false;
@@ -1686,501 +1679,3 @@ glyph_t* Font_GetGlyph(ft2_font_t *font, int sizeindex, float _w, float _h, Ucha
 
 	return glyph;
 }
-
-#if 0
-// DEPRECATED:
-static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
-{
-	char map_identifier[MAX_QPATH];
-	unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
-	unsigned char *data = NULL;
-	FT_ULong ch, mapch;
-	int status;
-	int tp;
-	FT_Int32 load_flags;
-	int gpad_l, gpad_r, gpad_t, gpad_b;
-	char vabuf[1024];
-
-	int pitch;
-	int gR, gC; // glyph position: row and column
-
-	ft2_font_map_t *map, *next;
-	ft2_font_t *usefont;
-
-	FT_Face fontface;
-
-	int bytesPerPixel = 4; // change the conversion loop too if you change this!
-
-	if (outmap)
-		*outmap = NULL;
-
-	if (r_font_use_alpha_textures.integer)
-		bytesPerPixel = 1;
-
-	if (font->image_font)
-		fontface = (FT_Face)font->next->face;
-	else
-		fontface = (FT_Face)font->face;
-
-	switch(font->settings->antialias)
-	{
-		case 0:
-			switch(font->settings->hinting)
-			{
-				case 0:
-					load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
-					break;
-				case 1:
-				case 2:
-					load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
-					break;
-				default:
-				case 3:
-					load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
-					break;
-			}
-			break;
-		default:
-		case 1:
-			switch(font->settings->hinting)
-			{
-				case 0:
-					load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
-					break;
-				case 1:
-					load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
-					break;
-				case 2:
-					load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
-					break;
-				default:
-				case 3:
-					load_flags = FT_LOAD_TARGET_NORMAL;
-					break;
-			}
-			break;
-	}
-
-	//status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
-	//if (status)
-	if (font->image_font && mapstart->intSize < 0)
-		mapstart->intSize = mapstart->size;
-	if (mapstart->intSize < 0)
-	{
-		/*
-		mapstart->intSize = mapstart->size;
-		while (1)
-		{
-			if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
-			{
-				Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
-				return false;
-			}
-			if ((fontface->size->metrics.height>>6) <= mapstart->size)
-				break;
-			if (mapstart->intSize < 2)
-			{
-				Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
-				return false;
-			}
-			--mapstart->intSize;
-		}
-		*/
-		if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
-			return false;
-		Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
-	}
-
-	if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
-	{
-		Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
-		return false;
-	}
-
-	map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
-	if (!map)
-	{
-		Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
-		return false;
-	}
-
-	// create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
-	dpsnprintf(map_identifier, sizeof(map_identifier),
-		"%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
-		font->name,
-		(double) mapstart->intSize,
-		(int) load_flags,
-		(double) font->settings->blur,
-		(double) font->settings->outline,
-		(double) font->settings->shadowx,
-		(double) font->settings->shadowy,
-		(double) font->settings->shadowz,
-		(unsigned) mapidx);
-
-	// create a cachepic_t from the data now, or reuse an existing one
-	map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
-	if (developer_font.integer)
-	{
-		if (map->pic->tex == r_texture_notexture)
-			Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
-		else
-			Con_Printf("Using cached font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
-	}
-
-	Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
-
-	// copy over the information
-	map->size = mapstart->size;
-	map->intSize = mapstart->intSize;
-	map->glyphSize = mapstart->glyphSize;
-	map->sfx = mapstart->sfx;
-	map->sfy = mapstart->sfy;
-
-	pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
-	if (map->pic->tex == r_texture_notexture)
-	{
-		data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
-		if (!data)
-		{
-			Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
-			Mem_Free(map);
-			return false;
-		}
-		// initialize as white texture with zero alpha
-		tp = 0;
-		while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
-		{
-			if (bytesPerPixel == 4)
-			{
-				data[tp++] = 0xFF;
-				data[tp++] = 0xFF;
-				data[tp++] = 0xFF;
-			}
-			data[tp++] = 0x00;
-		}
-	}
-
-	memset(map->width_of, 0, sizeof(map->width_of));
-
-	// insert the map
-	map->start = mapidx * FONT_CHARS_PER_MAP;
-	next = mapstart;
-	while(next->next && next->next->start < map->start)
-		next = next->next;
-	map->next = next->next;
-	next->next = map;
-
-	gR = 0;
-	gC = -1;
-	for (ch = map->start;
-	     ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
-	     ++ch)
-	{
-		FT_ULong glyphIndex;
-		int w, h, x, y;
-		FT_GlyphSlot glyph;
-		FT_Bitmap *bmp;
-		unsigned char *imagedata = NULL, *dst, *src;
-		glyph_slot_t *mapglyph;
-		FT_Face face;
-		int pad_l, pad_r, pad_t, pad_b;
-
-		mapch = ch - map->start;
-
-		if (developer_font.integer)
-			Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
-
-		++gC;
-		if (gC >= FONT_CHARS_PER_LINE)
-		{
-			gC -= FONT_CHARS_PER_LINE;
-			++gR;
-		}
-
-		if (data)
-		{
-			imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
-			imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
-		}
-		//status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
-		// we need the glyphIndex
-		face = (FT_Face)font->face;
-		usefont = NULL;
-		if (font->image_font && mapch == ch && img_fontmap[mapch])
-		{
-			map->glyphs[mapch].image = true;
-			continue;
-		}
-		glyphIndex = qFT_Get_Char_Index(face, ch);
-		if (glyphIndex == 0)
-		{
-			// by convention, 0 is the "missing-glyph"-glyph
-			// try to load from a fallback font
-			for(usefont = font->next; usefont != NULL; usefont = usefont->next)
-			{
-				if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
-					continue;
-				// try that glyph
-				face = (FT_Face)usefont->face;
-				glyphIndex = qFT_Get_Char_Index(face, ch);
-				if (glyphIndex == 0)
-					continue;
-				status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
-				if (status)
-					continue;
-				break;
-			}
-			if (!usefont)
-			{
-				//Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
-				// now we let it use the "missing-glyph"-glyph
-				face = (FT_Face)font->face;
-				glyphIndex = 0;
-			}
-		}
-
-		if (!usefont)
-		{
-			usefont = font;
-			face = (FT_Face)font->face;
-			status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
-			if (status)
-			{
-				//Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
-				Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
-				continue;
-			}
-		}
-
-		glyph = face->glyph;
-		bmp = &glyph->bitmap;
-
-		w = bmp->width;
-		h = bmp->rows;
-
-		if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
-			Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
-			if (w > map->glyphSize)
-				w = map->glyphSize - gpad_l - gpad_r;
-			if (h > map->glyphSize)
-				h = map->glyphSize;
-		}
-
-		if (imagedata)
-		{
-			switch (bmp->pixel_mode)
-			{
-			case FT_PIXEL_MODE_MONO:
-				if (developer_font.integer)
-					Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
-				break;
-			case FT_PIXEL_MODE_GRAY2:
-				if (developer_font.integer)
-					Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
-				break;
-			case FT_PIXEL_MODE_GRAY4:
-				if (developer_font.integer)
-					Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
-				break;
-			case FT_PIXEL_MODE_GRAY:
-				if (developer_font.integer)
-					Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
-				break;
-			default:
-				if (developer_font.integer)
-					Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
-				Mem_Free(data);
-				Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
-				return false;
-			}
-			for (y = 0; y < h; ++y)
-			{
-				dst = imagedata + y * pitch;
-				src = bmp->buffer + y * bmp->pitch;
-
-				switch (bmp->pixel_mode)
-				{
-				case FT_PIXEL_MODE_MONO:
-					dst += bytesPerPixel - 1; // shift to alpha byte
-					for (x = 0; x < bmp->width; x += 8)
-					{
-						unsigned char ch = *src++;
-						*dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
-						*dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
-					}
-					break;
-				case FT_PIXEL_MODE_GRAY2:
-					dst += bytesPerPixel - 1; // shift to alpha byte
-					for (x = 0; x < bmp->width; x += 4)
-					{
-						unsigned char ch = *src++;
-						*dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
-						*dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
-						*dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
-						*dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
-					}
-					break;
-				case FT_PIXEL_MODE_GRAY4:
-					dst += bytesPerPixel - 1; // shift to alpha byte
-					for (x = 0; x < bmp->width; x += 2)
-					{
-						unsigned char ch = *src++;
-						*dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
-						*dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
-					}
-					break;
-				case FT_PIXEL_MODE_GRAY:
-					// in this case pitch should equal width
-					for (tp = 0; tp < bmp->pitch; ++tp)
-						dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
-
-					//memcpy((void*)dst, (void*)src, bmp->pitch);
-					//dst += bmp->pitch;
-					break;
-				default:
-					break;
-				}
-			}
-
-			pad_l = gpad_l;
-			pad_r = gpad_r;
-			pad_t = gpad_t;
-			pad_b = gpad_b;
-			Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
-		}
-		else
-		{
-			pad_l = gpad_l;
-			pad_r = gpad_r;
-			pad_t = gpad_t;
-			pad_b = gpad_b;
-			Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
-		}
-
-
-		// now fill map->glyphs[ch - map->start]
-		mapglyph = &map->glyphs[mapch];
-
-		{
-			// old way
-			// double advance = (double)glyph->metrics.horiAdvance * map->sfx;
-
-			double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
-			//double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
-			double advance = (glyph->advance.x / 64.0) / map->size;
-			//double mWidth = (glyph->metrics.width >> 6) / map->size;
-			//double mHeight = (glyph->metrics.height >> 6) / map->size;
-
-			mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
-			mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
-			mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
-			mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
-			//mapglyph->vxmin = bearingX;
-			//mapglyph->vxmax = bearingX + mWidth;
-			mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
-			mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
-			//mapglyph->vymin = -bearingY;
-			//mapglyph->vymax = mHeight - bearingY;
-			mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
-			mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
-			//Con_Printf("dpi = %f %f (%f %d) %d %d\n", bmp->width / (mapglyph->vxmax - mapglyph->vxmin), bmp->rows / (mapglyph->vymax - mapglyph->vymin), map->size, map->glyphSize, (int)fontface->size->metrics.x_ppem, (int)fontface->size->metrics.y_ppem);
-			//mapglyph->advance_x = advance * usefont->size;
-			//mapglyph->advance_x = advance;
-			mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
-			mapglyph->advance_y = 0;
-
-			if (developer_font.integer)
-			{
-				Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
-				Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
-				if (ch >= 32 && ch <= 128)
-					Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
-				Con_DPrintf("glyphinfo:   Vertex info:\n");
-				Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
-				Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
-				Con_DPrintf("glyphinfo:   Texture info:\n");
-				Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
-				Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
-				Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
-			}
-		}
-		map->glyphs[mapch].image = false;
-	}
-
-	if (map->pic->tex == r_texture_notexture)
-	{
-		int w = map->glyphSize * FONT_CHARS_PER_LINE;
-		int h = map->glyphSize * FONT_CHAR_LINES;
-		rtexture_t *tex;
-		// abuse the Draw_CachePic system to keep track of this texture
-		tex = R_LoadTexture2D(drawtexturepool, map_identifier, w, h, data, r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA, TEXF_ALPHA | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0), -1, NULL);
-		// if tex is NULL for any reason, the pic->tex will remain set to r_texture_notexture
-		if (tex)
-			map->pic->tex = tex;
-
-		if (r_font_diskcache.integer >= 1)
-		{
-			// swap to BGRA for tga writing...
-			int s = w * h;
-			int x;
-			int b;
-			for (x = 0;x < s;x++)
-			{
-				b = data[x*4+0];
-				data[x*4+0] = data[x*4+2];
-				data[x*4+2] = b;
-			}
-			Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
-#ifndef USE_GLES2
-			if (r_font_compress.integer && qglGetCompressedTexImageARB && tex)
-				R_SaveTextureDDSFile(tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
-#endif
-		}
-	}
-
-	if(data)
-		Mem_Free(data);
-
-	if (map->pic->tex == r_texture_notexture)
-	{
-		// if the first try isn't successful, keep it with a broken texture
-		// otherwise we retry to load it every single frame where ft2 rendering is used
-		// this would be bad...
-		// only `data' must be freed
-		Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
-			   font->name, mapstart->size, mapidx);
-		return false;
-	}
-	if (outmap)
-		*outmap = map;
-	return true;
-}
-#endif
-
-#if 0
-// DEPRECATED:
-qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
-{
-	if (map_index < 0 || map_index >= MAX_FONT_SIZES)
-		return false;
-	// the first map must have been loaded already
-	if (!font->font_maps[map_index])
-		return false;
-	return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
-}
-
-ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
-{
-	while (start && start->start + FONT_CHARS_PER_MAP <= ch)
-		start = start->next;
-	if (start && start->start > ch)
-		return NULL;
-	return start;
-}
-#endif
diff --git a/ft2.h b/ft2.h
index 8b4f3498..00676663 100644
--- a/ft2.h
+++ b/ft2.h
@@ -116,6 +116,15 @@ typedef struct glyph_s
 	ft2_glyphtex_t *glyphtex;
 	// So we don't need to care about what glyphtex looks like outside the font code:
 	rtexture_t     *tex;
+
+	// We keep a linked list of glyphs of the same glyphtex
+	struct glyph_s *next;
+	struct glyph_s *prev;
+
+	// We also need to find the glyph, to be able to remove it
+	// from the glyphtree.
+	Uchar       treech;
+	ft2_font_size_t *ftsize;
 } glyph_t;
 
 
diff --git a/ft2_fontdefs.h b/ft2_fontdefs.h
index e475a63a..c89c3cd9 100644
--- a/ft2_fontdefs.h
+++ b/ft2_fontdefs.h
@@ -35,6 +35,7 @@ qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidt
 	// destroy this texture to have the used glyphs be
 	// moved to a more-active texture.
 	int glyph_count;
+	int glyphs_freed;
 	//int lastusedframe; // <- useful, we unload unused images
 
 	// NOTE: We also need a way to remove glyphs
@@ -42,7 +43,6 @@ qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidt
 	// this texture, to force them to reload them.
 	// NOTE: Rather then re-rendering, we could just copy them directly.
 	struct glyph_s *first_glyph;
-	struct glyph_s *last_glyph; // <- for faster insertion
 
 	struct ft2_glyphtex_s *prev;
 	struct ft2_glyphtex_s *next;