package font // ============================================================================ // COLR/CPAL — Color Fonts // ============================================================================ // RGBA color RGBA_Color :: struct { r, g, b, a: u8, } // A color layer in a COLR v0 glyph Color_Layer :: struct { glyph_index: i32, palette_entry: u16, } // Get the number of color palettes in the font (from CPAL table) get_color_palette_count :: proc(info: ^Font_Info) -> i32 { cpal := find_table(info.data, u32(info.fontstart), "CPAL") if cpal == 0 do return 0 data := info.data[cpal:] version := ttUSHORT(data) if version > 1 do return 0 return i32(ttUSHORT(data[4:])) } // Get a color from a palette. Returns false if indices are out of range. get_color_from_palette :: proc(info: ^Font_Info, palette_index: i32, entry_index: i32, color: ^RGBA_Color) -> bool { cpal := find_table(info.data, u32(info.fontstart), "CPAL") if cpal == 0 do return false data := info.data[cpal:] version := ttUSHORT(data) if version > 1 do return false num_entries := i32(ttUSHORT(data[2:])) num_palettes := i32(ttUSHORT(data[4:])) // num_colors := i32(ttUSHORT(data[6:])) color_records_offset := u32(ttULONG(data[8:])) if palette_index < 0 || palette_index >= num_palettes do return false if entry_index < 0 || entry_index >= num_entries do return false // Color indices array starts at offset 12 first_color_index := i32(ttUSHORT(data[12 + u32(palette_index) * 2:])) color_idx := first_color_index + entry_index // Color records are BGRA (4 bytes each) rec := data[color_records_offset + u32(color_idx) * 4:] color.b = rec[0] color.g = rec[1] color.r = rec[2] color.a = rec[3] return true } // Get color layers for a glyph (COLR v0). // Returns the number of layers, or 0 if the glyph has no color layers. // Layers are written to the provided slice. get_glyph_color_layers :: proc(info: ^Font_Info, glyph_index: i32, layers: []Color_Layer) -> i32 { colr := find_table(info.data, u32(info.fontstart), "COLR") if colr == 0 do return 0 data := info.data[colr:] version := ttUSHORT(data) // We support COLR v0 (layered glyphs) if version > 1 do return 0 num_base_glyphs := i32(ttUSHORT(data[2:])) base_glyph_offset := u32(ttULONG(data[4:])) layer_records_offset := u32(ttULONG(data[8:])) // num_layer_records := i32(ttUSHORT(data[12:])) // Binary search for the glyph in base glyph records base_glyphs := data[base_glyph_offset:] l: i32 = 0 r := num_base_glyphs - 1 for l <= r { m := (l + r) >> 1 rec := base_glyphs[m * 6:] gid := i32(ttUSHORT(rec)) if glyph_index < gid { r = m - 1 } else if glyph_index > gid { l = m + 1 } else { first_layer := i32(ttUSHORT(rec[2:])) num_layers := i32(ttUSHORT(rec[4:])) n := min(num_layers, i32(len(layers))) layer_data := data[layer_records_offset:] for i in 0.. bool { // Check COLR colr := find_table(info.data, u32(info.fontstart), "COLR") if colr != 0 { data := info.data[colr:] num_base := i32(ttUSHORT(data[2:])) base_off := u32(ttULONG(data[4:])) base_glyphs := data[base_off:] l: i32 = 0 r := num_base - 1 for l <= r { m := (l + r) >> 1 gid := i32(ttUSHORT(base_glyphs[m * 6:])) if glyph_index < gid { r = m - 1 } else if glyph_index > gid { l = m + 1 } else { return true } } } // Also check SVG if info.svg != 0 { svg_data: [^]u8 if get_glyph_svg(info, glyph_index, &svg_data) > 0 { return true } } return false }