Skip to content

Instantly share code, notes, and snippets.

@thebirk
Created March 7, 2018 03:15
Show Gist options
  • Save thebirk/a8e4de3db557a20ac590f2193b6589e5 to your computer and use it in GitHub Desktop.
Save thebirk/a8e4de3db557a20ac590f2193b6589e5 to your computer and use it in GitHub Desktop.
Basic texture atlas using freetype in odin
// Store the glyph index in the font for faster kerning lookup?
Glyph :: struct {
bearingX, bearingY: int,
width, height: int,
advanceX: int,
}
Font :: struct {
face: FT_Face,
contex: stbrp_context,
nodes: []stbrp_node,
atlas_w, atlas_h: int,
pixels: []u8,
glyphs: map[rune]Glyph,
}
the_library: FT_Library;
load_font :: proc(path: string, pixel_size: int, atlas_width, atlas_height: int) -> ^Font {
font := new(Font);
font.atlas_w = atlas_width;
font.atlas_h = atlas_height;
using font;
cpath := strings.new_c_string(path);
defer free(cpath);
if FT_New_Face(the_library, cpath, 0, &face) != 0 {
return nil;
}
FT_Set_Pixel_Sizes(face, 0, auto_cast pixel_size);
pixels = make([]u8, atlas_w*atlas_h);
nodes = make([]stbrp_node, atlas_w);
stbrp_init_target(&contex, i32(atlas_w-1), i32(atlas_h-1), &nodes[0], auto_cast len(nodes));
pack_range(font, ' ', '~');
return font;
}
pack_range :: proc(using font: ^Font, first, last: rune) -> (packed_all: bool = true) {
glyph_count := int(last-first+1);
reserve(&glyphs, glyph_count);
rects := make([]stbrp_rect, glyph_count);
defer free(rects);
fmt.printf("glyph_count %v\n", glyph_count);
// Get glyph data and setup packing
for cp in first...last {
if FT_Load_Char(face, auto_cast cp, FT_LOAD_RENDER) != 0 {
assert(false, "Invalid font cp!");
}
glyph := face.glyph;
g: Glyph;
g.width = auto_cast glyph.metrics.width;
g.height = auto_cast glyph.metrics.height;
g.bearingX = auto_cast glyph.metrics.horiBearingX;
g.bearingY = auto_cast glyph.metrics.horiBearingY;
g.advanceX = auto_cast glyph.metrics.horiAdvance;
glyphs[cp] = g;
rects[cp-first].w = auto_cast (glyph.bitmap.width+1);
rects[cp-first].h = auto_cast (glyph.bitmap.rows+1);
}
stbrp_pack_rects(&contex, &rects[0], i32(len(rects)));
copy_to_atlas :: proc(using font: ^Font, r: ^stbrp_rect, bitmap: []u8) {
for y in 0..r.h {
yy := int(r.y+y);
for x in 0..r.w {
xx := int(r.x+x);
font.pixels[xx+yy*font.atlas_w] = bitmap[x+y*r.w];
}
}
}
// Do packing and writing to atlas
for cp in first...last {
if FT_Load_Char(face, auto_cast cp, FT_LOAD_RENDER) != 0 {
assert(false, "Invalid font cp!!??!?!");
}
r := &rects[cp-first];
if r.was_packed == 0 {
packed_all = false;
continue;
}
r.x += 1;
r.y += 1;
r.w -= 1;
r.h -= 1;
pixels := mem.slice_ptr(face.glyph.bitmap.buffer, int(face.glyph.bitmap.width*face.glyph.bitmap.rows));
copy_to_atlas(font, r, pixels);
}
return;
}
import stbiw "shared:odin-stb/stb_image_write.odin"
main :: proc() {
FT_Init_FreeType(&the_library);
defer FT_Done_FreeType(the_library);
font := load_font("fonts/arial.ttf", 48, 1024, 1024);
if font == nil {
assert(false, "failed to init font!");
}
pack_range(font, 0xA0, 0xFF);
pack_range(font, 0x400, 0x4FF);
stbiw.write_png("test.png\x00", font.atlas_w, font.atlas_h, 1, font.pixels, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment