From 0988ab832bfc7a1b1c851125b6172cf68c6d9cb9 Mon Sep 17 00:00:00 2001 From: Xander Swan <[hidden email]> Date: Wed, 7 Jan 2026 23:12:22 -0500 Subject: doesn't compile but i'm commiting anywya --- tools/compile_assets/aseprite/utils/common.odin | 303 ++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 tools/compile_assets/aseprite/utils/common.odin (limited to 'tools/compile_assets/aseprite/utils/common.odin') diff --git a/tools/compile_assets/aseprite/utils/common.odin b/tools/compile_assets/aseprite/utils/common.odin new file mode 100644 index 0000000..69d7c71 --- /dev/null +++ b/tools/compile_assets/aseprite/utils/common.odin @@ -0,0 +1,303 @@ +package aseprite_file_handler_utility + +import "base:runtime" +import "core:mem" +import "core:slice" +import "core:image" + +@(require) import "core:fmt" +@(require) import "core:log" + +import ase ".." + + +UTILS_DEBUG_MODE :: #config(ASE_UTILS_DEBUG, ODIN_DEBUG) + + +/* ================================== Destruction procs ================================== */ +destroy_frame :: proc(frame: Frame, alloc := context.allocator) -> runtime.Allocator_Error { + for &cel in frame.cels { + destroy(&cel, alloc) or_return + } + return delete(frame.cels, alloc) +} + +destroy_frames :: proc(frames: []Frame, alloc := context.allocator) -> runtime.Allocator_Error { + for frame in frames { + for &cel in frame.cels { + destroy(&cel, alloc) or_return + } + delete(frame.cels, alloc) or_return + } + return delete(frames, alloc) +} + +destroy_image :: proc(img: Image, alloc := context.allocator) -> runtime.Allocator_Error { + return delete(img.data, alloc) +} + +destroy_images :: proc(imgs: []Image, alloc := context.allocator) -> runtime.Allocator_Error { + for img in imgs { + delete(img.data, alloc) or_return + } + return delete(imgs, alloc) +} + +destroy_image_bytes :: proc(imgs: [][]byte, alloc := context.allocator) -> runtime.Allocator_Error { + for img in imgs { + delete(img, alloc) or_return + } + return delete(imgs, alloc) +} + +destroy_animation :: proc(anim: ^Animation, alloc := context.allocator) -> runtime.Allocator_Error { + for frame in anim.frames { + delete(frame, alloc) or_return + } + return delete(anim.frames, alloc) +} + +destroy_info :: proc(info: ^Info) -> runtime.Allocator_Error { + context.allocator = info.allocator + destroy_frames(info.frames) or_return + delete(info.layers) or_return + delete(info.palette) or_return + delete(info.tags) or_return + delete(info.tilesets) or_return + destroy_slices(info.slices) or_return + return nil +} + +destroy_cel :: proc(cel: ^Cel, alloc := context.allocator) -> runtime.Allocator_Error { + return delete(cel.tilemap.tiles, alloc) +} + +destroy_layers :: proc(lay: []Layer, alloc := context.allocator) -> runtime.Allocator_Error { + return delete(lay, alloc) +} + +destroy_palette :: proc(pal: Palette, alloc := context.allocator) -> runtime.Allocator_Error { + return delete(pal) +} + +destroy_slices :: proc(sls: []Slice, alloc := context.allocator) -> runtime.Allocator_Error { + for sl in sls { + destroy_slice(sl, alloc) or_return + } + return delete(sls) +} + +destroy_slice :: proc(sl: Slice, alloc := context.allocator) -> runtime.Allocator_Error { + return delete(sl.keys) +} + +destroy_sheet :: proc(s: Sprite_Sheet, alloc := context.allocator) -> runtime.Allocator_Error { + return destroy_image(s.img, alloc) +} + +destroy :: proc { + destroy_frames, + destroy_image, + destroy_animation, + destroy_image_bytes, + destroy_images, + destroy_info, + destroy_cel, + destroy_frame, + destroy_layers, + destroy_palette, + destroy_slices, + destroy_slice, + destroy_sheet, +} +/* ======================================================================================= */ + + +get_metadata :: proc(header: ase.File_Header) -> (md: Metadata) { + return { + int(header.width), + int(header.height), + Pixel_Depth(header.color_depth), + u8(header.transparent_index), + } +} + + +// Use with slice.sort_by, .sort_by_with_indices, .stable_sort_by, .is_sorted_by & .reverse_sort_by +cel_less :: proc(i, j: Cel) -> bool { + // https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md#note5 + ior := i.layer + i.z_index + jor := j.layer + j.z_index + return ior < jor || (ior == jor && i.z_index < j.z_index ) +} + +// Use with slice.sort_by_cmp, .stable_sort_by_cmp, .is_sorted_cmp & .reverse_sort_by_cmp +cel_cmp :: proc(i, j: Cel) -> slice.Ordering { + // https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md#note5 + iz := i.z_index + jz := j.z_index + ior := i.layer + iz + jor := j.layer + jz + + if ior < jor { return .Less } + else if ior > jor { return .Greater } + else if iz < jz { return .Less } + else if iz > jz { return .Greater } + return .Equal +} + + +has_new_palette :: proc(doc: ^ase.Document) -> bool { + for f in doc.frames { + for c in f.chunks { + _ = c.(ase.Palette_Chunk) or_continue + return true + } + } + return false +} + + +has_tileset :: proc(doc: ^ase.Document) -> bool { + for f in doc.frames { + for c in f.chunks { + _ = c.(ase.Tileset_Chunk) or_continue + return true + } + } + return false +} + + +// Uses Nearest-neighbor Upscaling +upscale_image_from_bytes :: proc(img: []byte, md: Metadata, factor := 10, alloc := context.allocator) -> (res: []byte, res_md: Metadata, err: Errors) { + ch := int(md.bpp) >> 3 + if len(img) != (md.width * md.height * ch) { + fast_log(.Error, "image size doesn't match metadata") + err = .Buffer_Size_Not_Match_Metadata + return + } + + res = make([]byte, len(img) * factor * factor, alloc) or_return + res_md = {md.width*factor, md.height*factor, md.bpp, md.trans_idx} + + for h in 0.. (res: Image, err: Errors) { + res.data, res.md = upscale_image_from_bytes(img.data, img.md, factor, alloc) or_return + return +} + +// Uses Nearest-neighbor Upscaling +upscale_image :: proc { upscale_image_from_img, upscale_image_from_bytes } + + +upscale_all_from_imgs :: proc(imgs: []Image, factor := 10, alloc := context.allocator) -> (res: []Image, err: Errors) { + res = make([]Image, len(imgs), alloc) or_return + for img, pos in imgs { + res[pos] = upscale_image_from_img(img, factor, alloc) or_return + } + return +} + +upscale_all_from_byte:: proc(imgs: [][]byte, md: Metadata, factor := 10, alloc := context.allocator) -> (res: [][]byte, res_md: Metadata, err: Errors) { + res = make([][]byte, len(imgs), alloc) or_return + if len(imgs) == 0 { return } + res[0], res_md = upscale_image_from_bytes(imgs[0], md, factor, alloc) or_return + + for img, pos in imgs[1:] { + res[pos], _ = upscale_image_from_bytes(img, md, factor, alloc) or_return + } + return +} + +upscale_all :: proc{ upscale_all_from_imgs, upscale_all_from_byte } + + +compute_alpha :: proc(img: []u8, alloc := context.allocator) -> (res: []u8, err: Errors) { + if len(img) % 4 != 0 { + fast_log(.Error, "Given buffer isn't RGBA") + return nil, .Buffer_Not_RGBA + } + + img_buf := mem.slice_data_cast([][4]u8, img) + buf := make([][3]u8, len(img_buf), alloc) or_return + + for p, pos in img_buf { + bp := [4]i32{ i32(p.r), i32(p.g), i32(p.b), i32(p.a) } + bp.rgb = bp.rgb * bp.a / (255 + bp.a - alpha(bp.a, 255)) + buf[pos] = { u8(bp.r), u8(bp.g), u8(bp.b) } + } + + return mem.slice_data_cast([]u8, buf), nil +} + + +remove_alpha :: proc(img: []u8, alloc := context.allocator) -> (res: []u8, err: Errors) { + if len(img) % 4 != 0 { + fast_log(.Error, "Given buffer isn't RGBA") + return nil, .Buffer_Not_RGBA + } + + img_buf := mem.slice_data_cast([][4]u8, img) + buf := make([][3]u8, len(img_buf), alloc) or_return + + for p, pos in img_buf { + buf[pos] = p.rgb + } + + return mem.slice_data_cast([]u8, buf), nil +} + + +// Converts `utils.Image` to a `core:image.Image` with allocation +to_core_image :: proc(buf: []byte, md: Metadata, alloc := context.allocator) -> (img: image.Image, err: runtime.Allocator_Error) { + img.width = md.width + img.height = md.height + img.depth = 8 + img.channels = 4 + img.pixels.buf = make([dynamic]byte, len(buf), alloc) or_return + + copy(img.pixels.buf[:], buf) + return +} + +// Converts `utils.Image` to a `core:image.Image` with no allocation +to_core_image_non_alloc :: proc(buf: []byte, md: Metadata) -> (img: image.Image) { + img.width = md.width + img.height = md.height + img.depth = 8 + img.channels = 4 + + raw := runtime.Raw_Dynamic_Array { + data = raw_data(buf), + len = len(buf), + cap = len(buf), + allocator = runtime.nil_allocator(), + } + + img.pixels.buf = transmute([dynamic]byte)raw + return +} + + + -- cgit v1.3-2-g0d8e