aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/x/ttf/README.md
blob: 6394ac1e5c349234ce6ddae138a2063a3d213861 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# 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 modue 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 oksyntax
import os
import x.ttf

[console]
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 informations.

### 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 oksyntax
import os
import x.ttf

[console]
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_heigth := 64
	bmp_layers := 4 // number of planes for an RGBA buffer
	// memory size of the buffer
	bmp_size := bmp_width * bmp_heigth * 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: malloc(bmp_size)
		buf_size: bmp_size
		width: bmp_width
		height: bmp_heigth
		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 ther 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 syle
	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 oksyntax
import os
import x.ttf

[console]
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_heigth := 200
	bmp_layers := 4 // number of planes for an RGBA buffer
	// memory size of the buffer
	bmp_size := bmp_width * bmp_heigth * 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_heigth
		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_heigth)
	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 postion of the left high corner
	y         int  // y postion of the left high corner
	w         int  // width of the text block
	h         int  // heigth 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 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
```

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 mor esimpel to use in a `gg app` that 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 gx
import sokol.sapp
import sokol.sgl
import x.ttf
import os

const (
	win_width  = 600
	win_height = 700
	bg_color   = gx.white
	font_paths = [
		'arial.ttf',
	]
)

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
}

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()
}

[console]
fn main() {
	mut app := &App_data{
		gg: 0
	}

	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()
}
```