quakeforge/libs/video/renderer/r_font.c
Bill Currie c5099d30b1 [renderer] Fix access to freed memory loading fonts
QFS_LoadFile closes its file argument (this is a design error resulting
from changing QFS_LoadFile to take a file instead of a path and not
completing the update), resulting in the call to Qfilesize accessing
freed memory.
2022-09-15 14:32:16 +09:00

130 lines
3.3 KiB
C

/*
r_font.c
Renderer font management management
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2022/8/26
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <math.h>
#include "QF/quakefs.h"
#include "QF/sys.h"
#include "QF/math/bitop.h"
#include "r_font.h"
#include "compat.h"
static FT_Library ft;
static void
copy_glyph (rglyph_t *glyph, FT_GlyphSlot src_glyph)
{
int dst_pitch = glyph->font->scrap.width;
byte *dst = glyph->font->scrap_bitmap + glyph->rect->x + glyph->rect->y * dst_pitch;
int src_pitch = src_glyph->bitmap.pitch;
byte *src = src_glyph->bitmap.buffer;
for (unsigned i = 0; i < src_glyph->bitmap.rows; i++) {
memcpy (dst, src, src_glyph->bitmap.width);
dst += dst_pitch;
src += src_pitch;
}
}
VISIBLE void
R_FontInit (void)
{
if (FT_Init_FreeType (&ft)) {
Sys_Error ("Could not init FreeType library");
}
}
VISIBLE void
R_FontFree (rfont_t *font)
{
if (font->face) {
FT_Done_Face (font->face);
}
if (font->scrap.rects || font->scrap.free_rects) {
R_ScrapDelete (&font->scrap);
}
free (font->glyphs);
free (font->scrap_bitmap);
free (font->font_resource);
free (font);
}
VISIBLE rfont_t *
R_FontLoad (QFile *font_file, int size)
{
byte *font_data = QFS_LoadFile (font_file, 0);
if (!font_data) {
return 0;
}
size_t font_size = qfs_filesize;
rfont_t *font = calloc (1, sizeof (rfont_t));
font->font_resource = font_data;
if (FT_New_Memory_Face (ft, font_data, font_size, 0, &font->face)) {
R_FontFree (font);
return 0;
}
FT_Set_Pixel_Sizes(font->face, 0, size);
int pixels = 0;
for (FT_Long gind = 0; gind < font->face->num_glyphs; gind++) {
FT_Load_Glyph (font->face, gind, FT_LOAD_DEFAULT);
__auto_type g = font->face->glyph;
pixels += g->bitmap.width * g->bitmap.rows;
}
pixels = sqrt (2 * pixels);
pixels = BITOP_RUP (pixels);
R_ScrapInit (&font->scrap, pixels, pixels);
font->scrap_bitmap = calloc (1, pixels * pixels);
font->num_glyphs = font->face->num_glyphs;
font->glyphs = malloc (font->num_glyphs * sizeof (rglyph_t));
for (FT_Long gind = 0; gind < font->face->num_glyphs; gind++) {
rglyph_t *glyph = &font->glyphs[gind];
FT_Load_Glyph (font->face, gind, FT_LOAD_DEFAULT);
__auto_type slot = font->face->glyph;
FT_Render_Glyph (slot, FT_RENDER_MODE_NORMAL);
int width = slot->bitmap.width;
int height = slot->bitmap.rows;
glyph->font = font;
glyph->rect = R_ScrapAlloc (&font->scrap, width, height);
glyph->bearing = (vec2i_t) { slot->bitmap_left, slot->bitmap_top };
glyph->advance = slot->advance.x;
glyph->charcode = gind;
copy_glyph (glyph, slot);
}
return font;
}