quakeforge/libs/ui/font.c

171 lines
4.4 KiB
C

/*
font.c
Font 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 <fontconfig/fontconfig.h>
#include "QF/quakefs.h"
#include "QF/sys.h"
#include "QF/math/bitop.h"
#include "QF/plugin/vid_render.h"
#include "QF/ui/font.h"
#include "compat.h"
static FT_Library ft;
static void
copy_glyph (vrect_t *rect, FT_GlyphSlot src_glyph, font_t *font)
{
int dst_pitch = font->scrap.width;
byte *dst = font->scrap_bitmap + rect->x + 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;
}
}
static void
Font_shutdown (void *data)
{
FT_Done_FreeType (ft);
}
VISIBLE void
Font_Init (void)
{
if (FT_Init_FreeType (&ft)) {
Sys_Error ("Could not init FreeType library");
}
Sys_RegisterShutdown (Font_shutdown, 0);
}
VISIBLE void
Font_Free (font_t *font)
{
if (font->face) {
FT_Done_Face (font->face);
}
if (font->scrap.rects || font->scrap.free_rects) {
R_ScrapDelete (&font->scrap);
}
free (font->glyph_rects);
free (font->glyph_bearings);
free (font->scrap_bitmap);
free (font->font_resource);
free (font);
}
VISIBLE font_t *
Font_Load (QFile *font_file, int size)
{
byte *font_data = QFS_LoadFile (font_file, 0);
if (!font_data) {
return 0;
}
size_t font_size = qfs_filesize;
font_t *font = calloc (1, sizeof (font_t));
font->font_resource = font_data;
if (FT_New_Memory_Face (ft, font_data, font_size, 0, &font->face)) {
Font_Free (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;
// include padding around the glyph to avoid texel leaks
pixels += (g->bitmap.width + 1) * (g->bitmap.rows + 1);
}
pixels = sqrt (5 * pixels / 4);
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->glyph_rects = malloc (font->num_glyphs * sizeof (vrect_t));
font->glyph_bearings = malloc (font->num_glyphs * sizeof (vec2i_t));
for (FT_Long gind = 0; gind < font->face->num_glyphs; gind++) {
vrect_t *rect = &font->glyph_rects[gind];
vec2i_t *bearing = &font->glyph_bearings[gind];
FT_Load_Glyph (font->face, gind, FT_LOAD_DEFAULT);
__auto_type slot = font->face->glyph;
FT_Render_Glyph (slot, FT_RENDER_MODE_NORMAL);
// add padding to create a buffer around the glyph to prevent texel
// leaks
int width = slot->bitmap.width + 1;
int height = slot->bitmap.rows + 1;
*rect = *R_ScrapAlloc (&font->scrap, width, height);
*bearing = (vec2i_t) { slot->bitmap_left, slot->bitmap_top };
// shrink the rect so as to NOT include the padding
rect->width -= 1;
rect->height -= 1;
copy_glyph (rect, slot, font);
}
font->fontid = r_funcs->Draw_AddFont (font);
return font;
}
char *
Font_SystemFont (const char *font_pattern)
{
auto config = FcInitLoadConfigAndFonts ();
auto pattern = FcNameParse ((const FcChar8 *) font_pattern);
FcConfigSubstitute (config, pattern, FcMatchPattern);
FcDefaultSubstitute (pattern);
FcResult result;
auto font = FcFontMatch (config, pattern, &result);
char *filename = 0;
if (font) {
FcChar8 *str;
if (FcPatternGetString (font, FC_FILE, 0, &str) == FcResultMatch) {
filename = strdup ((char *) str);
}
FcPatternDestroy (font);
}
FcPatternDestroy (pattern);
return filename;
}