vlib: add a TTF font loader and render in `x.ttf` (#7995)
parent
a0b8191a94
commit
c067cc0357
|
@ -147,9 +147,15 @@ jobs:
|
||||||
- name: Build V
|
- name: Build V
|
||||||
run: |
|
run: |
|
||||||
make CC=clang
|
make CC=clang
|
||||||
|
- name: Show PWD and Environment
|
||||||
|
run: |
|
||||||
|
echo "PWD:"
|
||||||
|
pwd
|
||||||
|
echo "ENVIRONMENT"
|
||||||
|
env
|
||||||
- name: Test V fixed tests
|
- name: Test V fixed tests
|
||||||
run: |
|
run: |
|
||||||
v -silent test-fixed
|
./v -silent test-fixed
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
|
|
@ -122,13 +122,16 @@ pub fn new_test_session(_vargs string) TestSession {
|
||||||
}
|
}
|
||||||
if github_job != 'ubuntu-tcc' {
|
if github_job != 'ubuntu-tcc' {
|
||||||
skip_files << 'examples/wkhtmltopdf.v' // needs installation of wkhtmltopdf from https://github.com/wkhtmltopdf/packaging/releases
|
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', '')
|
vargs := _vargs.replace('-progress', '').replace('-progress', '')
|
||||||
vexe := pref.vexe_path()
|
vexe := pref.vexe_path()
|
||||||
|
vroot := os.dir(vexe)
|
||||||
new_vtmp_dir := setup_new_vtmp_folder()
|
new_vtmp_dir := setup_new_vtmp_folder()
|
||||||
return TestSession{
|
return TestSession{
|
||||||
vexe: vexe
|
vexe: vexe
|
||||||
vroot: os.dir(vexe)
|
vroot: vroot
|
||||||
skip_files: skip_files
|
skip_files: skip_files
|
||||||
vargs: vargs
|
vargs: vargs
|
||||||
vtmp_dir: new_vtmp_dir
|
vtmp_dir: new_vtmp_dir
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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.
|
|
@ -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()
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue