using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using Godot; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; public partial class DGifPlayer : Node { private ImageTexture texture; private List frames = new(); private List delays = new(); // in 1/100 sec private int width; private int height; private Thread playThread; private bool running; private int currentFrame = 0; private float frameTimer = 0f; public ImageTexture Texture => texture; /// /// Load GIF file and prepare frames /// public bool Load(string path) { if (!System.IO.File.Exists(path)) { GD.PrintErr("Unable to find gif file: " + path); return false; } frames.Clear(); delays.Clear(); try { using var gif = SixLabors.ImageSharp.Image.Load(path); width = gif.Width; height = gif.Height; texture = null; foreach (var frame in gif.Frames) { // Copy pixels to RGBA byte array var pixels = new byte[width * height * 4]; frame.CopyPixelDataTo(pixels); frames.Add(pixels); // Frame delay in 1/100 sec var delay = frame.Metadata.GetGifMetadata().FrameDelay; delays.Add(delay > 0 ? delay : 10); // fallback to 100ms if 0 } } catch(Exception ex) { GD.PrintErr("Failed to load gif file: " + ex.Message); } return true; } /// /// Start playback /// public void Play() { if (frames.Count == 0) return; running = true; playThread = new Thread(PlayLoop) { IsBackground = true }; playThread.Start(); } /// /// Stop playback /// public void Stop() { running = false; playThread?.Join(); } /// /// Worker thread: cycles frames based on GIF delays /// private void PlayLoop() { while (running) { try { byte[] frameData = frames[currentFrame]; // Push update to main thread CallDeferred(nameof(UpdateTexture), frameData); // Wait for frame delay int delayMs = delays[currentFrame] * 10; // GIF delay is in 1/100 sec Thread.Sleep(delayMs); // Next frame currentFrame = (currentFrame + 1) % frames.Count; } catch(Exception ex) { GD.PrintErr(ex.Message); break; } } } /// /// Update Godot texture with frame data /// private void UpdateTexture(byte[] frameData) { var img = Godot.Image.CreateFromData(width, height, false, Godot.Image.Format.Rgba8, frameData); if (texture == null) { texture = ImageTexture.CreateFromImage(img); } else { texture.Update(img); } } public void GifDispose() { Stop(); frames.Clear(); delays.Clear(); } }