I hate font rendering

master
Alessandro Mauri 2 years ago
parent 4162e6d302
commit 357884e2fb
  1. 3
      font-to-atlas/.gitignore
  2. 2
      font-to-atlas/Makefile
  3. 118
      font-to-atlas/ff.c
  4. 25
      font-to-atlas/ff.h
  5. 107
      font-to-atlas/main.c
  6. BIN
      font-to-atlas/monospace.ttf
  7. 5077
      font-to-atlas/stb_truetype.h
  8. 27
      opengl-ren/main.c

@ -0,0 +1,3 @@
*.ff
*.png
font-to-atlas

@ -0,0 +1,2 @@
font-to-atlas: main.c ff.c ff.h
cc -lm -g main.c ff.c -o font-to-atlas

@ -0,0 +1,118 @@
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ff.h"
#define MAX(a,b) a>b?a:b
struct ff * ff_new(uint32_t width, uint32_t height)
{
uint64_t size = (uint64_t)width*height*sizeof(struct ff);
struct ff *image = malloc(sizeof(struct ff) + size);
if (!image)
return NULL;
memcpy(image->magic, "farbfeld", 8);
image->width = htonl(width);
image->height = htonl(height);
// create a transparent image
memset(image->pixels, 0, size);
return image;
}
uint64_t ff_bytes(const struct ff *image)
{
if (!image)
return 0;
return sizeof(struct ff)+(uint64_t)ntohl(image->width)*ntohl(image->height)*sizeof(struct ff_px);
}
struct ff * ff_resize(struct ff *image, uint32_t width, uint32_t height)
{
struct ff *new = NULL;
int64_t size = sizeof(struct ff)+(int64_t)width*height*sizeof(uint64_t);
if (image) {
int64_t old_size = ff_bytes(image);
if (old_size == size)
return image;
new = realloc(image, size);
if (!new)
return NULL;
uint32_t old_width = ntohl(new->width);
uint32_t old_height = ntohl(new->height);
if (size-old_size > 0) {
struct ff_px *b = new->pixels;
memset(&b[old_width*old_height], 0, size-old_size);
for (int64_t c = (int64_t)old_height-1; c >= 0; c--) {
memmove(&b[width*c], &b[old_width*c], sizeof(struct ff_px)*old_width);
memset(&b[c*old_width], 0, sizeof(struct ff_px)*(c*width-c*old_width));
}
} else {
}
new->height = htonl(height);
new->width = htonl(width);
} else {
new = ff_new(width, height);
}
return new;
}
int ff_verify (const struct ff *image)
{
if (!image || strncmp("farbfeld", (char*)image->magic, 8))
return 1;
return 0;
}
int ff_free(struct ff *image)
{
if (!ff_verify(image))
free(image);
return 0;
}
// overlays the bitmap containing only 1 8bpp channel to the image starting at (x,y)
// be stands for the data is already big endian
int ff_overlay_8r(struct ff **image, const uint8_t *bitmap, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
if (!image || !*image)
return 1;
uint32_t iw = ntohl((*image)->width), ih = ntohl((*image)->height);
*image = ff_resize(*image, MAX(iw, x+w), MAX(ih, y+h));
if (!image)
return -1;
iw = ntohl((*image)->width);
ih = ntohl((*image)->height);
for (uint32_t r = 0; r < h; r++) {
for (uint32_t c = 0; c < w; c++) {
uint8_t col = bitmap[r*w+c];
struct ff_px p = {
.r = 0xffff,
.g = 0xffff,
.b = 0xffff,
.a = htons(257*col)
};
(*image)->pixels[(r+y)*iw + (c+x)] = p;
}
}
return 0;
}

@ -0,0 +1,25 @@
#ifndef _FARBFELD_EZ_H
#define _FARBFELD_EZ_H
#include <stdint.h>
struct __attribute__((packed)) ff_px { uint16_t r, g, b, a; };
struct __attribute__((packed)) ff {
int8_t magic[8];
uint32_t width, height;
struct ff_px pixels[];
};
struct ff * ff_new(uint32_t width, uint32_t height);
int ff_verify (const struct ff *image);
int ff_free(struct ff *image);
uint64_t ff_bytes(const struct ff *image);
struct ff * ff_resize(struct ff *image, uint32_t width, uint32_t height);
int ff_overlay_8r(struct ff **image, const uint8_t *bitmap, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
#endif

@ -0,0 +1,107 @@
#define _POSIX_C_SOURCE 200809l
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#include <sys/mman.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include "stb_truetype.h"
#include "ff.h"
const int font_size = 32;
void map_file(const unsigned char **str, int *size, const char *path)
{
if (!path)
err(EXIT_FAILURE, "NULL filename");
FILE *fp = fopen(path, "r");
if (!fp)
err(EXIT_FAILURE, "Cannot open file %s", path);
*size = lseek(fileno(fp), 0, SEEK_END);
if (*size == (off_t)-1)
err(EXIT_FAILURE, "lseek failed");
*str = mmap(0, *size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
if (*str == (void*)-1)
err(EXIT_FAILURE, "mmap failed");
if (fclose(fp))
err(EXIT_FAILURE, "Error closing file");
}
int main(int argc, char *argv[])
{
if (argc < 2)
return EXIT_FAILURE;
int len;
const unsigned char *map;
map_file(&map, &len, argv[1]);
stbtt_fontinfo font;
stbtt_InitFont(&font, map, stbtt_GetFontOffsetForIndex(map, 0));
// all this is to get the font bounding box in pixels
float font_scale = 1.0;
font_scale = stbtt_ScaleForPixelHeight(&font, font_size);
int ascent, descent, linegap;
stbtt_GetFontVMetrics(&font, &ascent, &descent, &linegap);
int x0,y0,x1,y1;
int bound_w, bound_h;
stbtt_GetFontBoundingBox(&font, &x0, &y0, &x1, &y1);
printf("font_scale: %f\n", font_scale);
printf("x0:%d y0:%d x1:%d y1:%d\n",x0,y0,x1,y1);
int baseline = font_scale * -y0;
bound_h = (baseline+font_scale*y1) - (baseline+font_scale*y0);
bound_w = (font_scale*x1) - (font_scale*x0);
baseline = bound_h - baseline;
unsigned char *bitmap = malloc(bound_h*bound_w);
if (!bitmap)
err(EXIT_FAILURE, "Cannot allocate bitmap");
printf("bounding h:%d w:%d\n", bound_h, bound_w);
printf("baseline: %d\n", baseline);
struct ff *image = ff_new(0, 0);
// get all ascii
int x = 0, y = 0, maxwidth = 64*bound_w;
for (unsigned int i = 0; i <= 0x7F; i++) {
int x0,y0,x1,y1,w,h,l,a, ox,oy;
int g = stbtt_FindGlyphIndex(&font, i);
stbtt_GetGlyphBitmapBoxSubpixel(&font, g, font_scale, font_scale, 0, 0, &x0, &y0, &x1, &y1);
w = x1 - x0;
h = y1 - y0;
//printf("%d\n", y0);
stbtt_GetGlyphHMetrics(&font, g, &a, &l);
stbtt_MakeGlyphBitmapSubpixel(&font, bitmap, w, h, w, font_scale, font_scale, 0, 0, g);
//printf("'%c' -> l*scale:%.0f, y0:%d\n", i, font_scale*l, bound_h+y0);
ox = font_scale*l;
oy = bound_h+y0;
ff_overlay_8r(&image, bitmap, x+ox, y+oy, w, h);
x += bound_w;
if (x >= maxwidth) y += bound_h;
x %= maxwidth;
}
FILE *fp = fopen("out.ff", "w");
if (fp) {
fwrite(image, 1, ff_bytes(image), fp);
fclose(fp);
}
free(bitmap);
ff_free(image);
munmap((void *)map, len);
return 0;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -1,5 +1,3 @@
#include <SDL2/SDL_events.h>
#include <stddef.h>
#define _POSIX_C_SOURCE 200809l #define _POSIX_C_SOURCE 200809l
#include <sys/mman.h> #include <sys/mman.h>
@ -47,8 +45,8 @@ typedef struct PACKED {
} vec4; } vec4;
// a vertex has a position and a color // a vertex has a position and a color
struct PACKED vertex { struct PACKED vertex {
vec2 pos; vec2 pos;
vec2 texture; vec2 texture;
vec4 color; vec4 color;
}; };
@ -83,12 +81,12 @@ void grow_stack(int step)
if(!vstack.v) if(!vstack.v)
err(-1, "Could not allocate stack #S: %s", strerror(errno)); err(-1, "Could not allocate stack #S: %s", strerror(errno));
memset(&(vstack.v[vstack.size]), 0, step*sizeof(*(vstack.v))); memset(&(vstack.v[vstack.size]), 0, step*sizeof(*(vstack.v)));
vstack.size += step; vstack.size += step;
} }
void push(struct vertex v) void push(struct vertex v)
{ {
if (vstack.idx <= vstack.size) if (vstack.idx >= vstack.size)
grow_stack(6); grow_stack(6);
vstack.v[vstack.idx++] = v; vstack.v[vstack.idx++] = v;
} }
@ -412,30 +410,30 @@ void import_font(const char *path)
img = (const struct _ff *)map; img = (const struct _ff *)map;
fw = ntohl(img->w); fw = ntohl(img->w);
fh = ntohl(img->h); fh = ntohl(img->h);
glGenTextures(1, &ren.font_texture); glGenTextures(1, &ren.font_texture);
glBindTexture(GL_TEXTURE_2D, ren.font_texture); glBindTexture(GL_TEXTURE_2D, ren.font_texture);
// farbfeld image // farbfeld image
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_SHORT, img->bytes); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_SHORT, img->bytes);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
munmap((void *)map, size); munmap((void *)map, size);
} }
void push_text(int x, int y, const char *s) void push_text(int x, int y, float scale, const char *s)
{ {
for (; *s; s++) { for (; *s; s++) {
int u, v; int u, v;
int idx = *s - ' '; int idx = *s - ' ';
u = idx % (fw / gw); u = idx % (fw / gw);
v = (idx / (fw / gw)) % (fh / gh); v = (idx / (fw / gw)) % (fh / gh);
vstack_push_quad_t(x, y, gw*2, gh*2, u*gw, v*gh); vstack_push_quad_t(x, y, gw*scale, gh*scale, u*gw, v*gh);
x += gw*2; x += gw*scale;
if (*s == '\n') if (*s == '\n')
y += gh; y += gh;
} }
@ -501,7 +499,7 @@ int main (void)
vstack_push_quad_c(0, 0, 100, 100, magenta); vstack_push_quad_c(0, 0, 100, 100, magenta);
vstack_push_quad_c(200, 0, 10, 10, magenta); vstack_push_quad_c(200, 0, 10, 10, magenta);
vstack_push_quad_c(10, 150, 100, 100, magenta); vstack_push_quad_c(10, 150, 100, 100, magenta);
push_text(250, 250, "ò Ciao Victoria <3"); push_text(250, 250, 1.0f, "Ciao Victoria <3");
update_hash(); update_hash();
ren_drawvertbuffer(); ren_drawvertbuffer();
@ -514,6 +512,7 @@ int main (void)
glDisableVertexAttribArray(vertindex); glDisableVertexAttribArray(vertindex);
glDisableVertexAttribArray(colindex); glDisableVertexAttribArray(colindex);
glDeleteTextures(1, &ren.font_texture); glDeleteTextures(1, &ren.font_texture);
glDeleteBuffers(1, &ren.gl_vertbuffer);
SDL_GL_DeleteContext(ren.gl); SDL_GL_DeleteContext(ren.gl);
SDL_DestroyWindow(ren.w); SDL_DestroyWindow(ren.w);
SDL_Quit(); SDL_Quit();

Loading…
Cancel
Save