aboutsummaryrefslogtreecommitdiff
path: root/tools/compile_assets/aseprite/utils/animation.odin
diff options
context:
space:
mode:
authorXander Swan <[hidden email]>2026-01-07 23:12:22 -0500
committerXander Swan <[hidden email]>2026-01-07 23:12:22 -0500
commit0988ab832bfc7a1b1c851125b6172cf68c6d9cb9 (patch)
tree460bc2d9f0bce463af273d6b2b2c20faa880ac29 /tools/compile_assets/aseprite/utils/animation.odin
parentade0dc4d257d053b7064184f193f8168c496e308 (diff)
doesn't compile but i'm commiting anywya
Diffstat (limited to 'tools/compile_assets/aseprite/utils/animation.odin')
-rw-r--r--tools/compile_assets/aseprite/utils/animation.odin181
1 files changed, 181 insertions, 0 deletions
diff --git a/tools/compile_assets/aseprite/utils/animation.odin b/tools/compile_assets/aseprite/utils/animation.odin
new file mode 100644
index 0000000..7fe5059
--- /dev/null
+++ b/tools/compile_assets/aseprite/utils/animation.odin
@@ -0,0 +1,181 @@
+package aseprite_file_handler_utility
+
+
+import "core:time"
+import "core:slice"
+
+@(require) import "core:fmt"
+@(require) import "core:log"
+
+import ase ".."
+
+get_animation_from_doc :: proc(
+ doc: ^ase.Document, anim: ^Animation,
+ use_tag := "", alloc := context.allocator
+) -> (err: Errors) {
+ info: Info
+ get_info(doc, &info, alloc) or_return
+ defer destroy(&info)
+ return get_animation_from_info(info, anim, use_tag)
+}
+
+
+get_animation_from_info :: proc (
+ info: Info, anim: ^Animation, use_tag := "",
+) -> (err: Errors) {
+ context.allocator = info.allocator
+
+ s, f := 0, len(info.frames)
+ tag: Tag
+
+ if use_tag != "" {
+ for t in info.tags {
+ if t.name == use_tag {
+ if len(info.frames) < t.to || len(info.frames) < t.from {
+ return Animation_Error.Tag_Index_Out_Of_Bounds
+ }
+ tag = t
+ s = t.from
+ f = t.to+1
+ break
+ }
+ }
+
+ if tag == {} {
+ return Animation_Error.Tag_Not_Found
+ }
+ }
+
+ if anim.fps == 0 {
+ anim.fps = 30
+ }
+
+ anim.md = info.md
+ anim_frames := make([dynamic][]byte) or_return
+ defer if err != nil { delete(anim_frames) }
+
+ pos: int
+
+ for frame in info.frames[s:f] {
+ img := get_image_bytes_from_frame(frame, info) or_return
+ defer delete(img)
+
+ to_add := f64(frame.duration) * f64(anim.fps) / 1000
+ for _ in 0..<to_add {
+ append_elem(&anim_frames, slice.clone(img) or_return) or_return
+ pos += 1
+ }
+ }
+
+ if tag.direction == .Reverse || tag.direction == .Ping_Pong_Reverse {
+ slice.reverse(anim_frames[:])
+ }
+
+ if tag.direction == .Ping_Pong || tag.direction == .Ping_Pong_Reverse {
+ rev := slice.clone(anim_frames[:])
+ defer delete(rev)
+
+ slice.reverse(rev)
+ append_elems(&anim_frames, ..rev) or_return
+ }
+
+ anim.frames = anim_frames[:]
+ anim.length = time.Second * time.Duration(len(anim_frames) / anim.fps)
+ return
+}
+
+// Assumes 30 FPS & 100ms Frame Duration
+get_animation_from_images :: proc(imgs: []Image, md: Metadata, anim: ^Animation, alloc := context.allocator) -> (err: Errors) {
+ context.allocator = alloc
+ anim.fps = 30
+ anim.md = md
+ anim.length = time.Second * time.Duration(len(imgs) / 10)
+
+ frames := make([dynamic][]byte, 0, 3 * len(imgs)) or_return
+ defer if err != nil { delete(frames) }
+
+ for img in imgs {
+ frame := slice.clone(img.data) or_return
+ append_elem(&frames, frame) or_return
+
+ frame = slice.clone(img.data) or_return
+ append_elem(&frames, frame) or_return
+
+ frame = slice.clone(img.data) or_return
+ append_elem(&frames, frame) or_return
+ }
+
+ anim.frames = frames[:]
+ return
+}
+
+// Assumes 30 FPS & 100ms Frame Duration
+get_animation_from_bytes :: proc(imgs: [][]byte, md: Metadata, anim: ^Animation, alloc := context.allocator) -> (err: Errors) {
+ context.allocator = alloc
+ anim.fps = 30
+ anim.md = md
+ anim.length = time.Second * time.Duration(len(imgs) / 10)
+
+ frames := make([dynamic][]byte, 0, 3 * len(imgs)) or_return
+ defer if err != nil { delete(frames) }
+
+ for img in imgs {
+ append_elem(&frames, slice.clone(img) or_return) or_return
+ append_elem(&frames, slice.clone(img) or_return) or_return
+ append_elem(&frames, slice.clone(img) or_return) or_return
+ }
+
+ anim.frames = frames[:]
+ return
+}
+
+
+get_animation :: proc {
+ get_animation_from_doc,
+ get_animation_from_info,
+ get_animation_from_images,
+ get_animation_from_bytes,
+}
+
+
+// Returns a new image set with Red/Blue Tint or Merge Onion Skin
+apply_onion_skin :: proc(
+ imgs: []Image, opacity := 69, merge := false,
+ alloc := context.allocator
+) -> (frames: []Image, err: Errors) {
+ context.allocator = alloc
+
+ frames = make([]Image, len(imgs)) or_return
+ defer if err != nil { delete(frames) }
+
+ blank := make([]byte, len(imgs[0].data)) or_return
+ defer delete(blank)
+
+ cur, next: []byte
+ last := blank
+ defer if cur != nil { delete(cur) }
+
+ for img, pos in imgs {
+ if pos < len(imgs)-1 {
+ next = imgs[pos+1].data
+ } else {
+ next = blank
+ }
+
+ cur = slice.clone(img.data) or_return
+ if merge {
+ blend_bytes(last, cur, opacity, .Merge) or_return
+ blend_bytes(next, cur, opacity, .Merge) or_return
+ } else {
+ blend_bytes(last, cur, opacity, .Red_Tint) or_return
+ blend_bytes(next, cur, opacity, .Blue_Tint) or_return
+ }
+
+
+ frames[pos] = img
+ frames[pos].data = cur
+ last = cur
+ }
+
+ return
+}