internal static void ExportGif(GifExportTask exportTask) { var instance = new AndroidNativeGif(exportTask); var worker = new Thread(instance.DoExportGif); worker.Priority = exportTask.workerPriority; worker.Start(); }
internal static void ExportGif(GifExportTask task) { var taskId = task.taskId; var filepath = task.filepath; var width = task.clip.Width; var height = task.clip.Height; var loop = task.loop; var fps = task.clip.FramePerSecond; var sampleFac = task.sampleFac; var frameCount = task.clip.Frames.Length; var imageData = task.imageData; var threadPriority = EncodeThreadPriority(task.workerPriority); var gcHandleArray = new GCHandle[imageData.Length]; var ptrArray = new IntPtr[imageData.Length]; for (int i = 0; i < imageData.Length; i++) { gcHandleArray[i] = GCHandle.Alloc(imageData[i], GCHandleType.Pinned); ptrArray[i] = gcHandleArray[i].AddrOfPinnedObject(); } if (gcHandles == null) { gcHandles = new Dictionary <int, GCHandle[]>(); } gcHandles.Add(taskId, gcHandleArray); _ExportGif(taskId, filepath, width, height, loop, fps, sampleFac, frameCount, ptrArray, threadPriority, GifExportProgressCallback, GifExportCompletedCallback); }
// 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); }
internal AndroidNativeGif(GifExportTask exportTask) { myExportTask = exportTask; }