Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | Copyright 2025 Joel Svensson svenssonjoel@yahoo.se | ||
3 | |||
4 | LispBM is free software: you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation, either version 3 of the License, or | ||
7 | (at your option) any later version. | ||
8 | |||
9 | LispBM is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <extensions/ttf_extensions.h> | ||
19 | #include <extensions.h> | ||
20 | #include <buffer.h> | ||
21 | |||
22 | #include "schrift.h" | ||
23 | |||
24 | #ifdef LBM_OPT_TTF_EXTENSIONS_SIZE | ||
25 | #pragma GCC optimize ("-Os") | ||
26 | #endif | ||
27 | #ifdef LBM_OPT_TTF_EXTENSIONS_SIZE_AGGRESSIVE | ||
28 | #pragma GCC optimize ("-Oz") | ||
29 | #endif | ||
30 | |||
31 | 15 | static bool mk_font_raw(SFT_Font *ft, lbm_value font_val) { | |
32 | 15 | lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(font_val); | |
33 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if (arr) { |
34 | 15 | ft->memory = (uint8_t*)arr->data; | |
35 | 15 | ft->size = (uint_fast32_t)arr->size; | |
36 | 15 | ft->unitsPerEm = 0; | |
37 | 15 | ft->locaFormat = 0; | |
38 | 15 | ft->numLongHmtx = 0; | |
39 | 15 | return (init_font(ft) >= 0) ? true : false; | |
40 | } | ||
41 | ✗ | return false; | |
42 | } | ||
43 | |||
44 | 14 | static SFT mk_sft(SFT_Font *ft, float x_scale, float y_scale) { | |
45 | SFT sft; | ||
46 | 14 | sft.font = ft; | |
47 | 14 | sft.xScale = x_scale; | |
48 | 14 | sft.yScale = y_scale; | |
49 | 14 | sft.xOffset = 0; | |
50 | 14 | sft.yOffset = 0; | |
51 | 14 | sft.flags = SFT_DOWNWARD_Y; | |
52 | |||
53 | 14 | return sft; | |
54 | } | ||
55 | |||
56 | // If we are not bin searching then sorting the UTF32 codes is not needed. | ||
57 | |||
58 | #define FONT_MAX_ID_STRING_LENGTH 10 | ||
59 | #define FONT_VERSION 0 | ||
60 | #define FONT_MAGIC_STRING "font" | ||
61 | #define FONT_LINE_METRICS_STRING "lmtx" | ||
62 | #define FONT_KERNING_STRING "kern" | ||
63 | #define FONT_GLYPHS_STRING "glyphs" | ||
64 | |||
65 | // sizeof when used on string literals include the the terminating 0 | ||
66 | #define FONT_PREAMBLE_SIZE (sizeof(uint16_t) * 2 + sizeof(FONT_MAGIC_STRING)) | ||
67 | #define FONT_LINE_METRICS_SIZE (sizeof(uint32_t) + (sizeof(float) * 3) + sizeof(FONT_LINE_METRICS_STRING)) | ||
68 | |||
69 | // "header sizes" excluding data payload | ||
70 | #define FONT_KERN_PAIR_SIZE (uint32_t)(4 + 4 + 4) | ||
71 | #define FONT_KERN_ROW_SIZE (uint32_t)(4 + 4) | ||
72 | #define FONT_KERN_TABLE_SIZE (uint32_t)(sizeof(FONT_KERNING_STRING) + 4 + 4) | ||
73 | #define FONT_GLYPH_TABLE_SIZE (uint32_t)(sizeof(FONT_GLYPHS_STRING) + 4 + 4 + 4) | ||
74 | #define FONT_GLYPH_SIZE (uint32_t)(6*4) | ||
75 | |||
76 | 295 | static int num_kern_pairs_row(SFT *sft, uint32_t utf32, uint32_t *codes, uint32_t num_codes) { | |
77 | |||
78 | 295 | int num = 0; | |
79 | |||
80 | SFT_Glyph lgid; | ||
81 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 295 times.
|
295 | if (sft_lookup(sft, utf32, &lgid) < 0) { |
82 | ✗ | return -1; | |
83 | } | ||
84 | |||
85 |
2/2✓ Branch 0 taken 4405 times.
✓ Branch 1 taken 295 times.
|
4700 | for (uint32_t i = 0; i < num_codes; i ++) { |
86 | 4405 | uint32_t right_utf32 = codes[i]; | |
87 | SFT_Kerning kern; | ||
88 | 4405 | kern.xShift = 0.0; | |
89 | 4405 | kern.yShift = 0.0; | |
90 | |||
91 | SFT_Glyph rgid; | ||
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4405 times.
|
4405 | if (sft_lookup(sft, right_utf32, &rgid) < 0) { |
93 | ✗ | return -1; | |
94 | } | ||
95 | |||
96 |
1/2✓ Branch 0 taken 4405 times.
✗ Branch 1 not taken.
|
4405 | if (sft->font->pairAdjustOffset) { |
97 | 4405 | sft_gpos_kerning(sft, lgid, rgid, &kern); //TODO: can it fail? | |
98 | } | ||
99 |
2/4✓ Branch 0 taken 4405 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4405 times.
✗ Branch 3 not taken.
|
4405 | if (kern.xShift == 0.0 && kern.yShift == 0.0) { |
100 | 4405 | sft_kerning(sft, lgid, rgid, &kern); //TODO: can it fail? | |
101 | } | ||
102 |
3/4✓ Branch 0 taken 3991 times.
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3991 times.
|
4405 | if (kern.xShift != 0.0 || kern.yShift != 0.0) { |
103 | 414 | num++; | |
104 | } | ||
105 | } | ||
106 | 295 | return num; | |
107 | } | ||
108 | |||
109 | 26 | static bool kern_table_dims(SFT *sft, uint32_t *codes, uint32_t num_codes, int *rows, int *tot_pairs) { | |
110 | |||
111 | 26 | int num_rows = 0; | |
112 | 26 | int tot_kern_pairs = 0; | |
113 |
2/2✓ Branch 0 taken 198 times.
✓ Branch 1 taken 26 times.
|
224 | for (uint32_t i = 0; i < num_codes; i ++) { |
114 | 198 | int r = num_kern_pairs_row(sft, codes[i], codes, num_codes); | |
115 |
2/2✓ Branch 0 taken 70 times.
✓ Branch 1 taken 128 times.
|
198 | if (r > 0) { |
116 | 70 | num_rows ++; | |
117 | 70 | tot_kern_pairs += r; | |
118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
|
128 | } else if (r < 0) { |
119 | ✗ | return false; | |
120 | } | ||
121 | } | ||
122 | 26 | *rows = num_rows; | |
123 | 26 | *tot_pairs = tot_kern_pairs; | |
124 | 26 | return true; | |
125 | } | ||
126 | |||
127 | 14 | static int kern_table_size_bytes(SFT *sft, uint32_t *codes, uint32_t num_codes) { | |
128 | 14 | int rows = 0; | |
129 | 14 | int tot_pairs = 0; | |
130 | |||
131 | int size_bytes; | ||
132 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (kern_table_dims(sft, codes, num_codes, &rows, &tot_pairs)) { |
133 | 14 | size_bytes = | |
134 | 14 | (int)(FONT_KERN_PAIR_SIZE * (uint32_t)tot_pairs + | |
135 | 14 | FONT_KERN_ROW_SIZE * (uint32_t)rows + | |
136 | FONT_KERN_TABLE_SIZE); | ||
137 | } else { | ||
138 | ✗ | return -1; | |
139 | } | ||
140 | 14 | return size_bytes; | |
141 | } | ||
142 | |||
143 | |||
144 | 48 | static void buffer_append_string(uint8_t *buffer, char *str, int32_t *index) { | |
145 | 48 | size_t n = strlen(str); | |
146 | 48 | memcpy(&buffer[*index], str, n + 1); // include the 0 | |
147 | 48 | *index = *index + (int32_t)n + 1; | |
148 | 48 | } | |
149 | |||
150 | 12 | static void buffer_append_font_preamble(uint8_t *buffer, int32_t *index) { | |
151 | 12 | buffer_append_uint16(buffer, 0, index); // 2 leading zero bytes | |
152 | 12 | buffer_append_uint16(buffer, 0, index); // version 0 | |
153 | 12 | buffer_append_string(buffer, FONT_MAGIC_STRING, index); | |
154 | 12 | } | |
155 | |||
156 | 12 | static void buffer_append_line_metrics(uint8_t *buffer, float ascender, float descender, float line_gap, int32_t *index) { | |
157 | 12 | buffer_append_string(buffer, FONT_LINE_METRICS_STRING, index); | |
158 | 12 | buffer_append_uint32(buffer, sizeof(float) * 3, index); | |
159 | 12 | buffer_append_float32_auto(buffer, ascender, index); | |
160 | 12 | buffer_append_float32_auto(buffer, descender, index); | |
161 | 12 | buffer_append_float32_auto(buffer, line_gap, index); | |
162 | 12 | } | |
163 | |||
164 | |||
165 | 12 | static bool buffer_append_kerning_table(uint8_t *buffer, SFT *sft, uint32_t *codes, uint32_t num_codes, int32_t *index) { | |
166 | |||
167 | 12 | int num_rows = 0; | |
168 | 12 | int tot_pairs = 0; | |
169 | |||
170 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (kern_table_dims(sft, codes, num_codes, &num_rows, &tot_pairs)) { |
171 | |||
172 | // TODO: compute size of "payload" only | ||
173 | 12 | uint32_t size_bytes = | |
174 | 12 | FONT_KERN_PAIR_SIZE * (uint32_t)tot_pairs + | |
175 | 12 | FONT_KERN_ROW_SIZE * (uint32_t)num_rows + | |
176 | + 4; // number of rows field | ||
177 | |||
178 | 12 | buffer_append_string(buffer, FONT_KERNING_STRING, index); | |
179 | 12 | buffer_append_uint32(buffer, size_bytes, index); // distance to jump ahead from index if not interested in kerning. | |
180 | 12 | buffer_append_uint32(buffer, (uint32_t)num_rows, index); | |
181 | |||
182 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 12 times.
|
109 | for (uint32_t left_ix = 0; left_ix < num_codes; left_ix ++) { // loop over all codes |
183 | 97 | int32_t row_len = num_kern_pairs_row(sft, codes[left_ix], codes, num_codes); | |
184 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 62 times.
|
97 | if ( row_len > 0) { |
185 | SFT_Glyph lgid; | ||
186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
|
35 | if (sft_lookup(sft, codes[left_ix], &lgid) < 0) { |
187 | ✗ | return false; | |
188 | } | ||
189 | |||
190 | // format kerning table row | ||
191 | // - UTF32 : leftGlyph | ||
192 | // - uint32 : numKernPairs | ||
193 | // - KernPair[] | ||
194 | |||
195 | 35 | buffer_append_uint32(buffer, codes[left_ix],index); | |
196 | 35 | buffer_append_uint32(buffer, (uint32_t)row_len, index); | |
197 | |||
198 |
2/2✓ Branch 0 taken 639 times.
✓ Branch 1 taken 35 times.
|
674 | for (uint32_t right_ix = 0; right_ix < num_codes; right_ix ++) { // and all codes |
199 | 639 | uint32_t right_utf32 = codes[right_ix]; | |
200 | SFT_Kerning kern; | ||
201 | 639 | kern.xShift = 0.0; | |
202 | 639 | kern.yShift = 0.0; | |
203 | |||
204 | // format KernPair | ||
205 | // - UTF32 : rightGlyph | ||
206 | // - float : xShift | ||
207 | // - float : yShift | ||
208 | |||
209 | SFT_Glyph rgid; | ||
210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 639 times.
|
639 | if (sft_lookup(sft, right_utf32, &rgid) < 0) { |
211 | ✗ | return false; | |
212 | } | ||
213 | |||
214 |
1/2✓ Branch 0 taken 639 times.
✗ Branch 1 not taken.
|
639 | if (sft->font->pairAdjustOffset) { |
215 | 639 | sft_gpos_kerning(sft, lgid, rgid, &kern); //TODO: can it fail? | |
216 | } | ||
217 |
2/4✓ Branch 0 taken 639 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 639 times.
✗ Branch 3 not taken.
|
639 | if (kern.xShift == 0.0 && kern.yShift == 0.0) { |
218 | 639 | sft_kerning(sft, lgid, rgid, &kern); //TODO: can it fail? | |
219 | } | ||
220 |
3/4✓ Branch 0 taken 501 times.
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 501 times.
|
639 | if (kern.xShift != 0.0 || kern.yShift != 0.0) { |
221 | 138 | buffer_append_uint32(buffer, right_utf32, index); | |
222 | 138 | buffer_append_float32_auto(buffer, kern.xShift, index); | |
223 | 138 | buffer_append_float32_auto(buffer, kern.yShift, index); | |
224 | } | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | 12 | return true; | |
230 | } | ||
231 | |||
232 | 26 | int glyphs_img_data_size(SFT *sft, color_format_t fmt, uint32_t *codes, uint32_t num_codes) { | |
233 | 26 | int total_size = 0; | |
234 |
2/2✓ Branch 0 taken 198 times.
✓ Branch 1 taken 26 times.
|
224 | for (uint32_t i = 0; i < num_codes; i ++) { |
235 | SFT_Glyph gid; | ||
236 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 198 times.
|
198 | if (sft_lookup(sft, codes[i], &gid) < 0) return -1; |
237 | SFT_GMetrics gmtx; | ||
238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 198 times.
|
198 | if (sft_gmetrics(sft, gid, &gmtx) < 0) return -1; |
239 | 198 | total_size += (int)image_dims_to_size_bytes(fmt, (uint16_t)gmtx.minWidth, (uint16_t)gmtx.minHeight); | |
240 | } | ||
241 | 26 | return total_size; | |
242 | } | ||
243 | |||
244 | 97 | static int buffer_append_glyph(uint8_t *buffer, SFT *sft, color_format_t fmt, uint32_t utf32, int32_t *index){ | |
245 | SFT_Glyph gid; | ||
246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | if (sft_lookup(sft, utf32, &gid) < 0) return -1; |
247 | SFT_GMetrics gmtx; | ||
248 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | if (sft_gmetrics(sft, gid, &gmtx) < 0) return -1; |
249 | |||
250 | 97 | buffer_append_uint32(buffer, utf32, index); | |
251 | 97 | buffer_append_float32_auto(buffer, gmtx.advanceWidth, index); | |
252 | 97 | buffer_append_float32_auto(buffer, gmtx.leftSideBearing, index); | |
253 | 97 | buffer_append_int32(buffer,gmtx.yOffset,index); | |
254 | 97 | buffer_append_int32(buffer,gmtx.minWidth, index); | |
255 | 97 | buffer_append_int32(buffer,gmtx.minHeight, index); | |
256 | |||
257 | image_buffer_t img; | ||
258 | 97 | img.width = (uint16_t)gmtx.minWidth; | |
259 | 97 | img.height = (uint16_t)gmtx.minHeight; | |
260 | 97 | img.fmt = fmt; | |
261 | 97 | img.mem_base = &buffer[*index]; | |
262 | 97 | img.data = &buffer[*index]; | |
263 | |||
264 | 97 | int r = sft_render(sft, gid, &img); | |
265 | 97 | *index += (int32_t)image_dims_to_size_bytes(fmt, (uint16_t)gmtx.minWidth, (uint16_t)gmtx.minHeight); | |
266 | 97 | return r; | |
267 | } | ||
268 | |||
269 | 12 | static int buffer_append_glyph_table(uint8_t *buffer, SFT *sft, color_format_t fmt, uint32_t *codes, uint32_t num_codes, int32_t *index) { | |
270 | |||
271 | 12 | uint32_t size_bytes = | |
272 | 4 + // number of glyphs | ||
273 | 4 + // image format | ||
274 | 24 | num_codes * 24 + // glyph metrics | |
275 | 12 | (uint32_t)glyphs_img_data_size(sft,fmt,codes,num_codes); | |
276 | |||
277 | 12 | buffer_append_string(buffer, FONT_GLYPHS_STRING, index); | |
278 | 12 | buffer_append_uint32(buffer, size_bytes, index); // distance to jump ahead from index if not interested in kerning. | |
279 | 12 | buffer_append_uint32(buffer, num_codes, index); | |
280 | 12 | buffer_append_uint32(buffer, (uint32_t)fmt, index); | |
281 | |||
282 | 12 | int r = 0; | |
283 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 12 times.
|
109 | for (uint32_t i = 0; i < num_codes; i ++) { |
284 | 97 | r = buffer_append_glyph(buffer,sft,fmt,codes[i], index); | |
285 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | if (r < 0) return r; |
286 | } | ||
287 | 12 | return r; | |
288 | } | ||
289 | |||
290 | //returns the increment for n | ||
291 | 106 | static int insert_nub(uint32_t *arr, uint32_t n, uint32_t new_elt) { | |
292 | uint32_t i; | ||
293 |
2/2✓ Branch 0 taken 487 times.
✓ Branch 1 taken 50 times.
|
537 | for (i = 0; i < n; i ++) { |
294 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 482 times.
|
487 | if (arr[i] == new_elt) return 0; |
295 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 431 times.
|
482 | if (arr[i] > new_elt) { |
296 | 51 | memmove(&arr[i+1], &arr[i], (n - i) * 4); | |
297 | 51 | arr[i] = new_elt; | |
298 | 51 | return 1; | |
299 | } | ||
300 | } | ||
301 | 50 | arr[i] = new_elt; | |
302 | 50 | return 1; | |
303 | } | ||
304 | |||
305 | // (ttf-prepare-bin font font-scale img-fmt chars-string) | ||
306 | 16 | lbm_value ext_ttf_prepare_bin(lbm_value *args, lbm_uint argn) { | |
307 |
3/4✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
31 | if (argn == 4 && |
308 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
30 | lbm_is_array_r(args[0]) && // font file data |
309 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
30 | lbm_is_number(args[1]) && |
310 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
30 | lbm_is_symbol(args[2]) && |
311 | 15 | lbm_is_array_r(args[3])) { | |
312 | |||
313 | 15 | float x_scale = lbm_dec_as_float(args[1]); | |
314 | 15 | float y_scale = x_scale; | |
315 | |||
316 | 15 | color_format_t fmt = sym_to_color_format(args[2]); | |
317 | |||
318 | 15 | lbm_value result_array_cell = lbm_heap_allocate_cell(LBM_TYPE_CONS, ENC_SYM_NIL, ENC_SYM_ARRAY_TYPE); | |
319 | |||
320 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (result_array_cell == ENC_SYM_MERROR) return result_array_cell; |
321 | 15 | lbm_array_header_t *result_array_header = (lbm_array_header_t *)lbm_malloc(sizeof(lbm_array_header_t)); | |
322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (!result_array_header) return ENC_SYM_MERROR; |
323 | |||
324 | 15 | lbm_array_header_t *utf8_array_header = (lbm_array_header_t*)(lbm_car(args[3])); | |
325 | |||
326 | // Try to keep the utf8 array as nubbed as possible or there will be waste of mem. | ||
327 | // Unfortunate dynamic tmp storage... | ||
328 | 15 | uint32_t* unique_utf32 = lbm_malloc(utf8_array_header->size * sizeof(uint32_t)); | |
329 | |||
330 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if (unique_utf32) { |
331 | |||
332 | SFT_Font ft; | ||
333 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
|
15 | if (!mk_font_raw(&ft,args[0])) { |
334 | 1 | lbm_free(unique_utf32); | |
335 | 1 | return ENC_SYM_EERROR; | |
336 | } | ||
337 | 14 | SFT sft = mk_sft(&ft, x_scale, y_scale); | |
338 | |||
339 | // We know which glyphs to prerender... | ||
340 | // So time to start collecting information to put into the binary prerender format | ||
341 | // and to figure out how much prerender space to allocate! | ||
342 | |||
343 | 14 | uint32_t i = 0; | |
344 | 14 | uint32_t next_i = 0; | |
345 | uint32_t utf32; | ||
346 | 14 | uint32_t n = 0; | |
347 | |||
348 |
2/2✓ Branch 0 taken 106 times.
✓ Branch 1 taken 14 times.
|
120 | while (get_utf32((uint8_t*)utf8_array_header->data, &utf32, i, &next_i)) { |
349 | 106 | n += (uint32_t)insert_nub(unique_utf32, n, utf32); | |
350 | 106 | i = next_i; | |
351 | } | ||
352 | |||
353 | // There could be zero kerning pairs and then we dont | ||
354 | // need the kerning table at all. | ||
355 | // TODO: Fix this. | ||
356 | 14 | int kern_tab_bytes = kern_table_size_bytes(&sft, unique_utf32, n); | |
357 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | if (kern_tab_bytes <= 0) { |
358 | ✗ | lbm_free(unique_utf32); | |
359 | ✗ | return ENC_SYM_EERROR; | |
360 | } | ||
361 | |||
362 | 14 | int glyph_gfx_size = glyphs_img_data_size(&sft, fmt, unique_utf32, n); | |
363 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
|
14 | if (glyph_gfx_size <= 0) { |
364 | 2 | lbm_free(unique_utf32); | |
365 | 2 | return ENC_SYM_EERROR; | |
366 | } | ||
367 | |||
368 | 12 | uint32_t bytes_required = | |
369 | FONT_PREAMBLE_SIZE + | ||
370 | FONT_LINE_METRICS_SIZE + | ||
371 | 12 | (uint32_t)kern_tab_bytes + | |
372 | 12 | FONT_GLYPH_TABLE_SIZE + | |
373 | 12 | n * FONT_GLYPH_SIZE + // per glyph metrics | |
374 | ✗ | (uint32_t)glyph_gfx_size; | |
375 | |||
376 | 12 | uint8_t *buffer = (uint8_t*)lbm_malloc(bytes_required); | |
377 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!buffer) { |
378 | ✗ | lbm_free(unique_utf32); | |
379 | ✗ | return ENC_SYM_MERROR; | |
380 | } | ||
381 | 12 | memset(buffer,0, bytes_required); | |
382 | |||
383 | SFT_LMetrics lmtx; | ||
384 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (sft_lmetrics(&sft, &lmtx) < 0) { |
385 | ✗ | lbm_free(unique_utf32); | |
386 | ✗ | return ENC_SYM_EERROR; | |
387 | } | ||
388 | 12 | int32_t index = 0; | |
389 | |||
390 | 12 | buffer_append_font_preamble(buffer, &index); | |
391 | 12 | buffer_append_line_metrics(buffer, | |
392 | lmtx.ascender, | ||
393 | lmtx.descender, | ||
394 | lmtx.lineGap, | ||
395 | &index); | ||
396 | 12 | buffer_append_kerning_table(buffer, &sft, unique_utf32, n, &index); | |
397 | |||
398 | 12 | int r = buffer_append_glyph_table(buffer, &sft, fmt, unique_utf32, n, &index); | |
399 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if ( r == SFT_MEM_ERROR) { |
400 | ✗ | lbm_free(unique_utf32); | |
401 | ✗ | lbm_free(buffer); | |
402 | ✗ | lbm_set_car_and_cdr(result_array_cell, ENC_SYM_NIL, ENC_SYM_NIL); | |
403 | ✗ | return ENC_SYM_MERROR; | |
404 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | } else if (r < 0) { |
405 | ✗ | lbm_free(unique_utf32); | |
406 | ✗ | lbm_free(buffer); | |
407 | ✗ | lbm_set_car_and_cdr(result_array_cell, ENC_SYM_NIL, ENC_SYM_NIL); | |
408 | ✗ | return ENC_SYM_EERROR; | |
409 | } | ||
410 | |||
411 | 12 | lbm_free(unique_utf32); // tmp data nolonger needed | |
412 | 12 | result_array_header->size = (lbm_uint)index; | |
413 | 12 | result_array_header->data = (lbm_uint*)buffer; | |
414 | 12 | lbm_set_car(result_array_cell, (lbm_uint)result_array_header); | |
415 | 12 | result_array_cell = lbm_set_ptr_type(result_array_cell, LBM_TYPE_ARRAY); | |
416 | 12 | return result_array_cell; | |
417 | } else { | ||
418 | ✗ | return ENC_SYM_MERROR; | |
419 | } | ||
420 | } | ||
421 | 1 | return ENC_SYM_TERROR; | |
422 | } | ||
423 | |||
424 | 39 | bool buffer_get_font_preamble(uint8_t* buffer, uint16_t *version, int32_t *index) { | |
425 | |||
426 | 39 | uint16_t zero = buffer_get_uint16(buffer, index); | |
427 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (zero == 0) { |
428 | 39 | *version = buffer_get_uint16(buffer, index); | |
429 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (strncmp((const char *)&buffer[*index], "font", 4) == 0) { |
430 | 39 | *index += (int32_t)sizeof("font"); // includes 0 for constant string | |
431 | 39 | return true; | |
432 | } | ||
433 | } | ||
434 | ✗ | return false; | |
435 | } | ||
436 | |||
437 | 38 | static bool font_get_line_metrics(uint8_t *buffer, int32_t buffer_size, float *ascender, float *descender, float *line_gap ,int32_t index) { | |
438 | |||
439 |
1/2✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
|
38 | while(index < buffer_size) { |
440 | 38 | char *str = (char*)&buffer[index]; | |
441 |
1/2✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
|
38 | if (strncmp(str, "lmtx", 4) == 0) { |
442 | 38 | int32_t i = index + 5 + 4; // skip over string and size field; | |
443 | 38 | *ascender = buffer_get_float32_auto(buffer, &i); | |
444 | 38 | *descender = buffer_get_float32_auto(buffer, &i); | |
445 | 38 | *line_gap = buffer_get_float32_auto(buffer, &i); | |
446 | 38 | return true; | |
447 | } | ||
448 | ✗ | index += (int32_t)(strlen(str) + 1); | |
449 | ✗ | index += (int32_t)buffer_get_uint32(buffer,&index); // just to next position | |
450 | } | ||
451 | ✗ | return false; | |
452 | } | ||
453 | |||
454 | 34 | static bool font_get_kerning_table_index(uint8_t *buffer, int32_t buffer_size, int32_t *res_index, int32_t index) { | |
455 | |||
456 |
1/2✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
|
68 | while (index < buffer_size) { |
457 | 68 | char *str = (char*)&buffer[index]; | |
458 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 34 times.
|
68 | if (strncmp(str, "kern", 4) == 0) { |
459 | 34 | *res_index = index + 5 + 4; | |
460 | 34 | return true; | |
461 | } | ||
462 | 34 | index += (int32_t)(strlen(str) + 1); | |
463 | 34 | index += (int32_t)buffer_get_uint32(buffer,&index); // jump to next position | |
464 | } | ||
465 | ✗ | return false; | |
466 | } | ||
467 | |||
468 | 35 | static bool font_get_glyphs_table_index(uint8_t *buffer, int32_t buffer_size, int32_t *res_index, uint32_t *num_codes, uint32_t *fmt, int32_t index) { | |
469 |
1/2✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
|
105 | while (index < buffer_size) { |
470 | 105 | char *str = (char*)&buffer[index]; | |
471 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 70 times.
|
105 | if (strncmp(str, "glyphs", 6) == 0) { |
472 | 35 | int32_t i = index + 7 + 4; | |
473 | 35 | *num_codes = buffer_get_uint32(buffer,&i); | |
474 | 35 | *fmt = buffer_get_uint32(buffer,&i); | |
475 | 35 | *res_index = i; | |
476 | 35 | return true; | |
477 | } | ||
478 | 70 | index += (int32_t)(strlen(str) + 1); | |
479 | 70 | index += (int32_t)buffer_get_uint32(buffer,&index); | |
480 | } | ||
481 | ✗ | return false; | |
482 | } | ||
483 | |||
484 | 268 | static bool font_get_glyph(uint8_t *buffer, | |
485 | float *advance_width, | ||
486 | float *left_side_bearing, | ||
487 | int32_t *y_offset, | ||
488 | int32_t *width, | ||
489 | int32_t *height, | ||
490 | uint8_t **gfx, | ||
491 | uint32_t utf32, | ||
492 | uint32_t num_codes, | ||
493 | color_format_t fmt, | ||
494 | int32_t index) { | ||
495 | |||
496 | 268 | uint32_t i = 0; | |
497 |
1/2✓ Branch 0 taken 2640 times.
✗ Branch 1 not taken.
|
2640 | while (i < num_codes) { |
498 | 2640 | uint32_t c = buffer_get_uint32(buffer, &index); | |
499 |
2/2✓ Branch 0 taken 268 times.
✓ Branch 1 taken 2372 times.
|
2640 | if (c == utf32) { |
500 | 268 | *advance_width = buffer_get_float32_auto(buffer, &index); | |
501 | 268 | *left_side_bearing = buffer_get_float32_auto(buffer, &index); | |
502 | 268 | *y_offset = buffer_get_int32(buffer, &index); | |
503 | 268 | *width = buffer_get_int32(buffer, &index); | |
504 | 268 | *height = buffer_get_int32(buffer,&index); | |
505 | 268 | *gfx = &buffer[index]; | |
506 | 268 | return true; | |
507 | } else { | ||
508 | 2372 | index += 12; | |
509 | 2372 | int32_t w = buffer_get_int32(buffer, &index); | |
510 | 2372 | int32_t h = buffer_get_int32(buffer, &index); | |
511 | 2372 | index += (int32_t)image_dims_to_size_bytes(fmt, (uint16_t)w, (uint16_t)h); | |
512 | } | ||
513 | 2372 | i++; | |
514 | } | ||
515 | ✗ | return false; | |
516 | } | ||
517 | |||
518 | 236 | bool font_get_kerning(uint8_t *buffer, uint32_t left, uint32_t right, float *x_shift, float *y_shift, int32_t index) { | |
519 | |||
520 | 236 | uint32_t num_rows = buffer_get_uint32(buffer, &index); | |
521 | |||
522 |
2/2✓ Branch 0 taken 2169 times.
✓ Branch 1 taken 195 times.
|
2364 | for (uint32_t row = 0; row < num_rows; row ++) { |
523 | |||
524 | 2169 | uint32_t row_code = buffer_get_uint32(buffer, &index); | |
525 | 2169 | uint32_t row_len = buffer_get_uint32(buffer, &index); | |
526 | |||
527 |
2/2✓ Branch 0 taken 168 times.
✓ Branch 1 taken 2001 times.
|
2169 | if (row_code == left) { |
528 |
2/2✓ Branch 0 taken 255 times.
✓ Branch 1 taken 127 times.
|
382 | for (uint32_t col = 0; col < row_len; col ++) { |
529 | 255 | uint32_t col_code = buffer_get_uint32(buffer, &index); | |
530 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 214 times.
|
255 | if (col_code == right) { |
531 | 41 | *x_shift = buffer_get_float32_auto(buffer, &index); | |
532 | 41 | *y_shift = buffer_get_float32_auto(buffer, &index); | |
533 | 41 | return true; | |
534 | } else { | ||
535 | 214 | index += 8; | |
536 | } | ||
537 | } | ||
538 | } else { | ||
539 | 2001 | index += (int32_t)(row_len * FONT_KERN_PAIR_SIZE); | |
540 | } | ||
541 | } | ||
542 | 195 | return false; | |
543 | } | ||
544 | |||
545 | 36 | lbm_value ttf_text_bin(lbm_value *args, lbm_uint argn) { | |
546 | 36 | lbm_value res = ENC_SYM_TERROR; | |
547 | lbm_array_header_t *img_arr; | ||
548 | lbm_value font; | ||
549 | char *utf8_str; | ||
550 | uint32_t colors[16]; | ||
551 | 36 | uint32_t next_arg = 0; | |
552 |
3/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 2 times.
|
72 | if (argn >= 6 && |
553 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
70 | (img_arr = get_image_buffer(args[0])) && |
554 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
68 | lbm_is_number(args[1]) && // x position |
555 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
68 | lbm_is_number(args[2]) && // y position |
556 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
68 | lbm_is_cons(args[3]) && // list of colors |
557 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1 times.
|
68 | lbm_is_array_r(args[4]) && // Binary font |
558 | 34 | lbm_is_array_r(args[5])) { // sequence of utf8 characters | |
559 | 33 | lbm_value curr = args[3]; | |
560 | 33 | int i = 0; | |
561 |
3/4✓ Branch 0 taken 132 times.
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 132 times.
✗ Branch 3 not taken.
|
165 | while(lbm_is_cons(curr) && i < 16) { |
562 | 132 | colors[i] = lbm_dec_as_u32(lbm_car(curr)); | |
563 | 132 | curr = lbm_cdr(curr); | |
564 | 132 | i ++; | |
565 | } | ||
566 | 33 | font = args[4]; | |
567 | 33 | utf8_str = lbm_dec_str(args[5]); | |
568 | 33 | next_arg = 6; | |
569 | } else { | ||
570 | 3 | return res; | |
571 | } | ||
572 | |||
573 | 33 | int x_pos = lbm_dec_as_i32(args[1]); | |
574 | 33 | int y_pos = lbm_dec_as_i32(args[2]); | |
575 | |||
576 | 33 | float line_spacing = 1.0f; | |
577 | 33 | bool up = false; | |
578 | 33 | bool down = false; | |
579 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 33 times.
|
58 | for (uint32_t i = next_arg; i < argn; i ++) { |
580 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 times.
|
25 | if (lbm_is_symbol(args[i])) { |
581 | 24 | up = display_is_symbol_up(args[i]); | |
582 | 24 | down = display_is_symbol_down(args[i]); | |
583 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | } else if (lbm_is_number(args[i])) { |
584 | 1 | line_spacing = lbm_dec_as_float(args[i]); | |
585 | } | ||
586 | } | ||
587 | |||
588 | 33 | lbm_array_header_t *font_arr = lbm_dec_array_r(font); | |
589 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
590 | |||
591 | 33 | int32_t index = 0; | |
592 | uint16_t version; | ||
593 | |||
594 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
595 | ✗ | return ENC_SYM_EERROR; | |
596 | } | ||
597 | |||
598 | float ascender; | ||
599 | float descender; | ||
600 | float line_gap; | ||
601 | |||
602 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if(!font_get_line_metrics((uint8_t*)font_arr->data, (int32_t)font_arr->size, &ascender, &descender, &line_gap , index)) { |
603 | ✗ | return ENC_SYM_EERROR; | |
604 | } | ||
605 | |||
606 | 33 | int32_t kern_index = 0; | |
607 | |||
608 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (!font_get_kerning_table_index((uint8_t*)font_arr->data, (int32_t)font_arr->size, &kern_index, index)) { |
609 | ✗ | return ENC_SYM_EERROR; | |
610 | } | ||
611 | |||
612 | 33 | int32_t glyphs_index = 0; | |
613 | uint32_t num_codes; | ||
614 | uint32_t color_fmt; | ||
615 | |||
616 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (!font_get_glyphs_table_index((uint8_t*)font_arr->data, (int32_t)font_arr->size, &glyphs_index, &num_codes, &color_fmt, index)) { |
617 | ✗ | return ENC_SYM_EERROR; | |
618 | } | ||
619 | |||
620 | 33 | color_format_t fmt = (color_format_t)color_fmt; | |
621 | 33 | float x = 0.0; | |
622 | 33 | float y = 0.0; | |
623 | |||
624 | image_buffer_t tgt; | ||
625 | 33 | tgt.width = image_buffer_width((uint8_t*)img_arr->data); | |
626 | 33 | tgt.height = image_buffer_height((uint8_t*)img_arr->data); | |
627 | 33 | tgt.fmt = image_buffer_format((uint8_t*)img_arr->data); | |
628 | 33 | tgt.mem_base = (uint8_t*)img_arr->data; | |
629 | 33 | tgt.data = image_buffer_data((uint8_t*)img_arr->data); | |
630 | |||
631 | uint32_t utf32; | ||
632 | uint32_t prev; | ||
633 | 33 | bool has_prev = false; | |
634 | 33 | uint32_t i = 0; | |
635 | 33 | uint32_t next_i = 0; | |
636 |
2/2✓ Branch 0 taken 263 times.
✓ Branch 1 taken 33 times.
|
296 | while (get_utf32((uint8_t*)utf8_str, &utf32, i, &next_i)) { |
637 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 262 times.
|
263 | if (utf32 == '\n') { |
638 | 1 | x = 0.0; | |
639 | 1 | y += line_spacing * (ascender - descender + line_gap); | |
640 | 1 | i++; | |
641 | 1 | continue; // next iteration | |
642 | } | ||
643 | |||
644 | 262 | float x_n = x; | |
645 | 262 | float y_n = y; | |
646 | |||
647 | float advance_width; | ||
648 | float left_side_bearing; | ||
649 | int32_t y_offset; | ||
650 | int32_t width; | ||
651 | int32_t height; | ||
652 | uint8_t *gfx; | ||
653 | |||
654 |
1/2✓ Branch 0 taken 262 times.
✗ Branch 1 not taken.
|
262 | if (font_get_glyph((uint8_t*)font_arr->data, |
655 | &advance_width, | ||
656 | &left_side_bearing, | ||
657 | &y_offset, | ||
658 | &width, | ||
659 | &height, | ||
660 | &gfx, | ||
661 | utf32, | ||
662 | num_codes, | ||
663 | fmt, | ||
664 | glyphs_index)) { | ||
665 | |||
666 | 262 | float x_shift = 0; | |
667 | 262 | float y_shift = 0; | |
668 |
2/2✓ Branch 0 taken 232 times.
✓ Branch 1 taken 30 times.
|
262 | if (has_prev) { |
669 | 232 | font_get_kerning((uint8_t*)font_arr->data, | |
670 | prev, | ||
671 | utf32, | ||
672 | &x_shift, | ||
673 | &y_shift, | ||
674 | kern_index); | ||
675 | } | ||
676 | 262 | x_n += x_shift; | |
677 | 262 | y_n += y_shift; | |
678 | 262 | y_n += (float)y_offset; | |
679 | |||
680 | image_buffer_t src; | ||
681 | 262 | src.width = (uint16_t)width; | |
682 | 262 | src.height = (uint16_t)height; | |
683 | 262 | src.fmt = fmt; | |
684 | //src.mem_base = gfx; | ||
685 | 262 | src.data = gfx; | |
686 | |||
687 | 262 | uint32_t num_colors = 1 << src.fmt; | |
688 |
2/2✓ Branch 0 taken 5406 times.
✓ Branch 1 taken 262 times.
|
5668 | for (int j = 0; j < src.height; j++) { |
689 |
2/2✓ Branch 0 taken 90134 times.
✓ Branch 1 taken 5406 times.
|
95540 | for (int k = 0; k < src.width; k ++) { |
690 | // the bearing should not be accumulated into the advances | ||
691 | |||
692 | 90134 | uint32_t p = getpixel(&src, k, j); | |
693 |
2/2✓ Branch 0 taken 37931 times.
✓ Branch 1 taken 52203 times.
|
90134 | if (p) { // only draw colored |
694 | 37931 | uint32_t c = colors[p & (num_colors-1)]; // ceiled | |
695 |
2/2✓ Branch 0 taken 12256 times.
✓ Branch 1 taken 25675 times.
|
37931 | if (up) { |
696 | 12256 | putpixel(&tgt, x_pos + (j + (int)y_n), y_pos - (k + (int)(x_n + left_side_bearing)), c); | |
697 |
2/2✓ Branch 0 taken 15604 times.
✓ Branch 1 taken 10071 times.
|
25675 | } else if (down) { |
698 | 15604 | putpixel(&tgt, x_pos - (j + (int)y_n), y_pos + (k + (int)(x_n + left_side_bearing)), c); | |
699 | } else { | ||
700 | 10071 | putpixel(&tgt, x_pos + (k + (int)(x_n + left_side_bearing)), y_pos + (j + (int)y_n), c); | |
701 | } | ||
702 | } | ||
703 | } | ||
704 | } | ||
705 | } else { | ||
706 | ✗ | lbm_set_error_reason("Character is not one of those listed in ttf-prepare\n"); | |
707 | ✗ | return ENC_SYM_EERROR; | |
708 | } | ||
709 | 262 | x = x_n + advance_width; | |
710 | 262 | i = next_i; | |
711 | 262 | prev = utf32; | |
712 | 262 | has_prev = true; | |
713 | } | ||
714 | 33 | return ENC_SYM_TRUE; | |
715 | } | ||
716 | |||
717 | 2 | lbm_value ext_ttf_wh(lbm_value *args, lbm_uint argn) { | |
718 | 2 | lbm_value res = ENC_SYM_TERROR; | |
719 | lbm_value font; | ||
720 | char *utf8_str; | ||
721 | 2 | uint32_t next_arg = 0; | |
722 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | if (argn >= 2 && |
723 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
4 | lbm_is_array_r(args[0]) && // Binary font |
724 | 2 | lbm_is_array_r(args[1])) { // sequence of utf8 characters | |
725 | 2 | font = args[0]; | |
726 | 2 | utf8_str = lbm_dec_str(args[1]); | |
727 | 2 | next_arg = 2; | |
728 | } else { | ||
729 | ✗ | return res; | |
730 | } | ||
731 | |||
732 | 2 | float line_spacing = 1.0f; | |
733 | 2 | bool up = false; | |
734 | 2 | bool down = false; | |
735 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | for (uint32_t i = next_arg; i < argn; i ++) { |
736 | ✗ | if (lbm_is_symbol(args[i])) { | |
737 | ✗ | up = display_is_symbol_up(args[i]); | |
738 | ✗ | down = display_is_symbol_down(args[i]); | |
739 | ✗ | } else if (lbm_is_number(args[i])) { | |
740 | ✗ | line_spacing = lbm_dec_as_float(args[i]); | |
741 | } | ||
742 | } | ||
743 | |||
744 | 2 | lbm_value r_list = lbm_heap_allocate_list(2); | |
745 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (lbm_is_symbol(r_list)) return r_list; |
746 | |||
747 | 2 | lbm_array_header_t *font_arr = lbm_dec_array_r(font); | |
748 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
749 | |||
750 | 1 | int32_t index = 0; | |
751 | uint16_t version; | ||
752 | |||
753 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
754 | ✗ | return ENC_SYM_EERROR; | |
755 | } | ||
756 | |||
757 | float ascender; | ||
758 | float descender; | ||
759 | float line_gap; | ||
760 | |||
761 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if(!font_get_line_metrics((uint8_t*)font_arr->data, (int32_t)font_arr->size, &ascender, &descender, &line_gap , index)) { |
762 | ✗ | return ENC_SYM_EERROR; | |
763 | } | ||
764 | |||
765 | 1 | int32_t kern_index = 0; | |
766 | |||
767 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!font_get_kerning_table_index((uint8_t*)font_arr->data, (int32_t)font_arr->size, &kern_index, index)) { |
768 | ✗ | return ENC_SYM_EERROR; | |
769 | } | ||
770 | |||
771 | 1 | int32_t glyphs_index = 0; | |
772 | uint32_t num_codes; | ||
773 | uint32_t color_fmt; | ||
774 | |||
775 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!font_get_glyphs_table_index((uint8_t*)font_arr->data, (int32_t)font_arr->size, &glyphs_index, &num_codes, &color_fmt, index)) { |
776 | ✗ | return ENC_SYM_EERROR; | |
777 | } | ||
778 | |||
779 | 1 | float x = 0.0; | |
780 | 1 | float y = 0.0; | |
781 | 1 | float max_x = 0.0; | |
782 | |||
783 | uint32_t utf32; | ||
784 | uint32_t prev; | ||
785 | 1 | bool has_prev = false; | |
786 | 1 | uint32_t i = 0; | |
787 | 1 | uint32_t next_i = 0; | |
788 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | while (get_utf32((uint8_t*)utf8_str, &utf32, i, &next_i)) { |
789 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (utf32 == '\n') { |
790 | ✗ | if (x > max_x) max_x = x; | |
791 | ✗ | x = 0.0; | |
792 | ✗ | y += line_spacing * (ascender - descender + line_gap); | |
793 | ✗ | i++; | |
794 | ✗ | continue; // next iteration | |
795 | } | ||
796 | |||
797 | 5 | float x_n = x; | |
798 | |||
799 | float advance_width; | ||
800 | float left_side_bearing; | ||
801 | int32_t y_offset; | ||
802 | int32_t width; | ||
803 | int32_t height; | ||
804 | uint8_t *gfx; | ||
805 | |||
806 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (font_get_glyph((uint8_t*)font_arr->data, |
807 | &advance_width, | ||
808 | &left_side_bearing, | ||
809 | &y_offset, | ||
810 | &width, | ||
811 | &height, | ||
812 | &gfx, | ||
813 | utf32, | ||
814 | num_codes, | ||
815 | (color_format_t)color_fmt, | ||
816 | glyphs_index)) { | ||
817 | |||
818 | 5 | float x_shift = 0; | |
819 | 5 | float y_shift = 0; | |
820 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (has_prev) { |
821 | 4 | font_get_kerning((uint8_t*)font_arr->data, | |
822 | prev, | ||
823 | utf32, | ||
824 | &x_shift, | ||
825 | &y_shift, | ||
826 | kern_index); | ||
827 | } | ||
828 | 5 | x_n += x_shift; | |
829 | } else { | ||
830 | ✗ | return ENC_SYM_EERROR; | |
831 | } | ||
832 | 5 | x = x_n + advance_width; | |
833 | 5 | i = next_i; | |
834 | 5 | prev = utf32; | |
835 | 5 | has_prev = true; | |
836 | } | ||
837 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (max_x < x) max_x = x; |
838 | 1 | lbm_value rest = lbm_cdr(r_list); | |
839 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (up || down) { |
840 | ✗ | lbm_set_car(r_list, lbm_enc_u((uint32_t)(y + line_spacing * (ascender - descender + line_gap)))); | |
841 | ✗ | lbm_set_car(rest, lbm_enc_u((uint32_t)max_x)); | |
842 | } else { | ||
843 | 1 | lbm_set_car(r_list, lbm_enc_u((uint32_t)max_x)); | |
844 | 1 | lbm_set_car(rest, lbm_enc_u((uint32_t)(y + line_spacing * (ascender - descender + line_gap)))); | |
845 | } | ||
846 | 1 | return r_list; | |
847 | } | ||
848 | |||
849 | 1 | lbm_value ext_ttf_glyph_dims(lbm_value *args, lbm_uint argn) { | |
850 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | if (argn == 2 && |
851 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
2 | lbm_is_array_r(args[0]) && |
852 | 1 | lbm_is_array_r(args[1])) { // string utf8, | |
853 | |||
854 | 1 | lbm_array_header_t *font_arr = lbm_dec_array_r(args[0]); | |
855 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | if (!font_arr) return ENC_SYM_FATAL_ERROR; |
856 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
857 | |||
858 | 1 | int32_t index = 0; | |
859 | uint16_t version; | ||
860 | |||
861 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
862 | ✗ | return ENC_SYM_EERROR; | |
863 | } | ||
864 | |||
865 | 1 | int32_t glyphs_index = 0; | |
866 | uint32_t num_codes; | ||
867 | uint32_t color_fmt; | ||
868 | |||
869 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!font_get_glyphs_table_index((uint8_t*)font_arr->data, (int32_t)font_arr->size, &glyphs_index, &num_codes, &color_fmt, index)) { |
870 | ✗ | return ENC_SYM_EERROR; | |
871 | } | ||
872 | |||
873 | 1 | lbm_array_header_t *utf8_array_header = (lbm_array_header_t*)(lbm_car(args[1])); | |
874 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!utf8_array_header) return ENC_SYM_FATAL_ERROR; |
875 | |||
876 | 1 | uint32_t next_i = 0; | |
877 | 1 | uint32_t utf32 = 0; | |
878 | 1 | get_utf32((uint8_t*)utf8_array_header->data, &utf32, 0, &next_i); | |
879 | |||
880 | float advance_width; | ||
881 | float left_side_bearing; | ||
882 | int32_t y_offset; | ||
883 | int32_t width; | ||
884 | int32_t height; | ||
885 | uint8_t *gfx; | ||
886 | |||
887 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (font_get_glyph((uint8_t*)font_arr->data, |
888 | &advance_width, | ||
889 | &left_side_bearing, | ||
890 | &y_offset, | ||
891 | &width, | ||
892 | &height, | ||
893 | &gfx, | ||
894 | utf32, | ||
895 | num_codes, | ||
896 | (color_format_t)color_fmt, | ||
897 | glyphs_index)) { | ||
898 | |||
899 | 1 | return lbm_heap_allocate_list_init(2, | |
900 | ✗ | lbm_enc_u((uint32_t)(width)), | |
901 | ✗ | lbm_enc_u((uint32_t)height)); | |
902 | } | ||
903 | } | ||
904 | ✗ | return ENC_SYM_TERROR; | |
905 | } | ||
906 | |||
907 | 2 | lbm_value ext_ttf_line_height(lbm_value *args, lbm_uint argn) { | |
908 | 2 | lbm_value res = ENC_SYM_TERROR; | |
909 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | if (argn == 1 && |
910 | 2 | lbm_is_array_r(args[0])) { | |
911 | |||
912 | 2 | lbm_array_header_t *font_arr = lbm_dec_array_r(args[0]); | |
913 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
3 | if (!font_arr) return ENC_SYM_FATAL_ERROR; |
914 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
915 | |||
916 | 1 | int32_t index = 0; | |
917 | uint16_t version; | ||
918 | |||
919 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
920 | ✗ | return ENC_SYM_EERROR; | |
921 | } | ||
922 | |||
923 | float ascender; | ||
924 | float descender; | ||
925 | float line_gap; | ||
926 | |||
927 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if(!font_get_line_metrics((uint8_t*)font_arr->data, (int32_t)font_arr->size, &ascender, &descender, &line_gap , index)) { |
928 | ✗ | return ENC_SYM_EERROR; | |
929 | } | ||
930 | |||
931 | 1 | res = lbm_enc_float(ascender - descender + line_gap); | |
932 | } | ||
933 | 1 | return res; | |
934 | } | ||
935 | |||
936 | 1 | lbm_value ext_ttf_ascender(lbm_value *args, lbm_uint argn) { | |
937 | 1 | lbm_value res = ENC_SYM_TERROR; | |
938 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | if (argn == 1 && |
939 | 1 | lbm_is_array_r(args[0])) { | |
940 | |||
941 | 1 | lbm_array_header_t *font_arr = lbm_dec_array_r(args[0]); | |
942 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!font_arr) return ENC_SYM_FATAL_ERROR; |
943 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
944 | |||
945 | 1 | int32_t index = 0; | |
946 | uint16_t version; | ||
947 | |||
948 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
949 | ✗ | return ENC_SYM_EERROR; | |
950 | } | ||
951 | |||
952 | float ascender; | ||
953 | float descender; | ||
954 | float line_gap; | ||
955 | |||
956 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if(!font_get_line_metrics((uint8_t*)font_arr->data, (int32_t)font_arr->size, &ascender, &descender, &line_gap , index)) { |
957 | ✗ | return ENC_SYM_EERROR; | |
958 | } | ||
959 | |||
960 | 1 | res = lbm_enc_float(ascender); | |
961 | } | ||
962 | 1 | return res; | |
963 | } | ||
964 | |||
965 | 1 | lbm_value ext_ttf_descender(lbm_value *args, lbm_uint argn) { | |
966 | 1 | lbm_value res = ENC_SYM_TERROR; | |
967 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | if (argn == 1 && |
968 | 1 | lbm_is_array_r(args[0])) { | |
969 | |||
970 | 1 | lbm_array_header_t *font_arr = lbm_dec_array_r(args[0]); | |
971 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!font_arr) return ENC_SYM_FATAL_ERROR; |
972 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
973 | |||
974 | 1 | int32_t index = 0; | |
975 | uint16_t version; | ||
976 | |||
977 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
978 | ✗ | return ENC_SYM_EERROR; | |
979 | } | ||
980 | |||
981 | float ascender; | ||
982 | float descender; | ||
983 | float line_gap; | ||
984 | |||
985 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if(!font_get_line_metrics((uint8_t*)font_arr->data, (int32_t)font_arr->size, &ascender, &descender, &line_gap , index)) { |
986 | ✗ | return ENC_SYM_EERROR; | |
987 | } | ||
988 | |||
989 | 1 | res = lbm_enc_float(descender); | |
990 | } | ||
991 | 1 | return res; | |
992 | } | ||
993 | |||
994 | 1 | lbm_value ext_ttf_line_gap(lbm_value *args, lbm_uint argn) { | |
995 | 1 | lbm_value res = ENC_SYM_TERROR; | |
996 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | if (argn == 1 && |
997 | 1 | lbm_is_array_r(args[0])) { | |
998 | |||
999 | 1 | lbm_array_header_t *font_arr = lbm_dec_array_r(args[0]); | |
1000 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!font_arr) return ENC_SYM_FATAL_ERROR; |
1001 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (font_arr->size < 10) return ENC_SYM_EERROR; |
1002 | |||
1003 | 1 | int32_t index = 0; | |
1004 | uint16_t version; | ||
1005 | |||
1006 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!buffer_get_font_preamble((uint8_t*)font_arr->data, &version, &index)) { |
1007 | ✗ | return ENC_SYM_EERROR; | |
1008 | } | ||
1009 | |||
1010 | float ascender; | ||
1011 | float descender; | ||
1012 | float line_gap; | ||
1013 | |||
1014 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if(!font_get_line_metrics((uint8_t*)font_arr->data, (int32_t)font_arr->size, &ascender, &descender, &line_gap , index)) { |
1015 | ✗ | return ENC_SYM_EERROR; | |
1016 | } | ||
1017 | |||
1018 | 1 | res = lbm_enc_float(line_gap); | |
1019 | } | ||
1020 | 1 | return res; | |
1021 | } | ||
1022 | |||
1023 | 188 | void lbm_ttf_extensions_init(void) { | |
1024 | |||
1025 | // metrics | ||
1026 | 188 | lbm_add_extension("ttf-line-height", ext_ttf_line_height); | |
1027 | 188 | lbm_add_extension("ttf-ascender", ext_ttf_ascender); | |
1028 | 188 | lbm_add_extension("ttf-descender", ext_ttf_descender); | |
1029 | 188 | lbm_add_extension("ttf-line-gap", ext_ttf_line_gap); | |
1030 | 188 | lbm_add_extension("ttf-text-dims",ext_ttf_wh); | |
1031 | 188 | lbm_add_extension("ttf-glyph-dims",ext_ttf_glyph_dims); | |
1032 | |||
1033 | // Prepare | ||
1034 | 188 | lbm_add_extension("ttf-prepare", ext_ttf_prepare_bin); | |
1035 | |||
1036 | // Draw text. | ||
1037 | 188 | lbm_add_extension("ttf-text", ttf_text_bin); | |
1038 | 188 | } | |
1039 |