text.c (29987B)
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <stdbool.h>
4 #include "files.h"
5 #include "display.h"
6 #include "text.h"
7
8 #define ONE 65536
9 #define MAX_COORD 16384
10 #define MAX_SIZE 2048
11 #define MAX_SEGMENTS 64
12
13 typedef struct {
14 int32_t x;
15 int32_t y;
16 } ta_text_pt;
17
18 typedef struct {
19 int32_t clip_x0;
20 int32_t clip_y0;
21 int32_t clip_x1;
22 int32_t clip_y1;
23 int32_t scale;
24 uint8_t red;
25 uint8_t green;
26 uint8_t blue;
27 uint8_t border_thickness;
28 uint8_t border_red;
29 uint8_t border_green;
30 uint8_t border_blue;
31 uint32_t *sprite_pixels;
32 int32_t sprite_x;
33 int32_t sprite_y;
34 int32_t sprite_width;
35 } ta_text_ctx;
36
37 typedef struct {
38 const uint8_t *glyf;
39 const uint8_t *loca;
40 const uint8_t *hmtx;
41 const uint8_t *cmap;
42 uint32_t glyf_length;
43 uint32_t cmap_length;
44 uint32_t units_per_em;
45 uint32_t horizontal_metric_count;
46 uint32_t glyph_count;
47 bool long_loca;
48 int32_t ascent;
49 int32_t descent;
50 int32_t line_gap;
51 } ta_text_font;
52
53 static ta_text_font font;
54 static int font_state;
55
56 static uint32_t read_u16(const uint8_t *data) {
57 return ((uint32_t)data[0] << 8) | data[1];
58 }
59
60 static int32_t read_s16(const uint8_t *data) {
61 uint32_t value = read_u16(data);
62
63 if (value >= 0x8000) {
64 return (int32_t)value - 0x10000;
65 }
66
67 return (int32_t)value;
68 }
69
70 static uint32_t read_u32(const uint8_t *data) {
71 return (read_u16(data) << 16) | read_u16(data + 2);
72 }
73
74 static uint64_t unsigned_divide64(uint64_t numerator, uint32_t denominator) {
75 #if TA_BITS == 64
76 return numerator / denominator;
77 #else
78 if ((numerator >> 32) == 0) {
79 return (uint32_t)numerator / denominator;
80 }
81
82 uint64_t quotient = 0;
83 uint64_t remainder = 0;
84
85 for (int bit = 63; bit >= 0; bit--) {
86 remainder = (remainder << 1) | ((numerator >> bit) & 1);
87
88 if (remainder >= denominator) {
89 remainder -= denominator;
90 quotient |= (uint64_t)1 << bit;
91 }
92 }
93
94 return quotient;
95 #endif
96 }
97
98 static int64_t signed_divide64(int64_t numerator, int32_t denominator) {
99 bool negative = false;
100 uint64_t unsigned_numerator;
101 uint32_t unsigned_denominator;
102
103 if (numerator < 0) {
104 negative = !negative;
105 unsigned_numerator = (uint64_t)(-numerator);
106 } else {
107 unsigned_numerator = (uint64_t)numerator;
108 }
109
110 if (denominator < 0) {
111 negative = !negative;
112 unsigned_denominator = (uint32_t)(-denominator);
113 } else {
114 unsigned_denominator = (uint32_t)denominator;
115 }
116
117 int64_t quotient = (int64_t)unsigned_divide64(unsigned_numerator, unsigned_denominator);
118
119 return negative ? -quotient : quotient;
120 }
121
122 static int32_t fixed_multiply(int32_t a, int32_t b) {
123 return (int32_t)(((int64_t)a * b) >> 16);
124 }
125
126 static const uint8_t *find_table(const uint8_t *data, uint32_t length, const char *tag, uint32_t *out_length) {
127 if (length < 12) {
128 return 0;
129 }
130
131 uint32_t table_count = read_u16(data + 4);
132
133 if (length < 12 + table_count * 16) {
134 return 0;
135 }
136
137 for (uint32_t i = 0; i < table_count; i++) {
138 const uint8_t *record = data + 12 + i * 16;
139
140 if (record[0] == (uint8_t)tag[0] && record[1] == (uint8_t)tag[1] && record[2] == (uint8_t)tag[2] && record[3] == (uint8_t)tag[3]) {
141 uint32_t offset = read_u32(record + 8);
142 uint32_t table_length = read_u32(record + 12);
143
144 if (offset > length || table_length > length - offset) {
145 return 0;
146 }
147
148 *out_length = table_length;
149
150 return data + offset;
151 }
152 }
153
154 return 0;
155 }
156
157 static bool font_load(void) {
158 const uint8_t *font_data = ta_file_dejavu_sans;
159 uint32_t font_length = ta_file_dejavu_sans_len;
160
161 uint32_t head_length;
162 uint32_t hhea_length;
163 uint32_t maxp_length;
164 uint32_t loca_length;
165 uint32_t hmtx_length;
166 uint32_t cmap_length;
167
168 const uint8_t *head = find_table(font_data, font_length, "head", &head_length);
169 const uint8_t *hhea = find_table(font_data, font_length, "hhea", &hhea_length);
170 const uint8_t *maxp = find_table(font_data, font_length, "maxp", &maxp_length);
171 const uint8_t *cmap = find_table(font_data, font_length, "cmap", &cmap_length);
172
173 font.loca = find_table(font_data, font_length, "loca", &loca_length);
174 font.hmtx = find_table(font_data, font_length, "hmtx", &hmtx_length);
175 font.glyf = find_table(font_data, font_length, "glyf", &font.glyf_length);
176
177 if (!head || !hhea || !maxp || !font.loca || !font.hmtx || !font.glyf || !cmap) {
178 return false;
179 }
180
181 if (head_length < 54 || hhea_length < 36 || maxp_length < 6 || cmap_length < 4) {
182 return false;
183 }
184
185 font.units_per_em = read_u16(head + 18);
186 font.long_loca = read_s16(head + 50) != 0;
187 font.ascent = read_s16(hhea + 4);
188 font.descent = read_s16(hhea + 6);
189 font.line_gap = read_s16(hhea + 8);
190 font.horizontal_metric_count = read_u16(hhea + 34);
191 font.glyph_count = read_u16(maxp + 4);
192
193 if (font.units_per_em == 0 || font.horizontal_metric_count == 0 || font.glyph_count == 0) {
194 return false;
195 }
196
197 if (font.ascent - font.descent + font.line_gap <= 0) {
198 return false;
199 }
200
201 if (loca_length < (font.glyph_count + 1) * (font.long_loca ? 4u : 2u)) {
202 return false;
203 }
204
205 if (hmtx_length < font.horizontal_metric_count * 4) {
206 return false;
207 }
208
209 const uint8_t *best_subtable = 0;
210 uint32_t best_length = 0;
211 uint32_t subtable_count = read_u16(cmap + 2);
212
213 if (cmap_length < 4 + subtable_count * 8) {
214 return false;
215 }
216
217 for (uint32_t i = 0; i < subtable_count; i++) {
218 const uint8_t *record = cmap + 4 + i * 8;
219 uint32_t platform = read_u16(record);
220 uint32_t encoding = read_u16(record + 2);
221 uint32_t offset = read_u32(record + 4);
222
223 if (offset > cmap_length || cmap_length - offset < 16) {
224 continue;
225 }
226
227 if (read_u16(cmap + offset) != 4) {
228 continue;
229 }
230
231 if (platform == 3 && encoding == 1) {
232 best_subtable = cmap + offset;
233 best_length = cmap_length - offset;
234
235 break;
236 }
237
238 if (platform == 0 && !best_subtable) {
239 best_subtable = cmap + offset;
240 best_length = cmap_length - offset;
241 }
242 }
243
244 if (!best_subtable) {
245 return false;
246 }
247
248 uint32_t segment_count_x2 = read_u16(best_subtable + 6);
249
250 if (best_length < 16 + segment_count_x2 * 4) {
251 return false;
252 }
253
254 font.cmap = best_subtable;
255 font.cmap_length = best_length;
256
257 return true;
258 }
259
260 static bool font_ready(void) {
261 if (font_state == 0) {
262 font_state = font_load() ? 1 : -1;
263 }
264
265 return font_state == 1;
266 }
267
268 static uint32_t glyph_for(uint32_t character) {
269 if (character > 0xFFFF) {
270 return 0;
271 }
272
273 const uint8_t *table = font.cmap;
274 uint32_t segment_count_x2 = read_u16(table + 6);
275 const uint8_t *end_codes = table + 14;
276 const uint8_t *start_codes = table + 16 + segment_count_x2;
277 const uint8_t *deltas = table + 16 + segment_count_x2 * 2;
278 const uint8_t *range_offsets = table + 16 + segment_count_x2 * 3;
279
280 for (uint32_t i = 0; i < segment_count_x2 / 2; i++) {
281 if (character > read_u16(end_codes + i * 2)) {
282 continue;
283 }
284
285 uint32_t start = read_u16(start_codes + i * 2);
286
287 if (character < start) {
288 return 0;
289 }
290
291 uint32_t delta = read_u16(deltas + i * 2);
292 uint32_t range_offset = read_u16(range_offsets + i * 2);
293
294 if (range_offset == 0) {
295 return (character + delta) & 0xFFFF;
296 }
297
298 const uint8_t *entry = range_offsets + i * 2 + range_offset + (character - start) * 2;
299
300 if ((size_t)(entry + 2 - font.cmap) > font.cmap_length) {
301 return 0;
302 }
303
304 uint32_t glyph = read_u16(entry);
305
306 return glyph ? (glyph + delta) & 0xFFFF : 0;
307 }
308
309 return 0;
310 }
311
312 static uint32_t advance_of(uint32_t glyph_id) {
313 if (glyph_id >= font.horizontal_metric_count) {
314 glyph_id = font.horizontal_metric_count - 1;
315 }
316
317 return read_u16(font.hmtx + glyph_id * 4);
318 }
319
320 static const uint8_t *glyph_data(uint32_t glyph_id, uint32_t *length) {
321 if (glyph_id >= font.glyph_count) {
322 return 0;
323 }
324
325 uint32_t begin;
326 uint32_t end;
327
328 if (font.long_loca) {
329 begin = read_u32(font.loca + glyph_id * 4);
330 end = read_u32(font.loca + glyph_id * 4 + 4);
331 } else {
332 begin = read_u16(font.loca + glyph_id * 2) * 2;
333 end = read_u16(font.loca + glyph_id * 2 + 2) * 2;
334 }
335
336 if (end <= begin || end > font.glyf_length || end - begin < 10) {
337 return 0;
338 }
339
340 *length = end - begin;
341
342 return font.glyf + begin;
343 }
344
345 static void raster_line(int32_t *coverage, int32_t stride, int32_t width, int32_t height, int32_t start_x, int32_t start_y, int32_t end_x, int32_t end_y) {
346 if (start_y == end_y) {
347 return;
348 }
349
350 int32_t direction = 1;
351
352 if (start_y > end_y) {
353 int32_t temp;
354
355 direction = -1;
356
357 temp = start_x;
358 start_x = end_x;
359 end_x = temp;
360
361 temp = start_y;
362 start_y = end_y;
363 end_y = temp;
364 }
365
366 if (end_y <= 0 || start_y >= height * ONE) {
367 return;
368 }
369
370 int32_t delta_x = end_x - start_x;
371 int32_t delta_y_total = end_y - start_y;
372 int32_t first_row = start_y < 0 ? 0 : (start_y >> 16);
373 int32_t last_row = (end_y + (ONE - 1)) >> 16;
374
375 if (last_row > height) {
376 last_row = height;
377 }
378
379 int32_t current_x = start_x;
380
381 if (start_y < 0) {
382 current_x = start_x + (int32_t)signed_divide64((int64_t)delta_x * (0 - start_y), delta_y_total);
383 }
384
385 int32_t max_x = width * ONE;
386
387 for (int32_t row_index = first_row; row_index < last_row; row_index++) {
388 int32_t row_top = row_index * ONE;
389 int32_t row_bottom = row_top + ONE;
390 int32_t segment_top = start_y > row_top ? start_y : row_top;
391 int32_t segment_bottom = end_y < row_bottom ? end_y : row_bottom;
392 int32_t segment_height = segment_bottom - segment_top;
393 int32_t next_x;
394
395 if (segment_bottom == end_y) {
396 next_x = end_x;
397 } else {
398 next_x = start_x + (int32_t)signed_divide64((int64_t)delta_x * (segment_bottom - start_y), delta_y_total);
399 }
400
401 int32_t signed_height = segment_height * direction;
402 int32_t left_x = current_x < next_x ? current_x : next_x;
403 int32_t right_x = current_x < next_x ? next_x : current_x;
404
405 if (left_x < 0) {
406 left_x = 0;
407 }
408
409 if (right_x < 0) {
410 right_x = 0;
411 }
412
413 if (left_x > max_x) {
414 left_x = max_x;
415 }
416
417 if (right_x > max_x) {
418 right_x = max_x;
419 }
420
421 int32_t first_cell = left_x >> 16;
422 int32_t last_cell = (right_x + (ONE - 1)) >> 16;
423 int32_t *row = coverage + row_index * stride;
424
425 if (last_cell <= first_cell + 1) {
426 int32_t middle_offset = ((left_x + right_x) >> 1) - (first_cell << 16);
427
428 row[first_cell] += signed_height - fixed_multiply(signed_height, middle_offset);
429 row[first_cell + 1] += fixed_multiply(signed_height, middle_offset);
430 } else {
431 int32_t span = right_x - left_x;
432 int32_t first_fraction = left_x - (first_cell << 16);
433 int32_t first_remaining = ONE - first_fraction;
434 int32_t last_fraction = right_x - ((last_cell - 1) << 16);
435 int32_t first_area = (int32_t)unsigned_divide64((uint64_t)first_remaining * (uint64_t)first_remaining, (uint32_t)span * 2);
436 int32_t last_area = (int32_t)unsigned_divide64((uint64_t)last_fraction * (uint64_t)last_fraction, (uint32_t)span * 2);
437
438 row[first_cell] += fixed_multiply(signed_height, first_area);
439
440 if (last_cell == first_cell + 2) {
441 row[first_cell + 1] += fixed_multiply(signed_height, ONE - first_area - last_area);
442 } else {
443 int32_t slope_step = (int32_t)unsigned_divide64((uint64_t)ONE << 16, (uint32_t)span);
444 int32_t second_area = (int32_t)unsigned_divide64((uint64_t)(ONE + ONE / 2 - first_fraction) << 16, (uint32_t)span);
445
446 row[first_cell + 1] += fixed_multiply(signed_height, second_area - first_area);
447
448 for (int32_t cell = first_cell + 2; cell < last_cell - 1; cell++) {
449 row[cell] += fixed_multiply(signed_height, slope_step);
450 }
451
452 int32_t final_area = second_area + (last_cell - first_cell - 3) * slope_step;
453
454 row[last_cell - 1] += fixed_multiply(signed_height, ONE - final_area - last_area);
455 }
456
457 row[last_cell] += fixed_multiply(signed_height, last_area);
458 }
459
460 current_x = next_x;
461 }
462 }
463
464 static void raster_quad(int32_t *coverage, int32_t stride, int32_t width, int32_t height, ta_text_pt start, ta_text_pt control, ta_text_pt end) {
465 int32_t second_diff_x = start.x - 2 * control.x + end.x;
466 int32_t second_diff_y = start.y - 2 * control.y + end.y;
467
468 if (second_diff_x < 0) {
469 second_diff_x = -second_diff_x;
470 }
471
472 if (second_diff_y < 0) {
473 second_diff_y = -second_diff_y;
474 }
475
476 int32_t deviation = (second_diff_x + second_diff_y) >> 2;
477 int32_t segment_count = 1;
478
479 while (segment_count < MAX_SEGMENTS && deviation > (ONE / 16) * segment_count * segment_count) {
480 segment_count++;
481 }
482
483 ta_text_pt previous = start;
484
485 for (int32_t i = 1; i <= segment_count; i++) {
486 ta_text_pt current;
487
488 if (i == segment_count) {
489 current = end;
490 } else {
491 int32_t t = (int32_t)((uint32_t)i * ONE / (uint32_t)segment_count);
492 int32_t one_minus_t = ONE - t;
493 int64_t weight_start = (int64_t)one_minus_t * one_minus_t;
494 int64_t weight_ctrl = (int64_t)2 * one_minus_t * t;
495 int64_t weight_end = (int64_t)t * t;
496
497 current.x = (int32_t)((weight_start * start.x + weight_ctrl * control.x + weight_end * end.x) >> 32);
498 current.y = (int32_t)((weight_start * start.y + weight_ctrl * control.y + weight_end * end.y) >> 32);
499 }
500
501 raster_line(coverage, stride, width, height, previous.x, previous.y, current.x, current.y);
502 previous = current;
503 }
504 }
505
506 #define DIV255(t) ((((t) + 128) + (((t) + 128) >> 8)) >> 8)
507
508 static uint32_t blend_channel(uint32_t destination, uint32_t source, uint32_t alpha) {
509 uint32_t total = destination * (255 - alpha) + source * alpha;
510
511 return DIV255(total);
512 }
513
514 static void put_pixel(const ta_text_ctx *ctx, int32_t pixel_x, int32_t pixel_y, uint32_t alpha) {
515 size_t index = (size_t)(pixel_y - ctx->sprite_y) * (size_t)ctx->sprite_width + (size_t)(pixel_x - ctx->sprite_x);
516 uint32_t destination = ctx->sprite_pixels[index];
517 uint32_t destination_alpha = destination & 255;
518 uint32_t inverse_alpha = 255 - alpha;
519 uint32_t output_alpha = alpha + (destination_alpha * inverse_alpha + 127) / 255;
520
521 if (output_alpha == 0) {
522 return;
523 }
524
525 uint32_t destination_red = (destination >> 24) & 255;
526 uint32_t destination_green = (destination >> 16) & 255;
527 uint32_t destination_blue = (destination >> 8) & 255;
528 uint32_t output_red = ((uint32_t)ctx->red * alpha * 255 + destination_red * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
529 uint32_t output_green = ((uint32_t)ctx->green * alpha * 255 + destination_green * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
530 uint32_t output_blue = ((uint32_t)ctx->blue * alpha * 255 + destination_blue * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
531
532 ctx->sprite_pixels[index] = (output_red << 24) | (output_green << 16) | (output_blue << 8) | output_alpha;
533 }
534
535 static void blit_pixel(size_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t alpha) {
536 if (ta_display->bpp == 32) {
537 uint32_t *pixel = (uint32_t *)ta_display->buffers[1] + index;
538 uint32_t destination = *pixel;
539
540 *pixel = (destination & 0xFF000000u) | (blend_channel((destination >> 16) & 255, red, alpha) << 16) | (blend_channel((destination >> 8) & 255, green, alpha) << 8) | blend_channel(destination & 255, blue, alpha);
541 } else {
542 uint8_t *pixel = (uint8_t *)ta_display->buffers[1] + index;
543 uint32_t destination = *pixel;
544 uint32_t dest_red = (destination >> 5) & 7;
545 uint32_t dest_green = (destination >> 2) & 7;
546 uint32_t dest_blue = destination & 3;
547
548 dest_red = (dest_red << 5) | (dest_red << 2) | (dest_red >> 1);
549 dest_green = (dest_green << 5) | (dest_green << 2) | (dest_green >> 1);
550 dest_blue = dest_blue * 85;
551
552 red = blend_channel(dest_red, red, alpha);
553 green = blend_channel(dest_green, green, alpha);
554 blue = blend_channel(dest_blue, blue, alpha);
555
556 *pixel = (uint8_t)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
557 }
558 }
559
560 static void emit_pixel(const ta_text_ctx *ctx, int32_t pixel_x, int32_t pixel_y, uint32_t alpha) {
561 put_pixel(ctx, pixel_x, pixel_y, alpha);
562 }
563
564 static ta_text_pt transform_point(int32_t font_x, int32_t font_y, int32_t origin_x, int32_t origin_y, int32_t scale) {
565 ta_text_pt point;
566
567 point.x = origin_x + (int32_t)((int64_t)font_x * scale);
568 point.y = origin_y - (int32_t)((int64_t)font_y * scale);
569
570 return point;
571 }
572
573 static void draw_glyph(const ta_text_ctx *ctx, uint32_t glyph_id, int32_t pen_x, int32_t baseline_y) {
574 uint32_t glyph_length;
575 const uint8_t *glyph = glyph_data(glyph_id, &glyph_length);
576
577 if (!glyph) {
578 return;
579 }
580
581 int32_t contour_count = read_s16(glyph);
582
583 if (contour_count <= 0) {
584 return;
585 }
586
587 int32_t scale = ctx->scale;
588 int32_t glyph_left = pen_x + (int32_t)((int64_t)read_s16(glyph + 2) * scale);
589 int32_t glyph_right = pen_x + (int32_t)((int64_t)read_s16(glyph + 6) * scale);
590 int32_t glyph_top = baseline_y - (int32_t)((int64_t)read_s16(glyph + 8) * scale);
591 int32_t glyph_bottom = baseline_y - (int32_t)((int64_t)read_s16(glyph + 4) * scale);
592 int32_t region_x0 = glyph_left >> 16;
593 int32_t region_x1 = (glyph_right + (ONE - 1)) >> 16;
594 int32_t region_y0 = glyph_top >> 16;
595 int32_t region_y1 = (glyph_bottom + (ONE - 1)) >> 16;
596
597 if (region_x0 < ctx->clip_x0) {
598 region_x0 = ctx->clip_x0;
599 }
600
601 if (region_y0 < ctx->clip_y0) {
602 region_y0 = ctx->clip_y0;
603 }
604
605 if (region_x1 > ctx->clip_x1) {
606 region_x1 = ctx->clip_x1;
607 }
608
609 if (region_y1 > ctx->clip_y1) {
610 region_y1 = ctx->clip_y1;
611 }
612
613 if (region_x0 >= region_x1 || region_y0 >= region_y1) {
614 return;
615 }
616
617 int32_t region_width = region_x1 - region_x0;
618 int32_t region_height = region_y1 - region_y0;
619 int32_t stride = region_width + 2;
620 size_t coverage_bytes = (size_t)stride * (size_t)region_height * sizeof(int32_t);
621 const uint8_t *end = glyph + glyph_length;
622 const uint8_t *cursor = glyph + 10;
623
624 if ((size_t)(end - cursor) < (size_t)contour_count * 2 + 2) {
625 return;
626 }
627
628 uint32_t point_count = read_u16(cursor + (contour_count - 1) * 2) + 1;
629 const uint8_t *contour_ends = cursor;
630
631 cursor += contour_count * 2;
632 cursor += 2 + read_u16(cursor);
633
634 if (cursor > end || point_count > 4096) {
635 return;
636 }
637
638 size_t points_bytes = (size_t)point_count * 5;
639 void *memory = malloc(points_bytes + coverage_bytes + 8);
640
641 if (!memory) {
642 return;
643 }
644
645 int16_t *point_x = (int16_t *)memory;
646 int16_t *point_y = point_x + point_count;
647 uint8_t *flags = (uint8_t *)(point_y + point_count);
648 int32_t *coverage = (int32_t *)(((size_t)(flags + point_count) + 7) & ~(size_t)7);
649 bool parse_ok = true;
650
651 for (uint32_t i = 0; i < point_count && parse_ok;) {
652 if (cursor >= end) {
653 parse_ok = false;
654
655 break;
656 }
657
658 uint8_t flag = *cursor++;
659
660 flags[i++] = flag;
661
662 if (flag & 8) {
663 if (cursor >= end) {
664 parse_ok = false;
665
666 break;
667 }
668
669 uint32_t repeat_count = *cursor++;
670
671 while (repeat_count > 0 && i < point_count) {
672 flags[i++] = flag;
673 repeat_count--;
674 }
675 }
676 }
677
678 for (uint32_t pass = 0; pass < 2 && parse_ok; pass++) {
679 int16_t *output = pass ? point_y : point_x;
680 uint8_t short_bit = pass ? 4 : 2;
681 uint8_t same_bit = pass ? 32 : 16;
682 int32_t value = 0;
683
684 for (uint32_t i = 0; i < point_count; i++) {
685 uint8_t flag = flags[i];
686
687 if (flag & short_bit) {
688 if (cursor >= end) {
689 parse_ok = false;
690
691 break;
692 }
693
694 int32_t delta = *cursor++;
695
696 value += (flag & same_bit) ? delta : -delta;
697 } else if (!(flag & same_bit)) {
698 if (end - cursor < 2) {
699 parse_ok = false;
700
701 break;
702 }
703
704 value += read_s16(cursor);
705 cursor += 2;
706 }
707
708 output[i] = (int16_t)value;
709 }
710 }
711
712 if (!parse_ok) {
713 free(memory);
714
715 return;
716 }
717
718 for (size_t i = 0; i < coverage_bytes / sizeof(int32_t); i++) {
719 coverage[i] = 0;
720 }
721
722 int32_t origin_x = pen_x - region_x0 * ONE;
723 int32_t origin_y = baseline_y - region_y0 * ONE;
724
725 #define POINT(index) transform_point((int32_t)point_x[index], (int32_t)point_y[index], origin_x, origin_y, scale)
726
727 uint32_t contour_first = 0;
728
729 for (int32_t contour = 0; contour < contour_count; contour++) {
730 uint32_t contour_last = read_u16(contour_ends + contour * 2);
731 uint32_t contour_start = contour_first;
732
733 contour_first = contour_last + 1;
734
735 if (contour_last < contour_start || contour_last >= point_count || contour_last - contour_start < 1) {
736 continue;
737 }
738
739 uint32_t contour_points = contour_last - contour_start + 1;
740
741 ta_text_pt start_point;
742 uint32_t begin;
743 uint32_t count;
744
745 if (flags[contour_start] & 1) {
746 start_point = POINT(contour_start);
747 begin = contour_start + 1;
748 count = contour_points - 1;
749 } else if (flags[contour_last] & 1) {
750 start_point = POINT(contour_last);
751 begin = contour_start;
752 count = contour_points - 1;
753 } else {
754 ta_text_pt first_point = POINT(contour_start);
755 ta_text_pt last_point = POINT(contour_last);
756 start_point.x = (first_point.x >> 1) + (last_point.x >> 1);
757 start_point.y = (first_point.y >> 1) + (last_point.y >> 1);
758 begin = contour_start;
759 count = contour_points;
760 }
761
762 ta_text_pt current = start_point;
763 ta_text_pt control = start_point;
764 bool has_control = false;
765
766 for (uint32_t k = 0; k < count; k++) {
767 uint32_t index = begin + k;
768 ta_text_pt point = POINT(index);
769
770 if (flags[index] & 1) {
771 if (has_control) {
772 raster_quad(coverage, stride, region_width, region_height, current, control, point);
773 } else {
774 raster_line(coverage, stride, region_width, region_height, current.x, current.y, point.x, point.y);
775 }
776
777 current = point;
778 has_control = false;
779 } else if (has_control) {
780 ta_text_pt midpoint;
781
782 midpoint.x = (control.x >> 1) + (point.x >> 1);
783 midpoint.y = (control.y >> 1) + (point.y >> 1);
784
785 raster_quad(coverage, stride, region_width, region_height, current, control, midpoint);
786
787 current = midpoint;
788 control = point;
789 } else {
790 control = point;
791 has_control = true;
792 }
793 }
794
795 if (has_control) {
796 raster_quad(coverage, stride, region_width, region_height, current, control, start_point);
797 } else {
798 raster_line(coverage, stride, region_width, region_height, current.x, current.y, start_point.x, start_point.y);
799 }
800 }
801
802 #undef POINT
803
804 for (int32_t row_index = 0; row_index < region_height; row_index++) {
805 int32_t running_sum = 0;
806 const int32_t *row = coverage + row_index * stride;
807
808 for (int32_t column = 0; column < region_width; column++) {
809 running_sum += row[column];
810
811 int32_t amount = running_sum < 0 ? -running_sum : running_sum;
812
813 if (amount > ONE) {
814 amount = ONE;
815 }
816
817 uint32_t alpha = ((uint32_t)amount * 255 + 0x8000) >> 16;
818
819 if (alpha) {
820 emit_pixel(ctx, region_x0 + column, region_y0 + row_index, alpha);
821 }
822 }
823 }
824
825 free(memory);
826 }
827
828 static bool printable(uint32_t character) {
829 return character >= 32 && character != 127;
830 }
831
832 static uint32_t line_height_units(void) {
833 return (uint32_t)(font.ascent - font.descent + font.line_gap);
834 }
835
836 static void measure_units(const char *text, uint32_t *max_width, uint32_t *lines) {
837 uint32_t current_width = 0;
838 uint32_t best_width = 0;
839 uint32_t line_count = 1;
840
841 for (; *text; text++) {
842 uint32_t character = (uint8_t)*text;
843
844 if (character == '\n') {
845 if (current_width > best_width) {
846 best_width = current_width;
847 }
848
849 current_width = 0;
850 line_count++;
851 } else if (printable(character)) {
852 current_width += advance_of(glyph_for(character));
853 }
854 }
855
856 if (current_width > best_width) {
857 best_width = current_width;
858 }
859
860 *max_width = best_width;
861 *lines = line_count;
862 }
863
864 static uint32_t line_units(const char *text) {
865 uint32_t width = 0;
866
867 for (; *text && *text != '\n'; text++) {
868 uint32_t character = (uint8_t)*text;
869
870 if (printable(character)) {
871 width += advance_of(glyph_for(character));
872 }
873 }
874
875 return width;
876 }
877
878 static int32_t line_offset(const char *text, uint32_t widest, int32_t scale) {
879 return (int32_t)(((int64_t)(widest - line_units(text)) * scale) >> 1);
880 }
881
882 static void draw_text_pass(const char *text, int32_t x, int32_t y, const ta_text_ctx *ctx) {
883 int32_t scale = ctx->scale;
884 int32_t ascent = (int32_t)((int64_t)font.ascent * scale);
885 int32_t line_height = (int32_t)((int64_t)line_height_units() * scale);
886 uint32_t widest;
887 uint32_t lines;
888
889 measure_units(text, &widest, &lines);
890
891 int32_t pen_x = x * ONE + line_offset(text, widest, scale);
892 int32_t baseline_y = y * ONE + ascent;
893
894 for (; *text; text++) {
895 uint32_t character = (uint8_t)*text;
896
897 if (character == '\n') {
898 pen_x = x * ONE + line_offset(text + 1, widest, scale);
899 baseline_y += line_height;
900
901 if (baseline_y - ascent >= ctx->clip_y1 * ONE) {
902 break;
903 }
904
905 continue;
906 }
907
908 if (!printable(character) || pen_x >= ctx->clip_x1 * ONE) {
909 continue;
910 }
911
912 uint32_t glyph_id = glyph_for(character);
913
914 draw_glyph(ctx, glyph_id, pen_x, baseline_y);
915 pen_x += (int32_t)((int64_t)advance_of(glyph_id) * scale);
916 }
917 }
918
919 static void draw_text_direct(const char *text, int32_t x, int32_t y, const ta_text_ctx *ctx) {
920 if (ctx->border_thickness != 0) {
921 ta_text_ctx border_ctx = *ctx;
922
923 border_ctx.red = ctx->border_red;
924 border_ctx.green = ctx->border_green;
925 border_ctx.blue = ctx->border_blue;
926 int32_t thickness = ctx->border_thickness;
927
928 for (int32_t offset_y = -thickness; offset_y <= thickness; offset_y++) {
929 for (int32_t offset_x = -thickness; offset_x <= thickness; offset_x++) {
930 if (offset_x == 0 && offset_y == 0) {
931 continue;
932 }
933
934 draw_text_pass(text, x + offset_x, y + offset_y, &border_ctx);
935 }
936 }
937 }
938
939 draw_text_pass(text, x, y, ctx);
940 }
941
942 void ta_text_rasterize(ta_text *text) {
943 if (!text) {
944 return;
945 }
946
947 ta_text_destroy(text);
948
949 if (!text->text || text->size == 0 || !font_ready()) {
950 return;
951 }
952
953 uint32_t units;
954 uint32_t lines;
955
956 measure_units(text->text, &units, &lines);
957
958 uint64_t scale = unsigned_divide64((uint64_t)text->size << 16, font.units_per_em);
959 uint64_t text_width = (units * scale + (ONE - 1)) >> 16;
960 uint64_t text_height = ((uint64_t)lines * line_height_units() * scale + (ONE - 1)) >> 16;
961 uint32_t border = text->border_width;
962 uint64_t sprite_width = text_width + (uint64_t)border * 2;
963 uint64_t sprite_height = text_height + (uint64_t)border * 2;
964
965 if (text_width == 0 || text_height == 0 || sprite_width > MAX_COORD || sprite_height > MAX_COORD) {
966 return;
967 }
968
969 size_t pixel_count = (size_t)sprite_width * (size_t)sprite_height;
970 ta_display_sprite *sprite = ta_display_sprite_create((uint16_t)sprite_width, (uint16_t)sprite_height);
971
972 if (!sprite) {
973 return;
974 }
975
976 uint32_t *pixels = (uint32_t *)sprite->pixels;
977
978 for (size_t i = 0; i < pixel_count; i++) {
979 pixels[i] = 0;
980 }
981
982 ta_text_ctx ctx;
983
984 ctx.scale = (int32_t)scale;
985 ctx.clip_x0 = 0;
986 ctx.clip_y0 = 0;
987 ctx.clip_x1 = (int32_t)sprite_width;
988 ctx.clip_y1 = (int32_t)sprite_height;
989 ctx.red = text->red;
990 ctx.green = text->green;
991 ctx.blue = text->blue;
992 ctx.border_thickness = text->border_width;
993 ctx.border_red = text->border_red;
994 ctx.border_green = text->border_green;
995 ctx.border_blue = text->border_blue;
996 ctx.sprite_pixels = pixels;
997 ctx.sprite_x = 0;
998 ctx.sprite_y = 0;
999 ctx.sprite_width = (int32_t)sprite_width;
1000
1001 draw_text_direct(text->text, (int32_t)border, (int32_t)border, &ctx);
1002
1003 text->width = (uint32_t)text_width;
1004 text->height = (uint32_t)text_height;
1005 text->sprite = sprite;
1006 }
1007
1008 void ta_text_draw(ta_text *text) {
1009 if (!text || !text->sprite) {
1010 return;
1011 }
1012
1013 const ta_display_sprite *source = text->sprite;
1014 int32_t destination_x = (int32_t)text->x - (int32_t)text->border_width;
1015 int32_t destination_y = (int32_t)text->y - (int32_t)text->border_width;
1016 int32_t column_start = destination_x < 0 ? -destination_x : 0;
1017 int32_t row_start = destination_y < 0 ? -destination_y : 0;
1018 int32_t column_end = (int32_t)source->width;
1019 int32_t row_end = (int32_t)source->height;
1020
1021 if (column_end > (int32_t)ta_display->width - destination_x) {
1022 column_end = (int32_t)ta_display->width - destination_x;
1023 }
1024
1025 if (row_end > (int32_t)ta_display->height - destination_y) {
1026 row_end = (int32_t)ta_display->height - destination_y;
1027 }
1028
1029 if (column_start >= column_end || row_start >= row_end) {
1030 return;
1031 }
1032
1033 const uint32_t *source_pixels = (const uint32_t *)source->pixels;
1034
1035 if (ta_display->sprite_mode) {
1036 if (!ta_display_sprites) {
1037 return;
1038 }
1039
1040 uint32_t visible_width = (uint32_t)(column_end - column_start);
1041 uint32_t visible_height = (uint32_t)(row_end - row_start);
1042 ta_display_sprite *copy = ta_display_sprite_create((uint16_t)visible_width, (uint16_t)visible_height);
1043
1044 if (!copy) {
1045 return;
1046 }
1047
1048 copy->x = (uint16_t)(destination_x + column_start);
1049 copy->y = (uint16_t)(destination_y + row_start);
1050
1051 uint32_t *copy_pixels = (uint32_t *)copy->pixels;
1052
1053 for (uint32_t row = 0; row < visible_height; row++) {
1054 const uint32_t *line = source_pixels + (size_t)(row_start + (int32_t)row) * source->width + column_start;
1055
1056 for (uint32_t column = 0; column < visible_width; column++) {
1057 copy_pixels[(size_t)row * visible_width + column] = line[column];
1058 }
1059 }
1060
1061 if (!ta_display_sprite_add(copy)) {
1062 free(copy);
1063 }
1064
1065 return;
1066 }
1067
1068 if (ta_display->buffers[1] == 0 || (ta_display->bpp != 32 && ta_display->bpp != 8)) {
1069 return;
1070 }
1071
1072 for (int32_t row = row_start; row < row_end; row++) {
1073 for (int32_t column = column_start; column < column_end; column++) {
1074 uint32_t pixel = source_pixels[(size_t)row * source->width + column];
1075 uint32_t alpha = pixel & 255;
1076
1077 if (alpha != 0) {
1078 blit_pixel((size_t)(destination_y + row) * ta_display->width + (size_t)(destination_x + column), (pixel >> 24) & 255, (pixel >> 16) & 255, (pixel >> 8) & 255, alpha);
1079 }
1080 }
1081 }
1082 }
1083
1084 bool ta_text_change_color(ta_text *text, uint32_t color) {
1085 if (!text || !text->sprite) {
1086 return false;
1087 }
1088
1089 uint8_t red = (uint8_t)((color >> 16) & 255);
1090 uint8_t green = (uint8_t)((color >> 8) & 255);
1091 uint8_t blue = (uint8_t)(color & 255);
1092 uint32_t old_rgb = ((uint32_t)text->red << 24) | ((uint32_t)text->green << 16) | ((uint32_t)text->blue << 8);
1093 uint32_t new_rgb = ((uint32_t)red << 24) | ((uint32_t)green << 16) | ((uint32_t)blue << 8);
1094 uint32_t *pixels = (uint32_t *)text->sprite->pixels;
1095 size_t count = (size_t)text->sprite->width * (size_t)text->sprite->height;
1096 bool bordered = text->border_width != 0;
1097
1098 for (size_t i = 0; i < count; i++) {
1099 uint32_t pixel = pixels[i];
1100
1101 if ((pixel & 255) == 0) {
1102 continue;
1103 }
1104
1105 if (bordered && (pixel & 0xffffff00u) != old_rgb) {
1106 continue;
1107 }
1108
1109 pixels[i] = new_rgb | (pixel & 255);
1110 }
1111
1112 text->red = red;
1113 text->green = green;
1114 text->blue = blue;
1115
1116 return true;
1117 }
1118
1119 void ta_text_destroy(ta_text *text) {
1120 if (!text) {
1121 return;
1122 }
1123
1124 free(text->sprite);
1125 text->sprite = 0;
1126 text->width = 0;
1127 text->height = 0;
1128 }