From 3a8cb05ea63cd7f8d6ed4356a34f579dd47ff8ae Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Mon, 1 Jun 2020 12:56:23 +0200 Subject: [PATCH] Replace stb's BakeBitmapFont with AGG based font's renderer --- src/font_render_capi.cpp | 27 ------------- src/font_render_capi.h | 26 ------------- src/font_renderer.cpp | 84 ++++++++++++++++++++++++++++++++++++++++ src/font_renderer.h | 34 ++++++++++++++++ src/meson.build | 2 +- src/renderer.c | 59 +++++++++++++--------------- 6 files changed, 146 insertions(+), 86 deletions(-) delete mode 100644 src/font_render_capi.cpp delete mode 100644 src/font_render_capi.h create mode 100644 src/font_renderer.cpp create mode 100644 src/font_renderer.h diff --git a/src/font_render_capi.cpp b/src/font_render_capi.cpp deleted file mode 100644 index 37e55441..00000000 --- a/src/font_render_capi.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "font_render_capi.h" - -#include "font_render_lcd.h" - -FontRenderer *FontRendererNew(unsigned int flags, float gamma) { - bool hinting = ((flags&FONT_RENDERER_HINTING) != 0); - bool kerning = ((flags&FONT_RENDERER_KERNING) != 0); - bool subpixel = ((flags&FONT_RENDERER_SUBPIXEL) != 0); - font_renderer_lcd *font_renderer = new font_renderer_lcd(hinting, kerning, subpixel, (double) gamma); - return (FontRenderer *) font_renderer; -} - -int FontRendererLoadFont(FontRenderer *fr_, const char *filename) { - font_renderer_lcd *font_renderer = (font_renderer_lcd *) fr_; - bool success = font_renderer->load_font(filename); - return (success ? 0 : 1); -} - -int FontRendererDrawText(FontRenderer *fr_, RenColor *pixels, int width, int height, int pitch, const char *text, float text_size, int x, int y, RenColor color) { - // FIXME: it works below as long as font_renderer_lcd use a BGRA32 pixel format - font_renderer_lcd *font_renderer = (font_renderer_lcd *) fr_; - agg::rendering_buffer ren_buf((agg::int8u *) pixels, width, height, -pitch); - const agg::rgba8 agg_color(color.r, color.g, color.b); - double xd = x, yd = (height - y - text_size); - font_renderer->render_text(ren_buf, (double) text_size, agg_color, xd, yd, text); - return int(xd); -} diff --git a/src/font_render_capi.h b/src/font_render_capi.h deleted file mode 100644 index 4e531278..00000000 --- a/src/font_render_capi.h +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#include "renderer.h" - -struct FontRenderer_; -typedef struct FontRenderer_ FontRenderer; - -enum { - FONT_RENDERER_HINTING = 1 << 0, - FONT_RENDERER_KERNING = 1 << 1, - FONT_RENDERER_SUBPIXEL = 1 << 2, - - FONT_RENDERER_DEFAULT = FONT_RENDERER_HINTING | FONT_RENDERER_KERNING | FONT_RENDERER_SUBPIXEL, -}; - -FontRenderer *FontRendererNew(unsigned int flags, float gamma); -int FontRendererLoadFont(FontRenderer *, const char *filename); -int FontRendererDrawText(FontRenderer *fr_, RenColor *pixels, int width, int height, int pitch, const char *text, float text_size, int x, int y, RenColor color); - -#ifdef __cplusplus -} -#endif diff --git a/src/font_renderer.cpp b/src/font_renderer.cpp new file mode 100644 index 00000000..32b4a916 --- /dev/null +++ b/src/font_renderer.cpp @@ -0,0 +1,84 @@ +#include "font_renderer.h" + +#include "font_renderer_alpha.h" + +FontRenderer *FontRendererNew(unsigned int flags) { + bool hinting = ((flags & FONT_RENDERER_HINTING) != 0); + bool kerning = ((flags & FONT_RENDERER_KERNING) != 0); + font_renderer_alpha *font_renderer = new font_renderer_alpha(hinting, kerning); + return (FontRenderer *) font_renderer; +} + +int FontRendererLoadFont(FontRenderer *fr_, const char *filename) { + font_renderer_alpha *font_renderer = (font_renderer_alpha *) fr_; + bool success = font_renderer->load_font(filename); + return (success ? 0 : 1); +} + +int FontRendererGetFontHeight(FontRenderer *fr_, float size) { + font_renderer_alpha *font_renderer = (font_renderer_alpha *) fr_; + double ascender, descender; + font_renderer->get_font_vmetrics(ascender, descender); + int face_height = font_renderer->get_face_height(); + + float scale = font_renderer->scale_for_em_to_pixels(size); + return int((ascender - descender) * face_height * scale + 0.5); +} + +int FontRendererBakeFontBitmap(FontRenderer *fr_, int font_height, + void *pixels, int pixels_width, int pixels_height, + int first_char, int num_chars, GlyphBitmapInfo *glyphs) +{ + font_renderer_alpha *font_renderer = (font_renderer_alpha *) fr_; + + const int pixel_size = 1; + memset(pixels, 0x00, pixels_width * pixels_height * pixel_size); + + double ascender, descender; + font_renderer->get_font_vmetrics(ascender, descender); + + const int ascender_px = int(ascender * font_height + 0.5); + const int descender_px = int(descender * font_height + 0.5); + + const int pad_y = font_height * 2 / 10; + const int y_step = font_height + 2 * pad_y; + + agg::rendering_buffer ren_buf((agg::int8u *) pixels, pixels_width, pixels_height, -pixels_width * pixel_size); + int x = 0, y = pixels_height; + int res = 0; + const agg::alpha8 text_color(0xff); + for (int i = 0; i < num_chars; i++) { + int codepoint = first_char + i; + if (x + font_height > pixels_width) { + x = 0; + y -= y_step; + } + if (y - font_height - 2 * pad_y < 0) { + res = -1; + break; + } + const int y_baseline = y - pad_y - font_height; + + double x_next = x, y_next = y_baseline; + font_renderer->render_codepoint(ren_buf, font_height, text_color, x_next, y_next, codepoint); + int x_next_i = int(x_next + 0.5); + GlyphBitmapInfo& glyph_info = glyphs[i]; + glyph_info.x0 = x; + glyph_info.y0 = pixels_height - (y_baseline + ascender_px + pad_y); + glyph_info.x1 = x_next_i; + glyph_info.y1 = pixels_height - (y_baseline + descender_px - pad_y); + glyph_info.xoff = 0; + glyph_info.yoff = -ascender * font_height; + glyph_info.xadvance = x_next - x; +#if 0 + fprintf(stderr, + "glyph codepoint %3d (ascii: %1c), BOX (%3d, %3d) (%3d, %3d), " + "OFFSET (%.5g, %.5g), X ADVANCE %.5g\n", + codepoint, i, + glyph_info.x0, glyph_info.y0, glyph_info.x1, glyph_info.y1, + glyph_info.xoff, glyph_info.yoff, glyph_info.xadvance); +#endif + x = x_next_i; + } + return res; +} diff --git a/src/font_renderer.h b/src/font_renderer.h new file mode 100644 index 00000000..b7cf62e5 --- /dev/null +++ b/src/font_renderer.h @@ -0,0 +1,34 @@ +#ifndef FONT_RENDERER_H +#define FONT_RENDERER_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Mirrors stbtt_bakedchar. +struct GlyphBitmapInfo_ { + unsigned short x0, y0, x1, y1; + float xoff, yoff, xadvance; +}; +typedef struct GlyphBitmapInfo_ GlyphBitmapInfo; + +struct FontRenderer_; +typedef struct FontRenderer_ FontRenderer; + +enum { + FONT_RENDERER_HINTING = 1 << 0, + FONT_RENDERER_KERNING = 1 << 1, +}; + +FontRenderer *FontRendererNew(unsigned int flags); +int FontRendererLoadFont(FontRenderer *, const char *filename); +int FontRendererGetFontHeight(FontRenderer *, float size); +int FontRendererBakeFontBitmap(FontRenderer *, int font_height, + void *pixels, int pixels_width, int pixels_height, + int first_char, int num_chars, GlyphBitmapInfo *glyph_info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/meson.build b/src/meson.build index 26a56b88..ba1fdf23 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,7 +5,7 @@ lite_sources = [ 'api/system.c', 'renderer.c', 'agg_font_freetype.cpp', - 'font_render_capi.cpp', + 'font_renderer.cpp', 'rencache.c', 'main.c', ] diff --git a/src/renderer.c b/src/renderer.c index a2209f9f..06a43dc3 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -4,7 +4,7 @@ #include #include "stb_truetype.h" #include "renderer.h" -#include "font_render_capi.h" +#include "font_renderer.h" #define MAX_GLYPHSET 256 @@ -13,15 +13,16 @@ struct RenImage { int width, height; }; -typedef struct { +struct GlyphSetA { RenImage *image; - stbtt_bakedchar glyphs[256]; -} GlyphSet; + GlyphBitmapInfo glyphs[256]; +}; +typedef struct GlyphSetA GlyphSetA; struct RenFont { void *data; stbtt_fontinfo stbfont; - GlyphSet *sets[MAX_GLYPHSET]; + GlyphSetA *sets[MAX_GLYPHSET]; float size; int height; FontRenderer *renderer; @@ -107,8 +108,8 @@ void ren_free_image(RenImage *image) { } -static GlyphSet* load_glyphset(RenFont *font, int idx) { - GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); +static GlyphSetA* load_glyphset(RenFont *font, int idx) { + GlyphSetA *set = check_alloc(calloc(1, sizeof(GlyphSetA))); /* init image */ int width = 128; @@ -116,13 +117,9 @@ static GlyphSet* load_glyphset(RenFont *font, int idx) { retry: set->image = ren_new_image(width, height); - /* load glyphs */ - float s = - stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) / - stbtt_ScaleForPixelHeight(&font->stbfont, 1); - int res = stbtt_BakeFontBitmap( - font->data, 0, font->size * s, (void*) set->image->pixels, - width, height, idx * 256, 256, set->glyphs); + int res = FontRendererBakeFontBitmap(font->renderer, font->height, + (void *) set->image->pixels, width, height, + idx << 8, 256, set->glyphs); /* retry with a larger image buffer if the buffer wasn't large enough */ if (res < 0) { @@ -132,6 +129,7 @@ retry: goto retry; } + // TODO: consider if this is still needed. /* adjust glyph yoffsets and xadvance */ int ascent, descent, linegap; stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); @@ -152,7 +150,7 @@ retry: } -static GlyphSet* get_glyphset(RenFont *font, int codepoint) { +static GlyphSetA* get_glyphset(RenFont *font, int codepoint) { int idx = (codepoint >> 8) % MAX_GLYPHSET; if (!font->sets[idx]) { font->sets[idx] = load_glyphset(font, idx); @@ -184,20 +182,22 @@ RenFont* ren_load_font(const char *filename, float size) { int ok = stbtt_InitFont(&font->stbfont, font->data, 0); if (!ok) { goto fail; } + // FIXME: remove, font's height is recalculated using new FontRenderer. /* get height and scale */ int ascent, descent, linegap; stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size); font->height = (ascent - descent + linegap) * scale + 0.5; - /* make tab and newline glyphs invisible */ - stbtt_bakedchar *g = get_glyphset(font, '\n')->glyphs; - g['\t'].x1 = g['\t'].x0; - g['\n'].x1 = g['\n'].x0; - - font->renderer = FontRendererNew(FONT_RENDERER_HINTING|FONT_RENDERER_SUBPIXEL, 1.8); + font->renderer = FontRendererNew(FONT_RENDERER_HINTING); // FIXME check for errors FontRendererLoadFont(font->renderer, filename); + font->height = FontRendererGetFontHeight(font->renderer, size); + + /* make tab and newline glyphs invisible */ + GlyphBitmapInfo *g = get_glyphset(font, '\n')->glyphs; + g['\t'].x1 = g['\t'].x0; + g['\n'].x1 = g['\n'].x0; return font; @@ -211,7 +211,7 @@ fail: void ren_free_font(RenFont *font) { for (int i = 0; i < MAX_GLYPHSET; i++) { - GlyphSet *set = font->sets[i]; + GlyphSetA *set = font->sets[i]; if (set) { ren_free_image(set->image); free(set); @@ -223,7 +223,7 @@ void ren_free_font(RenFont *font) { void ren_set_font_tab_width(RenFont *font, int n) { - GlyphSet *set = get_glyphset(font, '\t'); + GlyphSetA *set = get_glyphset(font, '\t'); set->glyphs['\t'].xadvance = n; } @@ -234,8 +234,8 @@ int ren_get_font_width(RenFont *font, const char *text) { unsigned codepoint; while (*p) { p = utf8_to_codepoint(p, &codepoint); - GlyphSet *set = get_glyphset(font, codepoint); - stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; + GlyphSetA *set = get_glyphset(font, codepoint); + GlyphBitmapInfo *g = &set->glyphs[codepoint & 0xff]; x += g->xadvance; } return x; @@ -334,17 +334,13 @@ void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { - SDL_Surface *surf = SDL_GetWindowSurface(window); - RenColor *pixels = (RenColor*) surf->pixels; - return FontRendererDrawText(font->renderer, pixels, surf->w, surf->h, surf->pitch, text, font->height * 0.8, x, y, color); -#if 0 RenRect rect; const char *p = text; unsigned codepoint; while (*p) { p = utf8_to_codepoint(p, &codepoint); - GlyphSet *set = get_glyphset(font, codepoint); - stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; + GlyphSetA *set = get_glyphset(font, codepoint); + GlyphBitmapInfo *g = &set->glyphs[codepoint & 0xff]; rect.x = g->x0; rect.y = g->y0; rect.width = g->x1 - g->x0; @@ -353,5 +349,4 @@ int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) x += g->xadvance; } return x; -#endif }