added cmaps to ttf decoding
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 21 Sep 2020 15:39:18 +0000 (10:39 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 21 Sep 2020 15:39:18 +0000 (10:39 -0500)
src/font.onyx
src/main.onyx
tags

index 4617dac530ad9ecc1de31df918a9572250aee1a3..eb0e3d440208f0b48fd6693dcffbe45f1599dd63 100644 (file)
@@ -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);
index 974ea10896101dedfe52c0acbfbb70dd73621f48..6a94bccc17c60762c8a893256e450de55e598e1a 100644 (file)
@@ -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 dc7befa9ee5595e92a13ff3a7b0c29ff9fedcb8f..f9f074e5296de223303deb090f41551492f014c5 100644 (file)
--- 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) {$/