Skip to content

Instantly share code, notes, and snippets.

@vassvik
Last active September 8, 2018 13:27
Show Gist options
  • Save vassvik/82a356a7a2c0adf09d72f132fe3b854e to your computer and use it in GitHub Desktop.
Save vassvik/82a356a7a2c0adf09d72f132fe3b854e to your computer and use it in GitHub Desktop.
Font atlas packing with stbtruetype.h, with optional colored visualization of packing
#define STB_RECT_PACK_IMPLEMENTATION
#include "stb_rect_pack.h" // For better packing overall
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h" // for saving png
/*
// this holds all the per-glyph data
typedef struct
{
unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
float xoff,yoff,xadvance;
float xoff2,yoff2;
} stbtt_packedchar;
*/
/*
// this is used to specify which ranges of glyphs to pack
typedef struct
{
float font_size;
int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint
int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints
int num_chars;
stbtt_packedchar *chardata_for_range; // output
unsigned char h_oversample, v_oversample; // don't set these, they're used internally
} stbtt_pack_range;
*/
int main() {
FILE *fp = fopen("consola.ttf", "rb");
int ttf_size_max = 1e6; // most likely large enough, 1MB
unsigned char *ttf_buffer = (unsigned char*)malloc(ttf_size_max);
fread(ttf_buffer, 1, ttf_size_max, fp);
fclose(fp);
// probably large enough
int width = 2048;
int height = 2048;
unsigned char *bitmap = (unsigned char*)malloc(height*width);
stbtt_pack_context pc;
stbtt_packedchar cdatas[100][95];
stbtt_PackBegin(&pc, bitmap, width, height, 0, 1, NULL);
stbtt_PackSetOversampling(&pc, 3, 1);
int num_sizes = 17;
stbtt_pack_range ranges[99] = {{72, 32, NULL, 95, cdatas[0], 0, 0},
{68, 32, NULL, 95, cdatas[1], 0, 0},
{64, 32, NULL, 95, cdatas[2], 0, 0},
{60, 32, NULL, 95, cdatas[3], 0, 0},
{56, 32, NULL, 95, cdatas[4], 0, 0},
{52, 32, NULL, 95, cdatas[5], 0, 0},
{48, 32, NULL, 95, cdatas[6], 0, 0},
{44, 32, NULL, 95, cdatas[7], 0, 0},
{40, 32, NULL, 95, cdatas[8], 0, 0},
{36, 32, NULL, 95, cdatas[9], 0, 0},
{32, 32, NULL, 95, cdatas[10], 0, 0},
{28, 32, NULL, 95, cdatas[11], 0, 0},
{24, 32, NULL, 95, cdatas[12], 0, 0},
{20, 32, NULL, 95, cdatas[13], 0, 0},
{16, 32, NULL, 95, cdatas[14], 0, 0},
{12, 32, NULL, 95, cdatas[15], 0, 0},
{8, 32, NULL, 95, cdatas[16], 0, 0}};
stbtt_PackFontRanges(&pc, ttf_buffer, 0, ranges, num_sizes);
stbtt_PackEnd(&pc);
// Calculate fill-rate (packing efficiency)
int filled = 0;
int max_y = 0;
for (int j = 0; j < num_sizes; j++) {
for (int i = 0; i < 95; i++) {
//printf("%d %d %d %d %d %d %f %f %f %f %f\n", j, i, cdatas[j][i].x0, cdatas[j][i].y0, cdatas[j][i].x1, cdatas[j][i].y1,
// cdatas[j][i].xoff, cdatas[j][i].yoff, cdatas[j][i].xoff2, cdatas[j][i].yoff2,
// cdatas[j][i].xadvance);
if (cdatas[j][i].y1 > max_y) {
max_y = cdatas[j][i].y1;
}
filled += (cdatas[j][i].x1 - cdatas[j][i].x0)*(cdatas[j][i].y1 - cdatas[j][i].y0);
}
}
printf("max_y = %d\n", max_y);
printf("fill rate = %f\n", filled/(double)(width*max_y));
stbi_write_png("font_3x1.png", width, max_y, 1, bitmap, 0);
// create an image with each size/range having a unique color
printf("%d\n", 3*width*max_y);
unsigned char *bitmap_colored = (unsigned char*)malloc(3*width*max_y);
for (int j = 0; j < num_sizes; j++) {
double R = 0.5 + 0.5*cos(2.0*j+0*12.45);
double G = 0.5 + 0.5*cos(3.0*j+0*12.45);
double B = 0.5 + 0.5*cos(5.0*j+0*12.45);
for (int i = 0; i < 95; i++) {
for (int x = cdatas[j][i].x0; x < cdatas[j][i].x1; x++) {
for (int y = cdatas[j][i].y0; y < cdatas[j][i].y1; y++) {
float s = bitmap[y*width + x];
bitmap_colored[3*(y*width + x)+0] = s*R;
bitmap_colored[3*(y*width + x)+1] = s*G;
bitmap_colored[3*(y*width + x)+2] = s*B;
}
}
}
}
stbi_write_png("font_3x1_color.png", width, max_y, 3, bitmap_colored, 0);
free(bitmap_colored);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment