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(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 LoadAsync(string path) { byte[] data = []; if (File.Exists(path)) { data = await File.ReadAllBytesAsync(path); } return data; } }