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

image.c (18961B)


      1 #include <stdint.h>
      2 #include <stdlib.h>
      3 #include <stdbool.h>
      4 #include "display.h"
      5 #include "fixed.h"
      6 
      7 #define TAIF_FLAG_8BPP 0x01
      8 #define TAIF_FLAG_TRANSPARENT 0x02
      9 #define TAIF_FLAG_ANIMATED 0x04
     10 #define TAIF_WINDOW_SIZE 4096
     11 
     12 typedef struct {
     13 	uint8_t window[TAIF_WINDOW_SIZE];
     14 	uint32_t stream_position;
     15 	uint32_t window_position;
     16 	uint32_t filled;
     17 	uint32_t match_distance;
     18 	uint32_t match_remaining;
     19 	uint8_t item_flags;
     20 	uint8_t flags_remaining;
     21 } ta_image_decoder;
     22 
     23 typedef struct ta_image_cache {
     24 	const uint8_t *data;
     25 	uint32_t length;
     26 	uint32_t width;
     27 	uint32_t height;
     28 	uint32_t stride;
     29 	uint32_t frame_size;
     30 	uint16_t frame_count;
     31 	uint16_t palette_entries;
     32 	uint32_t frames_decoded;
     33 	uint8_t flags;
     34 	uint8_t palette[256][3];
     35 	const uint8_t *stream;
     36 	uint32_t stream_length;
     37 	ta_image_decoder decoder;
     38 	uint8_t *pixels;
     39 	uint8_t *scaled;
     40 	uint32_t scaled_width;
     41 	uint32_t scaled_height;
     42 	uint8_t scaled_bpp;
     43 	struct ta_image_cache *next;
     44 } ta_image_cache;
     45 
     46 static ta_image_cache *image_cache;
     47 
     48 static uint16_t read_le16(const uint8_t *data) {
     49 	return (uint16_t)((uint16_t)data[0] | ((uint16_t)data[1] << 8));
     50 }
     51 
     52 static bool parse_image(const uint8_t *data, uint32_t length, ta_image_cache *image) {
     53 	if (data == 0 || length < 11 || data[0] != 'T' || data[1] != 'A' || data[2] != 'I' || data[3] != 'F' || data[4] != 1) {
     54 		return false;
     55 	}
     56 
     57 	uint8_t flags = data[5];
     58 	if ((flags & 0xf8) != 0) {
     59 		return false;
     60 	}
     61 
     62 	uint32_t width = read_le16(data + 6);
     63 	uint32_t height = read_le16(data + 8);
     64 	uint32_t palette_entries = (uint32_t)data[10] + 1;
     65 	bool eight_bpp = (flags & TAIF_FLAG_8BPP) != 0;
     66 	bool transparent = (flags & TAIF_FLAG_TRANSPARENT) != 0;
     67 	uint32_t max_entries = eight_bpp ? (transparent ? 255 : 256) : (transparent ? 15 : 16);
     68 	uint32_t header_size = 11;
     69 	uint16_t frame_count = 1;
     70 
     71 	if (width == 0 || height == 0 || palette_entries > max_entries) {
     72 		return false;
     73 	}
     74 
     75 	if ((flags & TAIF_FLAG_ANIMATED) != 0) {
     76 		if (length < 14 || data[11] == 0) {
     77 			return false;
     78 		}
     79 
     80 		frame_count = read_le16(data + 12);
     81 		if (frame_count < 2) {
     82 			return false;
     83 		}
     84 
     85 		header_size = 14;
     86 	}
     87 
     88 	uint64_t stride = eight_bpp ? width : (width + 1) / 2;
     89 	uint64_t frame_size = stride * height;
     90 	uint64_t max_size = (uint64_t)(size_t)-1;
     91 	uint64_t palette_size = (uint64_t)palette_entries * 3;
     92 
     93 	if (frame_size == 0 || frame_size > max_size || frame_size > 0xffffffffu || header_size + palette_size > length) {
     94 		return false;
     95 	}
     96 
     97 	const uint8_t *palette = data + header_size;
     98 
     99 	image->pixels = (uint8_t *)malloc((size_t)frame_size);
    100 	if (image->pixels == 0) {
    101 		return false;
    102 	}
    103 
    104 	for (uint32_t i = 0; i < (uint32_t)frame_size; i++) {
    105 		image->pixels[i] = 0;
    106 	}
    107 
    108 	image->data = data;
    109 	image->length = length;
    110 	image->width = width;
    111 	image->height = height;
    112 	image->stride = (uint32_t)stride;
    113 	image->frame_size = (uint32_t)frame_size;
    114 	image->frame_count = frame_count;
    115 	image->palette_entries = (uint16_t)palette_entries;
    116 	image->flags = flags;
    117 	image->stream = palette + palette_size;
    118 	image->stream_length = length - (uint32_t)(header_size + palette_size);
    119 
    120 	for (uint32_t i = 0; i < palette_entries; i++) {
    121 		for (uint32_t channel = 0; channel < 3; channel++) {
    122 			image->palette[i][channel] = palette[i * 3 + channel];
    123 		}
    124 	}
    125 
    126 	return true;
    127 }
    128 
    129 static void decoder_reset(ta_image_cache *image) {
    130 	ta_image_decoder *decoder = &image->decoder;
    131 
    132 	decoder->stream_position = 0;
    133 	decoder->window_position = 0;
    134 	decoder->filled = 0;
    135 	decoder->match_distance = 0;
    136 	decoder->match_remaining = 0;
    137 	decoder->item_flags = 0;
    138 	decoder->flags_remaining = 0;
    139 
    140 	for (uint32_t i = 0; i < image->frame_size; i++) {
    141 		image->pixels[i] = 0;
    142 	}
    143 
    144 	free(image->scaled);
    145 
    146 	image->scaled = 0;
    147 	image->frames_decoded = 0;
    148 }
    149 
    150 static bool decode_next_frame(ta_image_cache *image) {
    151 	ta_image_decoder *decoder = &image->decoder;
    152 	const uint8_t *stream = image->stream;
    153 	uint32_t stream_length = image->stream_length;
    154 	uint32_t frame_size = image->frame_size;
    155 	uint32_t position = 0;
    156 
    157 	while (position < frame_size) {
    158 		uint8_t value;
    159 
    160 		if (decoder->match_remaining != 0) {
    161 			uint32_t source_position = (decoder->window_position + TAIF_WINDOW_SIZE - decoder->match_distance) & (TAIF_WINDOW_SIZE - 1);
    162 
    163 			value = decoder->window[source_position];
    164 			decoder->match_remaining--;
    165 		} else {
    166 			if (decoder->flags_remaining == 0) {
    167 				if (decoder->stream_position >= stream_length) {
    168 					return false;
    169 				}
    170 
    171 				decoder->item_flags = stream[decoder->stream_position++];
    172 				decoder->flags_remaining = 8;
    173 			}
    174 
    175 			bool literal = (decoder->item_flags & 1) != 0;
    176 
    177 			decoder->item_flags >>= 1;
    178 			decoder->flags_remaining--;
    179 
    180 			if (literal) {
    181 				if (decoder->stream_position >= stream_length) {
    182 					return false;
    183 				}
    184 
    185 				value = stream[decoder->stream_position++];
    186 			} else {
    187 				if (stream_length - decoder->stream_position < 2) {
    188 					return false;
    189 				}
    190 
    191 				uint8_t b0 = stream[decoder->stream_position++];
    192 				uint8_t b1 = stream[decoder->stream_position++];
    193 
    194 				decoder->match_distance = ((((uint32_t)b1 >> 4) << 8) | b0) + 1;
    195 				decoder->match_remaining = (b1 & 0x0f) + 3;
    196 
    197 				if (decoder->match_distance > decoder->filled) {
    198 					return false;
    199 				}
    200 
    201 				continue;
    202 			}
    203 		}
    204 
    205 		image->pixels[position++] ^= value;
    206 		decoder->window[decoder->window_position] = value;
    207 		decoder->window_position = (decoder->window_position + 1) & (TAIF_WINDOW_SIZE - 1);
    208 
    209 		if (decoder->filled < TAIF_WINDOW_SIZE) {
    210 			decoder->filled++;
    211 		}
    212 	}
    213 
    214 	bool eight_bpp = (image->flags & TAIF_FLAG_8BPP) != 0;
    215 	bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
    216 	uint32_t max_index = transparent ? image->palette_entries : (uint32_t)image->palette_entries - 1;
    217 	uint32_t full_bytes = eight_bpp ? image->width : image->width / 2;
    218 	bool odd_column = !eight_bpp && (image->width & 1) != 0;
    219 
    220 	for (uint32_t row = 0; row < image->height; row++) {
    221 		const uint8_t *line = image->pixels + (size_t)row * image->stride;
    222 
    223 		for (uint32_t i = 0; i < full_bytes; i++) {
    224 			if (eight_bpp) {
    225 				if (line[i] > max_index) {
    226 					return false;
    227 				}
    228 			} else if ((uint32_t)(line[i] >> 4) > max_index || (uint32_t)(line[i] & 0x0f) > max_index) {
    229 				return false;
    230 			}
    231 		}
    232 
    233 		if (odd_column && (uint32_t)(line[full_bytes] >> 4) > max_index) {
    234 			return false;
    235 		}
    236 	}
    237 
    238 	free(image->scaled);
    239 
    240 	image->scaled = 0;
    241 	image->frames_decoded++;
    242 
    243 	return true;
    244 }
    245 
    246 static ta_image_cache *find_image(const uint8_t *data) {
    247 	if (data == 0) {
    248 		return 0;
    249 	}
    250 
    251 	for (ta_image_cache *image = image_cache; image != 0; image = image->next) {
    252 		if (image->data == data) {
    253 			return image;
    254 		}
    255 	}
    256 
    257 	return 0;
    258 }
    259 
    260 bool ta_image_init(const uint8_t *data, uint32_t length) {
    261 	if (find_image(data) != 0) {
    262 		return true;
    263 	}
    264 
    265 	ta_image_cache *image = (ta_image_cache *)malloc(sizeof(*image));
    266 	if (image == 0) {
    267 		return false;
    268 	}
    269 
    270 	*image = (ta_image_cache){0};
    271 
    272 	if (!parse_image(data, length, image)) {
    273 		free(image);
    274 		return false;
    275 	}
    276 
    277 	if (!decode_next_frame(image)) {
    278 		free(image->pixels);
    279 		free(image);
    280 		return false;
    281 	}
    282 
    283 	image->next = image_cache;
    284 	image_cache = image;
    285 	return true;
    286 }
    287 
    288 bool ta_image_get_size(const uint8_t *data, uint32_t *width, uint32_t *height) {
    289 	ta_image_cache *image = find_image(data);
    290 	if (image == 0 || width == 0 || height == 0) {
    291 		return false;
    292 	}
    293 
    294 	*width = image->width;
    295 	*height = image->height;
    296 
    297 	return true;
    298 }
    299 
    300 static ta_image_cache *get_image(const uint8_t *data, uint32_t length, uint16_t frame) {
    301 	if (!ta_image_init(data, length)) {
    302 		return 0;
    303 	}
    304 
    305 	ta_image_cache *image = find_image(data);
    306 	if (frame >= image->frame_count) {
    307 		return 0;
    308 	}
    309 
    310 	uint32_t wanted = (uint32_t)frame + 1;
    311 
    312 	if (image->frames_decoded > wanted) {
    313 		decoder_reset(image);
    314 	}
    315 
    316 	while (image->frames_decoded < wanted) {
    317 		if (!decode_next_frame(image)) {
    318 			decoder_reset(image);
    319 			return 0;
    320 		}
    321 	}
    322 
    323 	return image;
    324 }
    325 
    326 static uint8_t get_pixel_index(const ta_image_cache *image, uint32_t x, uint32_t y) {
    327 	if ((image->flags & TAIF_FLAG_8BPP) != 0) {
    328 		return image->pixels[y * image->stride + x];
    329 	}
    330 
    331 	uint8_t packed = image->pixels[y * image->stride + x / 2];
    332 	return (x & 1) == 0 ? packed >> 4 : packed & 0x0f;
    333 }
    334 
    335 typedef struct {
    336 	uint16_t first;
    337 	uint16_t second;
    338 	uint8_t fraction;
    339 } ta_image_axis;
    340 
    341 static ta_image_axis axis_position(uint32_t index, uint32_t count, uint32_t source_count) {
    342 	ta_image_axis axis;
    343 
    344 #ifdef TA_DISABLE_BILINEAR_FILTERING
    345 	axis.first = (uint16_t)((uint64_t)index * source_count / count);
    346 	axis.second = axis.first;
    347 	axis.fraction = 0;
    348 #else
    349 	uint64_t position = ((uint64_t)(2 * (uint64_t)index + 1) * source_count * 128) / count;
    350 
    351 	if (position <= 128) {
    352 		axis.first = 0;
    353 		axis.second = 0;
    354 		axis.fraction = 0;
    355 	} else {
    356 		position -= 128;
    357 
    358 		uint32_t first = (uint32_t)(position >> 8);
    359 
    360 		if (first >= source_count - 1) {
    361 			axis.first = (uint16_t)(source_count - 1);
    362 			axis.second = axis.first;
    363 			axis.fraction = 0;
    364 		} else {
    365 			axis.first = (uint16_t)first;
    366 			axis.second = (uint16_t)(first + 1);
    367 			axis.fraction = (uint8_t)(position & 0xff);
    368 		}
    369 	}
    370 #endif
    371 
    372 	return axis;
    373 }
    374 
    375 static uint32_t sample_image(const ta_image_cache *image, ta_image_axis column, ta_image_axis row, uint32_t *red, uint32_t *green, uint32_t *blue) {
    376 	uint32_t weight_x0 = 256 - column.fraction;
    377 	uint32_t weight_y0 = 256 - row.fraction;
    378 	bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
    379 
    380 	uint32_t weights[4] = {
    381 		weight_x0 * weight_y0,
    382 		column.fraction * weight_y0,
    383 		weight_x0 * row.fraction,
    384 		column.fraction * row.fraction
    385 	};
    386 
    387 	uint8_t indices[4] = {
    388 		get_pixel_index(image, column.first, row.first),
    389 		get_pixel_index(image, column.second, row.first),
    390 		get_pixel_index(image, column.first, row.second),
    391 		get_pixel_index(image, column.second, row.second)
    392 	};
    393 
    394 	uint32_t red_sum = 0;
    395 	uint32_t green_sum = 0;
    396 	uint32_t blue_sum = 0;
    397 	uint32_t alpha_sum = 0;
    398 
    399 	for (uint32_t sample = 0; sample < 4; sample++) {
    400 		if (transparent && indices[sample] == 0) {
    401 			continue;
    402 		}
    403 
    404 		uint32_t palette_index = indices[sample] - (transparent ? 1 : 0);
    405 
    406 		red_sum += (uint32_t)image->palette[palette_index][0] * weights[sample];
    407 		green_sum += (uint32_t)image->palette[palette_index][1] * weights[sample];
    408 		blue_sum += (uint32_t)image->palette[palette_index][2] * weights[sample];
    409 		alpha_sum += weights[sample];
    410 	}
    411 
    412 	*red = red_sum;
    413 	*green = green_sum;
    414 	*blue = blue_sum;
    415 
    416 	return alpha_sum;
    417 }
    418 
    419 static void copy_bytes(uint8_t *destination, const uint8_t *source, size_t count) {
    420 	if ((((size_t)destination | (size_t)source) & 3) == 0) {
    421 		size_t words = count / 4;
    422 
    423 		for (size_t i = 0; i < words; i++) {
    424 			((uint32_t *)destination)[i] = ((const uint32_t *)source)[i];
    425 		}
    426 
    427 		destination += words * 4;
    428 		source += words * 4;
    429 		count &= 3;
    430 	}
    431 
    432 	while (count--) {
    433 		*destination++ = *source++;
    434 	}
    435 }
    436 
    437 static bool prepare_scaled(ta_image_cache *image, uint32_t width, uint32_t height) {
    438 	if (image->scaled != 0 && image->scaled_width == width && image->scaled_height == height && image->scaled_bpp == ta_display->bpp) {
    439 		return true;
    440 	}
    441 
    442 	size_t bytes_per_pixel = ta_display->bpp / 8;
    443 	uint8_t *scaled = (uint8_t *)malloc((size_t)width * height * bytes_per_pixel);
    444 	ta_image_axis *columns = (ta_image_axis *)malloc((size_t)width * sizeof(*columns));
    445 
    446 	if (scaled == 0 || columns == 0) {
    447 		free(scaled);
    448 		free(columns);
    449 
    450 		return false;
    451 	}
    452 
    453 	for (uint32_t column = 0; column < width; column++) {
    454 		columns[column] = axis_position(column, width, image->width);
    455 	}
    456 
    457 	uint8_t *output = scaled;
    458 
    459 	for (uint32_t y = 0; y < height; y++) {
    460 		ta_image_axis row = axis_position(y, height, image->height);
    461 
    462 		for (uint32_t x = 0; x < width; x++) {
    463 			uint32_t red;
    464 			uint32_t green;
    465 			uint32_t blue;
    466 
    467 			sample_image(image, columns[x], row, &red, &green, &blue);
    468 
    469 			red = (red + 32768) >> 16;
    470 			green = (green + 32768) >> 16;
    471 			blue = (blue + 32768) >> 16;
    472 
    473 			if (ta_display->bpp == 32) {
    474 				*(uint32_t *)output = (red << 16) | (green << 8) | blue;
    475 				output += 4;
    476 			} else {
    477 				*output++ = (uint8_t)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
    478 			}
    479 		}
    480 	}
    481 
    482 	free(columns);
    483 	free(image->scaled);
    484 
    485 	image->scaled = scaled;
    486 	image->scaled_width = width;
    487 	image->scaled_height = height;
    488 	image->scaled_bpp = (uint8_t)ta_display->bpp;
    489 
    490 	return true;
    491 }
    492 
    493 bool ta_image_draw(const uint8_t *data, uint32_t length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, uint16_t frame) {
    494 	if (data == 0 || length == 0 || x > TA_FIXED_ONE || y > TA_FIXED_ONE || width == 0 || height == 0 || width > TA_FIXED_ONE || height > TA_FIXED_ONE || (!ta_display->sprite_mode && ta_display->buffers[1] == 0) || (ta_display->sprite_mode && (!ta_display_sprites || ta_display->bpp != 32)) || (ta_display->bpp != 8 && ta_display->bpp != 32)) {
    495 		return false;
    496 	}
    497 
    498 	uint32_t pixel_x = (uint32_t)(((uint64_t)x * ta_display->width) >> 16);
    499 	uint32_t pixel_y = (uint32_t)(((uint64_t)y * ta_display->height) >> 16);
    500 	uint32_t pixel_width = (uint32_t)(((uint64_t)width * ta_display->width) >> 16);
    501 	uint32_t pixel_height = (uint32_t)(((uint64_t)height * ta_display->height) >> 16);
    502 
    503 	if (pixel_width == 0 || pixel_height == 0) {
    504 		return false;
    505 	}
    506 
    507 	ta_image_cache *image = get_image(data, length, frame);
    508 	if (image == 0) {
    509 		return false;
    510 	}
    511 
    512 	uint32_t visible_width = 0;
    513 	uint32_t visible_height = 0;
    514 
    515 	if (pixel_x < ta_display->width) {
    516 		visible_width = pixel_width > ta_display->width - pixel_x ? (uint32_t)(ta_display->width - pixel_x) : pixel_width;
    517 	}
    518 
    519 	if (pixel_y < ta_display->height) {
    520 		visible_height = pixel_height > ta_display->height - pixel_y ? (uint32_t)(ta_display->height - pixel_y) : pixel_height;
    521 	}
    522 
    523 	if (visible_width == 0 || visible_height == 0) {
    524 		return true;
    525 	}
    526 
    527 	bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
    528 
    529 	if (ta_display->sprite_mode) {
    530 		uint32_t source_width = image->width;
    531 		uint32_t source_height = image->height;
    532 
    533 		if (visible_width < pixel_width) {
    534 			source_width = (uint32_t)(((uint64_t)visible_width * image->width + pixel_width - 1) / pixel_width);
    535 		}
    536 
    537 		if (visible_height < pixel_height) {
    538 			source_height = (uint32_t)(((uint64_t)visible_height * image->height + pixel_height - 1) / pixel_height);
    539 		}
    540 
    541 		ta_display_sprite *sprite = ta_display_sprite_create((uint16_t)source_width, (uint16_t)source_height);
    542 		if (!sprite) {
    543 			return false;
    544 		}
    545 
    546 		sprite->x = (uint16_t)pixel_x;
    547 		sprite->y = (uint16_t)pixel_y;
    548 		sprite->draw_width = (uint16_t)visible_width;
    549 		sprite->draw_height = (uint16_t)visible_height;
    550 
    551 		uint32_t *pixels = (uint32_t *)sprite->pixels;
    552 		uint32_t palette_offset = transparent ? 1 : 0;
    553 
    554 		for (uint32_t image_y = 0; image_y < source_height; image_y++) {
    555 			for (uint32_t image_x = 0; image_x < source_width; image_x++) {
    556 				uint8_t index = get_pixel_index(image, image_x, image_y);
    557 				uint32_t *pixel = &pixels[(size_t)image_y * source_width + image_x];
    558 
    559 				if (transparent && index == 0) {
    560 					*pixel = 0;
    561 				} else {
    562 					const uint8_t *color = image->palette[index - palette_offset];
    563 
    564 					*pixel = ((uint32_t)color[0] << 24) | ((uint32_t)color[1] << 16) | ((uint32_t)color[2] << 8) | 0xff;
    565 				}
    566 			}
    567 		}
    568 
    569 		if (!ta_display_sprite_add(sprite)) {
    570 			free(sprite);
    571 			return false;
    572 		}
    573 
    574 		return true;
    575 	}
    576 
    577 	if (!transparent && prepare_scaled(image, pixel_width, pixel_height)) {
    578 		size_t bytes_per_pixel = ta_display->bpp / 8;
    579 
    580 		for (uint32_t image_y = 0; image_y < visible_height; image_y++) {
    581 			const uint8_t *source = image->scaled + (size_t)image_y * pixel_width * bytes_per_pixel;
    582 			uint8_t *destination = (uint8_t *)ta_display->buffers[1] + ((size_t)(pixel_y + image_y) * ta_display->width + pixel_x) * bytes_per_pixel;
    583 
    584 			if (ta_display->bpp == 8) {
    585 				copy_bytes(destination, source, visible_width);
    586 			} else {
    587 				const uint32_t *source32 = (const uint32_t *)source;
    588 				uint32_t *destination32 = (uint32_t *)destination;
    589 
    590 				for (uint32_t image_x = 0; image_x < visible_width; image_x++) {
    591 					destination32[image_x] = (destination32[image_x] & 0xff000000u) | source32[image_x];
    592 				}
    593 			}
    594 		}
    595 
    596 		return true;
    597 	}
    598 
    599 	ta_image_axis *columns = (ta_image_axis *)malloc((size_t)visible_width * sizeof(*columns));
    600 	if (columns == 0) {
    601 		return false;
    602 	}
    603 
    604 	for (uint32_t image_x = 0; image_x < visible_width; image_x++) {
    605 		columns[image_x] = axis_position(image_x, pixel_width, image->width);
    606 	}
    607 
    608 	for (uint32_t image_y = 0; image_y < visible_height; image_y++) {
    609 		ta_image_axis row = axis_position(image_y, pixel_height, image->height);
    610 		size_t row_start = (size_t)(pixel_y + image_y) * ta_display->width + pixel_x;
    611 
    612 		for (uint32_t image_x = 0; image_x < visible_width; image_x++) {
    613 			uint32_t red;
    614 			uint32_t green;
    615 			uint32_t blue;
    616 			uint32_t alpha_sum = sample_image(image, columns[image_x], row, &red, &green, &blue);
    617 
    618 			if (alpha_sum == 0) {
    619 				continue;
    620 			}
    621 
    622 			size_t destination_index = row_start + image_x;
    623 			uint32_t inverse_alpha = 65536 - alpha_sum;
    624 
    625 			if (ta_display->bpp == 32) {
    626 				uint32_t *pixels = (uint32_t *)ta_display->buffers[1];
    627 				uint32_t destination = pixels[destination_index];
    628 
    629 				red = (red + ((destination >> 16) & 0xff) * inverse_alpha + 32768) >> 16;
    630 				green = (green + ((destination >> 8) & 0xff) * inverse_alpha + 32768) >> 16;
    631 				blue = (blue + (destination & 0xff) * inverse_alpha + 32768) >> 16;
    632 
    633 				pixels[destination_index] = (destination & 0xff000000u) | (red << 16) | (green << 8) | blue;
    634 			} else {
    635 				uint8_t *pixels = (uint8_t *)ta_display->buffers[1];
    636 				uint32_t destination = pixels[destination_index];
    637 
    638 				red = (red + (((destination >> 5) & 7) * 255 / 7) * inverse_alpha + 32768) >> 16;
    639 				green = (green + (((destination >> 2) & 7) * 255 / 7) * inverse_alpha + 32768) >> 16;
    640 				blue = (blue + ((destination & 3) * 255 / 3) * inverse_alpha + 32768) >> 16;
    641 
    642 				pixels[destination_index] = (uint8_t)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
    643 			}
    644 		}
    645 	}
    646 
    647 	free(columns);
    648 
    649 	return true;
    650 }
    651 
    652 bool ta_image_render(const uint8_t *data, uint32_t length, uint16_t frame, uint32_t width, uint32_t height, uint32_t *pixels) {
    653 	if (pixels == 0 || width == 0 || height == 0) {
    654 		return false;
    655 	}
    656 
    657 	ta_image_cache *image = get_image(data, length, frame);
    658 	if (image == 0) {
    659 		return false;
    660 	}
    661 
    662 	ta_image_axis *columns = (ta_image_axis *)malloc((size_t)width * sizeof(*columns));
    663 	if (columns == 0) {
    664 		return false;
    665 	}
    666 
    667 	for (uint32_t x = 0; x < width; x++) {
    668 		columns[x] = axis_position(x, width, image->width);
    669 	}
    670 
    671 	for (uint32_t y = 0; y < height; y++) {
    672 		ta_image_axis row = axis_position(y, height, image->height);
    673 
    674 		for (uint32_t x = 0; x < width; x++) {
    675 			uint32_t red;
    676 			uint32_t green;
    677 			uint32_t blue;
    678 
    679 			sample_image(image, columns[x], row, &red, &green, &blue);
    680 
    681 			red = (red + 32768) >> 16;
    682 			green = (green + 32768) >> 16;
    683 			blue = (blue + 32768) >> 16;
    684 
    685 			pixels[(size_t)y * width + x] = (red << 16) | (green << 8) | blue;
    686 		}
    687 	}
    688 
    689 	free(columns);
    690 
    691 	return true;
    692 }
    693 
    694 void ta_image_release(void) {
    695 	while (image_cache != 0) {
    696 		ta_image_cache *image = image_cache;
    697 		image_cache = image->next;
    698 		free(image->pixels);
    699 		free(image->scaled);
    700 		free(image);
    701 	}
    702 }