Harbor

branch main
showing the latest snapshot on main
types.odin 7.5 KB · Plain text
font/types.odin 0644 Raw
/*
	TrueType
	Refreshingly simple, pure-Odin TrueType library. Built from stb_truetype's work and Raph Levien's work
	on quadratic flattening.

	Sharkk Minimal License (license.md)
	Copyright (c) 2026 Sharkk

	Honorable mentions:
	- stb_truetype.h by Sean Barrett / RAD Game Tools (public domain)
	- Flattening quadratic Béziers by Raph Levien (Apache 2.0)
*/

package font

import "core:math"

// Core structures

Buf :: struct {
	data:   [^]u8,
	cursor: i32,
	size:   i32,
}

Font_Info :: struct {
	userdata:          rawptr,
	data:              [^]u8,
	data_size:         i32,
	fontstart:         i32,
	num_glyphs:        i32,
	loca:              i32,
	head:              i32,
	glyf:              i32,
	hhea:              i32,
	hmtx:              i32,
	kern:               i32,
	gpos:              i32,
	gsub:              i32,
	svg:               i32,
	index_map:         i32,
	index_to_loc_format: i32,
	is_cff:            bool,
	cff:               Buf,
	charstrings:       Buf,
	gsubrs:            Buf,
	subrs:             Buf,
	fontdicts:         Buf,
	fdselect:          Buf,
	// Caches
	kern_cache:        map[u64]i32,
	kern_cache_ready:  bool,
	shape_cache:       map[i32]Cached_Shape,
	shape_cache_ready: bool,
	cmap_cache:        map[i32]i32,
	cmap_cache_ready:  bool,
}

// Cached glyph shape (pre-extracted vertices)
Cached_Shape :: struct {
	vertices:    [^]Vertex,
	num_vertices: i32,
}

Vertex :: struct {
	x, y, cx, cy, cx1, cy1: i16,
	type:                   u8,
	padding:                u8,
}

Baked_Char :: struct {
	x0, y0, x1, y1: u16,
	xoff, yoff, xadvance: f32,
}

Packed_Char :: struct {
	x0, y0, x1, y1: u16,
	xoff, yoff, xadvance: f32,
	xoff2, yoff2: f32,
}

Aligned_Quad :: struct {
	x0, y0, s0, t0: f32,
	x1, y1, s1, t1: f32,
}

// ============================================================================
// ADVANCED FONT PACKING
// ============================================================================

// Maximum oversampling factor (must be power of 2)
MAX_OVERSAMPLE :: 8

// Rect packing context - simple row-by-row algorithm
Rp_Context :: struct {
	width:    i32,
	height:   i32,
	x:        i32,        // current x cursor
	y:        i32,        // current row y
	bottom_y: i32,        // bottom of current row
}

// Rect packing node (minimal for API compatibility)
Rp_Node :: struct {
	x: u8,
}

// Rectangle for packing
Rp_Rect :: struct {
	x, y:       i32,      // output position
	id:         i32,
	w, h:       i32,      // input dimensions
	was_packed: i32,      // output: 1 if packed
}

// Pack context for texture atlas creation
Pack_Context :: struct {
	user_allocator_context: rawptr,
	pack_info:              ^Rp_Context,
	width:                  i32,
	height:                 i32,
	stride_in_bytes:        i32,
	padding:                i32,
	skip_missing:           i32,
	h_oversample:           u32,
	v_oversample:           u32,
	pixels:                 [^]u8,
	nodes:                  ^Rp_Node,
}

// Pack range for multi-range packing
Pack_Range :: struct {
	font_size:                        f32,
	first_unicode_codepoint_in_range: i32,
	array_of_unicode_codepoints:      [^]i32,
	num_chars:                        i32,
	chardata_for_range:               [^]Packed_Char,
	h_oversample:                     u8,  // internal use
	v_oversample:                     u8,  // internal use
}

Kerning_Entry :: struct {
	glyph1:  i32,
	glyph2:  i32,
	advance: i32,
}

// Vertex types for glyph shapes
VMOVE  :: 1
VLINE  :: 2
VCURVE :: 3
VCUBIC :: 4

// Simple glyph flags (TrueType spec)
GLYPH_ON_CURVE       :: 1   // Point is on curve (vs control point)
GLYPH_X_SHORT_VECTOR :: 2   // X coordinate is 1 byte
GLYPH_Y_SHORT_VECTOR :: 4   // Y coordinate is 1 byte
GLYPH_REPEAT         :: 8   // Next byte specifies repeat count
GLYPH_X_IS_SAME      :: 16  // If X_SHORT: x is positive; else: x is same as previous
GLYPH_Y_IS_SAME      :: 32  // If Y_SHORT: y is positive; else: y is same as previous

// Composite glyph flags (TrueType spec)
COMP_ARG_1_AND_2_ARE_WORDS    :: 1       // Arguments are words (else bytes)
COMP_ARGS_ARE_XY_VALUES       :: 2       // Arguments are xy values (else point indices)
COMP_ROUND_XY_TO_GRID         :: 4       // Round xy values to grid
COMP_WE_HAVE_A_SCALE          :: 1 << 3  // Single scale value follows
COMP_MORE_COMPONENTS          :: 1 << 5  // More components follow this one
COMP_WE_HAVE_AN_X_AND_Y_SCALE :: 1 << 6  // X and Y scales follow
COMP_WE_HAVE_A_TWO_BY_TWO     :: 1 << 7  // 2x2 transformation matrix follows
COMP_WE_HAVE_INSTRUCTIONS     :: 1 << 8  // Instructions follow components
COMP_USE_MY_METRICS           :: 1 << 9  // Use this component's metrics

// Context for CFF CharString interpretation
CS_Ctx :: struct {
	bounds:       bool,
	started:      bool,
	first_x:      f32,
	first_y:      f32,
	x:            f32,
	y:            f32,
	min_x:        i32,
	max_x:        i32,
	min_y:        i32,
	max_y:        i32,
	pvertices:    [^]Vertex,
	num_vertices: i32,
}

// Bitmap output
Bitmap :: struct {
	w:       i32,
	h:       i32,
	stride:  i32,
	pixels:  [^]u8,
}

// Platform IDs
PLATFORM_ID_UNICODE   :: 0
PLATFORM_ID_MAC       :: 1
PLATFORM_ID_ISO       :: 2
PLATFORM_ID_MICROSOFT :: 3

// Microsoft encoding IDs
MS_EID_SYMBOL        :: 0
MS_EID_UNICODE_BMP   :: 1
MS_EID_SHIFTJIS      :: 2
MS_EID_UNICODE_FULL  :: 10

// Unicode encoding IDs
UNICODE_EID_UNICODE_1_0      :: 0
UNICODE_EID_UNICODE_1_1      :: 1
UNICODE_EID_ISO_10646        :: 2
UNICODE_EID_UNICODE_2_0_BMP  :: 3
UNICODE_EID_UNICODE_2_0_FULL :: 4

// Mac encoding IDs
MAC_EID_ROMAN        :: 0
MAC_EID_JAPANESE     :: 1
MAC_EID_CHINESE_TRAD :: 2
MAC_EID_KOREAN       :: 3
MAC_EID_ARABIC       :: 4
MAC_EID_HEBREW       :: 5
MAC_EID_GREEK        :: 6
MAC_EID_RUSSIAN      :: 7

// Microsoft language IDs
MS_LANG_ENGLISH  :: 0x0409
MS_LANG_CHINESE  :: 0x0804
MS_LANG_DUTCH    :: 0x0413
MS_LANG_FRENCH   :: 0x040c
MS_LANG_GERMAN   :: 0x0407
MS_LANG_HEBREW   :: 0x040d
MS_LANG_ITALIAN  :: 0x0410
MS_LANG_JAPANESE :: 0x0411
MS_LANG_KOREAN   :: 0x0412
MS_LANG_RUSSIAN  :: 0x0419
MS_LANG_SPANISH  :: 0x0409
MS_LANG_SWEDISH  :: 0x041D

// Mac language IDs
MAC_LANG_ENGLISH            :: 0
MAC_LANG_FRENCH             :: 1
MAC_LANG_GERMAN             :: 2
MAC_LANG_ITALIAN            :: 3
MAC_LANG_DUTCH              :: 4
MAC_LANG_SWEDISH            :: 5
MAC_LANG_SPANISH            :: 6
MAC_LANG_HEBREW             :: 10
MAC_LANG_JAPANESE           :: 11
MAC_LANG_ARABIC             :: 12
MAC_LANG_CHINESE_TRAD       :: 19
MAC_LANG_KOREAN             :: 23
MAC_LANG_RUSSIAN            :: 32
MAC_LANG_CHINESE_SIMPLIFIED :: 33

// Mac style flags (for find_matching_font)
MACSTYLE_DONTCARE   :: 0
MACSTYLE_BOLD       :: 1
MACSTYLE_ITALIC     :: 2
MACSTYLE_UNDERSCORE :: 4
MACSTYLE_NONE       :: 8

// Helper macros for reading big-endian data
@(private)
ttBYTE :: #force_inline proc(p: [^]u8) -> u8 {
	return p[0]
}

@(private)
ttCHAR :: #force_inline proc(p: [^]u8) -> i8 {
	return i8(p[0])
}

@(private)
ttUSHORT :: #force_inline proc(p: [^]u8) -> u16 {
	return u16(p[0]) << 8 | u16(p[1])
}

@(private)
ttSHORT :: #force_inline proc(p: [^]u8) -> i16 {
	return i16(p[0]) << 8 | i16(p[1])
}

@(private)
ttULONG :: #force_inline proc(p: [^]u8) -> u32 {
	return u32(p[0]) << 24 | u32(p[1]) << 16 | u32(p[2]) << 8 | u32(p[3])
}

@(private)
ttLONG :: #force_inline proc(p: [^]u8) -> i32 {
	return i32(p[0]) << 24 | i32(p[1]) << 16 | i32(p[2]) << 8 | i32(p[3])
}

@(private)
tag4 :: #force_inline proc(p: [^]u8, c0, c1, c2, c3: u8) -> bool {
	return p[0] == c0 && p[1] == c1 && p[2] == c2 && p[3] == c3
}

@(private)
tag :: #force_inline proc(p: [^]u8, str: string) -> bool {
	return tag4(p, str[0], str[1], str[2], str[3])
}