| 1 | module ttf |
| 2 | |
| 3 | /********************************************************************** |
| 4 | * |
| 5 | * BMP render module utility functions |
| 6 | * |
| 7 | * Copyright (c) 2021 Dario Deledda. All rights reserved. |
| 8 | * Use of this source code is governed by an MIT license |
| 9 | * that can be found in the LICENSE file. |
| 10 | * |
| 11 | * Note: |
| 12 | * |
| 13 | * TODO: |
| 14 | * - manage text directions R to L |
| 15 | **********************************************************************/ |
| 16 | import encoding.utf8 |
| 17 | import math |
| 18 | |
| 19 | // BitMap represents a bitmap image of text rendered with the font supplied via the `tf` field. |
| 20 | pub struct BitMap { |
| 21 | pub mut: |
| 22 | tf &TTF_File = unsafe { nil } |
| 23 | buf &u8 = unsafe { nil } // pointer to the memory buffer |
| 24 | buf_size int // allocated buf size in bytes |
| 25 | width int = 1 // width of the buffer |
| 26 | height int = 1 // height of the buffer |
| 27 | bp int = 4 // byte per pixel of the buffer |
| 28 | bg_color u32 = 0xFFFFFF_00 // background RGBA format |
| 29 | color u32 = 0x000000_FF // RGBA format |
| 30 | scale f32 = 1.0 // internal usage!! |
| 31 | scale_x f32 = 1.0 // X scale of the single glyph |
| 32 | scale_y f32 = 1.0 // Y scale of the single glyph |
| 33 | angle f32 = 0.0 // angle of rotation of the bitmap |
| 34 | // spaces |
| 35 | space_cw f32 = 1.0 // width of the space glyph internal usage!! |
| 36 | space_mult f32 = f32(0.0) // 1.0/16.0 // space between letter, is a multiplier for a standard space ax |
| 37 | // used only by internal text rendering!! |
| 38 | tr_matrix []f32 = [f32(1), 0, 0, 0, 1, 0, 0, 0, 0] // transformation matrix |
| 39 | ch_matrix []f32 = [f32(1), 0, 0, 0, 1, 0, 0, 0, 0] // character matrix |
| 40 | style Style = .filled // default style |
| 41 | align Text_align = .left // default text align |
| 42 | justify bool // justify text flag, default deactivated |
| 43 | justify_fill_ratio f32 = 0.5 // justify fill ratio, if the ratio of the filled row is >= of this then justify the text |
| 44 | filler [][]int // filler buffer for the renderer |
| 45 | // flag to force font embedded metrics |
| 46 | use_font_metrics bool |
| 47 | } |
| 48 | |
| 49 | /****************************************************************************** |
| 50 | * |
| 51 | * Utility |
| 52 | * |
| 53 | ******************************************************************************/ |
| 54 | // clear clears the bitmap with 0 bytes. |
| 55 | pub fn (mut bmp BitMap) clear() { |
| 56 | mut sz := bmp.width * bmp.height * bmp.bp |
| 57 | unsafe { |
| 58 | vmemset(bmp.buf, 0x00, sz) |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // trf_txt returns the transform matrix applied to the text. |
| 63 | pub fn (bmp &BitMap) trf_txt(p &Point) (int, int) { |
| 64 | return int(p.x * bmp.tr_matrix[0] + p.y * bmp.tr_matrix[3] + bmp.tr_matrix[6]), int( |
| 65 | p.x * bmp.tr_matrix[1] + p.y * bmp.tr_matrix[4] + bmp.tr_matrix[7]) |
| 66 | } |
| 67 | |
| 68 | // trf_ch returns the transform matrix applied to the char. |
| 69 | pub fn (bmp &BitMap) trf_ch(p &Point) (int, int) { |
| 70 | return int(p.x * bmp.ch_matrix[0] + p.y * bmp.ch_matrix[3] + bmp.ch_matrix[6]), int( |
| 71 | p.x * bmp.ch_matrix[1] + p.y * bmp.ch_matrix[4] + bmp.ch_matrix[7]) |
| 72 | } |
| 73 | |
| 74 | // set_pos sets the draw position in the buffer. |
| 75 | pub fn (mut bmp BitMap) set_pos(x f32, y f32) { |
| 76 | bmp.tr_matrix[6] = x |
| 77 | bmp.tr_matrix[7] = y |
| 78 | } |
| 79 | |
| 80 | // set_rotation sets the rotation angle in radians `a`. |
| 81 | pub fn (mut bmp BitMap) set_rotation(a f32) { |
| 82 | bmp.tr_matrix[0] = f32(math.cos(a)) // 1 |
| 83 | bmp.tr_matrix[1] = f32(-math.sin(a)) // 0 |
| 84 | bmp.tr_matrix[3] = f32(math.sin(a)) // 0 |
| 85 | bmp.tr_matrix[4] = f32(math.cos(a)) // 1 |
| 86 | } |
| 87 | |
| 88 | /****************************************************************************** |
| 89 | * |
| 90 | * Filler functions |
| 91 | * |
| 92 | ******************************************************************************/ |
| 93 | // init_filler initializes the internal `filler` buffer. |
| 94 | pub fn (mut bmp BitMap) init_filler() { |
| 95 | h := bmp.height - bmp.filler.len |
| 96 | if h < 1 { |
| 97 | return |
| 98 | } |
| 99 | for _ in 0 .. h { |
| 100 | bmp.filler << []int{len: 4} |
| 101 | } |
| 102 | // dprintln("Init filler: ${bmp.filler.len} rows") |
| 103 | } |
| 104 | |
| 105 | // clear_filler clears the internal `filler` buffer. |
| 106 | pub fn (mut bmp BitMap) clear_filler() { |
| 107 | for i in 0 .. bmp.height { |
| 108 | bmp.filler[i].clear() |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | // exec_filler plots the pixels of the `BitMap` to the internal buffer. |
| 113 | pub fn (mut bmp BitMap) exec_filler() { |
| 114 | for y in 0 .. bmp.height { |
| 115 | if bmp.filler[y].len > 0 { |
| 116 | bmp.filler[y].sort() |
| 117 | if bmp.filler[y].len & 1 != 0 { |
| 118 | // dprintln("even line!! ${y} => ${bmp.filler[y]}") |
| 119 | continue |
| 120 | } |
| 121 | mut index := 0 |
| 122 | for index < bmp.filler[y].len { |
| 123 | startx := bmp.filler[y][index] + 1 |
| 124 | endx := bmp.filler[y][index + 1] |
| 125 | if startx >= endx { |
| 126 | index += 2 |
| 127 | continue |
| 128 | } |
| 129 | for x in startx .. endx { |
| 130 | bmp.plot(x, y, bmp.color) |
| 131 | } |
| 132 | index += 2 |
| 133 | } |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // fline populates the internal `filler` buffer with a line segment from `in_x0`,`in_y0` to `in_x1`,`in_y1`. |
| 139 | pub fn (mut bmp BitMap) fline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { |
| 140 | mut x0 := f32(in_x0) |
| 141 | mut x1 := f32(in_x1) |
| 142 | mut y0 := f32(in_y0) |
| 143 | mut y1 := f32(in_y1) |
| 144 | mut tmp := f32(0) |
| 145 | |
| 146 | // check bounds |
| 147 | if (in_x0 < 0 && in_x1 < 0) || (in_x0 > bmp.width && in_x1 > bmp.width) { |
| 148 | return |
| 149 | } |
| 150 | |
| 151 | if y1 < y0 { |
| 152 | tmp = x0 |
| 153 | x0 = x1 |
| 154 | x1 = tmp |
| 155 | |
| 156 | tmp = y0 |
| 157 | y0 = y1 |
| 158 | y1 = tmp |
| 159 | } |
| 160 | |
| 161 | mut dx := x1 - x0 |
| 162 | mut dy := y1 - y0 |
| 163 | |
| 164 | if dy == 0 { |
| 165 | if in_y0 >= 0 && in_y0 < bmp.filler.len { |
| 166 | if in_x0 <= in_x1 { |
| 167 | bmp.filler[in_y0] << in_x0 |
| 168 | bmp.filler[in_y0] << in_x1 |
| 169 | } else { |
| 170 | bmp.filler[in_y0] << in_x1 |
| 171 | bmp.filler[in_y0] << in_x0 |
| 172 | } |
| 173 | } |
| 174 | return |
| 175 | } |
| 176 | mut n := dx / dy |
| 177 | for y in 0 .. int(dy + 0.5) { |
| 178 | yd := int(y + y0) |
| 179 | x := n * y + x0 |
| 180 | if x > bmp.width || yd >= bmp.filler.len { |
| 181 | break |
| 182 | } |
| 183 | if yd >= 0 && yd < bmp.filler.len { |
| 184 | bmp.filler[yd] << int(x + 0.5) |
| 185 | // bmp.plot(int(x+0.5), yd, bmp.color) |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | /****************************************************************************** |
| 191 | * |
| 192 | * Draw functions |
| 193 | * |
| 194 | ******************************************************************************/ |
| 195 | |
| 196 | // plot plots a pixel at `x`,`y` in color `c` in the internal bitmap buffer. |
| 197 | @[inline] |
| 198 | pub fn (mut bmp BitMap) plot(x int, y int, c u32) bool { |
| 199 | if x < 0 || x >= bmp.width || y < 0 || y >= bmp.height { |
| 200 | return false |
| 201 | } |
| 202 | mut index := (x + y * bmp.width) * bmp.bp |
| 203 | unsafe { |
| 204 | // bmp.buf[index]=0xFF |
| 205 | bmp.buf[index] = u8(c & 0xFF) // write only the alpha |
| 206 | } |
| 207 | /* |
| 208 | for count in 0..(bmp.bp) { |
| 209 | unsafe{ |
| 210 | bmp.buf[index + count] = u8((c >> (bmp.bp - count - 1) * 8) & 0x0000_00FF) |
| 211 | } |
| 212 | } |
| 213 | */ |
| 214 | return true |
| 215 | } |
| 216 | |
| 217 | /****************************************************************************** |
| 218 | * |
| 219 | * smooth draw functions |
| 220 | * |
| 221 | ******************************************************************************/ |
| 222 | // aline draws an aliased line on the bitmap. |
| 223 | pub fn (mut bmp BitMap) aline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { |
| 224 | // mut c1 := c |
| 225 | mut x0 := f32(in_x0) |
| 226 | mut x1 := f32(in_x1) |
| 227 | mut y0 := f32(in_y0) |
| 228 | mut y1 := f32(in_y1) |
| 229 | mut tmp := f32(0) |
| 230 | |
| 231 | mut dx := x1 - x0 |
| 232 | mut dy := y1 - y0 |
| 233 | |
| 234 | dist := f32(0.4) |
| 235 | |
| 236 | if math.abs(dx) > math.abs(dy) { |
| 237 | if x1 < x0 { |
| 238 | tmp = x0 |
| 239 | x0 = x1 |
| 240 | x1 = tmp |
| 241 | |
| 242 | tmp = y0 |
| 243 | y0 = y1 |
| 244 | y1 = tmp |
| 245 | } |
| 246 | dx = x1 - x0 |
| 247 | dy = y1 - y0 |
| 248 | |
| 249 | x0 += 0.5 |
| 250 | y0 += 0.5 |
| 251 | |
| 252 | m := dy / dx |
| 253 | mut x := x0 |
| 254 | for x <= x1 + 0.5 { |
| 255 | y := m * (x - x0) + y0 |
| 256 | e := 1 - math.abs(y - 0.5 - int(y)) |
| 257 | bmp.plot(int(x), int(y), color_multiply_alpha(c, e * 0.75)) |
| 258 | |
| 259 | ys1 := y + dist |
| 260 | if int(ys1) != int(y) { |
| 261 | v1 := math.abs(ys1 - y) / dist * (1 - e) |
| 262 | bmp.plot(int(x), int(ys1), color_multiply_alpha(c, v1)) |
| 263 | } |
| 264 | |
| 265 | ys2 := y - dist |
| 266 | if int(ys2) != int(y) { |
| 267 | v2 := math.abs(y - ys2) / dist * (1 - e) |
| 268 | bmp.plot(int(x), int(ys2), color_multiply_alpha(c, v2)) |
| 269 | } |
| 270 | |
| 271 | x += 1.0 |
| 272 | } |
| 273 | } else { |
| 274 | if y1 < y0 { |
| 275 | tmp = x0 |
| 276 | x0 = x1 |
| 277 | x1 = tmp |
| 278 | |
| 279 | tmp = y0 |
| 280 | y0 = y1 |
| 281 | y1 = tmp |
| 282 | } |
| 283 | dx = x1 - x0 |
| 284 | dy = y1 - y0 |
| 285 | |
| 286 | x0 += 0.5 |
| 287 | y0 += 0.5 |
| 288 | |
| 289 | n := dx / dy |
| 290 | mut y := y0 |
| 291 | for y <= y1 + 0.5 { |
| 292 | x := n * (y - y0) + x0 |
| 293 | if !math.is_nan(x) && !math.is_nan(y) { |
| 294 | e := f32(1 - math.abs(x - 0.5 - int(x))) |
| 295 | bmp.plot(int(x), int(y), color_multiply_alpha(c, f32(e * 0.75))) |
| 296 | |
| 297 | xs1 := x + dist |
| 298 | if int(xs1) != int(x) { |
| 299 | v1 := math.abs(xs1 - x) / dist * (1 - e) |
| 300 | bmp.plot(int(xs1), int(y), color_multiply_alpha(c, f32(v1))) |
| 301 | } |
| 302 | |
| 303 | xs2 := x - dist |
| 304 | if int(xs2) != int(x) { |
| 305 | v2 := math.abs(x - xs1) / dist * (1 - e) |
| 306 | bmp.plot(int(xs2), int(y), color_multiply_alpha(c, f32(v2))) |
| 307 | } |
| 308 | } |
| 309 | y += 1.0 |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | /****************************************************************************** |
| 315 | * |
| 316 | * draw functions |
| 317 | * |
| 318 | ******************************************************************************/ |
| 319 | // line plots a line segment to the internal buffer from `in_x0`,`in_y0` to `in_x1`,`in_y1` in the color `c`. |
| 320 | pub fn (mut bmp BitMap) line(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { |
| 321 | // outline with aliased borders |
| 322 | if bmp.style == .outline_aliased { |
| 323 | bmp.aline(in_x0, in_y0, in_x1, in_y1, c) |
| 324 | return |
| 325 | } |
| 326 | // filled with aliased borders |
| 327 | else if bmp.style == .filled { |
| 328 | bmp.aline(in_x0, in_y0, in_x1, in_y1, c) |
| 329 | bmp.fline(in_x0, in_y0, in_x1, in_y1, c) |
| 330 | return |
| 331 | } |
| 332 | // only the filler is drawn |
| 333 | else if bmp.style == .raw { |
| 334 | bmp.fline(in_x0, in_y0, in_x1, in_y1, c) |
| 335 | return |
| 336 | } |
| 337 | // if we are here we are drawing an outlined border |
| 338 | |
| 339 | x0 := int(in_x0) |
| 340 | x1 := int(in_x1) |
| 341 | y0 := int(in_y0) |
| 342 | y1 := int(in_y1) |
| 343 | // dprintln("line[${x0},${y0},${x1},${y1}]") |
| 344 | |
| 345 | mut x := x0 |
| 346 | mut y := y0 |
| 347 | |
| 348 | dx := math.abs(x1 - x0) |
| 349 | sx := if x0 < x1 { 1 } else { -1 } |
| 350 | dy := -math.abs(y1 - y0) |
| 351 | sy := if y0 < y1 { 1 } else { -1 } |
| 352 | |
| 353 | // vertical line |
| 354 | if dx == 0 { |
| 355 | if y0 < y1 { |
| 356 | for yt in y0 .. y1 + 1 { |
| 357 | bmp.plot(x0, yt, c) |
| 358 | } |
| 359 | return |
| 360 | } |
| 361 | for yt in y1 .. y0 + 1 { |
| 362 | bmp.plot(x0, yt, c) |
| 363 | } |
| 364 | // horizontal line |
| 365 | return |
| 366 | } else if dy == 0 { |
| 367 | if x0 < x1 { |
| 368 | for xt in x0 .. x1 + 1 { |
| 369 | bmp.plot(xt, y0, c) |
| 370 | } |
| 371 | return |
| 372 | } |
| 373 | for xt in x1 .. x0 + 1 { |
| 374 | bmp.plot(xt, y0, c) |
| 375 | } |
| 376 | return |
| 377 | } |
| 378 | |
| 379 | mut err := dx + dy // error value e_xy |
| 380 | for { |
| 381 | // bmp.plot(x, y, u32(0xFF00)) |
| 382 | bmp.plot(x, y, c) |
| 383 | |
| 384 | // dprintln("${x} ${y} [${x0},${y0},${x1},${y1}]") |
| 385 | if x == x1 && y == y1 { |
| 386 | break |
| 387 | } |
| 388 | e2 := 2 * err |
| 389 | if e2 >= dy { // e_xy+e_x > 0 |
| 390 | err += dy |
| 391 | x += sx |
| 392 | } |
| 393 | if e2 <= dx { // e_xy+e_y < 0 |
| 394 | err += dx |
| 395 | y += sy |
| 396 | } |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | // box plots a (hollow) box to the internal buffer from top-left `in_x0`, `in_y0` to bottom right `in_x1`, `in_y1` in color `c`. |
| 401 | pub fn (mut bmp BitMap) box(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) { |
| 402 | bmp.line(in_x0, in_y0, in_x1, in_y0, c) |
| 403 | bmp.line(in_x1, in_y0, in_x1, in_y1, c) |
| 404 | bmp.line(in_x0, in_y1, in_x1, in_y1, c) |
| 405 | bmp.line(in_x0, in_y0, in_x0, in_y1, c) |
| 406 | } |
| 407 | |
| 408 | // quadratic plots a quadratic Bezier curve in color `c`. |
| 409 | 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) { |
| 410 | /* |
| 411 | x0 := int(in_x0 * bmp.scale) |
| 412 | x1 := int(in_x1 * bmp.scale) |
| 413 | y0 := int(in_y0 * bmp.scale) |
| 414 | y1 := int(in_y1 * bmp.scale) |
| 415 | cx := int(in_cx * bmp.scale) |
| 416 | cy := int(in_cy * bmp.scale) |
| 417 | */ |
| 418 | x0 := int(in_x0) |
| 419 | x1 := int(in_x1) |
| 420 | y0 := int(in_y0) |
| 421 | y1 := int(in_y1) |
| 422 | cx := int(in_cx) |
| 423 | cy := int(in_cy) |
| 424 | |
| 425 | mut division := f64(1.0) |
| 426 | dx := math.abs(x0 - x1) |
| 427 | dy := math.abs(y0 - y1) |
| 428 | |
| 429 | // if few pixel draw a simple line |
| 430 | // if dx == 0 && dy == 0 { |
| 431 | if dx <= 2 || dy <= 2 { |
| 432 | // bmp.plot(x0, y0, c) |
| 433 | bmp.line(x0, y0, x1, y1, c) |
| 434 | return |
| 435 | } |
| 436 | |
| 437 | division = 1.0 / (f64(if dx > dy { dx } else { dy })) |
| 438 | |
| 439 | // division = 0.1 // 10 division |
| 440 | // division = 0.25 // 4 division |
| 441 | |
| 442 | // dprintln("div: ${division}") |
| 443 | |
| 444 | /* |
| 445 | ----- Bezier quadratic form ----- |
| 446 | t = 0.5; // given example value, half length of the curve |
| 447 | x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x; |
| 448 | y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y; |
| 449 | --------------------------------- |
| 450 | */ |
| 451 | |
| 452 | mut x_old := x0 |
| 453 | mut y_old := y0 |
| 454 | mut t := 0.0 |
| 455 | |
| 456 | for t <= (1.0 + division / 2.0) { |
| 457 | s := 1.0 - t |
| 458 | x := s * s * x0 + 2.0 * s * t * cx + t * t * x1 |
| 459 | y := s * s * y0 + 2.0 * s * t * cy + t * t * y1 |
| 460 | xi := int(x + 0.5) |
| 461 | yi := int(y + 0.5) |
| 462 | // bmp.plot(xi, yi, c) |
| 463 | bmp.line(x_old, y_old, xi, yi, c) |
| 464 | x_old = xi |
| 465 | y_old = yi |
| 466 | t += division |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | /****************************************************************************** |
| 471 | * |
| 472 | * TTF Query functions |
| 473 | * |
| 474 | ******************************************************************************/ |
| 475 | // get_chars_bbox returns all characters found in bounding box of string `in_string`. |
| 476 | pub fn (mut bmp BitMap) get_chars_bbox(in_string string) []int { |
| 477 | mut res := []int{} |
| 478 | mut w := 0 |
| 479 | |
| 480 | mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `)) |
| 481 | div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale) |
| 482 | space_cw = int(space_cw * bmp.scale) |
| 483 | |
| 484 | bmp.tf.reset_kern() |
| 485 | |
| 486 | mut i := 0 |
| 487 | for i < in_string.len { |
| 488 | mut chr := u16(in_string[i]) |
| 489 | |
| 490 | // draw the space |
| 491 | if int(chr) == 32 { |
| 492 | w += int(space_cw * bmp.space_cw) |
| 493 | i++ |
| 494 | continue |
| 495 | } |
| 496 | // manage unicode chars like latin greek etc |
| 497 | c_len := int(((u32(0xe5000000) >> ((chr >> 3) & 0x1e)) & 3) + 1) |
| 498 | if c_len > 1 { |
| 499 | tmp_char := utf8.get_rune(in_string, i) |
| 500 | // dprintln("tmp_char: ${tmp_char.hex()}") |
| 501 | chr = u16(tmp_char) |
| 502 | } |
| 503 | |
| 504 | c_index := bmp.tf.map_code(int(chr)) |
| 505 | // Glyph not found |
| 506 | if c_index == 0 { |
| 507 | w += int(space_cw * bmp.space_cw) |
| 508 | i += c_len |
| 509 | continue |
| 510 | } |
| 511 | |
| 512 | ax, ay := bmp.tf.next_kern(c_index) |
| 513 | // dprintln("char_index: ${c_index} ax: ${ax} ay: ${ay}") |
| 514 | |
| 515 | // cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr)) |
| 516 | // dprintln("metrics: [${u16(chr):c}] cw:${cw} lsb:${lsb}") |
| 517 | |
| 518 | //----- Calc Glyph transformations ----- |
| 519 | mut x0 := w + int(ax * bmp.scale) |
| 520 | mut y0 := 0 + int(ay * bmp.scale) |
| 521 | |
| 522 | p := Point{x0, y0, false} |
| 523 | x1, y1 := bmp.trf_txt(p) |
| 524 | // init ch_matrix |
| 525 | bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x |
| 526 | bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x |
| 527 | bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y |
| 528 | bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y |
| 529 | bmp.ch_matrix[6] = f32(x1) |
| 530 | bmp.ch_matrix[7] = f32(y1) |
| 531 | |
| 532 | // x_min, x_max, y_min, y_max := bmp.tf.read_glyph_dim(c_index) |
| 533 | x_min, x_max, _, _ := bmp.tf.read_glyph_dim(c_index) |
| 534 | //----------------- |
| 535 | |
| 536 | width := int((math.abs(x_max + x_min) + ax) * bmp.scale) |
| 537 | // width := int((cw+ax) * bmp.scale) |
| 538 | w += width + div_space_cw |
| 539 | h := int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) |
| 540 | res << w |
| 541 | res << h |
| 542 | |
| 543 | i += c_len |
| 544 | } |
| 545 | return res |
| 546 | } |
| 547 | |
| 548 | // get_bbox returns the bounding box (width and height) of text `in_string`. |
| 549 | pub fn (mut bmp BitMap) get_bbox(in_string string) (int, int) { |
| 550 | mut w := 0 |
| 551 | |
| 552 | mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `)) |
| 553 | div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale) |
| 554 | space_cw = int(space_cw * bmp.scale) |
| 555 | |
| 556 | bmp.tf.reset_kern() |
| 557 | |
| 558 | mut i := 0 |
| 559 | for i < in_string.len { |
| 560 | mut chr := u16(in_string[i]) |
| 561 | |
| 562 | // draw the space |
| 563 | if int(chr) == 32 { |
| 564 | w += int(space_cw * bmp.space_cw) |
| 565 | i++ |
| 566 | continue |
| 567 | } |
| 568 | // manage unicode chars like latin greek etc |
| 569 | c_len := int(((u32(0xe5000000) >> ((chr >> 3) & 0x1e)) & 3) + 1) |
| 570 | if c_len > 1 { |
| 571 | tmp_char := utf8.get_rune(in_string, i) |
| 572 | // dprintln("tmp_char: ${tmp_char.hex()}") |
| 573 | chr = u16(tmp_char) |
| 574 | } |
| 575 | |
| 576 | c_index := bmp.tf.map_code(int(chr)) |
| 577 | // Glyph not found |
| 578 | if c_index == 0 { |
| 579 | w += int(space_cw * bmp.space_cw) |
| 580 | i += c_len |
| 581 | continue |
| 582 | } |
| 583 | ax, ay := bmp.tf.next_kern(c_index) |
| 584 | // dprintln("char_index: ${c_index} ax: ${ax} ay: ${ay}") |
| 585 | |
| 586 | // cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr)) |
| 587 | // dprintln("metrics: [${u16(chr):c}] cw:${cw} lsb:${lsb}") |
| 588 | |
| 589 | //----- Calc Glyph transformations ----- |
| 590 | mut x0 := w + int(ax * bmp.scale) |
| 591 | mut y0 := 0 + int(ay * bmp.scale) |
| 592 | |
| 593 | p := Point{x0, y0, false} |
| 594 | x1, y1 := bmp.trf_txt(p) |
| 595 | // init ch_matrix |
| 596 | bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x |
| 597 | bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x |
| 598 | bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y |
| 599 | bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y |
| 600 | bmp.ch_matrix[6] = f32(x1) |
| 601 | bmp.ch_matrix[7] = f32(y1) |
| 602 | |
| 603 | x_min, x_max, _, _ := bmp.tf.read_glyph_dim(c_index) |
| 604 | // x_min := 1 |
| 605 | // x_max := 2 |
| 606 | //----------------- |
| 607 | |
| 608 | width := int((math.abs(x_max + x_min) + ax) * bmp.scale) |
| 609 | // width := int((cw+ax) * bmp.scale) |
| 610 | w += width + div_space_cw |
| 611 | |
| 612 | i += c_len |
| 613 | } |
| 614 | |
| 615 | // 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)}") |
| 616 | // 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) ) |
| 617 | return w, int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) |
| 618 | } |
| 619 | |
| 620 | /****************************************************************************** |
| 621 | * |
| 622 | * TTF draw glyph |
| 623 | * |
| 624 | ******************************************************************************/ |
| 625 | fn (mut bmp BitMap) draw_notdef_glyph(in_x int, in_w int) { |
| 626 | mut p := Point{in_x, 0, false} |
| 627 | x1, y1 := bmp.trf_txt(p) |
| 628 | // init ch_matrix |
| 629 | bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x |
| 630 | bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x |
| 631 | bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y |
| 632 | bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y |
| 633 | bmp.ch_matrix[6] = f32(x1) |
| 634 | bmp.ch_matrix[7] = f32(y1) |
| 635 | x, y := bmp.trf_ch(p) |
| 636 | |
| 637 | y_h := math.abs(bmp.tf.y_max - bmp.tf.y_min) * bmp.scale * 0.5 |
| 638 | |
| 639 | bmp.box(int(x), int(y), int(x - in_w), int(y - y_h), bmp.color) |
| 640 | bmp.line(int(x), int(y), int(x - in_w), int(y - y_h), bmp.color) |
| 641 | bmp.line(int(x - in_w), int(y), int(x), int(y - y_h), bmp.color) |
| 642 | } |
| 643 | |
| 644 | // draw_text plots the pixels of the text `in_string` to the internal buffer. |
| 645 | // It returns the text bounding box. |
| 646 | pub fn (mut bmp BitMap) draw_text(in_string string) (int, int) { |
| 647 | mut w := 0 |
| 648 | |
| 649 | mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `)) |
| 650 | div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale) |
| 651 | space_cw = int(space_cw * bmp.scale) |
| 652 | |
| 653 | bmp.tf.reset_kern() |
| 654 | |
| 655 | mut i := 0 |
| 656 | for i < in_string.len { |
| 657 | mut chr := u16(in_string[i]) |
| 658 | |
| 659 | // draw the space |
| 660 | if int(chr) == 32 { |
| 661 | w += int(space_cw * bmp.space_cw) |
| 662 | i++ |
| 663 | continue |
| 664 | } |
| 665 | // manage unicode chars like latin greek etc |
| 666 | c_len := int(((u32(0xe5000000) >> ((chr >> 3) & 0x1e)) & 3) + 1) |
| 667 | if c_len > 1 { |
| 668 | tmp_char := utf8.get_rune(in_string, i) |
| 669 | // dprintln("tmp_char: ${tmp_char.hex()}") |
| 670 | chr = u16(tmp_char) |
| 671 | } |
| 672 | |
| 673 | c_index := bmp.tf.map_code(int(chr)) |
| 674 | // Glyph not found |
| 675 | if c_index == 0 { |
| 676 | bmp.draw_notdef_glyph(w, int(space_cw * bmp.space_cw)) |
| 677 | w += int(space_cw * bmp.space_cw) |
| 678 | i += c_len |
| 679 | continue |
| 680 | } |
| 681 | |
| 682 | ax, ay := bmp.tf.next_kern(c_index) |
| 683 | // dprintln("char_index: ${c_index} ax: ${ax} ay: ${ay}") |
| 684 | |
| 685 | cw, _ := bmp.tf.get_horizontal_metrics(u16(chr)) |
| 686 | // cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr)) |
| 687 | // dprintln("metrics: [${u16(chr):c}] cw:${cw} lsb:${lsb}") |
| 688 | |
| 689 | //----- Draw_Glyph transformations ----- |
| 690 | mut x0 := w + int(ax * bmp.scale) |
| 691 | mut y0 := 0 + int(ay * bmp.scale) |
| 692 | |
| 693 | p := Point{x0, y0, false} |
| 694 | x1, y1 := bmp.trf_txt(p) |
| 695 | // init ch_matrix |
| 696 | bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x |
| 697 | bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x |
| 698 | bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y |
| 699 | bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y |
| 700 | bmp.ch_matrix[6] = f32(x1) |
| 701 | bmp.ch_matrix[7] = f32(y1) |
| 702 | |
| 703 | x_min, x_max := bmp.draw_glyph(c_index) |
| 704 | // x_min := 1 |
| 705 | // x_max := 2 |
| 706 | //----------------- |
| 707 | |
| 708 | mut width := int((math.abs(x_max + x_min) + ax) * bmp.scale) |
| 709 | if bmp.use_font_metrics { |
| 710 | width = int((cw + ax) * bmp.scale) |
| 711 | } |
| 712 | w += width + div_space_cw |
| 713 | i += c_len |
| 714 | } |
| 715 | |
| 716 | // 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)}") |
| 717 | // 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) ) |
| 718 | return w, int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale) |
| 719 | } |
| 720 | |
| 721 | // draw_glyph plots the pixels of the glyph at `index` to the internal buffer. |
| 722 | // It returns the `x_max` and `x_min` values. |
| 723 | pub fn (mut bmp BitMap) draw_glyph(index u16) (int, int) { |
| 724 | glyph := bmp.tf.read_glyph(index) |
| 725 | |
| 726 | if !glyph.valid_glyph { |
| 727 | return 0, 0 |
| 728 | } |
| 729 | |
| 730 | if bmp.style == .filled || bmp.style == .raw { |
| 731 | bmp.clear_filler() |
| 732 | } |
| 733 | |
| 734 | mut s := 0 // status |
| 735 | mut c := 0 // contours count |
| 736 | mut contour_start := 0 |
| 737 | mut x0 := 0 |
| 738 | mut y0 := 0 |
| 739 | color := bmp.color // u32(0xFFFF_FF00) // RGBA white |
| 740 | // color1 := u32(0xFF00_0000) // RGBA red |
| 741 | // color2 := u32(0x00FF_0000) // RGBA green |
| 742 | |
| 743 | mut sp_x := 0 |
| 744 | mut sp_y := 0 |
| 745 | mut point := Point{} |
| 746 | |
| 747 | for count, point_raw in glyph.points { |
| 748 | // dprintln("count: ${count}, state: ${s} pl:${glyph.points.len}") |
| 749 | point.x = point_raw.x |
| 750 | point.y = point_raw.y |
| 751 | |
| 752 | point.x, point.y = bmp.trf_ch(point) |
| 753 | point.on_curve = point_raw.on_curve |
| 754 | |
| 755 | if s == 0 { |
| 756 | x0 = point.x |
| 757 | y0 = point.y |
| 758 | sp_x = x0 |
| 759 | sp_y = y0 |
| 760 | s = 1 // next state |
| 761 | continue |
| 762 | } else if s == 1 { |
| 763 | if point.on_curve { |
| 764 | bmp.line(x0, y0, point.x, point.y, color) |
| 765 | // bmp.aline(x0, y0, point.x, point.y, u32(0xFFFF0000)) |
| 766 | x0 = point.x |
| 767 | y0 = point.y |
| 768 | } else { |
| 769 | s = 2 |
| 770 | } |
| 771 | } else { |
| 772 | // dprintln("s==2") |
| 773 | mut prev := glyph.points[count - 1] |
| 774 | prev.x, prev.y = bmp.trf_ch(prev) |
| 775 | if point.on_curve { |
| 776 | // dprintln("HERE1") |
| 777 | // ctx.quadraticCurveTo(prev.x + x, prev.y + y,point.x + x, point.y + y); |
| 778 | // bmp.line(x0, y0, point.x + in_x, point.y + in_y, color1) |
| 779 | // bmp.quadratic(x0, y0, point.x + in_x, point.y + in_y, prev.x + in_x, prev.y + in_y, u32(0xa0a00000)) |
| 780 | bmp.quadratic(x0, y0, point.x, point.y, prev.x, prev.y, color) |
| 781 | x0 = point.x |
| 782 | y0 = point.y |
| 783 | s = 1 |
| 784 | } else { |
| 785 | // dprintln("HERE2") |
| 786 | // ctx.quadraticCurveTo(prev.x + x, prev.y + y, |
| 787 | // (prev.x + point.x) / 2 + x, |
| 788 | // (prev.y + point.y) / 2 + y); |
| 789 | |
| 790 | // bmp.line(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, color2) |
| 791 | // bmp.quadratic(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, prev.x, prev.y, color2) |
| 792 | bmp.quadratic(x0, y0, (prev.x + point.x) / 2, (prev.y + point.y) / 2, prev.x, |
| 793 | prev.y, color) |
| 794 | x0 = (prev.x + point.x) / 2 |
| 795 | y0 = (prev.y + point.y) / 2 |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | if count == glyph.contour_ends[c] { |
| 800 | // dprintln("count == glyph.contour_ends[count]") |
| 801 | if s == 2 { // final point was off-curve. connect to start |
| 802 | |
| 803 | mut start_point := glyph.points[contour_start] |
| 804 | start_point.x, start_point.y = bmp.trf_ch(start_point) |
| 805 | if point.on_curve { |
| 806 | // ctx.quadraticCurveTo(prev.x + x, prev.y + y, |
| 807 | // point.x + x, point.y + y); |
| 808 | // bmp.line(x0, y0, start_point.x + in_x, start_point.y + in_y, u32(0x00FF0000)) |
| 809 | |
| 810 | // start_point.x + in_x, start_point.y + in_y, u32(0xFF00FF00)) |
| 811 | bmp.quadratic(x0, y0, start_point.x, start_point.y, start_point.x, |
| 812 | start_point.y, color) |
| 813 | } else { |
| 814 | // ctx.quadraticCurveTo(prev.x + x, prev.y + y, |
| 815 | // (prev.x + point.x) / 2 + x, |
| 816 | // (prev.y + point.y) / 2 + y); |
| 817 | |
| 818 | // bmp.line(x0, y0, start_point.x, start_point.y, u32(0x00FF0000) |
| 819 | // u32(0xFF000000)) |
| 820 | bmp.quadratic(x0, y0, start_point.x, start_point.y, |
| 821 | (point.x + start_point.x) / 2, (point.y + start_point.y) / 2, color) |
| 822 | } |
| 823 | } else { |
| 824 | // last point not in a curve |
| 825 | // bmp.line(point.x, point.y, sp_x, sp_y, u32(0x00FF0000)) |
| 826 | bmp.line(point.x, point.y, sp_x, sp_y, color) |
| 827 | } |
| 828 | contour_start = count + 1 |
| 829 | s = 0 |
| 830 | c++ |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | if bmp.style == .filled || bmp.style == .raw { |
| 835 | bmp.exec_filler() |
| 836 | } |
| 837 | x_min := glyph.x_min |
| 838 | x_max := glyph.x_max |
| 839 | return x_min, x_max |
| 840 | |
| 841 | // return glyph.x_min, glyph.x_max |
| 842 | } |
| 843 | |