Replace stb's BakeBitmapFont with AGG based font's renderer
This commit is contained in:
parent
ca6b7bc902
commit
3a8cb05ea6
|
@ -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);
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#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
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -5,7 +5,7 @@ lite_sources = [
|
||||||
'api/system.c',
|
'api/system.c',
|
||||||
'renderer.c',
|
'renderer.c',
|
||||||
'agg_font_freetype.cpp',
|
'agg_font_freetype.cpp',
|
||||||
'font_render_capi.cpp',
|
'font_renderer.cpp',
|
||||||
'rencache.c',
|
'rencache.c',
|
||||||
'main.c',
|
'main.c',
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "stb_truetype.h"
|
#include "stb_truetype.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "font_render_capi.h"
|
#include "font_renderer.h"
|
||||||
|
|
||||||
#define MAX_GLYPHSET 256
|
#define MAX_GLYPHSET 256
|
||||||
|
|
||||||
|
@ -13,15 +13,16 @@ struct RenImage {
|
||||||
int width, height;
|
int width, height;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
struct GlyphSetA {
|
||||||
RenImage *image;
|
RenImage *image;
|
||||||
stbtt_bakedchar glyphs[256];
|
GlyphBitmapInfo glyphs[256];
|
||||||
} GlyphSet;
|
};
|
||||||
|
typedef struct GlyphSetA GlyphSetA;
|
||||||
|
|
||||||
struct RenFont {
|
struct RenFont {
|
||||||
void *data;
|
void *data;
|
||||||
stbtt_fontinfo stbfont;
|
stbtt_fontinfo stbfont;
|
||||||
GlyphSet *sets[MAX_GLYPHSET];
|
GlyphSetA *sets[MAX_GLYPHSET];
|
||||||
float size;
|
float size;
|
||||||
int height;
|
int height;
|
||||||
FontRenderer *renderer;
|
FontRenderer *renderer;
|
||||||
|
@ -107,8 +108,8 @@ void ren_free_image(RenImage *image) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
static GlyphSetA* load_glyphset(RenFont *font, int idx) {
|
||||||
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
GlyphSetA *set = check_alloc(calloc(1, sizeof(GlyphSetA)));
|
||||||
|
|
||||||
/* init image */
|
/* init image */
|
||||||
int width = 128;
|
int width = 128;
|
||||||
|
@ -116,13 +117,9 @@ static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
||||||
retry:
|
retry:
|
||||||
set->image = ren_new_image(width, height);
|
set->image = ren_new_image(width, height);
|
||||||
|
|
||||||
/* load glyphs */
|
int res = FontRendererBakeFontBitmap(font->renderer, font->height,
|
||||||
float s =
|
(void *) set->image->pixels, width, height,
|
||||||
stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) /
|
idx << 8, 256, set->glyphs);
|
||||||
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 */
|
/* retry with a larger image buffer if the buffer wasn't large enough */
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -132,6 +129,7 @@ retry:
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: consider if this is still needed.
|
||||||
/* adjust glyph yoffsets and xadvance */
|
/* adjust glyph yoffsets and xadvance */
|
||||||
int ascent, descent, linegap;
|
int ascent, descent, linegap;
|
||||||
stbtt_GetFontVMetrics(&font->stbfont, &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;
|
int idx = (codepoint >> 8) % MAX_GLYPHSET;
|
||||||
if (!font->sets[idx]) {
|
if (!font->sets[idx]) {
|
||||||
font->sets[idx] = load_glyphset(font, 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);
|
int ok = stbtt_InitFont(&font->stbfont, font->data, 0);
|
||||||
if (!ok) { goto fail; }
|
if (!ok) { goto fail; }
|
||||||
|
|
||||||
|
// FIXME: remove, font's height is recalculated using new FontRenderer.
|
||||||
/* get height and scale */
|
/* get height and scale */
|
||||||
int ascent, descent, linegap;
|
int ascent, descent, linegap;
|
||||||
stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
|
stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
|
||||||
float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size);
|
float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size);
|
||||||
font->height = (ascent - descent + linegap) * scale + 0.5;
|
font->height = (ascent - descent + linegap) * scale + 0.5;
|
||||||
|
|
||||||
/* make tab and newline glyphs invisible */
|
font->renderer = FontRendererNew(FONT_RENDERER_HINTING);
|
||||||
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);
|
|
||||||
// FIXME check for errors
|
// FIXME check for errors
|
||||||
FontRendererLoadFont(font->renderer, filename);
|
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;
|
return font;
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ fail:
|
||||||
|
|
||||||
void ren_free_font(RenFont *font) {
|
void ren_free_font(RenFont *font) {
|
||||||
for (int i = 0; i < MAX_GLYPHSET; i++) {
|
for (int i = 0; i < MAX_GLYPHSET; i++) {
|
||||||
GlyphSet *set = font->sets[i];
|
GlyphSetA *set = font->sets[i];
|
||||||
if (set) {
|
if (set) {
|
||||||
ren_free_image(set->image);
|
ren_free_image(set->image);
|
||||||
free(set);
|
free(set);
|
||||||
|
@ -223,7 +223,7 @@ void ren_free_font(RenFont *font) {
|
||||||
|
|
||||||
|
|
||||||
void ren_set_font_tab_width(RenFont *font, int n) {
|
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;
|
set->glyphs['\t'].xadvance = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,8 +234,8 @@ int ren_get_font_width(RenFont *font, const char *text) {
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSetA *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
GlyphBitmapInfo *g = &set->glyphs[codepoint & 0xff];
|
||||||
x += g->xadvance;
|
x += g->xadvance;
|
||||||
}
|
}
|
||||||
return x;
|
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) {
|
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;
|
RenRect rect;
|
||||||
const char *p = text;
|
const char *p = text;
|
||||||
unsigned codepoint;
|
unsigned codepoint;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
p = utf8_to_codepoint(p, &codepoint);
|
p = utf8_to_codepoint(p, &codepoint);
|
||||||
GlyphSet *set = get_glyphset(font, codepoint);
|
GlyphSetA *set = get_glyphset(font, codepoint);
|
||||||
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
|
GlyphBitmapInfo *g = &set->glyphs[codepoint & 0xff];
|
||||||
rect.x = g->x0;
|
rect.x = g->x0;
|
||||||
rect.y = g->y0;
|
rect.y = g->y0;
|
||||||
rect.width = g->x1 - g->x0;
|
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;
|
x += g->xadvance;
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue