Skip to content

Instantly share code, notes, and snippets.

@baines
Created July 15, 2016 13:32
Show Gist options
  • Save baines/b0f9e4be04ba4e6f56cab82eef5008ff to your computer and use it in GitHub Desktop.
Save baines/b0f9e4be04ba4e6f56cab82eef5008ff to your computer and use it in GitHub Desktop.
minimal freetype texture atlas example
#include <stdio.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#define NUM_GLYPHS 128
struct glyph_info {
int x0, y0, x1, y1; // coords of glyph in the texture atlas
int x_off, y_off; // left & top bearing when rendering
int advance; // x advance when rendering
} info[NUM_GLYPHS];
int main(int argc, char** argv){
if(argc < 3){
printf("usage: %s <font> <size>\n", argv[0]);
return 1;
}
FT_Library ft;
FT_Face face;
FT_Init_FreeType(&ft);
FT_New_Face(ft, argv[1], 0, &face);
FT_Set_Char_Size(face, 0, atoi(argv[2]) << 6, 96, 96);
// quick and dirty max texture size estimate
int max_dim = (1 + (face->size->metrics.height >> 6)) * ceilf(sqrtf(NUM_GLYPHS));
int tex_width = 1;
while(tex_width < max_dim) tex_width <<= 1;
int tex_height = tex_width;
// render glyphs to atlas
char* pixels = (char*)calloc(tex_width * tex_height, 1);
int pen_x = 0, pen_y = 0;
for(int i = 0; i < NUM_GLYPHS; ++i){
FT_Load_Char(face, i, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT);
FT_Bitmap* bmp = &face->glyph->bitmap;
if(pen_x + bmp->width >= tex_width){
pen_x = 0;
pen_y += ((face->size->metrics.height >> 6) + 1);
}
for(int row = 0; row < bmp->rows; ++row){
for(int col = 0; col < bmp->width; ++col){
int x = pen_x + col;
int y = pen_y + row;
pixels[y * tex_width + x] = bmp->buffer[row * bmp->pitch + col];
}
}
// this is stuff you'd need when rendering individual glyphs out of the atlas
info[i].x0 = pen_x;
info[i].y0 = pen_y;
info[i].x1 = pen_x + bmp->width;
info[i].y1 = pen_y + bmp->rows;
info[i].x_off = face->glyph->bitmap_left;
info[i].y_off = face->glyph->bitmap_top;
info[i].advance = face->glyph->advance.x >> 6;
pen_x += bmp->width + 1;
}
FT_Done_FreeType(ft);
// write png
char* png_data = (char*)calloc(tex_width * tex_height * 4, 1);
for(int i = 0; i < (tex_width * tex_height); ++i){
png_data[i * 4 + 0] |= pixels[i];
png_data[i * 4 + 1] |= pixels[i];
png_data[i * 4 + 2] |= pixels[i];
png_data[i * 4 + 3] = 0xff;
}
stbi_write_png("font_output.png", tex_width, tex_height, 4, png_data, tex_width * 4);
free(png_data);
free(pixels);
return 0;
}
@ancientstraits
Copy link

Thank you so much for this example. I thought it would take around 1,000 lines of code to make a texture atlas with FreeType, but thanks to you, it is much, much simpler. While comprehensive examples are good, minimal ones are extremely great for people new to these concepts, like me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment