From 1130c10c5542d6997c8f761827e500f895ed1f1d Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 21 Sep 2020 10:39:18 -0500 Subject: [PATCH] added cmaps to ttf decoding --- src/font.onyx | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.onyx | 36 ++++++------ tags | 11 ++++ 3 files changed, 185 insertions(+), 18 deletions(-) diff --git a/src/font.onyx b/src/font.onyx index 4617dac..eb0e3d4 100644 --- a/src/font.onyx +++ b/src/font.onyx @@ -99,6 +99,7 @@ TrueTypeFont :: struct { range_shift : u16; table_map : I32Map(TTTableInfo); + char_maps : [..] TTCmap; version : u32; font_revision : u32; @@ -151,6 +152,8 @@ ttf_create :: proc (ttf_data: [] u8) -> TrueTypeFont { ttf : TrueTypeFont; ttf.reader = binary_reader_create(ttf_data); i32map_init(^ttf.table_map); + array_init(^ttf.char_maps); + ttf_read_offset_table(^ttf); return ttf; } @@ -352,6 +355,159 @@ ttf_glyph_count :: proc (use ttf: ^TrueTypeFont) -> u32 { return ~~read_u16(^reader); } +ttf_read_cmap_table :: proc (use ttf: ^TrueTypeFont) { + empty_table_hack := TTTableInfo.{}; + cmap_table_info := i32map_get(^table_map, string_to_beu32("cmap"), empty_table_hack); + seek(^reader, cmap_table_info.offset); + + version := read_u16(^reader); + num_subtables := read_u16(^reader); + + for i: 0 .. ~~num_subtables { + platform_id := read_u16(^reader); + platform_specific_id := read_u16(^reader); + offset := read_u32(^reader); + + // Microsoft Unicode, BMP only + if platform_id == ~~3 && platform_specific_id <= ~~1 { + ttf_read_cmap(ttf, offset + cmap_table_info.offset); + } + } +} + +TTCmapFormat :: enum (u16) { + Simple :: 0x00; + Segmented :: 0x04; +} + +TTCmapBase :: struct { format : TTCmapFormat; } +TTCmap0 :: struct { + use base: TTCmapBase; + + glyph_indicies: [] u8; +} +TTCmap4 :: struct { + use base: TTCmapBase; + + seg_count : u16; + search_range : u16; + entry_selector : u16; + range_shift : u16; + + segments : [..] TTSegment; + cache : I32Map(i32); +} + +TTSegment :: struct { + start_code : u16; + end_code : u16; + id_delta : u16; + id_range_offset : u16; +} + +TTCmap :: struct #union { + use base: TTCmapBase; + cmap0: TTCmap0; + cmap4: TTCmap4; +} + +ttf_read_cmap :: proc (use ttf: ^TrueTypeFont, offset: u32) { + old := seek(^reader, offset); + defer seek(^reader, old); + + format := read_u16(^reader); + length := read_u16(^reader); + lang := read_u16(^reader); + + switch cast(i32) format { + case 0 do ttf_read_cmap0(ttf); + case 4 do ttf_read_cmap4(ttf); + + case #default { print("Unsupported cmap format: "); println(cast(i32) format); } + } +} + +ttf_read_cmap0 :: proc (use ttf: ^TrueTypeFont) { + cmap : TTCmap; + cmap.cmap0.format = TTCmapFormat.Simple; + + glyphs : [..] u8; + array_init(^glyphs, 256); + for i: 0 .. 256 do array_push(^glyphs, read_u8(^reader)); + + cmap.cmap0.glyph_indicies = array_to_slice(^glyphs); + + array_push(^char_maps, cmap); +} + +ttf_read_cmap4 :: proc (use ttf: ^TrueTypeFont) { + cmap : TTCmap; + cmap.cmap4.format = TTCmapFormat.Segmented; + map := ^cmap.cmap4; + + map.seg_count = read_u16(^reader) >> ~~1; + map.search_range = read_u16(^reader); + map.entry_selector = read_u16(^reader); + map.range_shift = read_u16(^reader); + + array_init(^map.segments, ~~map.seg_count); + map.segments.count = cast(u32) map.seg_count; + + for ^seg: map.segments do seg.end_code = read_u16(^reader); + read_u16(^reader); // Reserved and unused + for ^seg: map.segments do seg.start_code = read_u16(^reader); + for ^seg: map.segments do seg.id_delta = read_u16(^reader); + for ^seg: map.segments { + seg.id_range_offset = read_u16(^reader); + if seg.id_range_offset != ~~0 do seg.id_range_offset += ~~(tell(^reader) - 2); + } + + array_push(^char_maps, cmap); +} + +ttf_lookup_glyph_by_char :: proc (use ttf: ^TrueTypeFont, charcode: u32) -> u32 { + potential_code := 0; + + for ^cmap: char_maps { + switch cmap.format { + case TTCmapFormat.Simple do potential_code = ttf_lookup_in_cmap0(ttf, ~~cmap, charcode); + case TTCmapFormat.Segmented do potential_code = ttf_lookup_in_cmap4(ttf, ~~cmap, charcode); + } + + if potential_code != 0 do return potential_code; + } + + return potential_code; +} + +#private_file +ttf_lookup_in_cmap0 :: proc (use ttf: ^TrueTypeFont, cmap: ^TTCmap0, charcode: u32) -> u32 { + if charcode < 0 || charcode >= 256 do return 0; + return ~~cmap.glyph_indicies[charcode]; +} + +#private_file +ttf_lookup_in_cmap4 :: proc (use ttf: ^TrueTypeFont, cmap: ^TTCmap4, charcode: u32) -> u32 { + // TODO: Lookup in the cache + + index := 0; + for ^seg: cmap.segments { + if ~~seg.start_code <= charcode && ~~charcode <= seg.end_code { + if seg.id_range_offset != ~~0 { + glyph_index_address := ~~seg.id_range_offset + 2 * (charcode - ~~seg.start_code); + seek(^reader, glyph_index_address); + index = cast(u32) read_u16(^reader); + } else { + index = (~~seg.id_delta + charcode) & 0xffff; + } + + break; + } + } + + return index; +} + #private_file ttf_calc_table_checksum :: proc (reader: ^BinaryReader, offset: u32, length: u32) -> u32 { old := seek(reader, offset); diff --git a/src/main.onyx b/src/main.onyx index 974ea10..6a94bcc 100644 --- a/src/main.onyx +++ b/src/main.onyx @@ -123,36 +123,36 @@ update :: proc () { draw :: proc () { defer render_context_flush(^renderer); - gl.clearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl.clearColor(0.2f, 0.2f, 0.2f, 1.0f); gl.clear(gl.COLOR_BUFFER_BIT); renderer.color = Color4f32.{ 1f, 0f, 0f, 1f }; - // points : [6] V2f; - // points[0] = V2f.{ 500.0f, 700.0f }; - // points[1] = V2f.{ 600.0f, 200.0f }; - // points[2] = V2f.{ 700.0f, 400.0f }; - // points[3] = V2f.{ 1700.0f, 800.0f }; - // points[4] = V2f.{ 1800.0f, 400.0f }; - // points[5] = V2f.{ 1000.0f, 200.0f }; + points : [6] V2f; + points[0] = V2f.{ 500.0f, 700.0f }; + points[1] = V2f.{ 600.0f, 200.0f }; + points[2] = V2f.{ 700.0f, 400.0f }; + points[3] = V2f.{ 1700.0f, 800.0f }; + points[4] = V2f.{ 1800.0f, 400.0f }; + points[5] = V2f.{ 1000.0f, 200.0f }; - // for i: 0 .. 200 { - // pos := bezier_curve(~~i / 200.0f, points[0 .. 6]); - // draw_rect(^renderer, pos.x, pos.y, 16f, 16f); - // } + for i: 0 .. 200 { + pos := bezier_curve(~~i / 200.0f, points[0 .. 6]); + draw_rect(^renderer, pos.x, pos.y, 16f, 16f); + } glyph : ^TTGlyph = null; - idx := 230; - while glyph == null { - glyph = ttf_read_glyph(^ttf, idx); - idx += 1; - } + glyph_index := ttf_lookup_glyph_by_char(^ttf, ~~ #char "A"); + glyph = ttf_read_glyph(^ttf, glyph_index); defer cfree(glyph); + defer array_free(^glyph.points); + defer array_free(^glyph.contour_ends); rx := 100.0f / ~~ cast(i32) (glyph.x_max - glyph.x_min); ry := 100.0f / ~~ cast(i32) (glyph.y_min - glyph.y_max); for p: glyph.points { + // if p.on_curve do continue; draw_rect(^renderer, ~~ cast(i32) p.x * rx + 100f, ~~ cast(i32) p.y * ry + 100f, 10f, 10f); } @@ -197,8 +197,8 @@ main :: proc (args: [] cstring) { ttf_data := #file_contents "res/Inconsolata-Regular.ttf"; ttf = ttf_create(ttf_data); - ttf_read_offset_table(^ttf); ttf_read_head_table(^ttf); + ttf_read_cmap_table(^ttf); // for i: 0 .. ttf_glyph_count(^ttf) { // glyph := ttf_read_glyph(^ttf, i); diff --git a/tags b/tags index dc7befa..f9f074e 100644 --- a/tags +++ b/tags @@ -558,9 +558,15 @@ TRANSFORM_FEEDBACK_VARYINGS /usr/share/onyx/core/js/webgl.onyx /^TRANSFORM_FEEDB TRIANGLES /usr/share/onyx/core/js/webgl.onyx /^TRIANGLES :: 0x0004$/ TRIANGLE_FAN /usr/share/onyx/core/js/webgl.onyx /^TRIANGLE_FAN :: 0x0006$/ TRIANGLE_STRIP /usr/share/onyx/core/js/webgl.onyx /^TRIANGLE_STRIP :: 0x0005$/ +TTCmap src/font.onyx /^TTCmap :: struct #union {$/ +TTCmap0 src/font.onyx /^TTCmap0 :: struct {$/ +TTCmap4 src/font.onyx /^TTCmap4 :: struct {$/ +TTCmapBase src/font.onyx /^TTCmapBase :: struct { format : TTCmapFormat; }$/ +TTCmapFormat src/font.onyx /^TTCmapFormat :: enum (u16) {$/ TTGlyph src/font.onyx /^TTGlyph :: struct {$/ TTGlyphPoint src/font.onyx /^TTGlyphPoint :: struct {$/ TTIndexToLocFormat src/font.onyx /^TTIndexToLocFormat :: enum (i16) {$/ +TTSegment src/font.onyx /^TTSegment :: struct {$/ TTTableInfo src/font.onyx /^TTTableInfo :: struct {$/ Texture src/gfx/texture.onyx /^Texture :: struct {$/ TrueTypeFont src/font.onyx /^TrueTypeFont :: struct {$/ @@ -913,7 +919,12 @@ trunc_f64 /usr/share/onyx/core/intrinsics.onyx /^trunc_f64 :: proc (val: f64) ttf src/main.onyx /^ttf : TrueTypeFont$/ ttf_create src/font.onyx /^ttf_create :: proc (ttf_data: [] u8) -> TrueTypeFont {$/ ttf_glyph_count src/font.onyx /^ttf_glyph_count :: proc (use ttf: ^TrueTypeFont) -> u32 {$/ +ttf_lookup_glyph_by_char src/font.onyx /^ttf_lookup_glyph_by_char :: proc (use ttf: ^TrueTypeFont, charcode: u32) -> u32 {$/ ttf_lookup_glyph_offset src/font.onyx /^ttf_lookup_glyph_offset :: proc (use ttf: ^TrueTypeFont, glyph_index: i32) -> u32 {$/ +ttf_read_cmap src/font.onyx /^ttf_read_cmap :: proc (use ttf: ^TrueTypeFont, offset: u32) {$/ +ttf_read_cmap0 src/font.onyx /^ttf_read_cmap0 :: proc (use ttf: ^TrueTypeFont) {$/ +ttf_read_cmap4 src/font.onyx /^ttf_read_cmap4 :: proc (use ttf: ^TrueTypeFont) {$/ +ttf_read_cmap_table src/font.onyx /^ttf_read_cmap_table :: proc (use ttf: ^TrueTypeFont) {$/ ttf_read_glyph src/font.onyx /^ttf_read_glyph :: proc (use ttf: ^TrueTypeFont, glyph_index: i32) -> ^TTGlyph {$/ ttf_read_head_table src/font.onyx /^ttf_read_head_table :: proc (use ttf: ^TrueTypeFont) {$/ ttf_read_offset_table src/font.onyx /^ttf_read_offset_table :: proc (use ttf: ^TrueTypeFont) {$/ -- 2.25.1