lite-xl/tests/agg_font_render_test.cpp

172 lines
4.9 KiB
C++
Raw Normal View History

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#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 <typename T>
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);
fprintf(stderr, "Using height: %g in BakeFontBitmap\n", font->height_bitmap);
agg::rendering_buffer ren_buf((agg::int8u *) set->image->pixels, width, height, -width * pixel_size);
// FIXME: figure out how to precisely layout each glyph.
double x = 0, y = height;
int res = 0;
const agg::alpha8 text_color(0xff);
for (int i = 0; i < 256; i++) {
if (x + font->height > width) {
x = 0;
y -= font->height;
}
if (y - font->height < 0) {
res = -1;
break;
}
// FIXME: we are ignoring idx and with a char we cannot pass codepoint > 255.
char text[2] = {char(i % 256), 0};
// 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 - font->height;
font->renderer->render_text(ren_buf, font->height_bitmap, text_color, x_next, y_next, text);
set->glyphs[i].x0 = x - 1;
set->glyphs[i].y0 = y - font->descender;
set->glyphs[i].x1 = x_next - 1;
set->glyphs[i].y1 = y + font->ascender;
set->glyphs[i].xoff = -1;
set->glyphs[i].yoff = -font->ascender;
set->glyphs[i].xadvance = x_next - x;
x = x_next;
}
/* 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 <font-filename> <size>\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;
}