module ttf /********************************************************************** * * BMP render module utility functions * * Copyright (c) 2021 Dario Deledda. All rights reserved. * Use of this source code is governed by an MIT license * that can be found in the LICENSE file. * * Note: * * TODO: * - manage text directions R to L **********************************************************************/ import encoding.utf8 import math pub struct BitMap { pub mut: tf &TTF_File = unsafe { nil } buf &u8 = unsafe { nil } // pointer to the memory buffer buf_size int // allocated buf size in bytes width int = 1 // width of the buffer height int = 1 // height of the buffer bp int = 4 // byte per pixel of the buffer bg_color u32 = 0xFFFFFF_00 // background RGBA format color u32 = 0x000000_FF // RGBA format scale f32 = 1.0 // internal usage!! scale_x f32 = 1.0 // X scale of the single glyph scale_y f32 = 1.0 // Y scale of the single glyph angle f32 = 0.0 // angle of rotation of the bitmap // spaces space_cw f32 = 1.0 // width of the space glyph internal usage!! space_mult f32 = f32(0.0) // 1.0/16.0 // space between letter, is a multiplier for a standrd space ax // used only by internal text rendering!! tr_matrix []f32 = [f32(1), 0, 0, 0, 1, 0, 0, 0, 0] // transformation matrix ch_matrix []f32 = [f32(1), 0, 0, 0, 1, 0, 0, 0, 0] // character matrix style Style = .filled // default syle align Text_align = .left // default text align justify bool // justify text flag, default deactivated justify_fill_ratio f32 = 0.5 // justify fill ratio, if the ratio of the filled row is >= of this then justify the text filler [][]int // filler buffer for the renderer // flag to force font embedded metrics use_font_metrics bool } /****************************************************************************** * * Utility * ******************************************************************************/ // clear clear the bitmap with 0 bytes pub fn (mut bmp BitMap) clear() { mut sz := bmp.width * bmp.height * bmp.bp unsafe { C.memset(bmp.buf, 0x00, sz) } } // transform matrix applied to the text pub fn (bmp &BitMap) trf_txt(p &Point) (int, int) { return int(p.x * bmp.tr_matrix[0] + p.y * bmp.tr_matrix[3] + bmp.tr_matrix[6]), int( p.x * bmp.tr_matrix[1] + p.y * bmp.tr_matrix[4] + bmp.tr_matrix[7]) } // transform matrix applied to the char pub fn (bmp &BitMap) trf_ch(p &Point) (int, int) { return int(p.x * bmp.ch_matrix[0] + p.y * bmp.ch_matrix[3] + bmp.ch_matrix[6]), int( p.x * bmp.ch_matrix[1] + p.y * bmp.ch_matrix[4] + bmp.ch_matrix[7]) } // set draw postion in the buffer pub fn (mut bmp BitMap) set_pos(x f32, y f32) { bmp.tr_matrix[6] = x bmp.tr_matrix[7] = y } // set the rotation angle in radiants pub fn (mut bmp BitMap) set_rotation(a f32) { bmp.tr_matrix[0] = f32(math.cos(a)) // 1 bmp.tr_matrix[1] = f32(-math.sin(a)) // 0 bmp.tr_matrix[3] = f32(math.sin(a)) // 0 bmp.tr_matrix[4] = f32(math.cos(a)) // 1 } /****************************************************************************** * * Filler functions * ******************************************************************************/ pub fn (mut bmp BitMap) init_filler() { h := bmp.height - bmp.filler.len if h < 1 { return } for _ in 0 .. h { bmp.filler << []int{len: 4} } // dprintln("Init filler: ${bmp.filler.len} rows") } pub fn (mut bmp BitMap) clear_filler() { for i in 0 .. bmp.height { bmp.filler[i].clear() } } pub fn (mut bmp BitMap) exec_filler() { for y in 0 .. bmp.height { if bmp.filler[y].len > 0 { bmp.filler[y].sort() if bmp.filler[y].len & 1 != 0 { // dprintln("even line!! $y => ${bmp.filler[y]}") continue } mut index := 0 for index < bmp.filler[y].len { startx := bmp.filler[y][index] + 1 endx := bmp.filler[y][index + 1] if startx >= endx { index += 2 continue } for x in startx .. endx { bmp.plot(x, y, bmp.color) } index += 2 } } } } pub fn (mut bmp BitMap) fline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { mut x0 := f32(in_x0) mut x1 := f32(in_x1) mut y0 := f32(in_y0) mut y1 := f32(in_y1) mut tmp := f32(0) // check bounds if (in_x0 < 0 && in_x1 < 0) || (in_x0 > bmp.width && in_x1 > bmp.width) { return } if y1 < y0 { tmp = x0 x0 = x1 x1 = tmp tmp = y0 y0 = y1 y1 = tmp } mut dx := x1 - x0 mut dy := y1 - y0 if dy == 0 { if in_y0 >= 0 && in_y0 < bmp.filler.len { if in_x0 <= in_x1 { bmp.filler[in_y0] << in_x0 bmp.filler[in_y0] << in_x1 } else { bmp.filler[in_y0] << in_x1 bmp.filler[in_y0] << in_x0 } } return } mut n := dx / dy for y in 0 .. int(dy + 0.5) { yd := int(y + y0) x := n * y + x0 if x > bmp.width || yd >= bmp.filler.len { break } if yd >= 0 && yd < bmp.filler.len { bmp.filler[yd] << int(x + 0.5) // bmp.plot(int(x+0.5), yd, bmp.color) } } } /****************************************************************************** * * Draw functions * ******************************************************************************/ [inline] pub fn (mut bmp BitMap) plot(x int, y int, c u32) bool { if x < 0 || x >= bmp.width || y < 0 || y >= bmp.height { return false } mut index := (x + y * bmp.width) * bmp.bp unsafe { // bmp.buf[index]=0xFF bmp.buf[index] = u8(c & 0xFF) // write only the alpha } /* for count in 0..(bmp.bp) { unsafe{ bmp.buf[index + count] = u8((c >> (bmp.bp - count - 1) * 8) & 0x0000_00FF) } } */ return true } /****************************************************************************** * * smooth draw functions * ******************************************************************************/ // aline draw an aliased line on the bitmap pub fn (mut bmp BitMap) aline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { // mut c1 := c mut x0 := f32(in_x0) mut x1 := f32(in_x1) mut y0 := f32(in_y0) mut y1 := f32(in_y1) mut tmp := f32(0) mut dx := x1 - x0 mut dy := y1 - y0 dist := f32(0.4) if math.abs(dx) > math.abs(dy) { if x1 < x0 { tmp = x0 x0 = x1 x1 = tmp tmp = y0 y0 = y1 y1 = tmp } dx = x1 - x0 dy = y1 - y0 x0 += 0.5 y0 += 0.5 m := dy / dx mut x := x0 for x <= x1 + 0.5 { y := m * (x - x0) + y0 e := 1 - math.abs(y - 0.5 - int(y)) bmp.plot(int(x), int(y), color_multiply_alpha(c, e * 0.75)) ys1 := y + dist if int(ys1) != int(y) { v1 := math.abs(ys1 - y) / dist * (1 - e) bmp.plot(int(x), int(ys1), color_multiply_alpha(c, v1)) } ys2 := y - dist if int(ys2) != int(y) { v2 := math.abs(y - ys2) / dist * (1 - e) bmp.plot(int(x), int(ys2), color_multiply_alpha(c, v2)) } x += 1.0 } } else { if y1 < y0 { tmp = x0 x0 = x1 x1 = tmp tmp = y0 y0 = y1 y1 = tmp } dx = x1 - x0 dy = y1 - y0 x0 += 0.5 y0 += 0.5 n := dx / dy mut y := y0 for y <= y1 + 0.5 { x := n * (y - y0) + x0 e := f32(1 - math.abs(x - 0.5 - int(x))) bmp.plot(int(x), int(y), color_multiply_alpha(c, f32(e * 0.75))) xs1 := x + dist if int(xs1) != int(x) { v1 := math.abs(xs1 - x) / dist * (1 - e) bmp.plot(int(xs1), int(y), color_multiply_alpha(c, f32(v1))) } xs2 := x - dist if int(xs2) != int(x) { v2 := math.abs(x - xs1) / dist * (1 - e) bmp.plot(int(xs2), int(y), color_multiply_alpha(c, f32(v2))) } y += 1.0 } } } /****************************************************************************** * * draw functions * ******************************************************************************/ pub fn (mut bmp BitMap) line(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { // outline with aliased borders if bmp.style == .outline_aliased { bmp.aline(in_x0, in_y0, in_x1, in_y1, c) return } // filled with aliased borders else if bmp.style == .filled { bmp.aline(in_x0, in_y0, in_x1, in_y1, c) bmp.fline(in_x0, in_y0, in_x1, in_y1, c) return } // only the filler is drawn else if bmp.style == .raw { bmp.fline(in_x0, in_y0, in_x1, in_y1, c) return } // if we are here we are drawing an outlined border x0 := int(in_x0) x1 := int(in_x1) y0 := int(in_y0) y1 := int(in_y1) // dprintln("line[$x0,$y0,$x1,$y1]") mut x := x0 mut y := y0 dx := math.abs(x1 - x0) sx := if x0 < x1 { 1 } else { -1 } dy := -math.abs(y1 - y0) sy := if y0 < y1 { 1 } else { -1 } // verical line if dx == 0 { if y0 < y1 { for yt in y0 .. y1 + 1 { bmp.plot(x0, yt, c) } return } for yt in y1 .. y0 + 1 { bmp.plot(x0, yt, c) } // horizontal line return } else if dy == 0 { if x0 < x1 { for xt in x0 .. x1 + 1 { bmp.plot(xt, y0, c) } return } for xt in x1 .. x0 + 1 { bmp.plot(xt, y0, c) } return } mut err := dx + dy // error value e_xy for { // bmp.plot(x, y, u32(0xFF00)) bmp.plot(x, y, c) // dprintln("$x $y [$x0,$y0,$x1,$y1]") if x == x1 && y == y1 { break } e2 := 2 * err if e2 >= dy { // e_xy+e_x > 0 err += dy x += sx } if e2 <= dx { // e_xy+e_y < 0 err += dx y += sy } } } pub fn (mut bmp BitMap) box(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { bmp.line(in_x0, in_y0, in_x1, in_y0, c) bmp.line(in_x1, in_y0, in_x1, in_y1, c) bmp.line(in_x0, in_y1, in_x1, in_y1, c) bmp.line(in_x0, in_y0, in_x0, in_y1, c) } pub fn (mut bmp BitMap) quadratic(in_x0 int, in_y0 int, in_x1 int, in_y1 int, in_cx int, in_cy int, c u32) { /* x0 := int(in_x0 * bmp.scale) x1 := int(in_x1 * bmp.scale) y0 := int(in_y0 * bmp.scale) y1 := int(in_y1 * bmp.scale) cx := int(in_cx * bmp.scale) cy := int(in_cy * bmp.scale) */ x0 := int(in_x0) x1 := int(in_x1) y0 := int(in_y0) y1 := int(in_y1) cx := int(in_cx) cy := int(in_cy) mut division := f64(1.0) dx := math.abs(x0 - x1) dy := math.abs(y0 - y1) // if few pixel draw a simple line // if dx == 0 && dy == 0 { if dx <= 2 || dy <= 2 { // bmp.plot(x0, y0, c) bmp.line(x0, y0, x1, y1, c) return } division = 1.0 / (f64(if dx > dy { dx } else { dy })) // division = 0.1 // 10 division // division = 0.25 // 4 division // dprintln("div: $division") /* ----- Bezier quadratic form ----- t = 0.5; // given example value, half length of the curve x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x; y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y; --------------------------------- */ mut x_old := x0 mut y_old := y0 mut t := 0.0 for t <= (1.0 + division / 2.0) { s := 1.0 - t x := s * s * x0 + 2.0 * s * t * cx + t * t * x1 y := s * s * y0 + 2.0 * s * t * cy + t * t * y1 xi := int(x + 0.5) yi := int(y + 0.5) // bmp.plot(xi, yi, c) bmp.line(x_old, y_old, xi, yi, c) x_old = xi y_old = yi t += division } } /****************************************************************************** * * TTF Query functions * ******************************************************************************/ pub fn (mut bmp BitMap) get_chars_bbox(in_string string) []int { mut res := []int{} mut w := 0 mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `)) div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale) space_cw = int(space_cw * bmp.scale) bmp.tf.reset_kern() mut i := 0 for i < in_string.len { mut chr := u16(in_string[i]) // draw the space if int(chr) == 32 { w += int(space_cw * bmp.space_cw) i++ continue } // manage unicode chars like latin greek etc c_len := ((0xe5000000 >> ((chr >> 3) & 0x1e)) & 3) + 1 if c_len > 1 { tmp_char := utf8.get_uchar(in_string, i) // dprintln("tmp_char: ${tmp_char.hex()}") chr = u16(tmp_char) } c_index := bmp.tf.map_code(int(chr)) // 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") // cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr)) // dprintln("metrics: [${u16(chr):c}] cw:$cw lsb:$lsb") //----- Calc 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) // x_min, x_max, y_min, y_max := bmp.tf.read_glyph_dim(c_index) x_min, x_max, _, _ := bmp.tf.read_glyph_dim(c_index) //----------------- width := int((math.abs(x_max + x_min) + ax) * bmp.scale) // width := int((cw+ax) * bmp.scale) w += width + div_space_cw h := int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) res << w res << h i += c_len } return res } pub fn (mut bmp BitMap) get_bbox(in_string string) (int, int) { mut w := 0 mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `)) div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale) space_cw = int(space_cw * bmp.scale) bmp.tf.reset_kern() mut i := 0 for i < in_string.len { mut chr := u16(in_string[i]) // draw the space if int(chr) == 32 { w += int(space_cw * bmp.space_cw) i++ continue } // manage unicode chars like latin greek etc c_len := ((0xe5000000 >> ((chr >> 3) & 0x1e)) & 3) + 1 if c_len > 1 { tmp_char := utf8.get_uchar(in_string, i) // dprintln("tmp_char: ${tmp_char.hex()}") chr = u16(tmp_char) } c_index := bmp.tf.map_code(int(chr)) // 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") // cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr)) // dprintln("metrics: [${u16(chr):c}] cw:$cw lsb:$lsb") //----- Calc 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) x_min, x_max, _, _ := bmp.tf.read_glyph_dim(c_index) // x_min := 1 // x_max := 2 //----------------- width := int((math.abs(x_max + x_min) + ax) * bmp.scale) // 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(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) } /****************************************************************************** * * 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 := math.abs(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 mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `)) div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale) space_cw = int(space_cw * bmp.scale) bmp.tf.reset_kern() mut i := 0 for i < in_string.len { mut chr := u16(in_string[i]) // draw the space if int(chr) == 32 { w += int(space_cw * bmp.space_cw) i++ continue } // manage unicode chars like latin greek etc c_len := ((0xe5000000 >> ((chr >> 3) & 0x1e)) & 3) + 1 if c_len > 1 { tmp_char := utf8.get_uchar(in_string, i) // dprintln("tmp_char: ${tmp_char.hex()}") chr = u16(tmp_char) } c_index := bmp.tf.map_code(int(chr)) // 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 } ax, ay := bmp.tf.next_kern(c_index) // dprintln("char_index: $c_index ax: $ax ay: $ay") cw, _ := bmp.tf.get_horizontal_metrics(u16(chr)) // cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr)) // dprintln("metrics: [${u16(chr):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) x_min, x_max := bmp.draw_glyph(c_index) // x_min := 1 // x_max := 2 //----------------- mut width := int((math.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(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) } pub fn (mut bmp BitMap) draw_glyph(index u16) (int, int) { glyph := bmp.tf.read_glyph(index) if !glyph.valid_glyph { return 0, 0 } if bmp.style == .filled || bmp.style == .raw { bmp.clear_filler() } mut s := 0 // status mut c := 0 // contours count mut contour_start := 0 mut x0 := 0 mut y0 := 0 color := bmp.color // u32(0xFFFF_FF00) // RGBA white // color1 := u32(0xFF00_0000) // RGBA red // color2 := u32(0x00FF_0000) // RGBA green mut sp_x := 0 mut sp_y := 0 mut point := Point{} for count, point_raw in glyph.points { // dprintln("count: $count, state: $s pl:$glyph.points.len") point.x = point_raw.x point.y = point_raw.y point.x, point.y = bmp.trf_ch(point) point.on_curve = point_raw.on_curve if s == 0 { x0 = point.x y0 = point.y sp_x = x0 sp_y = y0 s = 1 // next state continue } else if s == 1 { if point.on_curve { bmp.line(x0, y0, point.x, point.y, color) // bmp.aline(x0, y0, point.x, point.y, u32(0xFFFF0000)) x0 = point.x y0 = point.y } else { s = 2 } } else { // dprintln("s==2") mut prev := glyph.points[count - 1] prev.x, prev.y = bmp.trf_ch(prev) if point.on_curve { // dprintln("HERE1") // ctx.quadraticCurveTo(prev.x + x, prev.y + y,point.x + x, point.y + y); // bmp.line(x0, y0, point.x + in_x, point.y + in_y, color1) // bmp.quadratic(x0, y0, point.x + in_x, point.y + in_y, prev.x + in_x, prev.y + in_y, u32(0xa0a00000)) bmp.quadratic(x0, y0, point.x, point.y, prev.x, prev.y, color) x0 = point.x y0 = point.y s = 1 } 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 y0 = (prev.y + point.y) / 2 } } if count == glyph.contour_ends[c] { // 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 { // 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)) // start_point.x + in_x, start_point.y + in_y, u32(0xFF00FF00)) bmp.quadratic(x0, y0, start_point.x, start_point.y, 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) // u32(0xFF000000)) bmp.quadratic(x0, y0, start_point.x, start_point.y, (point.x + start_point.x) / 2, (point.y + start_point.y) / 2, color) } } 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) } contour_start = count + 1 s = 0 c++ } } if bmp.style == .filled || bmp.style == .raw { bmp.exec_filler() } x_min := glyph.x_min x_max := glyph.x_max return x_min, x_max // return glyph.x_min, glyph.x_max }