tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE

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 }