Created
July 29, 2018 18:16
-
-
Save thebirk/544e5cb6c927640ad30958648e5f1e29 to your computer and use it in GitHub Desktop.
freetype binding for odin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using import "core:c.odin" | |
//foreign import ftlib "freetype271.lib"; | |
when ODIN_OS == "windows" do foreign import ftlib "freetype271.lib"; | |
else { | |
#assert(false); | |
} | |
FT_Library_ :: struct {} | |
FT_Library :: ^FT_Library_; | |
FT_Size_Internal :: rawptr; | |
FT_Size_Metrics :: struct { | |
x_ppem : u16, | |
y_ppem : u16, | |
x_scale : c_long, | |
y_scale : c_long, | |
ascender : c_long, | |
descender : c_long, | |
height : c_long, | |
max_advance : c_long, | |
} | |
FT_Generic_Finalizer :: proc "c" (object: rawptr); | |
FT_Generic :: struct { | |
data: rawptr, | |
finalizer: FT_Generic_Finalizer, | |
} | |
FT_SizeRec :: struct { | |
face : FT_Face, | |
generic : FT_Generic, | |
metrics : FT_Size_Metrics, | |
internal : FT_Size_Internal, | |
} | |
FT_Size :: ^FT_SizeRec; | |
FT_Bitmap_Size :: struct { | |
height : i16, | |
width : i16, | |
size : c_long, | |
x_ppem : c_long, | |
y_ppem : c_long, | |
} | |
FT_Encoding :: enum i32 #export { | |
FT_ENCODING_NONE = 0, | |
FT_ENCODING_MS_SYMBOL = ('s' << 24) | ('y' << 16) | ('m' << 8) | 'b', | |
FT_ENCODING_UNICODE = ('u' << 24) | ('n' << 16) | ('i' << 8) | 'c', | |
FT_ENCODING_SJIS = ('s' << 24) | ('j' << 16) | ('i' << 8) | 's', | |
FT_ENCODING_PRC = ('g' << 24) | ('b' << 16) | (' ' << 8) | ' ', | |
FT_ENCODING_BIG5 = ('b' << 24) | ('i' << 16) | ('g' << 8) | '5', | |
FT_ENCODING_WANSUNG = ('w' << 24) | ('a' << 16) | ('n' << 8) | 's', | |
FT_ENCODING_JOHAB = ('j' << 24) | ('o' << 16) | ('h' << 8) | 'a', | |
/* for backward compatibility */ | |
FT_ENCODING_GB2312 = FT_ENCODING_PRC, | |
FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS, | |
FT_ENCODING_MS_GB2312 = FT_ENCODING_PRC, | |
FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5, | |
FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG, | |
FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB, | |
FT_ENCODING_ADOBE_STANDARD = ('A' << 24) | ('D' << 16) | ('O' << 8) | 'B', | |
FT_ENCODING_ADOBE_EXPERT = ('A' << 24) | ('D' << 16) | ('B' << 8) | 'E', | |
FT_ENCODING_ADOBE_CUSTOM = ('A' << 24) | ('D' << 16) | ('B' << 8) | 'C', | |
FT_ENCODING_ADOBE_LATIN_1 = ('l' << 24) | ('a' << 16) | ('t' << 8) | '1', | |
FT_ENCODING_OLD_LATIN_2 = ('l' << 24) | ('a' << 16) | ('t' << 8) | '2', | |
FT_ENCODING_APPLE_ROMAN = ('a' << 24) | ('r' << 16) | ('m' << 8) | 'n', | |
} | |
FT_CharMapRec :: struct { | |
face : FT_Face, | |
encoding : FT_Encoding, | |
platform_id : u16, | |
encoding_id : u16, | |
} | |
FT_CharMap :: ^FT_CharMapRec; | |
FT_BBox :: struct { | |
xMin, yMin: c_long, | |
xMax, yMax: c_long, | |
} | |
FT_Glyph_Metrics :: struct { | |
width : c_long, | |
height : c_long, | |
horiBearingX : c_long, | |
horiBearingY : c_long, | |
horiAdvance : c_long, | |
vertBearingX : c_long, | |
vertBearingY : c_long, | |
vertAdvance : c_long, | |
} | |
FT_Glyph_Format :: enum i32 #export { | |
FT_GLYPH_FORMAT_NONE = 0, | |
FT_GLYPH_FORMAT_COMPOSITE = ('c' << 24) | ('o' << 16) | ('m' << 8) | 'p', | |
FT_GLYPH_FORMAT_BITMAP = ('b' << 24) | ('i' << 16) | ('t' << 8) | 's', | |
FT_GLYPH_FORMAT_OUTLINE = ('o' << 24) | ('u' << 16) | ('t' << 8) | 'l', | |
FT_GLYPH_FORMAT_PLOTTER = ('p' << 24) | ('l' << 16) | ('o' << 8) | 't', | |
} | |
FT_Bitmap :: struct { | |
rows : u32, | |
width : u32, | |
pitch : int, | |
buffer : ^u8, | |
num_grays : u16, | |
pixel_mode : u8, | |
palette_mode : u8, | |
palette : rawptr, | |
} | |
FT_Vector :: struct { | |
x: c_long, | |
y: c_long, | |
} | |
FT_Outline :: struct { | |
n_contours : i16, | |
n_points : i16, | |
points : ^FT_Vector, | |
tags : ^u8, | |
contours : ^i16, | |
flags : i32, | |
} | |
FT_SubGlyph :: rawptr; | |
FT_Slot_Internal :: rawptr; | |
FT_GlyphSlotRec :: struct { | |
library : FT_Library, | |
face : FT_Face, | |
next : FT_GlyphSlot, | |
reserved : u32, | |
generic : FT_Generic, | |
metrics : FT_Glyph_Metrics, | |
linearHoriAdvance : c_long, | |
linearVertAdvance : c_long, | |
advance : FT_Vector, | |
format : FT_Glyph_Format, | |
bitmap : FT_Bitmap, | |
bitmap_left : i32, | |
bitmap_top : i32, | |
outline : FT_Outline, | |
num_subglyphs : u32, | |
subglyphs : FT_SubGlyph, | |
control_data : rawptr, | |
control_len : c_long, | |
lsb_delta : c_long, | |
rsb_delta : c_long, | |
other : rawptr, | |
internal : FT_Slot_Internal, | |
} | |
FT_GlyphSlot :: ^FT_GlyphSlotRec; | |
FT_Driver :: rawptr; | |
FT_Alloc_Func :: proc"c"(memory: FT_Memory, size: c_long) -> rawptr; | |
FT_Free_Func :: proc"c"(memory: FT_Memory, block: rawptr); | |
FT_Realloc_Func :: proc"c"(memory: FT_Memory, cur_size: c_long, new_size: c_long, block: rawptr) -> rawptr; | |
FT_MemoryRec :: struct { | |
user : rawptr, | |
alloc : FT_Alloc_Func, | |
free : FT_Free_Func, | |
realloc : FT_Realloc_Func, | |
} | |
FT_Memory :: ^FT_MemoryRec; | |
FT_Stream_IoFunc :: proc"c"(stream: FT_Stream, offset: c_ulong, buffer: ^u8, count: c_ulong) -> c_ulong; | |
FT_Stream_CloseFunc :: proc"c"(stream: FT_Stream); | |
FT_StreamDesc :: struct #raw_union { | |
value: c_long, | |
pointer: rawptr, | |
} | |
FT_StreamRec :: struct { | |
base : ^u8, | |
size : c_ulong, | |
pos : c_ulong, | |
descriptor : FT_StreamDesc, | |
pathname : FT_StreamDesc, | |
read : FT_Stream_IoFunc, | |
close : FT_Stream_CloseFunc, | |
memory : FT_Memory, | |
cursor : ^u8, | |
limit : ^u8, | |
} | |
FT_Stream :: ^FT_StreamRec; | |
FT_ListNode :: rawptr; | |
FT_ListRec :: struct { | |
head : FT_ListNode, | |
tail : FT_ListNode, | |
} | |
FT_List :: ^FT_ListRec; | |
FT_Face_Internal :: rawptr; | |
FT_FaceFlag :: enum c_long #export { | |
FT_FACE_FLAG_SCALABLE = 1 << 0, | |
FT_FACE_FLAG_FIXED_SIZES = 1 << 1, | |
FT_FACE_FLAG_FIXED_WIDTH = 1 << 2, | |
FT_FACE_FLAG_SFNT = 1 << 3, | |
FT_FACE_FLAG_HORIZONTAL = 1 << 4, | |
FT_FACE_FLAG_VERTICAL = 1 << 5, | |
FT_FACE_FLAG_KERNING = 1 << 6, | |
FT_FACE_FLAG_FAST_GLYPHS = 1 << 7, | |
FT_FACE_FLAG_MULTIPLE_MASTERS = 1 << 8, | |
FT_FACE_FLAG_GLYPH_NAMES = 1 << 9, | |
FT_FACE_FLAG_EXTERNAL_STREAM = 1 << 10, | |
FT_FACE_FLAG_HINTER = 1 << 11, | |
FT_FACE_FLAG_CID_KEYED = 1 << 12, | |
FT_FACE_FLAG_TRICKY = 1 << 13, | |
FT_FACE_FLAG_COLOR = 1 << 14, | |
FT_FACE_FLAG_VARIATION = 1 << 15, | |
} | |
FT_FaceRec :: struct { | |
num_faces : c_long, | |
face_index : c_long, | |
face_flags : FT_FaceFlag, | |
style_flags : c_long, | |
num_glyphs : c_long, | |
family_name : ^u8, | |
style_name : ^u8, | |
num_fixed_sizes : i32, | |
available_sizes : ^FT_Bitmap_Size, | |
num_charmaps : i32, | |
charmaps : ^FT_CharMap, | |
generic : FT_Generic, | |
/*# The following member variables (down to `underline_thickness') */ | |
/*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */ | |
/*# for bitmap fonts. */ | |
bbox : FT_BBox, | |
units_per_EM : u16, | |
ascender : i16, | |
descender : i16, | |
height : i16, | |
max_advance_width : i16, | |
max_advance_height : i16, | |
underline_position : i16, | |
underline_thickness : i16, | |
glyph : FT_GlyphSlot, | |
size : FT_Size, | |
charmap : FT_CharMap, | |
/*@private begin */ | |
driver : FT_Driver, | |
memory : FT_Memory, | |
stream : FT_Stream, | |
sizes_list : FT_ListRec, | |
autohint : FT_Generic, | |
extensions : rawptr, | |
internal : FT_Face_Internal, | |
} | |
FT_Face :: ^FT_FaceRec; | |
FT_Load_Flags :: enum i32 #export { | |
FT_LOAD_DEFAULT = 0x0, | |
FT_LOAD_NO_SCALE = 1 << 0, | |
FT_LOAD_NO_HINTING = 1 << 1, | |
FT_LOAD_RENDER = 1 << 2, | |
FT_LOAD_NO_BITMAP = 1 << 3, | |
FT_LOAD_VERTICAL_LAYOUT = 1 << 4, | |
FT_LOAD_FORCE_AUTOHINT = 1 << 5, | |
FT_LOAD_CROP_BITMAP = 1 << 6, | |
FT_LOAD_PEDANTIC = 1 << 7, | |
FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1 << 9, | |
FT_LOAD_NO_RECURSE = 1 << 10, | |
FT_LOAD_IGNORE_TRANSFORM = 1 << 11, | |
FT_LOAD_MONOCHROME = 1 << 12, | |
FT_LOAD_LINEAR_DESIGN = 1 << 13, | |
FT_LOAD_NO_AUTOHINT = 1 << 15, | |
FT_LOAD_COLOR = 1 << 20, | |
FT_LOAD_COMPUTE_METRICS = 1 << 21, | |
FT_LOAD_BITMAP_METRICS_ONLY = 1 << 22, | |
} | |
FT_Render_Mode :: enum i32 #export { | |
FT_RENDER_MODE_NORMAL = 0, | |
FT_RENDER_MODE_LIGHT, | |
FT_RENDER_MODE_MONO, | |
FT_RENDER_MODE_LCD, | |
FT_RENDER_MODE_LCD_V, | |
FT_RENDER_MODE_MAX, | |
} | |
FT_Kerning_Mode :: enum u32 #export { | |
FT_KERNING_DEFAULT = 0, | |
FT_KERNING_UNFITTED, | |
FT_KERNING_UNSCALED, | |
} | |
FT_HAS_GLYPH_NAMES :: proc(face: FT_Face) -> bool { | |
return (face.face_flags & FT_FACE_FLAG_GLYPH_NAMES) > 0; | |
} | |
FT_HAS_COLOR :: proc(face: FT_Face) -> bool { | |
return (face.face_flags & FT_FACE_FLAG_COLOR) > 0; | |
} | |
@(default_calling_convention="c") | |
foreign ftlib { | |
FT_Init_FreeType :: proc(lib: ^FT_Library) -> i32 ---; | |
FT_Done_FreeType :: proc(lib: FT_Library) -> i32 ---; | |
FT_New_Face :: proc(library: FT_Library, path: ^u8, face_index: c_long, face: ^FT_Face) -> i32 ---; | |
FT_Done_Face :: proc(face: FT_Face) -> i32 ---; | |
FT_Set_Pixel_Sizes :: proc(face: FT_Face, pixel_width: u32, pixel_height: u32) -> i32 ---; | |
FT_Load_Glyph :: proc(face: FT_Face, glyph_index: u32, load_flags: FT_Load_Flags) -> i32 ---; | |
FT_Load_Char :: proc(face: FT_Face, char_code: c_ulong, load_flags: FT_Load_Flags) -> i32 ---; | |
FT_Render_Glyph :: proc(slot: FT_GlyphSlot, render_mode: FT_Render_Mode) -> i32 ---; | |
FT_Get_Char_Index :: proc(face: FT_Face, charcode: c_ulong) -> u32 ---; | |
FT_Get_Kerning :: proc(face: FT_Face, left_glyph, right_glyph: u32, kern_mode: u32, kerning: ^FT_Vector) -> i32 ---; | |
FT_Get_Glyph_Name :: proc(face: FT_Face, glyph_index: u32, buffer: ^u8, buffer_max: u32) -> i32 ---; | |
} | |
import "core:fmt.odin" | |
/* face: FT_Face; | |
path := "fonts/arial.ttf\x00"; | |
if FT_New_Face(lib, &path[0], 0, &face) != 0 { | |
fmt.println("Failed to create face!\n"); | |
} | |
FT_Set_Pixel_Sizes(face, 0, 16); | |
print_rune(face, '£');*/ | |
print_rune :: proc(face: FT_Face, r: rune) { | |
if FT_Load_Char(face, auto_cast r, FT_LOAD_RENDER) != 0 { | |
fmt.println("Failed to load char!\n"); | |
} | |
chars := " .:ioV#@"; | |
bitmap := face.glyph.bitmap; | |
pixels := mem.slice_ptr(bitmap.buffer, int(bitmap.rows*bitmap.width)); | |
for y in 0..bitmap.rows { | |
for x in 0..bitmap.width { | |
v := pixels[x+y*bitmap.width] >> 5; | |
fmt.printf("%c", chars[v]); | |
} | |
fmt.println(); | |
} | |
fmt.println(); | |
} | |
using import "shared:odin-stb/stb_rect_pack.odin" | |
import "core:strings.odin" | |
import "core:mem.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