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