fix ase reading pixels outside of frame

This commit is contained in:
jacob 2024-03-06 17:21:24 -06:00
parent a25941d364
commit 6b21649c74

View File

@ -558,10 +558,13 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff
goto abort; goto abort;
} }
u64 frame_width = ase_header.width;
u64 frame_height = ase_header.height;
u32 frames_x = ase_header.frames; u32 frames_x = ase_header.frames;
u32 frames_y = 1; u32 frames_y = 1;
u64 image_width = ase_header.width * frames_x; u64 image_width = frame_width * frames_x;
u64 image_height = ase_header.height * frames_y; u64 image_height = frame_height * frames_y;
make_image_dimensions_squareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height); make_image_dimensions_squareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height);
res.image.width = image_width; 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); __profscope(assemble_image);
/* Assemble image from cels */ /* Assemble image from cels */
for (struct cel *cel = cel_head; cel; cel = cel->next) { for (struct cel *cel = cel_head; cel; cel = cel->next) {
struct layer *layer = layers_ordered[cel->layer_index]; struct layer *layer = layers_ordered[cel->layer_index];
/* Only draw visible layers */ /* Only draw visible layers */
if (layer->flags & 1) { if (layer->flags & 1) {
u8 opacity = (cel->opacity / 255.0f) * (layer->opacity / 255.0f) * 255.0f; u8 opacity = (cel->opacity / 255.0f) * (layer->opacity / 255.0f) * 255.0f;
i32 cel_width = cel->width; i32 cel_width = cel->width;
i32 cel_height = cel->height; i32 cel_height = cel->height;
u32 frame_x = cel->frame_index % frames_x; i32 cel_left = 0;
u32 frame_y = cel->frame_index / frames_x; 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 frame_left = cel->x_pos;
i32 cel_y_pos = cel->y_pos + (frame_y * ase_header.height); i32 frame_top = cel->y_pos;
for (i32 cel_y = 0; cel_y < cel_height; ++cel_y) { i32 image_left = frame_left + ((cel->frame_index % frames_x) * frame_width);
i32 image_y = cel_y_pos + cel_y; i32 image_top = frame_top + ((cel->frame_index / frames_x) * frame_height);
if (image_y >= 0) { /* TODO: remove this check */
i32 cel_stride = cel_y * cel_width; /* Adjust bounds to ensure pixels outside of frame boundaries
i32 image_stride = image_y * image_width; * aren't (aseprite keeps chunks outside of frame around in
for (i32 cel_x = 0; cel_x < cel_width; ++cel_x) { * project file). */
i32 image_x = cel_x_pos + cel_x; {
if (image_x >= 0) { /* TODO: remove this check */ i32 frame_right = cel_width + frame_left;
u32 cel_pixel = cel->pixels[cel_x + cel_stride]; i32 frame_bottom = frame_top + cel_height;
u32 *image_pixel = &res.image.pixels[image_x + image_stride]; if (frame_left < 0) {
*image_pixel = blend(cel_pixel, *image_pixel, opacity); 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; struct ase_header ase_header;
br_read_to_struct(&br, &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_x = ase_header.frames;
u32 frames_y = 1; u32 frames_y = 1;
u64 image_width = ase_header.width * frames_x; u64 image_width = frame_width * frames_x;
u64 image_height = ase_header.height * frames_y; u64 image_height = frame_height * frames_y;
make_image_dimensions_squareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height); make_image_dimensions_squareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height);
u32 num_tags = 0; 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_x = i % frames_x;
u32 frame_tile_y = i / frames_x; u32 frame_tile_y = i / frames_x;
u32 frame_x1 = frame_tile_x * ase_header.width; u32 frame_x1 = frame_tile_x * frame_width;
u32 frame_y1 = frame_tile_y * ase_header.height; u32 frame_y1 = frame_tile_y * frame_height;
u32 frame_x2 = frame_x1 + ase_header.width; u32 frame_x2 = frame_x1 + frame_width;
u32 frame_y2 = frame_y1 + ase_header.height; 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_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 }; struct v2 clip_p2 = { (f32)frame_x2 / (f32)image_width, (f32)frame_y2 / (f32)image_height };