diff --git a/src/font_renderer.cpp b/src/font_renderer.cpp index 2ced7328..8e7f1086 100644 --- a/src/font_renderer.cpp +++ b/src/font_renderer.cpp @@ -79,6 +79,15 @@ int FontRendererBakeFontBitmap(FontRenderer *fr_, int font_height, glyph_info.xadvance = x_next - x; x = x_next_i; + +#ifdef DEBUG_FONT_RENDERER + 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 } return res; } diff --git a/src/renderer.c b/src/renderer.c index 05fbe8f0..5988af49 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -127,6 +127,7 @@ retry: goto retry; } + // FIXME: to be removed. /* adjust glyph's xadvance */ for (int i = 0; i < 256; i++) { set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); diff --git a/tests/agg_font_render_test.c b/tests/agg_font_render_test.c new file mode 100644 index 00000000..b31b5912 --- /dev/null +++ b/tests/agg_font_render_test.c @@ -0,0 +1,119 @@ +#include +#include +#include + +#include "SDL_surface.h" +#include "font_renderer.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; + +struct GlyphSet { + RenImage *image; + GlyphBitmapInfo glyphs[256]; +}; +typedef struct GlyphSet GlyphSet; + +struct RenFont { + void *data; + GlyphSet *sets[MAX_GLYPHSET]; + float size; + int height; + FontRenderer *renderer; +}; +typedef struct RenFont RenFont; + +static void* check_alloc(void *ptr) { + if (ptr == 0) { + 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 = (RenImage *) malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); + check_alloc(image); + image->pixels = (RenColor*) (image + 1); + image->width = width; + image->height = height; + return image; +} + +void ren_free_image(RenImage *image) { + free(image); +} + +RenFont* ren_load_font_agg(const char *filename, float size) { + RenFont *font = NULL; + + /* init font */ + font = check_alloc(calloc(1, sizeof(RenFont))); + font->size = size; + + font->renderer = FontRendererNew(FONT_RENDERER_HINTING); + // FIXME check for errors + FontRendererLoadFont(font->renderer, filename); + font->height = FontRendererGetFontHeight(font->renderer, size); + + fprintf(stderr, "Font height: %d\n", font->height); + + return font; +} + +static GlyphSet* load_glyphset_agg(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); + + 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) { + width *= 2; + height *= 2; + ren_free_image(set->image); + goto retry; + } + + /* convert 8bit data to 32bit */ + for (int i = width * height - 1; i >= 0; i--) { + uint8_t n = *((uint8_t*) set->image->pixels + i); + RenColor c = {0xff, 0xff, 0xff, n}; + set->image->pixels[i] = c; + } + + 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_agg(filename, size); + GlyphSet *set = load_glyphset_agg(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, "agg-glyphset.bmp"); + return 0; +} diff --git a/tests/agg_font_render_test.cpp b/tests/agg_font_render_test.cpp deleted file mode 100644 index 63b25f1f..00000000 --- a/tests/agg_font_render_test.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include -#include -#include - -#include "SDL_surface.h" -#include "font_renderer_alpha.h" - -#define MAX_GLYPHSET 256 - -typedef struct { uint8_t b, g, r, a; } RenColor; -typedef struct { int x, y, width, height; } RenRect; - -// Mirrors stbtt_bakedchar. -struct GlyphBitmapInfo { - unsigned short x0, y0, x1, y1; - float xoff, yoff, xadvance; -}; - -struct RenImage { - RenColor *pixels; - int width, height; -}; - -struct GlyphSetA { - RenImage *image; - GlyphBitmapInfo glyphs[256]; -}; - -struct RenFontA { - font_renderer_alpha *renderer; - float size; - int height; - float ascender, descender; - // For some reason the font's height used in renderer.c - // when calling BakeFontBitmap is 0.5 off from 'height'. - float height_bitmap; -}; - -template -static T* check_alloc(T *ptr) { - if (ptr == 0) { - 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 = (RenImage *) malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); - check_alloc(image); - image->pixels = (RenColor*) (image + 1); - image->width = width; - image->height = height; - return image; -} - -void ren_free_image(RenImage *image) { - free(image); -} - -RenFontA* ren_load_font_agg(const char *filename, float size) { - RenFontA *font = NULL; - - /* init font */ - font = (RenFontA *) check_alloc(calloc(1, sizeof(RenFontA))); - font->size = size; - - font->renderer = new font_renderer_alpha(false, false); - font->renderer->load_font(filename); - - double ascender, descender; - font->renderer->get_font_vmetrics(ascender, descender); - int face_height = font->renderer->get_face_height(); - // Gives the font's ascender and descender like stbtt. - fprintf(stderr, "Font metrics ascender: %g descender: %g\n", ascender * face_height, descender * face_height); - - float scale = font->renderer->scale_for_em_to_pixels(size); - font->height = (ascender - descender) * face_height * scale + 0.5; - font->height_bitmap = (ascender - descender) * face_height * scale; - - font->descender = descender * font->height_bitmap; - font->ascender = ascender * font->height_bitmap; - - fprintf(stderr, "Font's pixel ascender: %g descender: %g sum: %g\n", font->ascender, font->descender, font->ascender - font->descender); - - fprintf(stderr, "Font height: %d\n", font->height); - - return font; -} - -static GlyphSetA* load_glyphset_agg(RenFontA *font, int idx) { - const int pixel_size = 1; - GlyphSetA *set = (GlyphSetA *) check_alloc(calloc(1, sizeof(GlyphSetA))); - - /* init image */ - int width = 128; - int height = 128; -retry: - set->image = ren_new_image(width, height); - - memset(set->image->pixels, 0x00, width * height * pixel_size); - - // NOTE here that render.c with stb_truetype is really using font->height_bitmap. - fprintf(stderr, "Using height: %d in BakeFontBitmap\n", font->height); - - const int pad_y = font->height * 2 / 10; - const int ascender_px = int(font->ascender + 0.5), descender_px = int(font->descender + 0.5); - const int y_step = font->height + 2 * pad_y; - - agg::rendering_buffer ren_buf((agg::int8u *) set->image->pixels, width, height, -width * pixel_size); - int x = 0, y = height; - int res = 0; - const agg::alpha8 text_color(0xff); - for (int i = 0; i < 256; i++) { - int codepoint = (idx << 8) | i; - if (x + font->height > 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; - - // FIXME: using font->height_bitmap below seems logically correct but - // the font size is bigger than what printed by BakeFontBitmap. - 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 = set->glyphs[i]; - glyph_info.x0 = x; - glyph_info.y0 = height - (y_baseline + ascender_px + pad_y); - glyph_info.x1 = x_next_i; - glyph_info.y1 = height - (y_baseline + descender_px - pad_y); - glyph_info.xoff = 0; - glyph_info.yoff = -font->ascender; - glyph_info.xadvance = x_next - x; - 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); - x = x_next_i; - } - - /* 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; - } - - /* convert 8bit data to 32bit */ - for (int i = width * height - 1; i >= 0; i--) { - uint8_t n = *((uint8_t*) set->image->pixels + i); - RenColor c = {0xff, 0xff, 0xff, n}; - set->image->pixels[i] = c; - } - - return set; -} - -extern "C" int main(int argc, char *argv[]); - -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]); - RenFontA *font = ren_load_font_agg(filename, size); - GlyphSetA *set = load_glyphset_agg(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, "agg-glyphset.bmp"); - return 0; -} diff --git a/tests/meson.build b/tests/meson.build index 63e25f72..c2a923bd 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -6,7 +6,7 @@ executable('stb-font-render-test', ) executable('agg-font-render-test', - ['agg_font_render_test.cpp', '../src/agg_font_freetype.cpp'], + ['../src/agg_font_freetype.cpp', '../src/font_renderer.cpp', 'agg_font_render_test.c'], include_directories: lite_include, dependencies: [sdl_dep, libagg_dep, freetype_dep, libm, libdl], install: false,