1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

vlib: add a TTF font loader and render in x.ttf (#7995)

This commit is contained in:
penguindark 2021-01-10 19:14:41 +01:00 committed by GitHub
parent a0b8191a94
commit c067cc0357
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 3021 additions and 2 deletions

View File

@ -147,9 +147,15 @@ jobs:
- name: Build V
run: |
make CC=clang
- name: Show PWD and Environment
run: |
echo "PWD:"
pwd
echo "ENVIRONMENT"
env
- name: Test V fixed tests
run: |
v -silent test-fixed
./v -silent test-fixed
macos:
runs-on: macOS-latest

View File

@ -122,13 +122,16 @@ pub fn new_test_session(_vargs string) TestSession {
}
if github_job != 'ubuntu-tcc' {
skip_files << 'examples/wkhtmltopdf.v' // needs installation of wkhtmltopdf from https://github.com/wkhtmltopdf/packaging/releases
// the ttf_test.v is not interactive, but needs X11 headers to be installed, which is done only on ubuntu-tcc for now
skip_files << 'vlib/x/ttf/ttf_test.v'
}
vargs := _vargs.replace('-progress', '').replace('-progress', '')
vexe := pref.vexe_path()
vroot := os.dir(vexe)
new_vtmp_dir := setup_new_vtmp_folder()
return TestSession{
vexe: vexe
vroot: os.dir(vexe)
vroot: vroot
skip_files: skip_files
vargs: vargs
vtmp_dir: new_vtmp_dir

Binary file not shown.

Binary file not shown.

93
examples/ttf_font/OFL.txt Normal file
View File

@ -0,0 +1,93 @@
Copyright (c) 2012, Eduardo Tunni (http://www.tipo.net.ar), with Reserved Font Name "Imprima"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,202 @@
import gg
import gx
import sokol.sapp
import sokol.sgl
import x.ttf
import os
//import math
const (
win_width = 600
win_height = 700
bg_color = gx.white
font_paths = [
"Imprima-Regular.ttf"
"Graduate-Regular.ttf"
]
)
/******************************************************************************
*
* UI
*
*****************************************************************************/
struct App_data {
pub mut:
gg &gg.Context
sg_img C.sg_image
init_flag bool
frame_c int
tf []ttf.TTF_File
ttf_render []ttf.TTF_render_Sokol
text_ready_flag bool
mouse_x int = -1
mouse_y int = -1
}
fn my_init(mut app App_data) {
app.init_flag = true
}
fn draw_frame(mut app &App_data) {
cframe_txt := "Current Frame: $app.frame_c"
app.gg.begin()
sgl.defaults()
sgl.matrix_mode_projection()
sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0)
sgl.c4b(0, 0, 0, 255) // black
// draw a line as background
sgl.begin_line_strip()
sgl.v2f(10, 10)
sgl.v2f(100, 100)
sgl.end()
// draw text only if the app is already initialized
if app.init_flag == true {
sgl.begin_line_strip()
sgl.v2f(410, 400)
sgl.v2f(510, 400)
sgl.end()
// update the text
mut txt1 := &app.ttf_render[0]
if app.frame_c % 2 == 0 {
txt1.destroy_texture()
txt1.create_text(cframe_txt ,43)
txt1.create_texture()
}
// ----- decomment if you want text rotation ----
//txt1.bmp.angle = 3.141592 / 180 * f32(app.frame_c % 360)
//txt1.draw_text_bmp(app.gg, 300, 350)
//txt1.bmp.angle = 0
txt1.draw_text_bmp(app.gg, 30, 60)
// block test
block_txt := "Today it is a good day!
Tommorow I'm not so sure :(
Frame: $app.frame_c
But Vwill prevail for sure, V is the way!!
òàèì@ò!£$%&
"
txt1 = &app.ttf_render[1]
if app.frame_c % 2 == 0 {
txt1.bmp.justify = false
if (app.frame_c>>6) % 2 == 0 {
//txt1.align = .left
txt1.bmp.justify = true
}
txt1.bmp.align = .left
if (app.frame_c>>6) % 3 == 0 {
txt1.bmp.align = .right
}
txt1.destroy_texture()
txt1.create_text_block(block_txt, 500, 500, 32)
txt1.create_texture()
}
// decomment if want block color change
//txt1.bmp.color = ttf.color_multiply(0xFF00FFFF, f32(app.frame_c % 255)/255.0)
// decomment if want block rotation wanted
//txt1.bmp.angle = 3.141592/180 * f32(app.frame_c % 45)
txt1.draw_text_bmp(app.gg, 30 + (app.frame_c>>1) & 0xFF , 200)
// draw mouse position
if app.mouse_x >= 0 {
txt1 = &app.ttf_render[2]
txt1.destroy_texture()
txt1.create_text("$app.mouse_x,$app.mouse_y",25)
txt1.create_texture()
r := app.mouse_x % 255
g := app.mouse_y % 255
color := u32(r << 24) | u32(g << 16) | 0xFF
txt1.bmp.color = color
txt1.draw_text_bmp(app.gg, app.mouse_x , app.mouse_y)
}
app.frame_c ++
}
app.gg.end()
}
fn my_event_manager(mut ev sapp.Event, mut app &App_data) {
if ev.typ == .mouse_move {
app.mouse_x = int(ev.mouse_x)
app.mouse_y = int(ev.mouse_y)
}
}
[console]
fn main(){
mut app := &App_data{
gg: 0
}
app.gg = gg.new_context({
width: win_width
height: win_height
use_ortho: true // This is needed for 2D drawing
create_window: true
window_title: 'Test TTF module'
user_data: app
bg_color: bg_color
frame_fn: draw_frame
event_fn: my_event_manager
init_fn: my_init
})
// load TTF fonts
for font_path in font_paths {
mut tf := ttf.TTF_File{}
tf.buf = os.read_bytes(font_path) or { panic(err) }
println("TrueTypeFont file [$font_path] len: ${tf.buf.len}")
tf.init()
println("Unit per EM: $tf.units_per_em")
app.tf << tf
}
// TTF render 0 Frame counter
app.ttf_render << &ttf.TTF_render_Sokol {
bmp: &ttf.BitMap{
tf: &(app.tf[0])
buf: malloc(32000000)
buf_size: (32000000)
color : 0xFF0000FF
//style: .raw
//use_font_metrics: true
}
}
// TTF render 1 Text Block
app.ttf_render << &ttf.TTF_render_Sokol {
bmp: &ttf.BitMap{
tf: &(app.tf[1])
//color : 0xFF0000_10
//style: .raw
//use_font_metrics: true
}
}
// TTF mouse position render
app.ttf_render << &ttf.TTF_render_Sokol {
bmp: &ttf.BitMap{
tf: &(app.tf[0])
}
}
/* setup sokol_gfx */
app.gg.run()
}

184
vlib/x/ttf/common.v Normal file
View File

@ -0,0 +1,184 @@
module ttf
/**********************************************************************
*
* Common data for the module
*
* 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:
**********************************************************************/
import os
import math
// text align
pub
enum Text_align {
left
center
right
justify
}
// draw style
pub
enum Style {
outline
outline_aliased
filled
raw
}
/******************************************************************************
*
* DEBUG Utility
*
******************************************************************************/
const debug_flag = false
fn dprintln(txt string){
if debug_flag {
println(txt)
}
}
/******************************************************************************
*
* Utility
*
******************************************************************************/
// write out a .ppm file
pub
fn (mut bmp BitMap) save_as_ppm(file_name string) {
npixels := bmp.width * bmp.height
mut f_out := os.create(file_name) or { panic(err) }
f_out.writeln('P3')
f_out.writeln('${bmp.width} ${bmp.height}')
f_out.writeln('255')
for i in 0..npixels {
pos := i * bmp.bp
unsafe {
c_r := 0xFF - bmp.buf[pos]
c_g := 0xFF - bmp.buf[pos +1 ]
c_b := 0xFF - bmp.buf[pos + 2]
f_out.write_str('${c_r} ${c_g} ${c_b} ')
}
}
f_out.close()
}
pub
fn (mut bmp BitMap) get_raw_bytes() []byte {
mut f_buf := []byte{len: bmp.buf_size/4}
mut i:=0
for i < bmp.buf_size {
unsafe { f_buf[i>>2] = *(bmp.buf + i) }
i += 4
}
return f_buf
}
pub
fn (mut bmp BitMap) save_raw_data(file_name string) {
os.write_file_array(file_name, bmp.get_raw_bytes())
}
//
// Math functions
//
[inline]
fn abs(a int) int {
if a < 0 {
return -a
} else {
return a
}
}
[inline]
fn fabs(a f32) f32 {
if a < 0 {
return -a
} else {
return a
}
}
// integer part of x
[inline]
fn ipart(x f32) f32 {
return f32(math.floor(x))
}
[inline]
fn round(x f32) f32 {
return ipart(x + 0.5)
}
// fractional part of x
[inline]
fn fpart(x f32) f32 {
return x - f32(math.floor(x))
}
[inline]
fn rfpart(x f32) f32 {
return 1 - fpart(x)
}
/******************************************************************************
*
* Colors
*
******************************************************************************/
/*
[inline]
pub
fn (mut dev BitMap) get_color(x int, y int) (int, int, int, int){
if x < 0 || x >= dev.width || y < 0 || y >= dev.height {
return 0,0,0,0
}
mut i := (x + y * dev.width)*dev.bp
unsafe{
return dev.buf[i], dev.buf[i+1], dev.buf[i+2], dev.buf[i+3]
}
}
[inline]
pub
fn (mut dev BitMap) get_color_u32(x int, y int) u32{
r, g, b, a := dev.get_color(x, y)
unsafe{
return u32(r<<24) | u32(g<<16) | u32(b<<8) | u32(a)
}
}
*/
/******************************************************************************
*
* Drawing
*
******************************************************************************/
[inline]
pub
fn color_multiply_alpha(c u32, level f32) u32 {
return u32(f32( c & 0xFF) * level)
}
[inline]
pub
fn color_multiply(c u32, level f32) u32 {
mut r := (f32((c >> 24) & 0xFF)/255.0) * level
mut g := (f32((c >> 16) & 0xFF)/255.0) * level
mut b := (f32((c >> 8) & 0xFF)/255.0) * level
mut a := (f32( c & 0xFF)/255.0) * level
r = if r > 1.0 { 1.0 } else { r }
g = if g > 1.0 { 1.0 } else { g }
b = if b > 1.0 { 1.0 } else { b }
a = if a > 1.0 { 1.0 } else { a }
return (u32(r * 255) << 24) | (u32(g * 255) << 16) | (u32(b * 255) << 8) | u32(a * 255)
}

818
vlib/x/ttf/render_bmp.v Normal file
View File

@ -0,0 +1,818 @@
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
buf byteptr // 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
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
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] = byte(c & 0xFF) // write only the alpha
}
/*
for count in 0..(bmp.bp) {
unsafe{
bmp.buf[index + count] = byte((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 fabs(dx) > fabs(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-fabs(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 := fabs(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 := fabs(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 - fabs(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 := fabs(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 := fabs(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 := abs(x1 - x0)
sx := if x0 < x1 { 1 } else { -1 }
dy := -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)
}
return
// horizontal line
} 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 := abs(x0 - x1)
dy := 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 char := u16(in_string[i])
// draw the space
if int(char) == 32 {
w += int(space_cw * bmp.space_cw)
i++
continue
}
// manage unicode chars like latin greek etc
c_len := ((0xe5000000>>((char>>3) & 0x1e)) & 3) + 1
if c_len > 1 {
tmp_char := utf8.get_uchar(in_string,i)
//dprintln("tmp_char: ${tmp_char.hex()}")
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")
//cw, lsb := bmp.tf.get_horizontal_metrics(u16(char))
//dprintln("metrics: [${u16(char):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( (abs(x_max + x_min) + ax) * bmp.scale)
//width := int((cw+ax) * bmp.scale)
w += width + div_space_cw
h := int(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 char := u16(in_string[i])
// draw the space
if int(char) == 32 {
w += int(space_cw * bmp.space_cw)
i++
continue
}
// manage unicode chars like latin greek etc
c_len := ((0xe5000000>>((char>>3) & 0x1e)) & 3) + 1
if c_len > 1 {
tmp_char := utf8.get_uchar(in_string,i)
//dprintln("tmp_char: ${tmp_char.hex()}")
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")
//cw, lsb := bmp.tf.get_horizontal_metrics(u16(char))
//dprintln("metrics: [${u16(char):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( (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(abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale)
}
/******************************************************************************
*
* TTF draw glyph
*
******************************************************************************/
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 char := u16(in_string[i])
// draw the space
if int(char) == 32 {
w += int(space_cw * bmp.space_cw)
i++
continue
}
// manage unicode chars like latin greek etc
c_len := ((0xe5000000>>((char>>3) & 0x1e)) & 3) + 1
if c_len > 1 {
tmp_char := utf8.get_uchar(in_string,i)
//dprintln("tmp_char: ${tmp_char.hex()}")
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")
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)
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
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))
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))
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
}

View File

@ -0,0 +1,234 @@
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:
**********************************************************************/
import math
import gg
import sokol.sgl
pub
struct TTF_render_Sokol {
pub mut:
bmp &BitMap // Base bitmap render
// rendering fields
sg_img C.sg_image // sokol image
scale_reduct f32 = 2.0 // scale of the cpu texture for filtering
device_dpi int = 72 // device DPI
}
/******************************************************************************
*
* Render functions
*
******************************************************************************/
fn (mut tf_skl TTF_render_Sokol) format_texture(){
r := byte(tf_skl.bmp.color >> 24)
g := byte((tf_skl.bmp.color >> 16) & 0xFF)
b := byte((tf_skl.bmp.color >> 8 ) & 0xFF)
a := byte(tf_skl.bmp.color & 0xFF)
b_r := byte(tf_skl.bmp.bg_color >> 24)
b_g := byte((tf_skl.bmp.bg_color >> 16) & 0xFF)
b_b := byte((tf_skl.bmp.bg_color >> 8 ) & 0xFF)
b_a := byte(tf_skl.bmp.bg_color & 0xFF)
// trasform buffer in a texture
x := byteptr(tf_skl.bmp.buf)
unsafe{
mut i := 0
for i<tf_skl.bmp.buf_size {
data := x[i]
if data > 0 {
x[i+0] = r
x[i+1] = g
x[i+2] = b
// alpha
x[i+3] = byte((a * data) >> 8)
} else {
x[i+0] = b_r
x[i+1] = b_g
x[i+2] = b_b
x[i+3] = b_a
}
i += 4
}
}
}
pub
fn (mut tf_skl TTF_render_Sokol) create_text(in_txt string, in_font_size f32){
scale_reduct := tf_skl.scale_reduct
device_dpi := tf_skl.device_dpi
font_size := in_font_size //* scale_reduct
// Formula: (font_size * device dpi) / (72dpi * em_unit)
//scale := ((1.0 * devide_dpi )/ f32(72 * tf_skl.bmp.tf.units_per_em))* font_size
scale := f32(font_size * device_dpi) / f32(72 * tf_skl.bmp.tf.units_per_em)
//dprintln("Scale: $scale")
tf_skl.bmp.scale = scale * scale_reduct
w, h := tf_skl.bmp.get_bbox(in_txt)
tf_skl.bmp.width = int(w)
tf_skl.bmp.height = int((h+8))
sz := tf_skl.bmp.width * tf_skl.bmp.height * tf_skl.bmp.bp
// RAM buffer
if sz > tf_skl.bmp.buf_size {
if sz > 0 {
free(tf_skl.bmp.buf)
}
dprintln("create_text Alloc: $sz bytes")
tf_skl.bmp.buf = malloc(sz)
tf_skl.bmp.buf_size = sz
}
tf_skl.bmp.init_filler()
// draw the text
mut y_base := int((tf_skl.bmp.tf.y_max - tf_skl.bmp.tf.y_min) * tf_skl.bmp.scale)
tf_skl.bmp.set_pos(0,y_base)
tf_skl.bmp.clear()
tf_skl.bmp.draw_text(in_txt)
tf_skl.format_texture()
}
pub
fn (mut tf_skl TTF_render_Sokol) create_text_block(in_txt string, in_w int, in_h int, in_font_size f32){
scale_reduct := tf_skl.scale_reduct
device_dpi := tf_skl.device_dpi
font_size := in_font_size //* scale_reduct
// Formula: (font_size * device dpi) / (72dpi * em_unit)
//scale := ((1.0 * devide_dpi )/ f32(72 * tf_skl.bmp.tf.units_per_em))* font_size
scale := f32(font_size * device_dpi) / f32(72 * tf_skl.bmp.tf.units_per_em)
//dprintln("Scale: $scale")
tf_skl.bmp.scale = scale * scale_reduct
w := in_w
h := in_h
tf_skl.bmp.width = int(w * scale_reduct + 0.5)
tf_skl.bmp.height = int((h+2) * scale_reduct + 0.5)
sz := tf_skl.bmp.width * tf_skl.bmp.height * tf_skl.bmp.bp
//if true { return }
// RAM buffer
if sz > tf_skl.bmp.buf_size {
if sz > 0 {
free(tf_skl.bmp.buf)
}
dprintln("Alloc: $sz bytes")
tf_skl.bmp.buf = malloc(sz)
tf_skl.bmp.buf_size = sz
}
tf_skl.bmp.init_filler()
// draw the text
mut y_base := int((tf_skl.bmp.tf.y_max - tf_skl.bmp.tf.y_min) * tf_skl.bmp.scale)
tf_skl.bmp.set_pos(0,y_base)
tf_skl.bmp.clear()
tf_skl.bmp.draw_text_block(in_txt, {x: 0, y:0, w:w, h:h})
tf_skl.format_texture()
}
/******************************************************************************
*
* Sokol Render functions
*
******************************************************************************/
pub
fn (mut tf_skl TTF_render_Sokol) create_texture(){
w := tf_skl.bmp.width
h := tf_skl.bmp.height
sz := tf_skl.bmp.width * tf_skl.bmp.height * tf_skl.bmp.bp
mut img_desc := C.sg_image_desc{
width: w
height: h
num_mipmaps: 0
min_filter: .linear
mag_filter: .linear
//usage: .dynamic
wrap_u: .clamp_to_edge
wrap_v: .clamp_to_edge
label: &byte(0)
d3d11_texture: 0
}
// comment for dynamic
img_desc.content.subimage[0][0] = C.sg_subimage_content{
ptr: tf_skl.bmp.buf
size: sz
}
simg := C.sg_make_image(&img_desc)
//free(tf_skl.bmp.buf) // DONT FREE IF Dynamic
tf_skl.sg_img = simg
}
pub
fn (tf_skl TTF_render_Sokol) destroy_texture(){
C.sg_destroy_image(tf_skl.sg_img)
}
// Use only if usage: .dynamic
pub
fn (mut tf_skl TTF_render_Sokol) update_text_texture(){
sz := tf_skl.bmp.width * tf_skl.bmp.height * tf_skl.bmp.bp
mut tmp_sbc := C.sg_image_content{}
tmp_sbc.subimage[0][0] = C.sg_subimage_content {
ptr: tf_skl.bmp.buf
size: sz
}
C.sg_update_image(tf_skl.sg_img, &tmp_sbc)
}
pub
fn (tf_skl TTF_render_Sokol) draw_text_bmp(ctx &gg.Context, x f32, y f32) {
//width := tf_skl.bmp.width >> 1
//height := tf_skl.bmp.height >> 1
sgl.push_matrix()
width := tf_skl.bmp.width / (tf_skl.scale_reduct)
height := tf_skl.bmp.height / (tf_skl.scale_reduct)
u0 := f32(0.0)
v0 := f32(0.0)
u1 := f32(1.0)
v1 := f32(1.0)
x0 := f32(0)
y0 := f32(0)
x1 := f32(width) * ctx.scale
y1 := f32(height) * ctx.scale
ca := f32(math.cos(tf_skl.bmp.angle))
sa := f32(math.sin(tf_skl.bmp.angle))
m := [
f32(ca),-sa,0,0,
sa,ca,0,0,
0,0,1,0,
x,y,0,1
]
sgl.mult_matrix(m)
//
sgl.load_pipeline(ctx.timage_pip)
sgl.enable_texture()
sgl.texture(tf_skl.sg_img)
sgl.begin_quads()
sgl.c4b(255, 255, 255, 255)
sgl.v2f_t2f(x0, y0, u0, v0)
sgl.v2f_t2f(x1, y0, u1, v0)
sgl.v2f_t2f(x1, y1, u1, v1)
sgl.v2f_t2f(x0, y1, u0, v1)
sgl.end()
sgl.disable_texture()
sgl.pop_matrix()
}

122
vlib/x/ttf/text_block.v Normal file
View File

@ -0,0 +1,122 @@
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:
**********************************************************************/
pub
struct Text_block {
x int
y int
w int // width of the text block
h int // heigth of the text block
cut_lines bool = true
}
fn (mut dev BitMap) get_justify_space_cw(txt string, w int, block_w int, space_cw int) f32 {
num_spaces := txt.count(" ")
if num_spaces < 1 {
return 0
}
delta := block_w - w
//println("num spc: $num_spaces")
//println("delta: ${txt} w:$w bw:$block_w space_cw:$space_cw")
res := f32(delta)/f32(num_spaces)/f32(space_cw)
//println("res: $res")
return res
}
// write out a text
pub
fn (mut bmp BitMap) draw_text_block(text string, block Text_block) {
mut x := block.x
mut y := block.y
mut y_base := int((bmp.tf.y_max - bmp.tf.y_min) * bmp.scale)
//bmp.box(x, y, x + block.w, y + block.h, u32(0xFF00_0000))
// spaces data
mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `))
space_cw = int(space_cw * bmp.scale)
old_space_cw := bmp.space_cw
mut offset_flag := f32(0) // default .left align
if bmp.align == .right {
offset_flag = 1
} else if bmp.align == .center {
offset_flag = 0.5
}
for txt in text.split_into_lines() {
bmp.space_cw = old_space_cw
mut w,mut h := bmp.get_bbox(txt)
if w <= block.w || block.cut_lines == false {
//println("Solid block!")
left_offset := int((block.w - w) * offset_flag)
if bmp.justify && (f32(w) / f32(block.w)) >= bmp.justify_fill_ratio {
bmp.space_cw = old_space_cw + bmp.get_justify_space_cw(txt, w, block.w, space_cw)
}
bmp.set_pos(x + left_offset ,y + y_base)
bmp.draw_text(txt)
//---- DEBUG ----
//mut txt_w , mut txt_h := bmp.draw_text(txt)
//bmp.box(x + left_offset,y+y_base - int((bmp.tf.y_min)*bmp.scale), x + txt_w + left_offset, y + y_base - int((bmp.tf.y_max) * bmp.scale), u32(0x00ff_0000) )
//---------------
y += y_base
} else {
//println("to cut: ${txt}")
mut txt1 := txt.split(" ")
mut c:= txt1.len
//mut done := false
for c > 0 {
tmp_str := txt1[0..c].join(' ')
//println("tmp_str: ${tmp_str}")
if tmp_str.len < 1 {
break
}
bmp.space_cw = old_space_cw
w,h = bmp.get_bbox(tmp_str)
if w <= block.w {
mut left_offset := int((block.w - w) * offset_flag)
if bmp.justify && (f32(w) / f32(block.w)) >= bmp.justify_fill_ratio {
//println("cut phase!")
bmp.space_cw = 0.0
w,h = bmp.get_bbox(tmp_str)
left_offset = int((block.w - w) * offset_flag)
bmp.space_cw = bmp.get_justify_space_cw(tmp_str, w, block.w, space_cw)
} else {
bmp.space_cw = old_space_cw
}
bmp.set_pos(x + left_offset, y + y_base)
bmp.draw_text(tmp_str)
//---- DEBUG ----
//txt_w , txt_h := bmp.draw_text(tmp_str)
//println("printing [${x},${y}] => '${tmp_str}' space_cw: $bmp.space_cw")
//bmp.box(x + left_offset,y + y_base - int((bmp.tf.y_min)*bmp.scale), x + txt_w + left_offset, y + y_base - int((bmp.tf.y_max) * bmp.scale), u32(0x00ff_0000) )
//---------------
y += y_base
txt1 = txt1[c..]
c= txt1.len
//---- DEBUG ----
//txt2 := txt1.join(' ')
//println("new string: ${txt2} len: ${c}")
//---------------
} else {
c--
}
}
}
}
bmp.space_cw = old_space_cw
}

1115
vlib/x/ttf/ttf.v Normal file

File diff suppressed because it is too large Load Diff

242
vlib/x/ttf/ttf_test.v Normal file

File diff suppressed because one or more lines are too long