From 20626ab9118cf209010b8dbb073c6c1668af74a9 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 29 May 2020 12:50:13 +0200 Subject: [PATCH] Add small test program for stb_truetype --- meson.build | 1 + tests/meson.build | 18 ++++ tests/stb_font_render_test.c | 156 +++++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 tests/meson.build create mode 100644 tests/stb_font_render_test.c diff --git a/meson.build b/meson.build index eba00f44..9089f3e5 100644 --- a/meson.build +++ b/meson.build @@ -14,3 +14,4 @@ lite_include = include_directories('src') install_subdir('data', install_dir : 'bin') subdir('src') +subdir('tests') diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 00000000..77d3bc65 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,18 @@ +lite_sources = [ + 'api/api.c', + 'api/renderer.c', + 'api/renderer_font.c', + 'api/system.c', + 'renderer.c', + 'agg_font_freetype.cpp', + 'font_render_capi.cpp', + 'rencache.c', + 'main.c', +] + +executable('stb-font-render-test', + 'stb_font_render_test.c', + include_directories: lite_include, + dependencies: [sdl_dep, stb_truetype_dep, libm, libdl], + install: false, +) diff --git a/tests/stb_font_render_test.c b/tests/stb_font_render_test.c new file mode 100644 index 00000000..3a70e703 --- /dev/null +++ b/tests/stb_font_render_test.c @@ -0,0 +1,156 @@ +#include +#include +#include + +#include "SDL_surface.h" +#include "stb_truetype.h" + +#define MAX_GLYPHSET 256 + +typedef struct { uint8_t b, g, r, a; } RenColor; +typedef struct { int x, y, width, height; } RenRect; + +struct RenImage { + RenColor *pixels; + int width, height; +}; +typedef struct RenImage RenImage; + +typedef struct { + RenImage *image; + stbtt_bakedchar glyphs[256]; +} GlyphSet; + +struct RenFont { + void *data; + stbtt_fontinfo stbfont; + GlyphSet *sets[MAX_GLYPHSET]; + float size; + int height; +}; +typedef struct RenFont RenFont; + +static void* check_alloc(void *ptr) { + if (!ptr) { + fprintf(stderr, "Fatal error: memory allocation failed\n"); + exit(EXIT_FAILURE); + } + return ptr; +} + +RenImage* ren_new_image(int width, int height) { + assert(width > 0 && height > 0); + RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); + check_alloc(image); + image->pixels = (void*) (image + 1); + image->width = width; + image->height = height; + return image; +} + + +void ren_free_image(RenImage *image) { + free(image); +} + +RenFont* ren_load_font(const char *filename, float size) { + RenFont *font = NULL; + FILE *fp = NULL; + + /* init font */ + font = check_alloc(calloc(1, sizeof(RenFont))); + font->size = size; + + /* load font into buffer */ + fp = fopen(filename, "rb"); + if (!fp) { return NULL; } + /* get size */ + fseek(fp, 0, SEEK_END); int buf_size = ftell(fp); fseek(fp, 0, SEEK_SET); + /* load */ + font->data = check_alloc(malloc(buf_size)); + int _ = fread(font->data, 1, buf_size, fp); (void) _; + fclose(fp); + fp = NULL; + + /* init stbfont */ + int ok = stbtt_InitFont(&font->stbfont, font->data, 0); + if (!ok) { goto fail; } + + /* get height and scale */ + int ascent, descent, linegap; + fprintf(stderr, "Requesting font size: %g\n", size); + stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); + float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size); + fprintf(stderr, "Font metrics ascent: %d descent: %d, linegap: %d scale: %g\n", ascent, descent, linegap, scale); + font->height = (ascent - descent + linegap) * scale + 0.5; + fprintf(stderr, "Font height: %d\n", font->height); + + return font; + +fail: + if (fp) { fclose(fp); } + if (font) { free(font->data); } + free(font); + return NULL; +} + +static GlyphSet* load_glyphset(RenFont *font, int idx) { + GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); + + /* init image */ + int width = 128; + int height = 128; +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); + + /* retry with a larger image buffer if the buffer wasn't large enough */ + if (res < 0) { + width *= 2; + height *= 2; + ren_free_image(set->image); + goto retry; + } + + /* adjust glyph yoffsets and xadvance */ + int ascent, descent, linegap; + stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); + float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size); + int scaled_ascent = ascent * scale + 0.5; + for (int i = 0; i < 256; i++) { + set->glyphs[i].yoff += scaled_ascent; + set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); + } + + /* convert 8bit data to 32bit */ + for (int i = width * height - 1; i >= 0; i--) { + uint8_t n = *((uint8_t*) set->image->pixels + i); + set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n }; + } + + return set; +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + const char *filename = argv[1]; + const int size = atoi(argv[2]); + RenFont *font = ren_load_font(filename, size); + GlyphSet *set = load_glyphset(font, 0); + SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormatFrom( + set->image->pixels, + set->image->width, set->image->height, 32, set->image->width * 4, + SDL_PIXELFORMAT_RGBA32); + SDL_SaveBMP(surface, "stb-glyphset.bmp"); + return 0; +}