/// <summary> /// Decodes the GIF file at the provided filepath into an array to textures. /// If framesToRead is smaller than 1 or bigger than the total number of frames in the GIF, the whole file will be read. /// Otherwise, only the number of frames determined by framesToRead will be decoded. /// </summary> /// <param name="filepath"></param> /// <param name="framesToRead"></param> /// <param name="threadPriority"></param> /// <param name="completeCallback"></param> public static void DecodeGif(string filepath, int framesToRead, System.Threading.ThreadPriority threadPriority, Action <Texture[]> completeCallback) { #if UNITY_EDITOR Debug.LogWarning("DecodeGif is not supported in Unity editor. Please test on an iOS or Android device."); #elif UNITY_IOS // Read the GIF partially. iOSNativeGif.DecodeGif(curDecodeId++, filepath, framesToRead, threadPriority, (int taskId, GifMetadata gifMetadata, GifFrameMetadata[] gifFrameMetadata, Color32[][] imageData) => { if (completeCallback != null) { completeCallback(ToTextureArray(gifMetadata, gifFrameMetadata, imageData)); } }); #elif UNITY_ANDROID // Read the GIF partially. AndroidNativeGif.DecodeGif(curDecodeId++, filepath, framesToRead, threadPriority, (int taskId, GifMetadata gifMetadata, GifFrameMetadata[] gifFrameMetadata, Color32[][] imageData) => { if (completeCallback != null) { completeCallback(ToTextureArray(gifMetadata, gifFrameMetadata, imageData)); } }); #endif }
internal static void ExportGif(GifExportTask exportTask) { var instance = new AndroidNativeGif(exportTask); var worker = new Thread(instance.DoExportGif); worker.Priority = exportTask.workerPriority; worker.Start(); }
/// <summary> /// Decodes the GIF file at the provided filepath into an <see cref="AnimatedClip"/> object. /// If framesToRead is smaller than 1 or bigger than the total number of frames in the GIF, the whole file will be read. /// Otherwise, only the number of frames determined by framesToRead will be decoded. /// </summary> /// <param name="filepath"></param> /// <param name="framesToRead"></param> /// <param name="threadPriority"></param> /// <param name="completeCallback"></param> public static void DecodeGif(string filepath, int framesToRead, System.Threading.ThreadPriority threadPriority, Action <AnimatedClip> completeCallback) { #if UNITY_EDITOR EM_3DI70R_GIF.DecodeRequest request = new EM_3DI70R_GIF.DecodeRequest(filepath) { frameToRead = framesToRead, threadPriority = threadPriority }; request.Completed += () => { if (completeCallback != null) { completeCallback(request.AnimatedClip); } }; request.Request(); // Debug.LogWarning("DecodeGif is not supported in Unity editor. Please test on an iOS or Android device."); #elif UNITY_IOS // Read the GIF partially. iOSNativeGif.DecodeGif(curDecodeId++, filepath, framesToRead, threadPriority, (int taskId, GifMetadata gifMetadata, GifFrameMetadata[] gifFrameMetadata, Color32[][] imageData) => { if (completeCallback != null) { completeCallback(ToAnimatedClip(gifMetadata, gifFrameMetadata, imageData)); } }); #elif UNITY_ANDROID // Read the GIF partially. AndroidNativeGif.DecodeGif(curDecodeId++, filepath, framesToRead, threadPriority, (int taskId, GifMetadata gifMetadata, GifFrameMetadata[] gifFrameMetadata, Color32[][] imageData) => { if (completeCallback != null) { completeCallback(ToAnimatedClip(gifMetadata, gifFrameMetadata, imageData)); } }); #endif }
// GIF exporting coroutine: preprocess the image data then send it to native code (mobile) or a worker thread (other platforms) to export GIF file. static IEnumerator CRExportGif(AnimatedClip clip, string filename, int loop, int quality, System.Threading.ThreadPriority threadPriority, Action <AnimatedClip, float> exportProgressCallback, Action <AnimatedClip, string> exportCompletedCallback) { // The encoder don't want loop to be < -1 if (loop < -1) { loop = -1; } // Compute the NeuQuant sample factor from the inverse of the quality value. // Note that NeuQuant prefers values in range [1,30] so we'll also scale the factor to that range. int sampleFac = Mathf.RoundToInt(Mathf.Lerp(30, 1, (float)(Mathf.Clamp(quality, 1, 100)) / 100)); // Construct filepath string folder; #if UNITY_EDITOR folder = Application.dataPath; // Assets folder #else folder = Application.persistentDataPath; #endif string filepath = System.IO.Path.Combine(folder, filename + ".gif"); // Construct a new export task var exportTask = new GifExportTask(); exportTask.taskId = curExportId++; // assign this task a unique id exportTask.clip = clip; exportTask.imageData = null; exportTask.filepath = filepath; exportTask.loop = loop; exportTask.sampleFac = sampleFac; exportTask.exportProgressCallback = exportProgressCallback; exportTask.exportCompletedCallback = exportCompletedCallback; exportTask.workerPriority = threadPriority; exportTask.isExporting = true; exportTask.isDone = false; exportTask.progress = 0; // Add task to the list with its unique id key gifExportTasks.Add(exportTask.taskId, exportTask); yield return(null); // Create a temporary texture to read RenderTexture data Texture2D temp = new Texture2D(clip.Width, clip.Height, TextureFormat.RGB24, false); temp.hideFlags = HideFlags.HideAndDontSave; temp.wrapMode = TextureWrapMode.Clamp; temp.filterMode = FilterMode.Bilinear; temp.anisoLevel = 0; // On iOS and Android, the GIF encoding is done in native code. // In Unity editor (and other platforms), we use Moments encoder for testing purpose. #if UNITY_EDITOR || (!UNITY_IOS && !UNITY_ANDROID) // Converts to GIF frames List <GifFrame> frames = new List <GifFrame>(clip.Frames.Length); for (int i = 0; i < clip.Frames.Length; i++) { RenderTexture source = clip.Frames[i]; RenderTexture.active = source; temp.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0); temp.Apply(); RenderTexture.active = null; GifFrame frame = new GifFrame() { Width = temp.width, Height = temp.height, Data = temp.GetPixels32() }; frames.Add(frame); OnGifPreProcessing(exportTask.taskId, (float)i / clip.Frames.Length); yield return(null); } // Setup a worker thread and let it do its magic GifEncoder encoder = new GifEncoder(loop, sampleFac); encoder.SetDelay(Mathf.RoundToInt(1000f / clip.FramePerSecond)); Worker worker = new Worker( exportTask.taskId, threadPriority, frames, encoder, filepath, OnGifExportProgress, OnGifExportCompleted); worker.Start(); #else // Allocate an array to hold the serialized image data exportTask.imageData = new Color32[clip.Frames.Length][]; // Construct the serialized image data, note that texture data is layered down-top, so flip it for (int i = 0; i < clip.Frames.Length; i++) { var source = clip.Frames[i]; RenderTexture.active = source; temp.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0); temp.Apply(); RenderTexture.active = null; // Get the frame's pixel data exportTask.imageData[i] = temp.GetPixels32(); // Call the preprocessing handler directly float progress = (float)i / clip.Frames.Length; OnGifPreProcessing(exportTask.taskId, progress); yield return(null); } #if UNITY_IOS iOSNativeGif.ExportGif(exportTask); #elif UNITY_ANDROID AndroidNativeGif.ExportGif(exportTask); #endif #endif // UNITY_EDITOR || (!UNITY_IOS && !UNITY_ANDROID) // Dispose the temporary texture Destroy(temp); }