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;
}
@baines
Copy link
Author

baines commented Aug 24, 2021

If anyone have an idea how to render outlines on the glyph please let me know

You can do outlines with FT_Stroker. I used it in this code, but I can't remember all the details:
https://github.com/baines/engine/blob/new/src/font.cpp#L65-L74

The loop further down in that file uses some other functions:
FT_Glyph_StrokeBorder and FT_Stroker_Rewind

@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