Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,330 @@
|
||||
# TTF font utility
|
||||
|
||||
## introduction
|
||||
|
||||
This module is designed to perform two main task
|
||||
|
||||
- Load the font file
|
||||
- Render text using a TTF font
|
||||
|
||||
The render system can be single or multiple, for example it is possible to have a bitmap
|
||||
render and a HW accelerated render.
|
||||
|
||||
## TTF loader
|
||||
|
||||
This part of the module do a simple task, load a TTF file and preprocess all the loaded data
|
||||
in order to simplify the rendering phase.
|
||||
|
||||
Let's start with a simple snippet of code that load a font from the disk:
|
||||
|
||||
```v ignore
|
||||
mut ttf_font := ttf.TTF_File{}
|
||||
ttf_font.buf = os.read_bytes("arial.ttf") or { panic(err) }
|
||||
ttf_font.init()
|
||||
```
|
||||
|
||||
> **Note**
|
||||
> The font must be passed to the `TTF_file` as RAM buffer.
|
||||
|
||||
At this point the font "arial" is loaded and parsed and if it is a valid TTF font it is
|
||||
ready for the rendering.
|
||||
We can get some quick info on the font as string using the `get_info_string` function:
|
||||
|
||||
```v oksyntax
|
||||
println(ttf_font.get_info_string())
|
||||
```
|
||||
|
||||
produces an output like this:
|
||||
|
||||
```
|
||||
----- Font Info -----
|
||||
font_family : Arial
|
||||
font_sub_family : Normal
|
||||
full_name : Arial
|
||||
postscript_name : ArialMT
|
||||
version : 1
|
||||
font_revision : 5.06
|
||||
magic_number : 5f0f3cf5
|
||||
flags : 81b
|
||||
created unixTS : 649950890
|
||||
modified unixTS : 1282151447
|
||||
units_per_em : 2048
|
||||
box : [x_min:-1361, y_min:-665, x_Max:4096, y_Max:2060]
|
||||
mac_style : 0
|
||||
-----------------------
|
||||
```
|
||||
|
||||
Once loaded a font the `TTF_File` struct is filled with the font data and texts can be rendered.
|
||||
At high level no more action are required to use the loaded font.
|
||||
Multiple fonts can be loaded without problems at the same time.
|
||||
|
||||
## TTF Bitmap render
|
||||
|
||||
In this module it is possible to have different renders running at the same time.
|
||||
At the present time all the rendering are made on the CPU, sokol is used only to draw the
|
||||
rendered text to the screen.
|
||||
Let's start with a simple snippet of code:
|
||||
|
||||
```v
|
||||
import os
|
||||
import x.ttf
|
||||
|
||||
fn main() {
|
||||
mut ttf_font := ttf.TTF_File{}
|
||||
ttf_font.buf = os.read_bytes('arial.ttf') or { panic(err) }
|
||||
ttf_font.init()
|
||||
// print font info
|
||||
println(ttf_font.get_info_string())
|
||||
}
|
||||
```
|
||||
|
||||
This simple code load a TTF font and display its basic information.
|
||||
|
||||
### draw_text
|
||||
|
||||
The draw text function draw simple strings without indentation or other imagination tasks.
|
||||
At this point we can render a simple text:
|
||||
|
||||
```v
|
||||
import os
|
||||
import x.ttf
|
||||
|
||||
fn main() {
|
||||
mut ttf_font := ttf.TTF_File{}
|
||||
ttf_font.buf = os.read_bytes('arial.ttf') or { panic(err) }
|
||||
ttf_font.init()
|
||||
// print font info
|
||||
println(ttf_font.get_info_string())
|
||||
|
||||
bmp_width := 200
|
||||
bmp_height := 64
|
||||
bmp_layers := 4 // number of planes for an RGBA buffer
|
||||
// memory size of the buffer
|
||||
bmp_size := bmp_width * bmp_height * bmp_layers
|
||||
|
||||
font_size := 32 // font size in points
|
||||
device_dpi := 72 // default screen DPI
|
||||
// Formula for scale calculation
|
||||
// scaler := (font_size * device dpi) / (72dpi * em_unit)
|
||||
scale := f32(font_size * device_dpi) / f32(72 * ttf_font.units_per_em)
|
||||
// height of the font to use in the buffer to separate the lines
|
||||
y_base := int((ttf_font.y_max - ttf_font.y_min) * scale)
|
||||
|
||||
// declare the bitmap struct
|
||||
mut bmp := ttf.BitMap{
|
||||
tf: &ttf_font
|
||||
buf: unsafe { malloc(bmp_size) }
|
||||
buf_size: bmp_size
|
||||
width: bmp_width
|
||||
height: bmp_height
|
||||
bp: bmp_layers
|
||||
color: 0x000000_FF // RGBA black
|
||||
scale: scale
|
||||
}
|
||||
bmp.init_filler()
|
||||
bmp.clear()
|
||||
bmp.set_pos(10, y_base)
|
||||
bmp.draw_text('Test Text!')
|
||||
bmp.save_as_ppm('test.ppm')
|
||||
}
|
||||
```
|
||||
|
||||
This is the low level render that draw the text on a bitmap and save the bitmap on a disk as
|
||||
`.ppm` file.
|
||||
|
||||
> **Note**
|
||||
> The render in this case is a raw rendering without any postfiltering or other processing.
|
||||
|
||||
Using the low level rendering you need to manage all the amenities like allocate and release
|
||||
memory and other tasks like calc the character dimensions.
|
||||
|
||||
You can specify the style for the text rendering in the `BitMap` struct::
|
||||
|
||||
```v
|
||||
enum Style {
|
||||
outline
|
||||
outline_aliased
|
||||
filled // default style
|
||||
raw
|
||||
}
|
||||
```
|
||||
|
||||
Use this level only if you want achieve particular result on text rendering.
|
||||
|
||||
### draw_text_block
|
||||
|
||||
Draw text block draw a justified and indented block of multiline text in the bitmap.
|
||||
|
||||
```v
|
||||
import os
|
||||
import x.ttf
|
||||
|
||||
fn main() {
|
||||
mut ttf_font := ttf.TTF_File{}
|
||||
ttf_font.buf = os.read_bytes('arial.ttf') or { panic(err) }
|
||||
ttf_font.init()
|
||||
// print font info
|
||||
println(ttf_font.get_info_string())
|
||||
|
||||
bmp_width := 200
|
||||
bmp_height := 200
|
||||
bmp_layers := 4 // number of planes for an RGBA buffer
|
||||
// memory size of the buffer
|
||||
bmp_size := bmp_width * bmp_height * bmp_layers
|
||||
|
||||
font_size := 32 // font size in points
|
||||
device_dpi := 72 // default screen DPI
|
||||
// Formula for scale calculation
|
||||
// scaler := (font_size * device dpi) / (72dpi * em_unit)
|
||||
scale := f32(font_size * device_dpi) / f32(72 * ttf_font.units_per_em)
|
||||
// height of the font to use in the buffer to separate the lines
|
||||
y_base := int((ttf_font.y_max - ttf_font.y_min) * scale)
|
||||
|
||||
text := "Today it is a good day!
|
||||
Tomorrow I'm not so sure :(
|
||||
But Vwill prevail for sure, V is the way!!
|
||||
òàèì@ò!£$%&
|
||||
"
|
||||
// declare the bitmap struct
|
||||
mut bmp := ttf.BitMap{
|
||||
tf: &ttf_font
|
||||
buf: malloc(bmp_size)
|
||||
buf_size: bmp_size
|
||||
width: bmp_width
|
||||
height: bmp_height
|
||||
bp: bmp_layers
|
||||
color: 0x000000_FF // RGBA black
|
||||
scale: scale
|
||||
}
|
||||
bmp.init_filler()
|
||||
bmp.clear()
|
||||
bmp.justify = true
|
||||
bmp.align = .left
|
||||
bmp.draw_text_block(text, x: 0, y: 0, w: bmp_width - 20, h: bmp_height)
|
||||
bmp.save_as_ppm('test.ppm')
|
||||
}
|
||||
```
|
||||
|
||||
This is the low level render that draw text block on the bitmap.
|
||||
A text block is defined from a `Text_block` struct:
|
||||
|
||||
```v
|
||||
struct Text_block {
|
||||
x int // x position of the left high corner
|
||||
y int // y position of the left high corner
|
||||
w int // width of the text block
|
||||
h int // height of the text block
|
||||
cut_lines bool = true // force to cut the line if the length is over the text block width
|
||||
}
|
||||
```
|
||||
|
||||
and use the following bitmap fields:
|
||||
|
||||
```v ignore
|
||||
style Style = .filled // default style
|
||||
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
|
||||
```
|
||||
|
||||
It is possible to modify these parameters to obtain the desired effect on the text rendering.
|
||||
|
||||
## TTF Sokol render
|
||||
|
||||
The sokol render use the bitmap render to create the text and the `gg` functions to render
|
||||
the text to the screen.
|
||||
It is simpler to use in a `gg app` than the raw bitmap render.
|
||||
Each single text rendered need its own reder to be declared, after you can modify it.
|
||||
Here a simple example of the usage:
|
||||
|
||||
```v oksyntax
|
||||
import gg
|
||||
import sokol.sapp
|
||||
import sokol.sgl
|
||||
import sokol.gfx
|
||||
import x.ttf
|
||||
import os
|
||||
|
||||
const win_width = 600
|
||||
const win_height = 700
|
||||
const bg_color = gg.white
|
||||
const font_paths = [
|
||||
'arial.ttf',
|
||||
]
|
||||
|
||||
struct App_data {
|
||||
pub mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
sg_img gfx.Image
|
||||
init_flag bool
|
||||
frame_c int
|
||||
|
||||
tf []ttf.TTF_File
|
||||
ttf_render []ttf.TTF_render_Sokol
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// draw text only if the app is already initialized
|
||||
if app.init_flag == true {
|
||||
// update the text
|
||||
mut txt1 := &app.ttf_render[0]
|
||||
txt1.destroy_texture()
|
||||
txt1.create_text(cframe_txt, 43)
|
||||
txt1.create_texture()
|
||||
txt1.draw_text_bmp(app.gg, 30, 60)
|
||||
}
|
||||
app.frame_c++
|
||||
app.gg.end()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := &App_data{}
|
||||
|
||||
app.gg = gg.new_context(
|
||||
width: win_width
|
||||
height: win_height
|
||||
create_window: true
|
||||
window_title: 'Test TTF module'
|
||||
user_data: app
|
||||
bg_color: bg_color
|
||||
frame_fn: draw_frame
|
||||
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(tf.get_info_string())
|
||||
app.tf << tf
|
||||
}
|
||||
|
||||
// TTF render 0 Frame counter
|
||||
app.ttf_render << &ttf.TTF_render_Sokol{
|
||||
bmp: &ttf.BitMap{
|
||||
tf: &app.tf[0]
|
||||
buf: unsafe { malloc(32000000) }
|
||||
buf_size: (32000000)
|
||||
color: 0xFF0000FF
|
||||
// style: .raw
|
||||
}
|
||||
}
|
||||
|
||||
app.gg.run()
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,209 @@
|
||||
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
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// dprintln prints the `txt` message, *only* when `-d debug_flag` is passed while compiling a program
|
||||
@[if debug_flag ?]
|
||||
pub fn dprintln(txt string) {
|
||||
println(txt)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Utility
|
||||
*
|
||||
******************************************************************************/
|
||||
// format_texture transforms the bitmap from one layer to color layers
|
||||
pub fn (mut bmp BitMap) format_texture() {
|
||||
r := u8(bmp.color >> 24)
|
||||
g := u8((bmp.color >> 16) & 0xFF)
|
||||
b := u8((bmp.color >> 8) & 0xFF)
|
||||
a := u8(bmp.color & 0xFF)
|
||||
|
||||
b_r := u8(bmp.bg_color >> 24)
|
||||
b_g := u8((bmp.bg_color >> 16) & 0xFF)
|
||||
b_b := u8((bmp.bg_color >> 8) & 0xFF)
|
||||
b_a := u8(bmp.bg_color & 0xFF)
|
||||
|
||||
// transform buffer in a texture
|
||||
x := bmp.buf
|
||||
unsafe {
|
||||
mut i := 0
|
||||
for i < 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] = u8(u16(u16(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save_as_ppm saves the `BitMap` data in .ppm file format to `file_name`.
|
||||
pub fn (mut bmp BitMap) save_as_ppm(file_name string) {
|
||||
tmp_buf := bmp.buf
|
||||
mut buf := unsafe { malloc_noscan(bmp.buf_size) }
|
||||
unsafe { vmemcpy(buf, tmp_buf, bmp.buf_size) }
|
||||
bmp.buf = buf
|
||||
|
||||
bmp.format_texture()
|
||||
npixels := bmp.width * bmp.height
|
||||
mut f_out := os.create(file_name) or { panic(err) }
|
||||
f_out.writeln('P3') or { panic(err) }
|
||||
f_out.writeln('${bmp.width} ${bmp.height}') or { panic(err) }
|
||||
f_out.writeln('255') or { panic(err) }
|
||||
for i in 0 .. npixels {
|
||||
pos := i * bmp.bp
|
||||
unsafe {
|
||||
c_r := bmp.buf[pos]
|
||||
c_g := bmp.buf[pos + 1]
|
||||
c_b := bmp.buf[pos + 2]
|
||||
f_out.write_string('${c_r} ${c_g} ${c_b} ') or { panic(err) }
|
||||
}
|
||||
}
|
||||
f_out.close()
|
||||
|
||||
unsafe {
|
||||
free(buf)
|
||||
}
|
||||
bmp.buf = tmp_buf
|
||||
}
|
||||
|
||||
// get_raw_bytes returns the raw bytes of the bitmap.
|
||||
pub fn (mut bmp BitMap) get_raw_bytes() []u8 {
|
||||
mut f_buf := []u8{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
|
||||
}
|
||||
|
||||
// save_raw_data saves the raw data to `file_name`.
|
||||
pub fn (mut bmp BitMap) save_raw_data(file_name string) {
|
||||
os.write_file_array(file_name, bmp.get_raw_bytes()) or { panic(err) }
|
||||
}
|
||||
|
||||
//
|
||||
// Math functions
|
||||
//
|
||||
// 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
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// color_multiply_alpha multiplies color `c`'s alpha channel with the `level` value.
|
||||
@[inline]
|
||||
pub fn color_multiply_alpha(c u32, level f32) u32 {
|
||||
return u32(f32(c & 0xFF) * level)
|
||||
}
|
||||
|
||||
// color_multiply multiplies R,G,B channels of color `c` with the `level` value.
|
||||
@[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)
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
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
|
||||
|
||||
// BitMap represents a bitmap image of text rendered with the font supplied via the `tf` field.
|
||||
pub struct BitMap {
|
||||
pub mut:
|
||||
tf &TTF_File = unsafe { nil }
|
||||
buf &u8 = unsafe { nil } // pointer to the memory buffer
|
||||
buf_size int // allocated buf size in bytes
|
||||
width int = 1 // width of the buffer
|
||||
height int = 1 // height of the buffer
|
||||
bp int = 4 // byte per pixel of the buffer
|
||||
bg_color u32 = 0xFFFFFF_00 // background RGBA format
|
||||
color u32 = 0x000000_FF // RGBA format
|
||||
scale f32 = 1.0 // internal usage!!
|
||||
scale_x f32 = 1.0 // X scale of the single glyph
|
||||
scale_y f32 = 1.0 // Y scale of the single glyph
|
||||
angle f32 = 0.0 // angle of rotation of the bitmap
|
||||
// spaces
|
||||
space_cw f32 = 1.0 // width of the space glyph internal usage!!
|
||||
space_mult f32 = f32(0.0) // 1.0/16.0 // space between letter, is a multiplier for a standard 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 style
|
||||
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 clears the bitmap with 0 bytes.
|
||||
pub fn (mut bmp BitMap) clear() {
|
||||
mut sz := bmp.width * bmp.height * bmp.bp
|
||||
unsafe {
|
||||
vmemset(bmp.buf, 0x00, sz)
|
||||
}
|
||||
}
|
||||
|
||||
// trf_txt returns the transform matrix applied to the text.
|
||||
pub fn (bmp &BitMap) trf_txt(p &Point) (int, int) {
|
||||
return int(p.x * bmp.tr_matrix[0] + p.y * bmp.tr_matrix[3] + bmp.tr_matrix[6]), int(
|
||||
p.x * bmp.tr_matrix[1] + p.y * bmp.tr_matrix[4] + bmp.tr_matrix[7])
|
||||
}
|
||||
|
||||
// trf_ch returns the transform matrix applied to the char.
|
||||
pub fn (bmp &BitMap) trf_ch(p &Point) (int, int) {
|
||||
return int(p.x * bmp.ch_matrix[0] + p.y * bmp.ch_matrix[3] + bmp.ch_matrix[6]), int(
|
||||
p.x * bmp.ch_matrix[1] + p.y * bmp.ch_matrix[4] + bmp.ch_matrix[7])
|
||||
}
|
||||
|
||||
// set_pos sets the draw position 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_rotation sets the rotation angle in radians `a`.
|
||||
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
|
||||
*
|
||||
******************************************************************************/
|
||||
// init_filler initializes the internal `filler` buffer.
|
||||
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")
|
||||
}
|
||||
|
||||
// clear_filler clears the internal `filler` buffer.
|
||||
pub fn (mut bmp BitMap) clear_filler() {
|
||||
for i in 0 .. bmp.height {
|
||||
bmp.filler[i].clear()
|
||||
}
|
||||
}
|
||||
|
||||
// exec_filler plots the pixels of the `BitMap` to the internal buffer.
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fline populates the internal `filler` buffer with a line segment from `in_x0`,`in_y0` to `in_x1`,`in_y1`.
|
||||
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
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
// plot plots a pixel at `x`,`y` in color `c` in the internal bitmap buffer.
|
||||
@[inline]
|
||||
pub fn (mut bmp BitMap) plot(x int, y int, c u32) bool {
|
||||
if x < 0 || x >= bmp.width || y < 0 || y >= bmp.height {
|
||||
return false
|
||||
}
|
||||
mut index := (x + y * bmp.width) * bmp.bp
|
||||
unsafe {
|
||||
// bmp.buf[index]=0xFF
|
||||
bmp.buf[index] = u8(c & 0xFF) // write only the alpha
|
||||
}
|
||||
/*
|
||||
for count in 0..(bmp.bp) {
|
||||
unsafe{
|
||||
bmp.buf[index + count] = u8((c >> (bmp.bp - count - 1) * 8) & 0x0000_00FF)
|
||||
}
|
||||
}
|
||||
*/
|
||||
return true
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* smooth draw functions
|
||||
*
|
||||
******************************************************************************/
|
||||
// aline draws an aliased line on the bitmap.
|
||||
pub fn (mut bmp BitMap) aline(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) {
|
||||
// mut c1 := c
|
||||
mut x0 := f32(in_x0)
|
||||
mut x1 := f32(in_x1)
|
||||
mut y0 := f32(in_y0)
|
||||
mut y1 := f32(in_y1)
|
||||
mut tmp := f32(0)
|
||||
|
||||
mut dx := x1 - x0
|
||||
mut dy := y1 - y0
|
||||
|
||||
dist := f32(0.4)
|
||||
|
||||
if math.abs(dx) > math.abs(dy) {
|
||||
if x1 < x0 {
|
||||
tmp = x0
|
||||
x0 = x1
|
||||
x1 = tmp
|
||||
|
||||
tmp = y0
|
||||
y0 = y1
|
||||
y1 = tmp
|
||||
}
|
||||
dx = x1 - x0
|
||||
dy = y1 - y0
|
||||
|
||||
x0 += 0.5
|
||||
y0 += 0.5
|
||||
|
||||
m := dy / dx
|
||||
mut x := x0
|
||||
for x <= x1 + 0.5 {
|
||||
y := m * (x - x0) + y0
|
||||
e := 1 - math.abs(y - 0.5 - int(y))
|
||||
bmp.plot(int(x), int(y), color_multiply_alpha(c, e * 0.75))
|
||||
|
||||
ys1 := y + dist
|
||||
if int(ys1) != int(y) {
|
||||
v1 := math.abs(ys1 - y) / dist * (1 - e)
|
||||
bmp.plot(int(x), int(ys1), color_multiply_alpha(c, v1))
|
||||
}
|
||||
|
||||
ys2 := y - dist
|
||||
if int(ys2) != int(y) {
|
||||
v2 := math.abs(y - ys2) / dist * (1 - e)
|
||||
bmp.plot(int(x), int(ys2), color_multiply_alpha(c, v2))
|
||||
}
|
||||
|
||||
x += 1.0
|
||||
}
|
||||
} else {
|
||||
if y1 < y0 {
|
||||
tmp = x0
|
||||
x0 = x1
|
||||
x1 = tmp
|
||||
|
||||
tmp = y0
|
||||
y0 = y1
|
||||
y1 = tmp
|
||||
}
|
||||
dx = x1 - x0
|
||||
dy = y1 - y0
|
||||
|
||||
x0 += 0.5
|
||||
y0 += 0.5
|
||||
|
||||
n := dx / dy
|
||||
mut y := y0
|
||||
for y <= y1 + 0.5 {
|
||||
x := n * (y - y0) + x0
|
||||
if !math.is_nan(x) && !math.is_nan(y) {
|
||||
e := f32(1 - math.abs(x - 0.5 - int(x)))
|
||||
bmp.plot(int(x), int(y), color_multiply_alpha(c, f32(e * 0.75)))
|
||||
|
||||
xs1 := x + dist
|
||||
if int(xs1) != int(x) {
|
||||
v1 := math.abs(xs1 - x) / dist * (1 - e)
|
||||
bmp.plot(int(xs1), int(y), color_multiply_alpha(c, f32(v1)))
|
||||
}
|
||||
|
||||
xs2 := x - dist
|
||||
if int(xs2) != int(x) {
|
||||
v2 := math.abs(x - xs1) / dist * (1 - e)
|
||||
bmp.plot(int(xs2), int(y), color_multiply_alpha(c, f32(v2)))
|
||||
}
|
||||
}
|
||||
y += 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* draw functions
|
||||
*
|
||||
******************************************************************************/
|
||||
// line plots a line segment to the internal buffer from `in_x0`,`in_y0` to `in_x1`,`in_y1` in the color `c`.
|
||||
pub fn (mut bmp BitMap) line(in_x0 int, in_y0 int, in_x1 int, in_y1 int, c u32) {
|
||||
// outline with aliased borders
|
||||
if bmp.style == .outline_aliased {
|
||||
bmp.aline(in_x0, in_y0, in_x1, in_y1, c)
|
||||
return
|
||||
}
|
||||
// filled with aliased borders
|
||||
else if bmp.style == .filled {
|
||||
bmp.aline(in_x0, in_y0, in_x1, in_y1, c)
|
||||
bmp.fline(in_x0, in_y0, in_x1, in_y1, c)
|
||||
return
|
||||
}
|
||||
// only the filler is drawn
|
||||
else if bmp.style == .raw {
|
||||
bmp.fline(in_x0, in_y0, in_x1, in_y1, c)
|
||||
return
|
||||
}
|
||||
// if we are here we are drawing an outlined border
|
||||
|
||||
x0 := int(in_x0)
|
||||
x1 := int(in_x1)
|
||||
y0 := int(in_y0)
|
||||
y1 := int(in_y1)
|
||||
// dprintln("line[${x0},${y0},${x1},${y1}]")
|
||||
|
||||
mut x := x0
|
||||
mut y := y0
|
||||
|
||||
dx := math.abs(x1 - x0)
|
||||
sx := if x0 < x1 { 1 } else { -1 }
|
||||
dy := -math.abs(y1 - y0)
|
||||
sy := if y0 < y1 { 1 } else { -1 }
|
||||
|
||||
// vertical line
|
||||
if dx == 0 {
|
||||
if y0 < y1 {
|
||||
for yt in y0 .. y1 + 1 {
|
||||
bmp.plot(x0, yt, c)
|
||||
}
|
||||
return
|
||||
}
|
||||
for yt in y1 .. y0 + 1 {
|
||||
bmp.plot(x0, yt, c)
|
||||
}
|
||||
// horizontal line
|
||||
return
|
||||
} else if dy == 0 {
|
||||
if x0 < x1 {
|
||||
for xt in x0 .. x1 + 1 {
|
||||
bmp.plot(xt, y0, c)
|
||||
}
|
||||
return
|
||||
}
|
||||
for xt in x1 .. x0 + 1 {
|
||||
bmp.plot(xt, y0, c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mut err := dx + dy // error value e_xy
|
||||
for {
|
||||
// bmp.plot(x, y, u32(0xFF00))
|
||||
bmp.plot(x, y, c)
|
||||
|
||||
// dprintln("${x} ${y} [${x0},${y0},${x1},${y1}]")
|
||||
if x == x1 && y == y1 {
|
||||
break
|
||||
}
|
||||
e2 := 2 * err
|
||||
if e2 >= dy { // e_xy+e_x > 0
|
||||
err += dy
|
||||
x += sx
|
||||
}
|
||||
if e2 <= dx { // e_xy+e_y < 0
|
||||
err += dx
|
||||
y += sy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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`.
|
||||
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)
|
||||
}
|
||||
|
||||
// quadratic plots a quadratic Bezier curve in color `c`.
|
||||
pub fn (mut bmp BitMap) quadratic(in_x0 int, in_y0 int, in_x1 int, in_y1 int, in_cx int, in_cy int, c u32) {
|
||||
/*
|
||||
x0 := int(in_x0 * bmp.scale)
|
||||
x1 := int(in_x1 * bmp.scale)
|
||||
y0 := int(in_y0 * bmp.scale)
|
||||
y1 := int(in_y1 * bmp.scale)
|
||||
cx := int(in_cx * bmp.scale)
|
||||
cy := int(in_cy * bmp.scale)
|
||||
*/
|
||||
x0 := int(in_x0)
|
||||
x1 := int(in_x1)
|
||||
y0 := int(in_y0)
|
||||
y1 := int(in_y1)
|
||||
cx := int(in_cx)
|
||||
cy := int(in_cy)
|
||||
|
||||
mut division := f64(1.0)
|
||||
dx := math.abs(x0 - x1)
|
||||
dy := math.abs(y0 - y1)
|
||||
|
||||
// if few pixel draw a simple line
|
||||
// if dx == 0 && dy == 0 {
|
||||
if dx <= 2 || dy <= 2 {
|
||||
// bmp.plot(x0, y0, c)
|
||||
bmp.line(x0, y0, x1, y1, c)
|
||||
return
|
||||
}
|
||||
|
||||
division = 1.0 / (f64(if dx > dy { dx } else { dy }))
|
||||
|
||||
// division = 0.1 // 10 division
|
||||
// division = 0.25 // 4 division
|
||||
|
||||
// dprintln("div: ${division}")
|
||||
|
||||
/*
|
||||
----- Bezier quadratic form -----
|
||||
t = 0.5; // given example value, half length of the curve
|
||||
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x;
|
||||
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y;
|
||||
---------------------------------
|
||||
*/
|
||||
|
||||
mut x_old := x0
|
||||
mut y_old := y0
|
||||
mut t := 0.0
|
||||
|
||||
for t <= (1.0 + division / 2.0) {
|
||||
s := 1.0 - t
|
||||
x := s * s * x0 + 2.0 * s * t * cx + t * t * x1
|
||||
y := s * s * y0 + 2.0 * s * t * cy + t * t * y1
|
||||
xi := int(x + 0.5)
|
||||
yi := int(y + 0.5)
|
||||
// bmp.plot(xi, yi, c)
|
||||
bmp.line(x_old, y_old, xi, yi, c)
|
||||
x_old = xi
|
||||
y_old = yi
|
||||
t += division
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* TTF Query functions
|
||||
*
|
||||
******************************************************************************/
|
||||
// get_chars_bbox returns all characters found in bounding box of string `in_string`.
|
||||
pub fn (mut bmp BitMap) get_chars_bbox(in_string string) []int {
|
||||
mut res := []int{}
|
||||
mut w := 0
|
||||
|
||||
mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `))
|
||||
div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale)
|
||||
space_cw = int(space_cw * bmp.scale)
|
||||
|
||||
bmp.tf.reset_kern()
|
||||
|
||||
mut i := 0
|
||||
for i < in_string.len {
|
||||
mut chr := u16(in_string[i])
|
||||
|
||||
// draw the space
|
||||
if int(chr) == 32 {
|
||||
w += int(space_cw * bmp.space_cw)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// manage unicode chars like latin greek etc
|
||||
c_len := int(((u32(0xe5000000) >> ((chr >> 3) & 0x1e)) & 3) + 1)
|
||||
if c_len > 1 {
|
||||
tmp_char := utf8.get_rune(in_string, i)
|
||||
// dprintln("tmp_char: ${tmp_char.hex()}")
|
||||
chr = u16(tmp_char)
|
||||
}
|
||||
|
||||
c_index := bmp.tf.map_code(int(chr))
|
||||
// Glyph not found
|
||||
if c_index == 0 {
|
||||
w += int(space_cw * bmp.space_cw)
|
||||
i += c_len
|
||||
continue
|
||||
}
|
||||
|
||||
ax, ay := bmp.tf.next_kern(c_index)
|
||||
// dprintln("char_index: ${c_index} ax: ${ax} ay: ${ay}")
|
||||
|
||||
// cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr))
|
||||
// dprintln("metrics: [${u16(chr):c}] cw:${cw} lsb:${lsb}")
|
||||
|
||||
//----- Calc Glyph transformations -----
|
||||
mut x0 := w + int(ax * bmp.scale)
|
||||
mut y0 := 0 + int(ay * bmp.scale)
|
||||
|
||||
p := Point{x0, y0, false}
|
||||
x1, y1 := bmp.trf_txt(p)
|
||||
// init ch_matrix
|
||||
bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[6] = f32(x1)
|
||||
bmp.ch_matrix[7] = f32(y1)
|
||||
|
||||
// x_min, x_max, y_min, y_max := bmp.tf.read_glyph_dim(c_index)
|
||||
x_min, x_max, _, _ := bmp.tf.read_glyph_dim(c_index)
|
||||
//-----------------
|
||||
|
||||
width := int((math.abs(x_max + x_min) + ax) * bmp.scale)
|
||||
// width := int((cw+ax) * bmp.scale)
|
||||
w += width + div_space_cw
|
||||
h := int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale)
|
||||
res << w
|
||||
res << h
|
||||
|
||||
i += c_len
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// get_bbox returns the bounding box (width and height) of text `in_string`.
|
||||
pub fn (mut bmp BitMap) get_bbox(in_string string) (int, int) {
|
||||
mut w := 0
|
||||
|
||||
mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `))
|
||||
div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale)
|
||||
space_cw = int(space_cw * bmp.scale)
|
||||
|
||||
bmp.tf.reset_kern()
|
||||
|
||||
mut i := 0
|
||||
for i < in_string.len {
|
||||
mut chr := u16(in_string[i])
|
||||
|
||||
// draw the space
|
||||
if int(chr) == 32 {
|
||||
w += int(space_cw * bmp.space_cw)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// manage unicode chars like latin greek etc
|
||||
c_len := int(((u32(0xe5000000) >> ((chr >> 3) & 0x1e)) & 3) + 1)
|
||||
if c_len > 1 {
|
||||
tmp_char := utf8.get_rune(in_string, i)
|
||||
// dprintln("tmp_char: ${tmp_char.hex()}")
|
||||
chr = u16(tmp_char)
|
||||
}
|
||||
|
||||
c_index := bmp.tf.map_code(int(chr))
|
||||
// Glyph not found
|
||||
if c_index == 0 {
|
||||
w += int(space_cw * bmp.space_cw)
|
||||
i += c_len
|
||||
continue
|
||||
}
|
||||
ax, ay := bmp.tf.next_kern(c_index)
|
||||
// dprintln("char_index: ${c_index} ax: ${ax} ay: ${ay}")
|
||||
|
||||
// cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr))
|
||||
// dprintln("metrics: [${u16(chr):c}] cw:${cw} lsb:${lsb}")
|
||||
|
||||
//----- Calc Glyph transformations -----
|
||||
mut x0 := w + int(ax * bmp.scale)
|
||||
mut y0 := 0 + int(ay * bmp.scale)
|
||||
|
||||
p := Point{x0, y0, false}
|
||||
x1, y1 := bmp.trf_txt(p)
|
||||
// init ch_matrix
|
||||
bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[6] = f32(x1)
|
||||
bmp.ch_matrix[7] = f32(y1)
|
||||
|
||||
x_min, x_max, _, _ := bmp.tf.read_glyph_dim(c_index)
|
||||
// x_min := 1
|
||||
// x_max := 2
|
||||
//-----------------
|
||||
|
||||
width := int((math.abs(x_max + x_min) + ax) * bmp.scale)
|
||||
// width := int((cw+ax) * bmp.scale)
|
||||
w += width + div_space_cw
|
||||
|
||||
i += c_len
|
||||
}
|
||||
|
||||
// dprintln("y_min: ${bmp.tf.y_min} y_max: ${bmp.tf.y_max} res: ${int((bmp.tf.y_max - bmp.tf.y_min)*buf.scale)} width: ${int( (cw) * buf.scale)}")
|
||||
// buf.box(0,y_base - int((bmp.tf.y_min)*buf.scale), int( (x_max) * buf.scale), y_base-int((bmp.tf.y_max)*buf.scale), u32(0xFF00_0000) )
|
||||
return w, int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* TTF draw glyph
|
||||
*
|
||||
******************************************************************************/
|
||||
fn (mut bmp BitMap) draw_notdef_glyph(in_x int, in_w int) {
|
||||
mut p := Point{in_x, 0, false}
|
||||
x1, y1 := bmp.trf_txt(p)
|
||||
// init ch_matrix
|
||||
bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[6] = f32(x1)
|
||||
bmp.ch_matrix[7] = f32(y1)
|
||||
x, y := bmp.trf_ch(p)
|
||||
|
||||
y_h := math.abs(bmp.tf.y_max - bmp.tf.y_min) * bmp.scale * 0.5
|
||||
|
||||
bmp.box(int(x), int(y), int(x - in_w), int(y - y_h), bmp.color)
|
||||
bmp.line(int(x), int(y), int(x - in_w), int(y - y_h), bmp.color)
|
||||
bmp.line(int(x - in_w), int(y), int(x), int(y - y_h), bmp.color)
|
||||
}
|
||||
|
||||
// draw_text plots the pixels of the text `in_string` to the internal buffer.
|
||||
// It returns the text bounding box.
|
||||
pub fn (mut bmp BitMap) draw_text(in_string string) (int, int) {
|
||||
mut w := 0
|
||||
|
||||
mut space_cw, _ := bmp.tf.get_horizontal_metrics(u16(` `))
|
||||
div_space_cw := int((f32(space_cw) * bmp.space_mult) * bmp.scale)
|
||||
space_cw = int(space_cw * bmp.scale)
|
||||
|
||||
bmp.tf.reset_kern()
|
||||
|
||||
mut i := 0
|
||||
for i < in_string.len {
|
||||
mut chr := u16(in_string[i])
|
||||
|
||||
// draw the space
|
||||
if int(chr) == 32 {
|
||||
w += int(space_cw * bmp.space_cw)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// manage unicode chars like latin greek etc
|
||||
c_len := int(((u32(0xe5000000) >> ((chr >> 3) & 0x1e)) & 3) + 1)
|
||||
if c_len > 1 {
|
||||
tmp_char := utf8.get_rune(in_string, i)
|
||||
// dprintln("tmp_char: ${tmp_char.hex()}")
|
||||
chr = u16(tmp_char)
|
||||
}
|
||||
|
||||
c_index := bmp.tf.map_code(int(chr))
|
||||
// Glyph not found
|
||||
if c_index == 0 {
|
||||
bmp.draw_notdef_glyph(w, int(space_cw * bmp.space_cw))
|
||||
w += int(space_cw * bmp.space_cw)
|
||||
i += c_len
|
||||
continue
|
||||
}
|
||||
|
||||
ax, ay := bmp.tf.next_kern(c_index)
|
||||
// dprintln("char_index: ${c_index} ax: ${ax} ay: ${ay}")
|
||||
|
||||
cw, _ := bmp.tf.get_horizontal_metrics(u16(chr))
|
||||
// cw, lsb := bmp.tf.get_horizontal_metrics(u16(chr))
|
||||
// dprintln("metrics: [${u16(chr):c}] cw:${cw} lsb:${lsb}")
|
||||
|
||||
//----- Draw_Glyph transformations -----
|
||||
mut x0 := w + int(ax * bmp.scale)
|
||||
mut y0 := 0 + int(ay * bmp.scale)
|
||||
|
||||
p := Point{x0, y0, false}
|
||||
x1, y1 := bmp.trf_txt(p)
|
||||
// init ch_matrix
|
||||
bmp.ch_matrix[0] = bmp.tr_matrix[0] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[1] = bmp.tr_matrix[1] * bmp.scale * bmp.scale_x
|
||||
bmp.ch_matrix[3] = bmp.tr_matrix[3] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[4] = bmp.tr_matrix[4] * -bmp.scale * bmp.scale_y
|
||||
bmp.ch_matrix[6] = f32(x1)
|
||||
bmp.ch_matrix[7] = f32(y1)
|
||||
|
||||
x_min, x_max := bmp.draw_glyph(c_index)
|
||||
// x_min := 1
|
||||
// x_max := 2
|
||||
//-----------------
|
||||
|
||||
mut width := int((math.abs(x_max + x_min) + ax) * bmp.scale)
|
||||
if bmp.use_font_metrics {
|
||||
width = int((cw + ax) * bmp.scale)
|
||||
}
|
||||
w += width + div_space_cw
|
||||
i += c_len
|
||||
}
|
||||
|
||||
// dprintln("y_min: ${bmp.tf.y_min} y_max: ${bmp.tf.y_max} res: ${int((bmp.tf.y_max - bmp.tf.y_min)*buf.scale)} width: ${int( (cw) * buf.scale)}")
|
||||
// buf.box(0,y_base - int((bmp.tf.y_min)*buf.scale), int( (x_max) * buf.scale), y_base-int((bmp.tf.y_max)*buf.scale), u32(0xFF00_0000) )
|
||||
return w, int(math.abs(int(bmp.tf.y_max - bmp.tf.y_min)) * bmp.scale)
|
||||
}
|
||||
|
||||
// draw_glyph plots the pixels of the glyph at `index` to the internal buffer.
|
||||
// It returns the `x_max` and `x_min` values.
|
||||
pub fn (mut bmp BitMap) draw_glyph(index u16) (int, int) {
|
||||
glyph := bmp.tf.read_glyph(index)
|
||||
|
||||
if !glyph.valid_glyph {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
if bmp.style == .filled || bmp.style == .raw {
|
||||
bmp.clear_filler()
|
||||
}
|
||||
|
||||
mut s := 0 // status
|
||||
mut c := 0 // contours count
|
||||
mut contour_start := 0
|
||||
mut x0 := 0
|
||||
mut y0 := 0
|
||||
color := bmp.color // u32(0xFFFF_FF00) // RGBA white
|
||||
// color1 := u32(0xFF00_0000) // RGBA red
|
||||
// color2 := u32(0x00FF_0000) // RGBA green
|
||||
|
||||
mut sp_x := 0
|
||||
mut sp_y := 0
|
||||
mut point := Point{}
|
||||
|
||||
for count, point_raw in glyph.points {
|
||||
// dprintln("count: ${count}, state: ${s} pl:${glyph.points.len}")
|
||||
point.x = point_raw.x
|
||||
point.y = point_raw.y
|
||||
|
||||
point.x, point.y = bmp.trf_ch(point)
|
||||
point.on_curve = point_raw.on_curve
|
||||
|
||||
if s == 0 {
|
||||
x0 = point.x
|
||||
y0 = point.y
|
||||
sp_x = x0
|
||||
sp_y = y0
|
||||
s = 1 // next state
|
||||
continue
|
||||
} else if s == 1 {
|
||||
if point.on_curve {
|
||||
bmp.line(x0, y0, point.x, point.y, color)
|
||||
// bmp.aline(x0, y0, point.x, point.y, u32(0xFFFF0000))
|
||||
x0 = point.x
|
||||
y0 = point.y
|
||||
} else {
|
||||
s = 2
|
||||
}
|
||||
} else {
|
||||
// dprintln("s==2")
|
||||
mut prev := glyph.points[count - 1]
|
||||
prev.x, prev.y = bmp.trf_ch(prev)
|
||||
if point.on_curve {
|
||||
// dprintln("HERE1")
|
||||
// ctx.quadraticCurveTo(prev.x + x, prev.y + y,point.x + x, point.y + y);
|
||||
// bmp.line(x0, y0, point.x + in_x, point.y + in_y, color1)
|
||||
// bmp.quadratic(x0, y0, point.x + in_x, point.y + in_y, prev.x + in_x, prev.y + in_y, u32(0xa0a00000))
|
||||
bmp.quadratic(x0, y0, point.x, point.y, prev.x, prev.y, color)
|
||||
x0 = point.x
|
||||
y0 = point.y
|
||||
s = 1
|
||||
} else {
|
||||
// dprintln("HERE2")
|
||||
// ctx.quadraticCurveTo(prev.x + x, prev.y + y,
|
||||
// (prev.x + point.x) / 2 + x,
|
||||
// (prev.y + point.y) / 2 + y);
|
||||
|
||||
// bmp.line(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, color2)
|
||||
// bmp.quadratic(x0, y0, (prev.x + point.x)/2, (prev.y + point.y)/2, prev.x, prev.y, color2)
|
||||
bmp.quadratic(x0, y0, (prev.x + point.x) / 2, (prev.y + point.y) / 2, prev.x,
|
||||
prev.y, color)
|
||||
x0 = (prev.x + point.x) / 2
|
||||
y0 = (prev.y + point.y) / 2
|
||||
}
|
||||
}
|
||||
|
||||
if count == glyph.contour_ends[c] {
|
||||
// dprintln("count == glyph.contour_ends[count]")
|
||||
if s == 2 { // final point was off-curve. connect to start
|
||||
|
||||
mut start_point := glyph.points[contour_start]
|
||||
start_point.x, start_point.y = bmp.trf_ch(start_point)
|
||||
if point.on_curve {
|
||||
// ctx.quadraticCurveTo(prev.x + x, prev.y + y,
|
||||
// point.x + x, point.y + y);
|
||||
// bmp.line(x0, y0, start_point.x + in_x, start_point.y + in_y, u32(0x00FF0000))
|
||||
|
||||
// start_point.x + in_x, start_point.y + in_y, u32(0xFF00FF00))
|
||||
bmp.quadratic(x0, y0, start_point.x, start_point.y, start_point.x,
|
||||
start_point.y, color)
|
||||
} else {
|
||||
// ctx.quadraticCurveTo(prev.x + x, prev.y + y,
|
||||
// (prev.x + point.x) / 2 + x,
|
||||
// (prev.y + point.y) / 2 + y);
|
||||
|
||||
// bmp.line(x0, y0, start_point.x, start_point.y, u32(0x00FF0000)
|
||||
// u32(0xFF000000))
|
||||
bmp.quadratic(x0, y0, start_point.x, start_point.y,
|
||||
(point.x + start_point.x) / 2, (point.y + start_point.y) / 2, color)
|
||||
}
|
||||
} else {
|
||||
// last point not in a curve
|
||||
// bmp.line(point.x, point.y, sp_x, sp_y, u32(0x00FF0000))
|
||||
bmp.line(point.x, point.y, sp_x, sp_y, color)
|
||||
}
|
||||
contour_start = count + 1
|
||||
s = 0
|
||||
c++
|
||||
}
|
||||
}
|
||||
|
||||
if bmp.style == .filled || bmp.style == .raw {
|
||||
bmp.exec_filler()
|
||||
}
|
||||
x_min := glyph.x_min
|
||||
x_max := glyph.x_max
|
||||
return x_min, x_max
|
||||
|
||||
// return glyph.x_min, glyph.x_max
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
module render_sokol
|
||||
|
||||
/**********************************************************************
|
||||
*
|
||||
* 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
|
||||
import sokol.gfx
|
||||
import x.ttf
|
||||
|
||||
// TTF_render_Sokol is a structure containing data for rendering a TTF font as a sokol texture.
|
||||
pub struct TTF_render_Sokol {
|
||||
pub mut:
|
||||
bmp &ttf.BitMap = unsafe { nil } // Base bitmap render
|
||||
// rendering fields
|
||||
sg_img gfx.Image // sokol image
|
||||
sg_smp gfx.Sampler // sokol sampler
|
||||
scale_reduct f32 = 2.0 // scale of the cpu texture for filtering
|
||||
device_dpi int = 72 // device DPI
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Render functions
|
||||
*
|
||||
******************************************************************************/
|
||||
// format_texture formats the BMP (bitmap).
|
||||
pub fn (mut tf_skl TTF_render_Sokol) format_texture() {
|
||||
tf_skl.bmp.format_texture()
|
||||
}
|
||||
|
||||
// create_text prepares the text `in_txt` in size `in_font_size` as a sokol texture.
|
||||
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 * device_dpi )/ f32(72 * tf_skl.bmp.tf.units_per_em))* font_size
|
||||
scale := f32(font_size * device_dpi) / f32(72 * int(tf_skl.bmp.tf.units_per_em))
|
||||
// ttf.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 {
|
||||
unsafe { free(tf_skl.bmp.buf) }
|
||||
}
|
||||
ttf.dprintln('create_text Alloc: ${sz} bytes')
|
||||
tf_skl.bmp.buf = unsafe { malloc_noscan(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()
|
||||
}
|
||||
|
||||
// create_text_block prepares a block of text as a sokol 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 * device_dpi )/ f32(72 * tf_skl.bmp.tf.units_per_em))* font_size
|
||||
scale := f32(font_size * device_dpi) / f32(72 * int(tf_skl.bmp.tf.units_per_em))
|
||||
// ttf.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 {
|
||||
unsafe { free(tf_skl.bmp.buf) }
|
||||
}
|
||||
ttf.dprintln('Alloc: ${sz} bytes')
|
||||
tf_skl.bmp.buf = unsafe { malloc_noscan(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
|
||||
*
|
||||
******************************************************************************/
|
||||
// create_texture creates the sokol texture from the internal buffer state.
|
||||
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 := gfx.ImageDesc{
|
||||
width: w
|
||||
height: h
|
||||
num_mipmaps: 0
|
||||
// usage: .dynamic
|
||||
label: &char(unsafe { nil })
|
||||
d3d11_texture: 0
|
||||
}
|
||||
// comment for dynamic
|
||||
img_desc.data.subimage[0][0] = gfx.Range{
|
||||
ptr: tf_skl.bmp.buf
|
||||
size: usize(sz)
|
||||
}
|
||||
|
||||
simg := gfx.make_image(&img_desc)
|
||||
// free(tf_skl.bmp.buf) // DONT FREE IF Dynamic
|
||||
|
||||
mut smp_desc := gfx.SamplerDesc{
|
||||
min_filter: .linear
|
||||
mag_filter: .linear
|
||||
wrap_u: .clamp_to_edge
|
||||
wrap_v: .clamp_to_edge
|
||||
}
|
||||
|
||||
ssmp := gfx.make_sampler(&smp_desc)
|
||||
|
||||
tf_skl.sg_img = simg
|
||||
tf_skl.sg_smp = ssmp
|
||||
}
|
||||
|
||||
// destroy_texture detroys the internal sokol texture.
|
||||
pub fn (tf_skl TTF_render_Sokol) destroy_texture() {
|
||||
gfx.destroy_image(tf_skl.sg_img)
|
||||
gfx.destroy_sampler(tf_skl.sg_smp)
|
||||
}
|
||||
|
||||
// update_text_texture updates the sokol texture with current internal state.
|
||||
// NOTE: Only use if `.dynamic` is set.
|
||||
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 := gfx.ImageData{}
|
||||
tmp_sbc.subimage[0][0] = gfx.Range{
|
||||
ptr: tf_skl.bmp.buf
|
||||
size: usize(sz)
|
||||
}
|
||||
gfx.update_image(tf_skl.sg_img, &tmp_sbc)
|
||||
}
|
||||
|
||||
// draw_text_bmp renders the internal state to the current sokol pipeline.
|
||||
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 * ctx.scale,
|
||||
y * ctx.scale,
|
||||
0,
|
||||
1,
|
||||
]
|
||||
sgl.mult_matrix(m)
|
||||
|
||||
sgl.load_pipeline(ctx.pipeline.alpha)
|
||||
sgl.enable_texture()
|
||||
sgl.texture(tf_skl.sg_img, tf_skl.sg_smp)
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
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:
|
||||
**********************************************************************/
|
||||
// Text_block represents a visual block of TTF text.
|
||||
pub struct Text_block {
|
||||
pub:
|
||||
x int // x position of the left high corner
|
||||
y int // y position of the left high corner
|
||||
w int // width of the text block
|
||||
h int // height of the text block
|
||||
cut_lines bool = true // force to cut the line if the length is over the text block width
|
||||
}
|
||||
|
||||
// get_justify_space_cw returns the space needed to justify `txt`.
|
||||
pub 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
|
||||
}
|
||||
|
||||
// draw_text_block renders out `text` in the `Text_block` `block`.
|
||||
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, _ := 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 == '' {
|
||||
break
|
||||
}
|
||||
|
||||
bmp.space_cw = old_space_cw
|
||||
w, _ = 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, _ = 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 = unsafe { txt1[c..] }
|
||||
c = txt1.len
|
||||
//---- DEBUG ----
|
||||
// txt2 := txt1.join(' ')
|
||||
// println("new string: ${txt2} len: ${c}")
|
||||
//---------------
|
||||
} else {
|
||||
c--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp.space_cw = old_space_cw
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,238 @@
|
||||
// vtest build: !docker-ubuntu-musl
|
||||
import x.ttf
|
||||
import os
|
||||
import strings
|
||||
|
||||
/**********************************************************************
|
||||
*
|
||||
* 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:
|
||||
* use `v -d create_data vlib/x/ttf/ttf_test.v` to generate binary data for this test file
|
||||
*
|
||||
* TODO:
|
||||
* - manage text directions R to L
|
||||
**********************************************************************/
|
||||
const font_path = 'Qarmic_sans_Abridged.ttf'
|
||||
|
||||
const font_bytes = $embed_file('ttf_test_data.bin')
|
||||
|
||||
const test_data = '
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
00bf bfbf bfbf bfbf bfbf bfbf bf00 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
00bf bfbf bfbf bfbf bfbf bfbf bf00 0000
|
||||
bfff ffff ffff ffff ffff ffff ffbf 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
bfff ffff ffff ffff ffff ffff ffbf 0000
|
||||
00bf ffff ffbf ffff bfff ffff bf00 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
00bf ffff ffbf ffff bfff ffff bf00 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
bf00 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 00bf
|
||||
ffbf 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 00bf
|
||||
ffbf 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 bfbf
|
||||
ffbf bfbf bf00 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0032 72bf
|
||||
bfbf 0000 0000 bfbf bfbf 5400 00bf ffff
|
||||
ffff ffff ffbf 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0000 0032 72bf
|
||||
0000 0000 00bf ffff bf00 0065 9999 ffff
|
||||
ffff bf00 00bf ffff ffff ff7f 0000 bfff
|
||||
bfff bfff bf00 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 0065 9999 6500
|
||||
0000 0000 00bf ffff bf00 bfff ffff ffbf
|
||||
ffff ffbf bfff bfff bfbf ffff bf00 bfff
|
||||
bf00 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 bf72 3300 7fbf
|
||||
0000 0000 00bf ffff bf7f 5fff ffbf 3f7f
|
||||
8fbf ffbf ffbf 5500 0000 5fbf 0000 bfff
|
||||
bf00 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf7f 5fff ffbf 3f7f
|
||||
0000 0000 00bf ffff bfbf ffbf bfbf ffff
|
||||
ffff ffbf ffff ff7f 0000 0000 0000 bfff
|
||||
bf00 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bfbf 00bf bfbf 8f5f
|
||||
0000 0000 00bf ffff 7f5f ffff ffff ffff
|
||||
ffff ffbf 5fbf ffff bfbf bfbf 0000 bfff
|
||||
bf00 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff 7f5f 0000 0000 0000
|
||||
0000 0000 00bf ffff bfff bfff ffbf ffff
|
||||
ffff ffbf 0000 5fbf ffff ffff bf00 bfff
|
||||
bf00 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bfff bfff ffbf ffff
|
||||
0000 0000 00bf ffff bfff bf00 0000 0000
|
||||
0000 0000 0000 0000 7f7f ffff bf00 bfff
|
||||
bf00 0000 bf00 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bfff bf00 0000 0000
|
||||
0000 0000 00bf ffff bfff bf00 0000 0000
|
||||
0000 bf00 bf00 0000 0055 bfff ffbf bfff
|
||||
ff7f 00bf ff5f 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bfff bf00 0000 0000
|
||||
0000 0000 00bf ffff bfbf ffbf 0000 0055
|
||||
7fbf ffbf ffbf 7f55 00bf ffff bf00 7f5f
|
||||
ff7f 7f5f ffbf 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bfbf ffbf 0000 0055
|
||||
0000 0000 00bf ffff bfbf ffff bfbf bfff
|
||||
ffff bfbf ffff ffff ffff ffff bf00 00bf
|
||||
ffff ffff ffbf 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bfbf 0000 bfbf bf7f
|
||||
0000 0000 00bf ffff bf00 bfff ffff ffff
|
||||
ffbf 0000 bfbf ffff ffff bfbf 0000 00bf
|
||||
ffbf ffff bf00 0000 0000 0000 0000 0000
|
||||
0000 0000 00bf ffff bf00 bf00 0000 3f7f
|
||||
0000 0000 0000 5fbf 0000 00bf ffbf 8f5f
|
||||
3f00 0000 0000 5fbf bf5f 0000 0000 0000
|
||||
0000 bf5f 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 5fbf 0000 00bf ffbf 8f5f
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
'
|
||||
|
||||
fn save_raw_data_as_array(buf_bin []u8, file_name string) {
|
||||
mut buf := strings.new_builder(buf_bin.len * 5)
|
||||
for x in buf_bin {
|
||||
buf.write_string('0x${x:02x},')
|
||||
}
|
||||
os.write_file_array(file_name, buf) or { panic(err) }
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut tf := ttf.TTF_File{}
|
||||
$if create_data ? {
|
||||
tf.buf = os.read_bytes(font_path) or { panic(err) }
|
||||
println('TrueTypeFont file [${font_path}] len: ${tf.buf.len}')
|
||||
save_raw_data_as_array(tf.buf, 'test_ttf_Font_arr.bin')
|
||||
} $else {
|
||||
mut mut_font_bytes := font_bytes
|
||||
tf.buf = unsafe { mut_font_bytes.data().vbytes(font_bytes.len) }
|
||||
}
|
||||
tf.init()
|
||||
// println("Unit per EM: ${tf.units_per_em}")
|
||||
|
||||
w := 64
|
||||
h := 32
|
||||
bp := 4
|
||||
sz := w * h * bp
|
||||
|
||||
font_size := 20
|
||||
device_dpi := 72
|
||||
scale := f32(font_size * device_dpi) / f32(72 * int(tf.units_per_em))
|
||||
|
||||
mut bmp := ttf.BitMap{
|
||||
tf: &tf
|
||||
buf: unsafe { malloc(sz) }
|
||||
buf_size: sz
|
||||
scale: scale
|
||||
width: w
|
||||
height: h
|
||||
}
|
||||
|
||||
y_base := int((tf.y_max - tf.y_min) * bmp.scale)
|
||||
bmp.clear()
|
||||
bmp.set_pos(0, y_base)
|
||||
bmp.init_filler()
|
||||
bmp.draw_text('Test Text')
|
||||
|
||||
mut test_buf := get_raw_data(test_data)
|
||||
$if create_data ? {
|
||||
bmp.save_as_ppm('test_ttf.ppm')
|
||||
bmp.save_raw_data('test_ttf.bin')
|
||||
test_buf = os.read_bytes('test_ttf.bin') or { panic(err) }
|
||||
}
|
||||
|
||||
ram_buf := bmp.get_raw_bytes()
|
||||
assert ram_buf.len == test_buf.len
|
||||
for i in 0 .. ram_buf.len {
|
||||
if test_buf[i] != ram_buf[i] {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw_data(data string) []u8 {
|
||||
mut buf := []u8{}
|
||||
mut c := 0
|
||||
mut b := u32(0)
|
||||
for ch in data {
|
||||
if ch >= `0` && ch <= `9` {
|
||||
b = b << 4
|
||||
b += u32(ch - `0`)
|
||||
c++
|
||||
} else if ch >= `a` && ch <= `f` {
|
||||
b = b << 4
|
||||
b += u32(ch - `a` + 10)
|
||||
c++
|
||||
}
|
||||
|
||||
if c == 2 {
|
||||
buf << u8(b)
|
||||
b = 0
|
||||
c = 0
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user