From 6b21649c749f7af2ebceacadbaad36cade64320c Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 6 Mar 2024 17:21:24 -0600 Subject: [PATCH] fix ase reading pixels outside of frame --- src/ase.c | 84 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/src/ase.c b/src/ase.c index 5c68f0f4..1073bbb1 100644 --- a/src/ase.c +++ b/src/ase.c @@ -558,10 +558,13 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff goto abort; } + u64 frame_width = ase_header.width; + u64 frame_height = ase_header.height; + u32 frames_x = ase_header.frames; u32 frames_y = 1; - u64 image_width = ase_header.width * frames_x; - u64 image_height = ase_header.height * frames_y; + u64 image_width = frame_width * frames_x; + u64 image_height = frame_height * frames_y; make_image_dimensions_squareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height); res.image.width = image_width; @@ -718,35 +721,61 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff { __profscope(assemble_image); + /* Assemble image from cels */ for (struct cel *cel = cel_head; cel; cel = cel->next) { struct layer *layer = layers_ordered[cel->layer_index]; - /* Only draw visible layers */ if (layer->flags & 1) { u8 opacity = (cel->opacity / 255.0f) * (layer->opacity / 255.0f) * 255.0f; + i32 cel_width = cel->width; i32 cel_height = cel->height; - u32 frame_x = cel->frame_index % frames_x; - u32 frame_y = cel->frame_index / frames_x; + i32 cel_left = 0; + i32 cel_right = cel_width; + i32 cel_top = 0; + i32 cel_bottom = cel_height; - i32 cel_x_pos = cel->x_pos + (frame_x * ase_header.width); - i32 cel_y_pos = cel->y_pos + (frame_y * ase_header.height); + i32 frame_left = cel->x_pos; + i32 frame_top = cel->y_pos; - for (i32 cel_y = 0; cel_y < cel_height; ++cel_y) { - i32 image_y = cel_y_pos + cel_y; - if (image_y >= 0) { /* TODO: remove this check */ - i32 cel_stride = cel_y * cel_width; - i32 image_stride = image_y * image_width; - for (i32 cel_x = 0; cel_x < cel_width; ++cel_x) { - i32 image_x = cel_x_pos + cel_x; - if (image_x >= 0) { /* TODO: remove this check */ - u32 cel_pixel = cel->pixels[cel_x + cel_stride]; - u32 *image_pixel = &res.image.pixels[image_x + image_stride]; - *image_pixel = blend(cel_pixel, *image_pixel, opacity); - } - } + i32 image_left = frame_left + ((cel->frame_index % frames_x) * frame_width); + i32 image_top = frame_top + ((cel->frame_index / frames_x) * frame_height); + + /* Adjust bounds to ensure pixels outside of frame boundaries + * aren't (aseprite keeps chunks outside of frame around in + * project file). */ + { + i32 frame_right = cel_width + frame_left; + i32 frame_bottom = frame_top + cel_height; + if (frame_left < 0) { + cel_left += -frame_left; + frame_left = 0; + } + if (frame_top < 0) { + cel_top += -frame_top; + frame_top = 0; + } + if (frame_right > (i32)frame_width) { + cel_right -= (frame_right - frame_width); + frame_right = frame_width; + } + if (frame_bottom > (i32)frame_height) { + cel_bottom -= (frame_bottom - frame_height); + frame_bottom = frame_height; + } + } + + for (i32 cel_y = cel_top; cel_y < cel_bottom; ++cel_y) { + i32 image_y = image_top + cel_y; + i32 cel_stride = cel_y * cel_width; + i32 image_stride = image_y * image_width; + for (i32 cel_x = cel_left; cel_x < cel_right; ++cel_x) { + i32 image_x = image_left + cel_x; + u32 cel_pixel = cel->pixels[cel_x + cel_stride]; + u32 *image_pixel = &res.image.pixels[image_x + image_stride]; + *image_pixel = blend(cel_pixel, *image_pixel, opacity); } } } @@ -779,10 +808,13 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct buff struct ase_header ase_header; br_read_to_struct(&br, &ase_header); + u64 frame_width = ase_header.width; + u64 frame_height = ase_header.height; + u32 frames_x = ase_header.frames; u32 frames_y = 1; - u64 image_width = ase_header.width * frames_x; - u64 image_height = ase_header.height * frames_y; + u64 image_width = frame_width * frames_x; + u64 image_height = frame_height * frames_y; make_image_dimensions_squareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height); u32 num_tags = 0; @@ -809,10 +841,10 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct buff u32 frame_tile_x = i % frames_x; u32 frame_tile_y = i / frames_x; - u32 frame_x1 = frame_tile_x * ase_header.width; - u32 frame_y1 = frame_tile_y * ase_header.height; - u32 frame_x2 = frame_x1 + ase_header.width; - u32 frame_y2 = frame_y1 + ase_header.height; + u32 frame_x1 = frame_tile_x * frame_width; + u32 frame_y1 = frame_tile_y * frame_height; + u32 frame_x2 = frame_x1 + frame_width; + u32 frame_y2 = frame_y1 + frame_height; struct v2 clip_p1 = { (f32)frame_x1 / (f32)image_width, (f32)frame_y1 / (f32)image_height }; struct v2 clip_p2 = { (f32)frame_x2 / (f32)image_width, (f32)frame_y2 / (f32)image_height };