Files
AetherBags/KamiToolKit/Nodes/Basic/GifImageNode.cs
T
KnackAtNite 8db4ce6094
Debug Build and Test / Build against Latest Dalamud (push) Has been cancelled
Debug Build and Test / Build against Staging Dalamud (push) Has been cancelled
Initial commit: AetherBags + KamiToolKit for FC Gitea
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 14:46:31 -05:00

121 lines
3.5 KiB
C#

using System;
using System.IO;
using System.Numerics;
using System.Threading.Tasks;
using Dalamud.Interface.Textures;
using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes;
using KamiToolKit.Timelines;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace KamiToolKit.Nodes;
public class GifImageNode : ResNode {
public ImageNode ImageNode;
public GifImageNode() {
ImageNode = new ImageNode();
ImageNode.AttachNode(this);
}
public required string FilePath {
set {
Task.Run(() => LoadFrames(value));
}
}
public override float Width {
get => base.Width;
set {
ImageNode.Width = value;
base.Width = value;
}
}
public override float Height {
get => base.Height;
set {
ImageNode.Height = value;
base.Height = value;
}
}
public Vector2 GifFrameSize { get; private set; }
public bool FitNodeToGif { get; set; }
public Action? OnGifLoaded { get; set; }
private async void LoadFrames(string filepath) {
try {
var image = await LoadAsync(filepath);
if (image.Length <= 0) return;
using var memoryStream = new MemoryStream(image);
using var processedImage = Image.Load<Rgba32>(memoryStream);
if (processedImage.Frames.Count is 0) return;
uint currentPartId = 0;
var frameDelay = processedImage.Frames.RootFrame.Metadata.GetGifMetadata().FrameDelay / 3.33333333f;
var frameCount = (int)(processedImage.Frames.Count * frameDelay);
GifFrameSize = new Vector2(processedImage.Width, processedImage.Height);
if (FitNodeToGif) {
Size = GifFrameSize;
}
foreach (var frame in processedImage.Frames) {
var buffer = new byte[8 * frame.Width * frame.Height];
frame.CopyPixelDataTo(buffer);
var texture = await DalamudInterface.Instance.TextureProvider.CreateFromRawAsync(RawImageSpecification.Rgba32(frame.Width, frame.Height), buffer);
unsafe {
var newPart = ImageNode.AddPart(new Part {
Size = texture.Size,
Id = currentPartId++,
});
newPart->LoadTexture(texture);
}
}
ImageNode.AddTimeline(new TimelineBuilder()
.BeginFrameSet(1, frameCount)
.AddFrame(0, partId: 0)
.AddFrame(frameCount, partId: currentPartId)
.EndFrameSet()
.Build());
AddTimeline(new TimelineBuilder()
.BeginFrameSet(1, frameCount)
.AddLabel(1, 200, AtkTimelineJumpBehavior.Start, 0)
.AddLabel(frameCount, 0, AtkTimelineJumpBehavior.LoopForever, 200)
.EndFrameSet()
.Build());
Timeline?.PlayAnimation( AtkTimelineJumpBehavior.LoopForever, 200);
await DalamudInterface.Instance.Framework.RunOnFrameworkThread(() => {
OnGifLoaded?.Invoke();
});
}
catch (Exception e) {
Log.Exception(e);
}
}
private static async Task<byte[]> LoadAsync(string path) {
byte[] data = [];
if (File.Exists(path)) {
data = await File.ReadAllBytesAsync(path);
}
return data;
}
}