diff --git a/Makefile.am b/Makefile.am index 33aa5b689..194fcd984 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ NOCONV_DIST= \ BUILT_SOURCES = $(top_srcdir)/.version #AM_CFLAGS= @PREFER_NON_PIC@ -AM_CPPFLAGS= -I$(top_srcdir)/include $(PTHREAD_CFLAGS) $(FNM_FLAGS) $(NCURSES_CFLAGS) +AM_CPPFLAGS= -I$(top_srcdir)/include $(PTHREAD_CFLAGS) $(FNM_FLAGS) $(NCURSES_CFLAGS) $(FREETYPE_CFLAGS) common_ldflags= -export-dynamic @PTHREAD_LDFLAGS@ diff --git a/config.d/freetype.m4 b/config.d/freetype.m4 new file mode 100644 index 000000000..7f13bf1ee --- /dev/null +++ b/config.d/freetype.m4 @@ -0,0 +1,15 @@ +AC_ARG_ENABLE(freetype, + AS_HELP_STRING([--disable-freetype], [disable freetype support])) +if test "x$enable_freetype" != "xno"; then + if test "x$PKG_CONFIG" != "x"; then + PKG_CHECK_MODULES([FREETYPE], [freetype2], HAVE_FREETYPE=yes, HAVE_FREETYPE=no) + fi +else + HAVE_FREETYPE=no + FREETYPE_LIBS= +fi +AC_SUBST(FREETYPE_LIBS) + +if test "x$HAVE_FREETYPE" == "xyes"; then + AC_DEFINE(HAVE_FREETYPE, 1, [Define if you have the freetype library]) +fi diff --git a/configure.ac b/configure.ac index 17c225b2e..79f74edac 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,7 @@ m4_include(config.d/x11.m4) m4_include(config.d/sdl.m4) m4_include(config.d/curses.m4) +m4_include(config.d/freetype.m4) dnl ================================================================== dnl Checks for system type diff --git a/include/r_font.h b/include/r_font.h new file mode 100644 index 000000000..a27281b5d --- /dev/null +++ b/include/r_font.h @@ -0,0 +1,66 @@ +/* + r_font.h + + Renderer font management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + 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 + +*/ + +#ifndef __r_font_h +#define __r_font_h + +#include +#include FT_FREETYPE_H + +#include "QF/hash.h" +#include "QF/progs.h" //FIXME for PR_RESMAP + +#include "QF/ui/vrect.h" +#include "QF/simd/types.h" + +#include "r_scrap.h" + +typedef struct rglyph_s { + struct rfont_s *font; + vrect_t *rect; + vec2i_t bearing; + int advance; + int charcode; +} rglyph_t; + +typedef struct rfont_s { + void *font_resource; + FT_Face face; + rscrap_t scrap; + byte *scrap_bitmap; + hashtab_t *glyphmap; + PR_RESMAP(rglyph_t) glyphs; +} rfont_t; + +void R_FontInit (void); +void R_FontFree (rfont_t *font); +rfont_t *R_FontLoad (QFile *font_file, int size, const int *preload); + +#endif//__r_font_h diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am index 6a52b1af4..49e229b1a 100644 --- a/libs/video/renderer/Makemodule.am +++ b/libs/video/renderer/Makemodule.am @@ -30,13 +30,15 @@ video_renderer_common_sources = \ libs/video/renderer/r_billboard.c \ libs/video/renderer/r_dyn_textures.c \ libs/video/renderer/r_ent.c \ + libs/video/renderer/r_font.c \ libs/video/renderer/r_iqm.c \ libs/video/renderer/r_sprite.c \ libs/video/renderer/vid_common.c renderer_libs= \ @vid_render_static_plugin_libs@ \ - libs/util/libQFutil.la + libs/util/libQFutil.la \ + $(FREETYPE_LIBS) libs_video_renderer_libQFrenderer_la_LDFLAGS= @STATIC@ libs_video_renderer_libQFrenderer_la_LIBADD= $(renderer_libs) diff --git a/libs/video/renderer/r_font.c b/libs/video/renderer/r_font.c new file mode 100644 index 000000000..fb053dcda --- /dev/null +++ b/libs/video/renderer/r_font.c @@ -0,0 +1,186 @@ +/* + r_font.c + + Renderer font management management + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + 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 + +#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 uintptr_t +glyph_get_hash (const void *_glyph, void *unused) +{ + __auto_type glyph = (const rglyph_t *) _glyph; + return glyph->charcode; +} + +static int +glyph_compare (const void *_a, const void *_b, void *unused) +{ + __auto_type a = (const rglyph_t *) _a; + __auto_type b = (const rglyph_t *) _b; + return a->charcode == b->charcode; +} + +static rglyph_t * +alloc_glyph (rfont_t *font) +{ + return PR_RESNEW(font->glyphs); +} + +static void +free_glyph (rfont_t *font, rglyph_t *glyph) +{ + PR_RESFREE (font->glyphs, glyph); +} + +static void +copy_glypn (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; + } +} + +static void +glyphmap_free_glyph (void *_g, void *_f) +{ + rglyph_t *glyph = _g; + rfont_t *font = _f; + free_glyph (font, glyph); +} + +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->glyphmap) { + Hash_DelTable (font->glyphmap); + } + if (font->scrap.rects || font->scrap.free_rects) { + R_ScrapDelete (&font->scrap); + } + free (font->scrap_bitmap); + free (font->font_resource); + free (font); +} + +VISIBLE rfont_t * +R_FontLoad (QFile *font_file, int size, const int *preload) +{ + byte *font_data = QFS_LoadFile (font_file, 0); + if (!font_data) { + return 0; + } + size_t font_size = Qfilesize (font_file);; + 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; + } + + font->glyphmap = Hash_NewTable (0x10000, 0, glyphmap_free_glyph, font, 0); + Hash_SetHashCompare (font->glyphmap, glyph_get_hash, glyph_compare); + + FT_Set_Pixel_Sizes(font->face, 0, size); + int pixels = 0; + for (const int *c = preload; *c; c++) { + rglyph_t search = { .charcode = *c }; + rglyph_t *glyph = Hash_FindElement (font->glyphmap, &search); + if (glyph) { + continue; + } + if (FT_Load_Char(font->face, *c, FT_LOAD_DEFAULT)) { + continue; + } + __auto_type g = font->face->glyph; + pixels += g->bitmap.width * g->bitmap.rows; + + glyph = alloc_glyph (font); + glyph->charcode = *c; + Hash_AddElement (font->glyphmap, glyph); + } + Hash_FlushTable (font->glyphmap); + pixels = sqrt (2 * pixels); + pixels = BITOP_RUP (pixels); + R_ScrapInit (&font->scrap, pixels, pixels); + font->scrap_bitmap = calloc (1, pixels * pixels); + + for (const int *c = preload; *c; c++) { + rglyph_t search = { .charcode = *c }; + rglyph_t *glyph = Hash_FindElement (font->glyphmap, &search); + if (glyph) { + continue; + } + if (FT_Load_Char(font->face, *c, FT_LOAD_RENDER)) { + continue; + } + __auto_type g = font->face->glyph; + int width = g->bitmap.width; + int height = g->bitmap.rows; + glyph = alloc_glyph (font); + glyph->font = font; + glyph->rect = R_ScrapAlloc (&font->scrap, width, height); + glyph->bearing = (vec2i_t) { g->bitmap_left, g->bitmap_top }; + glyph->advance = g->advance.x; + glyph->charcode = *c; + Hash_AddElement (font->glyphmap, glyph); + + copy_glypn (glyph, g); + } + + return font; +} diff --git a/libs/video/renderer/r_progs.c b/libs/video/renderer/r_progs.c index db9760e37..f7d884c4f 100644 --- a/libs/video/renderer/r_progs.c +++ b/libs/video/renderer/r_progs.c @@ -39,12 +39,14 @@ #include "QF/draw.h" #include "QF/hash.h" #include "QF/progs.h" +#include "QF/quakefs.h" #include "QF/render.h" #include "QF/sys.h" #include "QF/ui/view.h" #include "r_internal.h" +#include "r_font.h" typedef struct { pr_int_t width; @@ -332,6 +334,18 @@ bi_Draw_Height (progs_t *pr, void *_res) R_INT (pr) = r_data->vid->conview->ylen; } +static void +bi_Font_Load (progs_t *pr, void *_res) +{ + const char *font_path = P_GSTRING (pr, 0); + int font_size = P_INT (pr, 1); + int *preload = (int *) P_GPOINTER (pr, 2); + + QFile *font_file = QFS_FOpenFile (font_path); + rfont_t *font = R_FontLoad (font_file, font_size, preload); + (void)font; +} + static const char * bi_draw_get_key (const void *p, void *unused) { @@ -381,6 +395,8 @@ static builtin_t builtins[] = { bi(Draw_Fill, 5, p(int), p(int), p(int), p(int), p(int)), bi(Draw_Line, 5, p(int), p(int), p(int), p(int), p(int)), bi(Draw_Crosshair, 5, p(int), p(int), p(int), p(int)), + + bi(Font_Load, 3, p(string), p(int), p(ptr)), {0} }; diff --git a/ruamoko/qwaq/builtins/graphics.c b/ruamoko/qwaq/builtins/graphics.c index 709f05b4f..47f51a9cc 100644 --- a/ruamoko/qwaq/builtins/graphics.c +++ b/ruamoko/qwaq/builtins/graphics.c @@ -57,6 +57,7 @@ static __attribute__ ((used)) const char rcsid[] = "$Id$"; #include "QF/plugin/console.h" #include "rua_internal.h" +#include "r_font.h" #include "ruamoko/qwaq/qwaq.h" @@ -330,6 +331,8 @@ BI_Graphics_Init (progs_t *pr) IN_Init (); Mod_Init (); R_Init (); + R_FontInit (); + R_Progs_Init (pr); RUA_Game_Init (pr, thread->rua_security); S_Progs_Init (pr);