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:
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));
}
}
}