From 55e3e50b9b298da4e9a3238dc3e3070b8e162b77 Mon Sep 17 00:00:00 2001 From: penguindark <57967770+penguindark@users.noreply.github.com> Date: Tue, 12 Jan 2021 00:49:58 +0100 Subject: [PATCH] ttf: improvement on not found glyphs (#8050) --- vlib/x/ttf/common.v | 8 +- vlib/x/ttf/render_bmp.v | 163 +++++++++++++-------- vlib/x/ttf/render_sokol_cpu.v | 2 +- vlib/x/ttf/ttf.v | 259 +++++++++++++++++----------------- 4 files changed, 235 insertions(+), 197 deletions(-) diff --git a/vlib/x/ttf/common.v b/vlib/x/ttf/common.v index 25ec6eb8ed..aa070e0c30 100644 --- a/vlib/x/ttf/common.v +++ b/vlib/x/ttf/common.v @@ -111,23 +111,23 @@ fn fabs(a f32) f32 { // integer part of x [inline] fn ipart(x f32) f32 { - return f32(math.floor(x)) + return f32(math.floor(x)) } [inline] fn round(x f32) f32 { - return ipart(x + 0.5) + return ipart(x + 0.5) } // fractional part of x [inline] fn fpart(x f32) f32 { - return x - f32(math.floor(x)) + return x - f32(math.floor(x)) } [inline] fn rfpart(x f32) f32 { - return 1 - fpart(x) + return 1 - fpart(x) } /****************************************************************************** diff --git a/vlib/x/ttf/render_bmp.v b/vlib/x/ttf/render_bmp.v index 9aa3296d34..59fb89714e 100644 --- a/vlib/x/ttf/render_bmp.v +++ b/vlib/x/ttf/render_bmp.v @@ -515,6 +515,13 @@ fn (mut bmp BitMap) get_chars_bbox(in_string string) []int { } c_index := bmp.tf.map_code(int(char)) + // Glyph not found + if c_index == 0 { + w += int(space_cw * bmp.space_cw) + i += c_len + continue + } + ax , ay := bmp.tf.next_kern(c_index) //dprintln("char_index: $c_index ax: $ax ay: $ay") @@ -548,7 +555,7 @@ fn (mut bmp BitMap) get_chars_bbox(in_string string) []int { i+= c_len } - return res + return res } pub @@ -581,6 +588,12 @@ fn (mut bmp BitMap) get_bbox(in_string string) (int, int){ } c_index := bmp.tf.map_code(int(char)) + // Glyph not found + if c_index == 0 { + w += int(space_cw * bmp.space_cw) + i += c_len + continue + } ax , ay := bmp.tf.next_kern(c_index) //dprintln("char_index: $c_index ax: $ax ay: $ay") @@ -612,7 +625,7 @@ fn (mut bmp BitMap) get_bbox(in_string string) (int, int){ i+= c_len } - + //dprintln("y_min: $bmp.tf.y_min y_max: $bmp.tf.y_max res: ${int((bmp.tf.y_max - bmp.tf.y_min)*buf.scale)} width: ${int( (cw) * buf.scale)}") //buf.box(0,y_base - int((bmp.tf.y_min)*buf.scale), int( (x_max) * buf.scale), y_base-int((bmp.tf.y_max)*buf.scale), u32(0xFF00_0000) ) return w , int(abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) @@ -623,6 +636,25 @@ fn (mut bmp BitMap) get_bbox(in_string string) (int, int){ * TTF draw glyph * ******************************************************************************/ +fn (mut bmp BitMap) draw_notdef_glyph(in_x int, in_w int) { + mut p := Point{in_x, 0, false} + x1 , y1 := bmp.trf_txt(p) + // init ch_matrix + bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x + bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x + bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y + bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y + bmp.ch_matrix[6] = int(x1) + bmp.ch_matrix[7] = int(y1) + x,y := bmp.trf_ch(p) + + y_h := fabs(bmp.tf.y_max-bmp.tf.y_min)* bmp.scale * 0.5 + + bmp.box(int(x), int(y), int(x - in_w), int(y - y_h), bmp.color) + bmp.line(int(x), int(y), int(x - in_w ), int(y - y_h), bmp.color) + bmp.line(int(x - in_w ), int(y), int(x), int(y - y_h), bmp.color) +} + pub fn (mut bmp BitMap) draw_text(in_string string) (int, int){ mut w := 0 @@ -652,45 +684,52 @@ fn (mut bmp BitMap) draw_text(in_string string) (int, int){ char = u16(tmp_char) } - c_index := bmp.tf.map_code(int(char)) - ax , ay := bmp.tf.next_kern(c_index) - //dprintln("char_index: $c_index ax: $ax ay: $ay") + c_index := bmp.tf.map_code(int(char)) + // Glyph not found + if c_index == 0 { + bmp.draw_notdef_glyph(w, int(space_cw * bmp.space_cw)) + w += int(space_cw * bmp.space_cw) + i += c_len + continue + } - cw, _ := bmp.tf.get_horizontal_metrics(u16(char)) - //cw, lsb := bmp.tf.get_horizontal_metrics(u16(char)) - //dprintln("metrics: [${u16(char):c}] cw:$cw lsb:$lsb") - - //----- Draw_Glyph transformations ----- - mut x0 := w + int(ax * bmp.scale) - mut y0 := 0 + int(ay * bmp.scale) - - p := Point{x0,y0,false} - x1 , y1 := bmp.trf_txt(p) - // init ch_matrix - bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x - bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x - bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y - bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y - bmp.ch_matrix[6] = int(x1) - bmp.ch_matrix[7] = int(y1) + ax , ay := bmp.tf.next_kern(c_index) + //dprintln("char_index: $c_index ax: $ax ay: $ay") - x_min, x_max := bmp.draw_glyph(c_index) - // x_min := 1 - // x_max := 2 - //----------------- + cw, _ := bmp.tf.get_horizontal_metrics(u16(char)) + //cw, lsb := bmp.tf.get_horizontal_metrics(u16(char)) + //dprintln("metrics: [${u16(char):c}] cw:$cw lsb:$lsb") - mut width := int( (abs(x_max + x_min) + ax) * bmp.scale) - if bmp.use_font_metrics { - width = int((cw+ax) * bmp.scale) - } - w += width + div_space_cw + //----- Draw_Glyph transformations ----- + mut x0 := w + int(ax * bmp.scale) + mut y0 := 0 + int(ay * bmp.scale) - i+= c_len - } - - //dprintln("y_min: $bmp.tf.y_min y_max: $bmp.tf.y_max res: ${int((bmp.tf.y_max - bmp.tf.y_min)*buf.scale)} width: ${int( (cw) * buf.scale)}") - //buf.box(0,y_base - int((bmp.tf.y_min)*buf.scale), int( (x_max) * buf.scale), y_base-int((bmp.tf.y_max)*buf.scale), u32(0xFF00_0000) ) - return w , int(abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) + p := Point{x0,y0,false} + x1 , y1 := bmp.trf_txt(p) + // init ch_matrix + bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x + bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x + bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y + bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y + bmp.ch_matrix[6] = int(x1) + bmp.ch_matrix[7] = int(y1) + + x_min, x_max := bmp.draw_glyph(c_index) + // x_min := 1 + // x_max := 2 + //----------------- + + mut width := int( (abs(x_max + x_min) + ax) * bmp.scale) + if bmp.use_font_metrics { + width = int((cw+ax) * bmp.scale) + } + w += width + div_space_cw + i+= c_len + } + + //dprintln("y_min: $bmp.tf.y_min y_max: $bmp.tf.y_max res: ${int((bmp.tf.y_max - bmp.tf.y_min)*buf.scale)} width: ${int( (cw) * buf.scale)}") + //buf.box(0,y_base - int((bmp.tf.y_min)*buf.scale), int( (x_max) * buf.scale), y_base-int((bmp.tf.y_max)*buf.scale), u32(0xFF00_0000) ) + return w , int(abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) } pub @@ -759,13 +798,13 @@ fn (mut bmp BitMap) draw_glyph(index u16) (int, int){ } else { //dprintln("HERE2") // ctx.quadraticCurveTo(prev.x + x, prev.y + y, - // (prev.x + point.x) / 2 + x, - // (prev.y + point.y) / 2 + y); - - //bmp.line(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, color2) - //bmp.quadratic(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, prev.x, prev.y, color2) - bmp.quadratic(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, prev.x, prev.y, color) - x0 = (prev.x + point.x)/2 + // (prev.x + point.x) / 2 + x, + // (prev.y + point.y) / 2 + y); + + //bmp.line(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, color2) + //bmp.quadratic(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, prev.x, prev.y, color2) + bmp.quadratic(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, prev.x, prev.y, color) + x0 = (prev.x + point.x)/2 y0 = (prev.y + point.y)/2 } } @@ -774,29 +813,29 @@ fn (mut bmp BitMap) draw_glyph(index u16) (int, int){ //dprintln("count == glyph.contour_ends[count]") if s == 2 { // final point was off-curve. connect to start - mut start_point := glyph.points[contour_start] - start_point.x, start_point.y = bmp.trf_ch(start_point) - if point.on_curve { + mut start_point := glyph.points[contour_start] + start_point.x, start_point.y = bmp.trf_ch(start_point) + if point.on_curve { //ctx.quadraticCurveTo(prev.x + x, prev.y + y, - // point.x + x, point.y + y); - //bmp.line(x0, y0, start_point.x + in_x, start_point.y + in_y, u32(0x00FF0000)) - - bmp.quadratic(x0, y0, start_point.x, start_point.y , - // start_point.x + in_x, start_point.y + in_y, u32(0xFF00FF00)) - start_point.x, start_point.y, color) + //point.x + x, point.y + y); + //bmp.line(x0, y0, start_point.x + in_x, start_point.y + in_y, u32(0x00FF0000)) + + bmp.quadratic(x0, y0, start_point.x, start_point.y , + // start_point.x + in_x, start_point.y + in_y, u32(0xFF00FF00)) + start_point.x, start_point.y, color) } else { //ctx.quadraticCurveTo(prev.x + x, prev.y + y, - // (prev.x + point.x) / 2 + x, - // (prev.y + point.y) / 2 + y); - - //bmp.line(x0, y0, start_point.x, start_point.y, u32(0x00FF0000) - bmp.quadratic(x0, y0, start_point.x, start_point.y, - (point.x + start_point.x)/2, - (point.y + start_point.y)/2, - //u32(0xFF000000)) + // (prev.x + point.x) / 2 + x, + // (prev.y + point.y) / 2 + y); + + //bmp.line(x0, y0, start_point.x, start_point.y, u32(0x00FF0000) + bmp.quadratic(x0, y0, start_point.x, start_point.y, + (point.x + start_point.x)/2, + (point.y + start_point.y)/2, + //u32(0xFF000000)) color) } - }else{ + } else { // last point not in a curve //bmp.line(point.x, point.y, sp_x, sp_y, u32(0x00FF0000)) bmp.line(point.x, point.y, sp_x, sp_y, color) diff --git a/vlib/x/ttf/render_sokol_cpu.v b/vlib/x/ttf/render_sokol_cpu.v index 9695459b01..96d3be06ac 100644 --- a/vlib/x/ttf/render_sokol_cpu.v +++ b/vlib/x/ttf/render_sokol_cpu.v @@ -156,7 +156,7 @@ fn (mut tf_skl TTF_render_Sokol) create_texture(){ height: h num_mipmaps: 0 min_filter: .linear - mag_filter: .linear + mag_filter: .linear //usage: .dynamic wrap_u: .clamp_to_edge wrap_v: .clamp_to_edge diff --git a/vlib/x/ttf/ttf.v b/vlib/x/ttf/ttf.v index 12902abf32..26fbb2ef18 100644 --- a/vlib/x/ttf/ttf.v +++ b/vlib/x/ttf/ttf.v @@ -24,10 +24,10 @@ import strings ******************************************************************************/ struct Segment { mut: - id_range_offset u32 - start_code u16 - end_code u16 - id_delta u16 + id_range_offset u32 + start_code u16 + end_code u16 + id_delta u16 } struct TrueTypeCmap { @@ -220,8 +220,8 @@ fn (mut tf TTF_File) glyph_count() u16{ old_pos := tf.pos tf.pos = tf.tables["maxp"].offset + 4 count := tf.get_u16() - tf.pos = old_pos - return count + tf.pos = old_pos + return count } pub @@ -474,22 +474,21 @@ fn (mut tf TTF_File) read_compound_glyph(mut in_glyph Glyph){ simple_glyph := tf.read_glyph(component.glyph_index) if simple_glyph.valid_glyph { point_offset := in_glyph.points.len - for i in 0..simple_glyph.contour_ends.len { - in_glyph.contour_ends << u16(simple_glyph.contour_ends[i] + point_offset) - } + for i in 0..simple_glyph.contour_ends.len { + in_glyph.contour_ends << u16(simple_glyph.contour_ends[i] + point_offset) + } - for p in simple_glyph.points { - mut x := f32(p.x) - mut y := f32(p.y) - x = component.matrix[0] * x + component.matrix[1] * y + component.matrix[4] - y = component.matrix[2] * x + component.matrix[3] * y + component.matrix[5] - in_glyph.points << Point{ - x: int(x) - y: int(y) - on_curve: p.on_curve - } - - } + for p in simple_glyph.points { + mut x := f32(p.x) + mut y := f32(p.y) + x = component.matrix[0] * x + component.matrix[1] * y + component.matrix[4] + y = component.matrix[2] * x + component.matrix[3] * y + component.matrix[5] + in_glyph.points << Point{ + x: int(x) + y: int(y) + on_curve: p.on_curve + } + } } tf.pos = old_pos } @@ -501,7 +500,7 @@ fn (mut tf TTF_File) read_compound_glyph(mut in_glyph Glyph){ } // ok we have a valid glyph - in_glyph.valid_glyph = true + in_glyph.valid_glyph = true } /****************************************************************************** @@ -695,9 +694,9 @@ fn (mut tf TTF_File) read_head_table() { tf.y_max = tf.get_i16() tf.mac_style = tf.get_u16() tf.lowest_rec_ppem = tf.get_u16() - tf.font_direction_hint = tf.get_i16() - tf.index_to_loc_format = tf.get_i16() - tf.glyph_data_format = tf.get_i16() + tf.font_direction_hint = tf.get_i16() + tf.index_to_loc_format = tf.get_i16() + tf.glyph_data_format = tf.get_i16() } /****************************************************************************** @@ -721,36 +720,36 @@ fn (mut tf TTF_File) read_name_table() { /*platform_specific_id :=*/ tf.get_u16() /*language_id :=*/ tf.get_u16() name_id := tf.get_u16() - length := tf.get_u16() - offset := tf.get_u16() + length := tf.get_u16() + offset := tf.get_u16() - old_pos := tf.pos - tf.pos = table_offset + string_offset + offset + old_pos := tf.pos + tf.pos = table_offset + string_offset + offset - mut name := "" - if platform_id == 0 || platform_id == 3 { + mut name := "" + if platform_id == 0 || platform_id == 3 { name = tf.get_unicode_string(length) - } else { - name = tf.get_string(length) - } - //dprintln("Name [${platform_id} / ${platform_specific_id}] id:[$name_id] language:[$language_id] [$name]") - tf.pos = old_pos + } else { + name = tf.get_string(length) + } + //dprintln("Name [${platform_id} / ${platform_specific_id}] id:[$name_id] language:[$language_id] [$name]") + tf.pos = old_pos - match name_id { - 1 { - tf.font_family = name + match name_id { + 1 { + tf.font_family = name + } + 2 { + tf.font_sub_family = name + } + 4 { + tf.full_name = name + } + 6 { + tf.postscript_name = name + } + else {} } - 2 { - tf.font_sub_family = name - } - 4 { - tf.full_name = name - } - 6 { - tf.postscript_name = name - } - else {} - } } } @@ -767,24 +766,24 @@ fn (mut tf TTF_File) read_cmap_table() { version := tf.get_u16() // must be 0 assert version == 0 - number_sub_tables := tf.get_u16() + number_sub_tables := tf.get_u16() // tables must be sorted by platform id and then platform specific - // encoding. - for _ in 0..number_sub_tables { - // platforms are: - // 0 - Unicode -- use specific id 6 for full coverage. 0/4 common. - // 1 - Macintosh (Discouraged) - // 2 - reserved - // 3 - Microsoft - platform_id := tf.get_u16() + // encoding. + for _ in 0..number_sub_tables { + // platforms are: + // 0 - Unicode -- use specific id 6 for full coverage. 0/4 common. + // 1 - Macintosh (Discouraged) + // 2 - reserved + // 3 - Microsoft + platform_id := tf.get_u16() platform_specific_id := tf.get_u16() offset := tf.get_u32() dprintln("CMap platform_id=${platform_id} specific_id=${platform_specific_id} offset=${offset}") - if platform_id == 3 && platform_specific_id <= 1 { - tf.read_cmap(table_offset + offset) - } - } + if platform_id == 3 && platform_specific_id <= 1 { + tf.read_cmap(table_offset + offset) + } + } } fn (mut tf TTF_File) read_cmap(offset u32) { @@ -817,17 +816,17 @@ fn (mut tf TTF_File) read_cmap(offset u32) { ******************************************************************************/ fn (mut tf TTF_File) map_code(char_code int) u16{ mut index := 0 - for i in 0..tf.cmaps.len { - mut cmap := tf.cmaps[i] - if cmap.format == 0 { - //dprintln("format 0") - index = cmap.map_0(char_code) - } else if cmap.format == 4 { - //dprintln("format 4") - index = cmap.map_4(char_code, mut tf) + for i in 0..tf.cmaps.len { + mut cmap := tf.cmaps[i] + if cmap.format == 0 { + //dprintln("format 0") + index = cmap.map_0(char_code) + } else if cmap.format == 4 { + //dprintln("format 4") + index = cmap.map_4(char_code, mut tf) + } } - } - return u16(index) + return u16(index) } fn (mut tm TrueTypeCmap) init_0(mut tf TTF_File) { @@ -851,45 +850,45 @@ fn (mut tm TrueTypeCmap) init_4(mut tf TTF_File) { tm.format = 4 // 2x segcount - seg_count := tf.get_u16() >> 1 - /*search_range :=*/ tf.get_u16() - /*entry_selector :=*/ tf.get_u16() - /*range_shift :=*/ tf.get_u16() + seg_count := tf.get_u16() >> 1 + /*search_range :=*/ tf.get_u16() + /*entry_selector :=*/ tf.get_u16() + /*range_shift :=*/ tf.get_u16() - // Ending character code for each segment, last is 0xffff - for _ in 0..seg_count { + // Ending character code for each segment, last is 0xffff + for _ in 0..seg_count { tm.segments << Segment{0, 0, tf.get_u16(), 0} - } + } - // reservePAD - tf.get_u16() + // reservePAD + tf.get_u16() - // starting character code for each segment - for i in 0..seg_count { + // starting character code for each segment + for i in 0..seg_count { tm.segments[i].start_code = tf.get_u16() - } + } - // Delta for all character codes in segment - for i in 0..seg_count { + // Delta for all character codes in segment + for i in 0..seg_count { tm.segments[i].id_delta = tf.get_u16() - } + } - // offset in bytes to glyph indexArray, or 0 - for i in 0..seg_count { + // offset in bytes to glyph indexArray, or 0 + for i in 0..seg_count { ro := u32(tf.get_u16()) if ro != 0 { tm.segments[i].id_range_offset = tf.pos - 2 + ro } else { tm.segments[i].id_range_offset = 0 } - } + } /* - // DEBUG LOG - for i in 0..seg_count { + // DEBUG LOG + for i in 0..seg_count { seg := tm.segments[i] dprintln(" segments[$i] = $seg.start_code $seg.end_code $seg.id_delta $seg.id_range_offset") - } + } */ } @@ -935,21 +934,21 @@ fn (mut tf TTF_File) read_hhea_table() { /*version :=*/ tf.get_fixed() // 0x00010000 tf.ascent = tf.get_fword() - tf.descent = tf.get_fword() - tf.line_gap = tf.get_fword() - tf.advance_width_max = tf.get_ufword() - tf.min_left_side_bearing = tf.get_fword() - tf.min_right_side_bearing = tf.get_fword() - tf.x_max_extent = tf.get_fword() - tf.caret_slope_rise = tf.get_i16() - tf.caret_slope_run = tf.get_i16() - tf.caret_offset = tf.get_fword() - tf.get_i16() // reserved - tf.get_i16() // reserved - tf.get_i16() // reserved - tf.get_i16() // reserved - tf.metric_data_format = tf.get_i16() - tf.num_of_long_hor_metrics = tf.get_u16() + tf.descent = tf.get_fword() + tf.line_gap = tf.get_fword() + tf.advance_width_max = tf.get_ufword() + tf.min_left_side_bearing = tf.get_fword() + tf.min_right_side_bearing = tf.get_fword() + tf.x_max_extent = tf.get_fword() + tf.caret_slope_rise = tf.get_i16() + tf.caret_slope_run = tf.get_i16() + tf.caret_offset = tf.get_fword() + tf.get_i16() // reserved + tf.get_i16() // reserved + tf.get_i16() // reserved + tf.get_i16() // reserved + tf.metric_data_format = tf.get_i16() + tf.num_of_long_hor_metrics = tf.get_u16() } /****************************************************************************** @@ -959,11 +958,11 @@ fn (mut tf TTF_File) read_hhea_table() { ******************************************************************************/ struct Kern0Table { mut: - swap bool - offset u32 - n_pairs int - kmap map[u32]i16 - old_index int = -1 + swap bool + offset u32 + n_pairs int + kmap map[u32]i16 + old_index int = -1 } fn (mut kt Kern0Table) reset() { @@ -1027,23 +1026,23 @@ fn (mut tf TTF_File) read_kern_table() { dprintln("Kern Table version: $version Kern nTables: $n_tables") - for _ in 0..n_tables{ - st_version := tf.get_u16() // sub table version - length := tf.get_u16() - coverage := tf.get_u16() - format := coverage >> 8 - cross := coverage & 4 - vertical := (coverage & 0x1) == 0 - dprintln("Kerning subtable version [$st_version] format [$format] length [$length] coverage: [${coverage.hex()}]") - if format == 0 { - dprintln("kern format: 0") - kern := tf.create_kern_table0(vertical, cross != 0) - tf.kern << kern - } else { - dprintln("Unknown format -- skip") - tf.pos = tf.pos + length + for _ in 0..n_tables{ + st_version := tf.get_u16() // sub table version + length := tf.get_u16() + coverage := tf.get_u16() + format := coverage >> 8 + cross := coverage & 4 + vertical := (coverage & 0x1) == 0 + dprintln("Kerning subtable version [$st_version] format [$format] length [$length] coverage: [${coverage.hex()}]") + if format == 0 { + dprintln("kern format: 0") + kern := tf.create_kern_table0(vertical, cross != 0) + tf.kern << kern + } else { + dprintln("Unknown format -- skip") + tf.pos = tf.pos + length + } } - } } fn (mut tf TTF_File) reset_kern() {