v2 / vlib / x / ttf / render_bmp.v
842 lines · 735 sloc · 22.83 KB · 574ab6f4f6b3cc18a3eea4bf0946f2a7980cf1fb
Raw
1module 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**********************************************************************/
16import encoding.utf8
17import math
18
19// BitMap represents a bitmap image of text rendered with the font supplied via the `tf` field.
20pub struct BitMap {
21pub 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.
55pub 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.
63pub 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.
69pub 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.
75pub 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`.
81pub 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.
94pub 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.
106pub 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.
113pub 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`.
139pub 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]
198pub 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.
223pub 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`.
320pub 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`.
401pub 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`.
409pub 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`.
476pub 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`.
549pub 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******************************************************************************/
625fn (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.
646pub 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.
723pub 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