tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE
commit 82ead357a7725a67d4451be04df1b9a849a601f4
parent fbe6a927fb5356ec4c1aa97579458167d2b606bc
Author: Connor Thomson <connor@gnufault.org>
Date:   Sun, 18 Oct 2026 18:03:37 -0700

Remove animation and add bilinear filtering

Diffstat:
MMakefile | 2+-
Mfiles/images/bg.tai | 0
Minclude/ta_button.h | 3---
Msrc/ta_button.c | 46++++------------------------------------------
Msrc/ta_image.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
5 files changed, 113 insertions(+), 65 deletions(-)

diff --git a/Makefile b/Makefile @@ -17,7 +17,7 @@ OUT := $(BUILD_DIR)/tuxanci2.a CONFIG_HEADER := $(BUILD_DIR)/ta_config.h -FLAGS := -ffreestanding -nostdlib -nostdlib -nostdinc -fno-builtin -fno-pie -fno-pic -fno-stack-protector -Wall -Wextra -Werror -pedantic -pedantic-errors +FLAGS := -ffreestanding -nostdlib -nostdlib -nostdinc -fno-builtin -fno-stack-protector -Wall -Wextra -Werror -pedantic -pedantic-errors # Needed for ta_types.h ifeq ($(BITS),64) diff --git a/files/images/bg.tai b/files/images/bg.tai Binary files differ. diff --git a/include/ta_button.h b/include/ta_button.h @@ -14,9 +14,6 @@ typedef struct { ta_button *buttons; ta_size size; ta_size selected; - ta_size animation_from; - ta_uint32 animation_frame; - ta_bool animation_active; ta_bool up_was_down; ta_bool down_was_down; ta_bool enter_was_down; diff --git a/src/ta_button.c b/src/ta_button.c @@ -2,7 +2,6 @@ #include "ta_button.h" #include "ta_text.h" -#define TA_BUTTON_ANIMATION_FRAMES 8 #define TA_BUTTON_FONT_SIZE 32 #define TA_BUTTON_SPACING 48 #define TA_BUTTON_COLOR_IDLE 118 @@ -16,9 +15,6 @@ void ta_button_lists_init(ta_button_lists *list) { list->buttons = 0; list->size = 0; list->selected = 0; - list->animation_from = 0; - list->animation_frame = 0; - list->animation_active = ta_false; list->up_was_down = ta_false; list->down_was_down = ta_false; list->enter_was_down = ta_false; @@ -66,32 +62,12 @@ void ta_button_lists_update(ta_button_lists *list) { list->down_was_down = down_down; list->enter_was_down = enter_down; - if (list->animation_active) { - list->animation_frame++; - - if (list->animation_frame >= TA_BUTTON_ANIMATION_FRAMES) { - list->animation_frame = TA_BUTTON_ANIMATION_FRAMES; - list->animation_active = ta_false; - } - - if (enter_pressed && list->buttons[list->selected].callback != 0) { - list->buttons[list->selected].callback(); - } - - return; - } - if (down_pressed || up_pressed) { - list->animation_from = list->selected; if (down_pressed) { list->selected = (list->selected + 1) % list->size; } else { list->selected = list->selected == 0 ? list->size - 1 : list->selected - 1; } - if (list->selected != list->animation_from) { - list->animation_frame = 0; - list->animation_active = ta_true; - } } if (enter_pressed && list->buttons[list->selected].callback != 0) { @@ -118,15 +94,7 @@ void ta_button_lists_draw(const ta_button_lists *list) { spacing = 1; } - double progress = 1.0; - - if (list->animation_active) { - progress = (double)list->animation_frame / TA_BUTTON_ANIMATION_FRAMES; - progress = progress * progress * (3.0 - 2.0 * progress); - } - - ta_int32 center_y = (ta_int32)(ta_fb.height / 2); - ta_int32 selected_shift = (ta_int32)list->selected - (ta_int32)list->animation_from; + ta_int32 center_y = (ta_int32)(ta_fb.height / 2); for (ta_size i = 0; i < list->size; i++) { ta_uint32 text_width = 0; @@ -134,20 +102,14 @@ void ta_button_lists_draw(const ta_button_lists *list) { ta_text_measure(list->buttons[i].label, font_size, &text_width, &text_height); - ta_int32 row_from = (ta_int32)i - (ta_int32)list->animation_from; - ta_int32 y = center_y + (ta_int32)(row_from * spacing - selected_shift * spacing * progress) - (ta_int32)(text_height / 2); + ta_int32 row_from = (ta_int32)i - (ta_int32)list->selected; + ta_int32 y = center_y + row_from * spacing - (ta_int32)(text_height / 2); if (y < 0 || (ta_size)y >= ta_fb.height || text_width > ta_fb.width) { continue; } - double selection = i == list->animation_from ? 1.0 - progress : 0.0; - - if (i == list->selected) { - selection += progress; - } - - ta_uint8 color = (ta_uint8)(TA_BUTTON_COLOR_IDLE + (TA_BUTTON_COLOR_SELECTED - TA_BUTTON_COLOR_IDLE) * selection + 0.5); + ta_uint8 color = i == list->selected ? TA_BUTTON_COLOR_SELECTED : TA_BUTTON_COLOR_IDLE; ta_uint32 x = (ta_uint32)(ta_fb.width - text_width) / 2; ta_text_draw_outline(list->buttons[i].label, x, (ta_uint32)y, text_width, text_height, color, color, color, 1, 0, 0, 0); diff --git a/src/ta_image.c b/src/ta_image.c @@ -272,8 +272,9 @@ ta_bool ta_image_get_size(const ta_uint8 *data, ta_uint32 length, ta_uint32 *wid return ta_false; } - *width = image->width; + *width = image->width; *height = image->height; + return ta_true; } @@ -294,13 +295,23 @@ static ta_image_cache *get_image(const ta_uint8 *data, ta_uint32 length, ta_uint ta_uint8 *old_pixels = image->pixels; ta_image_cache *next = image->next; - *image = decoded; - image->next = next; + *image = decoded; + image->next = next; + ta_free(old_pixels); return image; } +static ta_uint8 get_pixel_index(const ta_image_cache *image, ta_uint32 x, ta_uint32 y) { + if ((image->flags & TAIF_FLAG_8BPP) != 0) { + return image->pixels[y * image->stride + x]; + } + + ta_uint8 packed = image->pixels[y * image->stride + x / 2]; + return (x & 1) == 0 ? packed >> 4 : packed & 0x0f; +} + ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, float x, float y, float width, float height, ta_uint16 frame) { if (data == 0 || length == 0 || x != x || y != y || width != width || height != height || x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f || width <= 0.0f || height <= 0.0f || width > 1.0f || height > 1.0f || ta_fb.buffers[1] == 0 || (ta_fb.bpp != 8 && ta_fb.bpp != 32)) { return ta_false; @@ -326,40 +337,118 @@ ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, float x, float y, continue; } - ta_uint32 source_y = (ta_uint32)((ta_uint64)image_y * image->height / pixel_height); + ta_uint64 source_y_position = ((ta_uint64)(2 * (ta_uint64)image_y + 1) * image->height * 128) / pixel_height; + ta_uint32 source_y0; + ta_uint32 source_y1; + ta_uint32 fraction_y; + + if (source_y_position <= 128) { + source_y0 = 0; + source_y1 = 0; + fraction_y = 0; + } else { + source_y_position -= 128; + source_y0 = (ta_uint32)(source_y_position >> 8); + fraction_y = (ta_uint32)(source_y_position & 0xff); + + if (source_y0 >= image->height - 1) { + source_y0 = image->height - 1; + source_y1 = source_y0; + fraction_y = 0; + } else { + source_y1 = source_y0 + 1; + } + } + for (ta_uint32 image_x = 0; image_x < pixel_width; image_x++) { ta_uint64 screen_x = (ta_uint64)pixel_x + image_x; + if (screen_x >= ta_fb.width) { continue; } - ta_uint32 source_x = (ta_uint32)((ta_uint64)image_x * image->width / pixel_width); - ta_uint8 index; - - if ((image->flags & TAIF_FLAG_8BPP) != 0) { - index = image->pixels[source_y * image->stride + source_x]; + ta_uint64 source_x_position = ((ta_uint64)(2 * (ta_uint64)image_x + 1) * image->width * 128) / pixel_width; + ta_uint32 source_x0; + ta_uint32 source_x1; + ta_uint32 fraction_x; + + if (source_x_position <= 128) { + source_x0 = 0; + source_x1 = 0; + fraction_x = 0; } else { - ta_uint8 packed = image->pixels[source_y * image->stride + source_x / 2]; - index = (source_x & 1) == 0 ? packed >> 4 : packed & 0x0f; + source_x_position -= 128; + source_x0 = (ta_uint32)(source_x_position >> 8); + fraction_x = (ta_uint32)(source_x_position & 0xff); + + if (source_x0 >= image->width - 1) { + source_x0 = image->width - 1; + source_x1 = source_x0; + fraction_x = 0; + } else { + source_x1 = source_x0 + 1; + } } - - if ((image->flags & TAIF_FLAG_TRANSPARENT) != 0 && index == 0) { + + ta_uint32 weight_x0 = 256 - fraction_x; + ta_uint32 weight_y0 = 256 - fraction_y; + + ta_uint32 weights[4] = { + weight_x0 * weight_y0, + fraction_x * weight_y0, + weight_x0 * fraction_y, + fraction_x * fraction_y + }; + + ta_uint8 indices[4] = { + get_pixel_index(image, source_x0, source_y0), + get_pixel_index(image, source_x1, source_y0), + get_pixel_index(image, source_x0, source_y1), + get_pixel_index(image, source_x1, source_y1) + }; + + ta_uint64 red_sum = 0; + ta_uint64 green_sum = 0; + ta_uint64 blue_sum = 0; + ta_uint32 alpha_sum = 0; + + for (ta_uint32 sample = 0; sample < 4; sample++) { + if ((image->flags & TAIF_FLAG_TRANSPARENT) != 0 && indices[sample] == 0) { + continue; + } + + ta_uint32 palette_index = indices[sample] - ((image->flags & TAIF_FLAG_TRANSPARENT) != 0 ? 1 : 0); + red_sum += (ta_uint64)image->palette[palette_index][0] * weights[sample]; + green_sum += (ta_uint64)image->palette[palette_index][1] * weights[sample]; + blue_sum += (ta_uint64)image->palette[palette_index][2] * weights[sample]; + alpha_sum += weights[sample]; + } + + if (alpha_sum == 0) { continue; } - ta_uint32 palette_index = index - ((image->flags & TAIF_FLAG_TRANSPARENT) != 0 ? 1 : 0); - ta_uint32 red = image->palette[palette_index][0]; - ta_uint32 green = image->palette[palette_index][1]; - ta_uint32 blue = image->palette[palette_index][2]; ta_size destination_index = (ta_size)screen_y * ta_fb.width + (ta_size)screen_x; if (ta_fb.bpp == 32) { ta_uint32 *pixels = (ta_uint32 *)ta_fb.buffers[1]; ta_uint32 destination = pixels[destination_index]; + ta_uint32 inverse_alpha = 65536 - alpha_sum; + ta_uint32 red = (ta_uint32)((red_sum + ((destination >> 16) & 0xff) * (ta_uint64)inverse_alpha + 32768) >> 16); + ta_uint32 green = (ta_uint32)((green_sum + ((destination >> 8) & 0xff) * (ta_uint64)inverse_alpha + 32768) >> 16); + ta_uint32 blue = (ta_uint32)((blue_sum + (destination & 0xff) * (ta_uint64)inverse_alpha + 32768) >> 16); pixels[destination_index] = (destination & 0xff000000u) | (red << 16) | (green << 8) | blue; } else { - ta_uint8 *pixels = (ta_uint8 *)ta_fb.buffers[1]; - pixels[destination_index] = (ta_uint8)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6)); + ta_uint8 *pixels = (ta_uint8 *)ta_fb.buffers[1]; + ta_uint32 destination = pixels[destination_index]; + ta_uint32 inverse_alpha = 65536 - alpha_sum; + ta_uint32 red_destination = ((destination >> 5) & 7) * 255 / 7; + ta_uint32 green_destination = ((destination >> 2) & 7) * 255 / 7; + ta_uint32 blue_destination = (destination & 3) * 255 / 3; + ta_uint32 red = (ta_uint32)((red_sum + red_destination * (ta_uint64)inverse_alpha + 32768) >> 16); + ta_uint32 green = (ta_uint32)((green_sum + green_destination * (ta_uint64)inverse_alpha + 32768) >> 16); + ta_uint32 blue = (ta_uint32)((blue_sum + blue_destination * (ta_uint64)inverse_alpha + 32768) >> 16); + pixels[destination_index] = (ta_uint8)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6)); } } }