public static void OutputAPNG(IEnumerable <Frame> frames, String fn) { Apng apng = new Apng(); foreach (Frame f in frames) { apng.AddFrame(f.Image, f.Delay, 1000); } apng.WriteApng(fn, false, true); }
private async void Encode(List <FrameInfo> listFrames, int id, Parameters param, CancellationTokenSource tokenSource) { var processing = this.DispatcherStringResource("Encoder.Processing"); try { switch (param.Type) { case Export.Gif: #region Gif #region Cut/Paint Unchanged Pixels if (param.EncoderType == GifEncoderType.Legacy || param.EncoderType == GifEncoderType.ScreenToGif) { if (param.DetectUnchangedPixels) { Update(id, 0, FindResource("Encoder.Analyzing").ToString()); if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); listFrames = ImageMethods.PaintTransparentAndCut(listFrames, color, id, tokenSource); } else { listFrames = ImageMethods.CutUnchanged(listFrames, id, tokenSource); } } else { var size = listFrames[0].Path.ScaledSize(); listFrames.ForEach(x => x.Rect = new Int32Rect(0, 0, (int)size.Width, (int)size.Height)); } } #endregion switch (param.EncoderType) { case GifEncoderType.ScreenToGif: #region Improved encoding using (var stream = new MemoryStream()) { using (var encoder = new GifFile(stream, param.RepeatCount)) { encoder.UseGlobalColorTable = param.UseGlobalColorTable; encoder.TransparentColor = param.DummyColor; encoder.MaximumNumberColor = param.MaximumNumberColors; for (var i = 0; i < listFrames.Count; i++) { if (!listFrames[i].HasArea && param.DetectUnchangedPixels) { continue; } if (listFrames[i].Delay == 0) { listFrames[i].Delay = 10; } encoder.AddFrame(listFrames[i].Path, listFrames[i].Rect, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Improved Encoding"); } } #endregion break; case GifEncoderType.Legacy: #region Legacy Encoding using (var encoder = new AnimatedGifEncoder()) { if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); encoder.SetTransparent(color); encoder.SetDispose(1); //Undraw Method, "Leave". } encoder.Start(param.Filename); encoder.SetQuality(param.Quality); encoder.SetRepeat(param.RepeatCount); var numImage = 0; foreach (var frame in listFrames) { #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion if (!frame.HasArea && param.DetectUnchangedPixels) { continue; } var bitmapAux = new Bitmap(frame.Path); encoder.SetDelay(frame.Delay); encoder.AddFrame(bitmapAux, frame.Rect.X, frame.Rect.Y); bitmapAux.Dispose(); Update(id, numImage, string.Format(processing, numImage)); numImage++; } } #endregion break; case GifEncoderType.PaintNet: #region paint.NET encoding using (var stream = new MemoryStream()) { using (var encoder = new GifEncoder(stream, null, null, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { var bitmapAux = new Bitmap(listFrames[i].Path); encoder.AddFrame(bitmapAux, 0, 0, TimeSpan.FromMilliseconds(listFrames[i].Delay)); bitmapAux.Dispose(); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } stream.Position = 0; try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, false)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Encoding with paint.Net."); } } #endregion break; case GifEncoderType.FFmpeg: #region FFmpeg encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.Path + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].Path) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding the gif with FFmpeg.") { HelpLink = $"Command:\n\r{param.Command}\n\rResult:\n\r{str}" } } ; #endregion break; case GifEncoderType.Gifski: #region Gifski encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsGifskiPresent()) { throw new ApplicationException("Gifski not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } var gifski = new GifskiInterop(); var handle = gifski.Start(UserSettings.All.GifskiQuality, UserSettings.All.Looped); ThreadPool.QueueUserWorkItem(delegate { Thread.Sleep(500); SetStatus(Status.Processing, id, null, false); for (var i = 0; i < listFrames.Count; i++) { Update(id, i, string.Format(processing, i)); gifski.AddFrame(handle, (uint)i, listFrames[i].Path, listFrames[i].Delay); } gifski.EndAdding(handle); }, null); gifski.End(handle, param.Filename); var fileInfo2 = new FileInfo(param.Filename); if (!fileInfo2.Exists || fileInfo2.Length == 0) { throw new Exception("Error while encoding the gif with Gifski.", new Win32Exception()) { HelpLink = $"Command:\n\r{param.Command}\n\rResult:\n\r{Marshal.GetLastWin32Error()}" } } ; #endregion break; default: throw new Exception("Undefined Gif encoder type"); } #endregion break; case Export.Apng: #region Apng #region Cut/Paint Unchanged Pixels if (param.DetectUnchangedPixels) { Update(id, 0, FindResource("Encoder.Analyzing").ToString()); if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.A, param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); listFrames = ImageMethods.PaintTransparentAndCut(listFrames, color, id, tokenSource); } else { listFrames = ImageMethods.CutUnchanged(listFrames, id, tokenSource); } } else { var size = listFrames[0].Path.ScaledSize(); listFrames.ForEach(x => x.Rect = new Int32Rect(0, 0, (int)size.Width, (int)size.Height)); } #endregion #region Encoding using (var stream = new MemoryStream()) { var frameCount = listFrames.Count(x => x.HasArea); using (var encoder = new Apng(stream, frameCount, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { if (!listFrames[i].HasArea && param.DetectUnchangedPixels) { continue; } if (listFrames[i].Delay == 0) { listFrames[i].Delay = 10; } encoder.AddFrame(listFrames[i].Path, listFrames[i].Rect, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Apng Encoding"); } } #endregion #endregion break; case Export.Video: #region Video switch (param.VideoEncoder) { case VideoEncoderType.AviStandalone: #region Avi Standalone var image = listFrames[0].Path.SourceFrom(); if (File.Exists(param.Filename)) { File.Delete(param.Filename); } //1000 / listFrames[0].Delay using (var aviWriter = new AviWriter(param.Filename, param.Framerate, image.PixelWidth, image.PixelHeight, param.VideoQuality)) { var numImage = 0; foreach (var frame in listFrames) { using (var outStream = new MemoryStream()) { var bitImage = frame.Path.SourceFrom(); var enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitImage)); enc.Save(outStream); outStream.Flush(); using (var bitmap = new Bitmap(outStream)) aviWriter.AddFrame(bitmap, param.FlipVideo); } Update(id, numImage, string.Format(processing, numImage)); numImage++; #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } #endregion break; case VideoEncoderType.Ffmpg: #region Video using FFmpeg SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.Path + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].Path) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding with FFmpeg.") { HelpLink = str } } ; #endregion break; default: throw new Exception("Undefined video encoder"); } #endregion break; default: throw new ArgumentOutOfRangeException(nameof(param)); } //If it was canceled, try deleting the file. if (tokenSource.Token.IsCancellationRequested) { if (File.Exists(param.Filename)) { File.Delete(param.Filename); } SetStatus(Status.Canceled, id); return; } #region Upload if (param.Upload && File.Exists(param.Filename)) { InternalUpdate(id, "Encoder.Uploading", true, true); try { ICloud cloud = CloudFactory.CreateCloud(param.UploadDestinationIndex); var uploadedFile = await cloud.UploadFileAsync(param.Filename, CancellationToken.None); InternalSetUpload(id, true, uploadedFile.Link, uploadedFile.DeleteLink); } catch (Exception e) { LogWriter.Log(e, "It was not possible to run the post encoding command."); InternalSetUpload(id, false, null, null, e); } } #endregion #region Copy to clipboard if (param.CopyToClipboard && File.Exists(param.Filename)) { Dispatcher.Invoke(() => { try { var data = new DataObject(); switch (param.CopyType) { case CopyType.File: if (param.Type != Export.Video) { data.SetImage(param.Filename.SourceFrom()); } data.SetText(param.Filename, TextDataFormat.Text); data.SetFileDropList(new StringCollection { param.Filename }); break; case CopyType.FolderPath: data.SetText(Path.GetDirectoryName(param.Filename) ?? param.Filename, TextDataFormat.Text); break; case CopyType.Link: var link = InternalGetUpload(id); data.SetText(string.IsNullOrEmpty(link) ? param.Filename : link, TextDataFormat.Text); break; default: data.SetText(param.Filename, TextDataFormat.Text); break; } //It tries to set the data to the clipboard 10 times before failing it to do so. //This issue may happen if the clipboard is opened by any clipboard manager. for (var i = 0; i < 10; i++) { try { Clipboard.SetDataObject(data, true); break; } catch (COMException ex) { if ((uint)ex.ErrorCode != 0x800401D0) //CLIPBRD_E_CANT_OPEN { throw; } } Thread.Sleep(100); } InternalSetCopy(id, true); } catch (Exception e) { LogWriter.Log(e, "It was not possible to copy the file."); InternalSetCopy(id, false, e); } }); } #endregion #region Execute commands if (param.ExecuteCommands && !string.IsNullOrWhiteSpace(param.PostCommands)) { InternalUpdate(id, "Encoder.Executing", true, true); var command = param.PostCommands.Replace("{p}", "\"" + param.Filename + "\"").Replace("{f}", "\"" + Path.GetDirectoryName(param.Filename) + "\""); var output = ""; try { foreach (var com in command.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { var procStartInfo = new ProcessStartInfo("cmd", "/c " + com) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = new Process()) { process.StartInfo = procStartInfo; process.Start(); var message = process.StandardOutput.ReadToEnd(); var error = process.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(error)) { throw new Exception(error); } process.WaitForExit(1000); } } InternalSetCommand(id, true, command, output); } catch (Exception e) { LogWriter.Log(e, "It was not possible to run the post encoding command."); InternalSetCommand(id, false, command, output, e); } } #endregion if (!tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Completed, id, param.Filename); } } catch (Exception ex) { LogWriter.Log(ex, "Encode"); SetStatus(Status.Error, id, null, false, ex); } finally { #region Delete Encoder Folder try { var encoderFolder = Path.GetDirectoryName(listFrames[0].Path); if (!string.IsNullOrEmpty(encoderFolder)) { if (Directory.Exists(encoderFolder)) { Directory.Delete(encoderFolder, true); } } } catch (Exception ex) { LogWriter.Log(ex, "Cleaning the Encode folder"); } #endregion GC.Collect(); } }
private async void Encode(ProjectInfo project, int id, Parameters param, CancellationTokenSource tokenSource) { var processing = this.DispatcherStringResource("Encoder.Processing"); var listFrames = project.Frames; try { switch (param.Type) { case Export.Gif: #region Gif switch (param.EncoderType) { case GifEncoderType.ScreenToGif: #region Improved encoding using (var stream = new MemoryStream()) { using (var encoder = new GifFile(stream, param.RepeatCount)) { encoder.UseGlobalColorTable = param.UseGlobalColorTable; encoder.TransparentColor = param.DummyColor; encoder.MaximumNumberColor = param.MaximumNumberColors; for (var i = 0; i < listFrames.Count; i++) { encoder.AddFrame(listFrames[i].FullPath, project.FrameSize, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Improved Encoding"); } } #endregion break; case GifEncoderType.Legacy: #region Legacy Encoding using (var encoder = new AnimatedGifEncoder()) { if (param.DummyColor.HasValue) { encoder.SetTransparent(param.DummyColor.Value); encoder.SetDispose(1); //Undraw Method, "Leave". } encoder.Start(param.Filename); encoder.SetQuality(param.Quality); encoder.SetRepeat(param.RepeatCount); var numImage = 0; foreach (var frame in listFrames) { #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion var bitmapAux = new Bitmap(frame.FullPath); encoder.SetDelay(frame.Delay); encoder.AddFrame(bitmapAux, project.FrameSize.X, project.FrameSize.Y); bitmapAux.Dispose(); Update(id, numImage, string.Format(processing, numImage)); numImage++; } } #endregion break; case GifEncoderType.PaintNet: #region paint.NET encoding using (var stream = new MemoryStream()) { using (var encoder = new GifEncoder(stream, null, null, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { var bitmapAux = new Bitmap(listFrames[i].FullPath); encoder.AddFrame(bitmapAux, 0, 0, TimeSpan.FromMilliseconds(listFrames[i].Delay)); bitmapAux.Dispose(); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } stream.Position = 0; try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, false)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Encoding with paint.Net."); } } #endregion break; case GifEncoderType.FFmpeg: #region FFmpeg encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.FullPath + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].FullPath) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding the gif with FFmpeg.") { HelpLink = $"Command:\n\r{param.Command}\n\rResult:\n\r{str}" } } ; #endregion break; case GifEncoderType.Gifski: #region Gifski encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsGifskiPresent()) { throw new ApplicationException("Gifski not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } var gifski = new GifskiInterop(); var handle = gifski.Start(UserSettings.All.GifskiQuality, UserSettings.All.Looped); ThreadPool.QueueUserWorkItem(delegate { Thread.Sleep(500); SetStatus(Status.Processing, id, null, false); for (var i = 0; i < listFrames.Count; i++) { Update(id, i, string.Format(processing, i)); gifski.AddFrame(handle, (uint)i, listFrames[i].FullPath, listFrames[i].Delay); } gifski.EndAdding(handle); }, null); gifski.End(handle, param.Filename); var fileInfo2 = new FileInfo(param.Filename); if (!fileInfo2.Exists || fileInfo2.Length == 0) { throw new Exception("Error while encoding the gif with Gifski.", new Win32Exception()) { HelpLink = $"Result:\n\r{Marshal.GetLastWin32Error()}" } } ; #endregion break; default: throw new Exception("Undefined Gif encoder type"); } #endregion break; case Export.Apng: #region Apng switch (param.ApngEncoder) { case ApngEncoderType.ScreenToGif: { #region Encoding using (var stream = new MemoryStream()) { var frameCount = listFrames.Count; using (var encoder = new Apng(stream, frameCount, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { encoder.AddFrame(listFrames[i].FullPath, project.FrameSize, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Apng Encoding"); } } #endregion break; } case ApngEncoderType.FFmpeg: { #region FFmpeg encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.FullPath + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].FullPath) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.RepeatCount, param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding the apng with FFmpeg.") { HelpLink = $"Command:\n\r{param.Command}\n\rResult:\n\r{str}" } } ; #endregion break; } } #endregion break; case Export.Video: #region Video switch (param.VideoEncoder) { case VideoEncoderType.AviStandalone: #region Avi Standalone var image = listFrames[0].FullPath.SourceFrom(); if (File.Exists(param.Filename)) { File.Delete(param.Filename); } //1000 / listFrames[0].Delay using (var aviWriter = new AviWriter(param.Filename, param.Framerate, image.PixelWidth, image.PixelHeight, param.VideoQuality)) { var numImage = 0; foreach (var frame in listFrames) { using (var outStream = new MemoryStream()) { var bitImage = frame.FullPath.SourceFrom(); var enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitImage)); enc.Save(outStream); outStream.Flush(); using (var bitmap = new Bitmap(outStream)) aviWriter.AddFrame(bitmap, param.FlipVideo); } Update(id, numImage, string.Format(processing, numImage)); numImage++; #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } #endregion break; case VideoEncoderType.Ffmpg: #region Video using FFmpeg SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.FullPath + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].FullPath) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding with FFmpeg.") { HelpLink = str } } ; #endregion break; default: throw new Exception("Undefined video encoder"); } #endregion break; default: throw new ArgumentOutOfRangeException(nameof(param)); } //If it was canceled, try deleting the file. if (tokenSource.Token.IsCancellationRequested) { if (File.Exists(param.Filename)) { File.Delete(param.Filename); } SetStatus(Status.Canceled, id); return; } #region Execute commands #if !UWP if (param.ExecuteCommands && !string.IsNullOrWhiteSpace(param.PostCommands)) { InternalUpdate(id, "Encoder.Executing", true, true); var command = param.PostCommands.Replace("{p}", "\"" + param.Filename + "\"").Replace("{f}", "\"" + Path.GetDirectoryName(param.Filename) + "\""); var output = ""; try { foreach (var com in command.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { var procStartInfo = new ProcessStartInfo("cmd", "/c " + com) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = new Process()) { process.StartInfo = procStartInfo; process.Start(); var message = process.StandardOutput.ReadToEnd(); var error = process.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(error)) { throw new Exception(error); } process.WaitForExit(1000); } } InternalSetCommand(id, true, command, output); } catch (Exception e) { LogWriter.Log(e, "It was not possible to run the post encoding command."); InternalSetCommand(id, false, command, output, e); } } #endif #endregion if (!tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Completed, id, param.Filename); } } catch (Exception ex) { LogWriter.Log(ex, "Encode"); SetStatus(Status.Error, id, null, false, ex); } finally { #region Delete Encoder Folder try { var encoderFolder = Path.GetDirectoryName(listFrames[0].FullPath); if (!string.IsNullOrEmpty(encoderFolder)) { if (Directory.Exists(encoderFolder)) { Directory.Delete(encoderFolder, true); } } } catch (Exception ex) { LogWriter.Log(ex, "Cleaning the Encode folder"); } #endregion GC.Collect(); } }
public static void ExtractAnimation(WzSubProperty parent, string savePath, bool apng, bool apngFirstFrame) { List <Bitmap> bmpList = new List <Bitmap>(parent.WzProperties.Count); List <int> delayList = new List <int>(parent.WzProperties.Count); Point biggestPng = new Point(0, 0); Point SmallestEmptySpace = new Point(65535, 65535); Point MaximumPngMappingEndingPts = new Point(0, 0); foreach (IWzImageProperty subprop in parent.WzProperties) { if (subprop is WzCanvasProperty) { //WzVectorProperty origin = (WzVectorProperty)subprop["origin"]; WzPngProperty png = ((WzCanvasProperty)subprop).PngProperty; if (png.Height > biggestPng.Y) { biggestPng.Y = png.Height; } if (png.Width > biggestPng.X) { biggestPng.X = png.Width; } } } List <WzCanvasProperty> sortedProps = new List <WzCanvasProperty>(); foreach (IWzImageProperty subprop in parent.WzProperties) { if (subprop is WzCanvasProperty) { sortedProps.Add((WzCanvasProperty)subprop); WzPngProperty png = ((WzCanvasProperty)subprop).PngProperty; WzVectorProperty origin = (WzVectorProperty)subprop["origin"]; Point StartPoints = new Point(biggestPng.X - origin.X.Value, biggestPng.Y - origin.Y.Value); Point PngMapppingEndingPts = new Point(StartPoints.X + png.Width, StartPoints.Y + png.Height); if (StartPoints.X < SmallestEmptySpace.X) { SmallestEmptySpace.X = StartPoints.X; } if (StartPoints.Y < SmallestEmptySpace.Y) { SmallestEmptySpace.Y = StartPoints.Y; } if (PngMapppingEndingPts.X > MaximumPngMappingEndingPts.X) { MaximumPngMappingEndingPts.X = PngMapppingEndingPts.X; } if (PngMapppingEndingPts.Y > MaximumPngMappingEndingPts.Y) { MaximumPngMappingEndingPts.Y = PngMapppingEndingPts.Y; } } } sortedProps.Sort(new Comparison <WzCanvasProperty>(PropertySorter)); /* foreach (IWzImageProperty subprop in parent.WzProperties) * { * if (subprop is WzCanvasProperty) * { * WzCompressedIntProperty delayProp = (WzCompressedIntProperty)subprop["delay"]; * if (delayProp != null) delay = delayProp.Value; * } * }*/ /* Brush bgcolor = null; * switch (toolStripComboBox2.SelectedIndex) * { * case 0: * bgcolor = Brushes.Widthhite; * break; * case 1: * bgcolor = Brushes.Black; * break; * default: * bgcolor = Brushes.Black; * break; * }*/ for (int i = 0; i < sortedProps.Count; i++) { WzCanvasProperty subprop = sortedProps[i]; if (i.ToString() != subprop.Name) { Warning.Error("Something f****d up at animation builder, frame " + i.ToString()); return; } Bitmap bmp = subprop.PngProperty.GetPNG(false); WzVectorProperty origin = (WzVectorProperty)subprop["origin"]; // if (apng) bmpList.Add(OptimizeBitmapTransparent(bmp, origin, biggestPng, SmallestEmptySpace, MaximumPngMappingEndingPts)); /* else * bmpList.Add(OptimizeBitmap(bmp, origin, biggestPng, SmallestEmptySpace, MaximumPngMappingEndingPts, bgcolor));*/ WzCompressedIntProperty delayProp = (WzCompressedIntProperty)subprop["delay"]; int delay = 100; if (delayProp != null) { delay = delayProp.Value; } delayList.Add(delay); //} } if (apng) { //List<Frame> frameList = new List<Frame>(); /* List<int> delayList = new List<int>(); * foreach (TreeNode subnode in parent.Nodes) * { * if (subnode.Tag2 is PNG) * { * TreeNode delayNode = FindNodeInSubnodes(subnode, "delay"); * if (delayNode == null) delayList.Add(0); * else delayList.Add((int)delayNode.Tag2); * } * } * if (delayList.Count != bmp.Count) * { * MessageBox.Show("Weird error, seems like there are more PNGs than delay values"); * return; * }*/ Apng apngBuilder = new Apng(); if (apngFirstFrame) { apngBuilder.AddFrame(new Frame(CreateIncompatibilityFrame(new Size(bmpList[0].Width, bmpList[0].Height)), 1, 1)); } for (int i = 0; i < bmpList.Count; i++) { apngBuilder.AddFrame(new Frame(bmpList[i], getNumByDelay(delayList[i]), getDenByDelay(delayList[i]))); } apngBuilder.WriteApng(savePath, apngFirstFrame, true); //createapng(frameList, savePath); } else { AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder(); for (int i = 0; i < bmpList.Count; i++) { gifEncoder.AddFrame(new GifFrame(bmpList[i]) { Delay = delayList[i] / 10 }); } gifEncoder.WriteToFile(savePath); } }
private void Encode(List <FrameInfo> listFrames, int id, Parameters param, CancellationTokenSource tokenSource) { var processing = FindResource("Encoder.Processing").ToString(); try { switch (param.Type) { case Export.Gif: #region Gif #region Cut/Paint Unchanged Pixels if (param.EncoderType == GifEncoderType.Legacy || param.EncoderType == GifEncoderType.ScreenToGif) { if (param.DetectUnchangedPixels) { Update(id, 0, FindResource("Encoder.Analyzing").ToString()); if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); listFrames = ImageMethods.PaintTransparentAndCut(listFrames, color, id, tokenSource); } else { listFrames = ImageMethods.CutUnchanged(listFrames, id, tokenSource); } } else { var size = listFrames[0].Path.ScaledSize(); listFrames.ForEach(x => x.Rect = new Int32Rect(0, 0, (int)size.Width, (int)size.Height)); } } #endregion switch (param.EncoderType) { case GifEncoderType.ScreenToGif: #region Improved encoding using (var stream = new MemoryStream()) { using (var encoder = new GifFile(stream, param.RepeatCount)) { encoder.UseGlobalColorTable = param.UseGlobalColorTable; encoder.TransparentColor = param.DummyColor; encoder.MaximumNumberColor = param.MaximumNumberColors; for (var i = 0; i < listFrames.Count; i++) { if (!listFrames[i].HasArea && param.DetectUnchangedPixels) { continue; } if (listFrames[i].Delay == 0) { listFrames[i].Delay = 10; } encoder.AddFrame(listFrames[i].Path, listFrames[i].Rect, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Improved Encoding"); } } #endregion break; case GifEncoderType.Legacy: #region Legacy Encoding using (var encoder = new AnimatedGifEncoder()) { if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); encoder.SetTransparent(color); encoder.SetDispose(1); //Undraw Method, "Leave". } encoder.Start(param.Filename); encoder.SetQuality(param.Quality); encoder.SetRepeat(param.RepeatCount); var numImage = 0; foreach (var frame in listFrames) { #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion if (!frame.HasArea && param.DetectUnchangedPixels) { continue; } var bitmapAux = new Bitmap(frame.Path); encoder.SetDelay(frame.Delay); encoder.AddFrame(bitmapAux, frame.Rect.X, frame.Rect.Y); bitmapAux.Dispose(); Update(id, numImage, string.Format(processing, numImage)); numImage++; } } #endregion break; case GifEncoderType.PaintNet: #region paint.NET encoding using (var stream = new MemoryStream()) { using (var encoder = new GifEncoder(stream, null, null, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { var bitmapAux = new Bitmap(listFrames[i].Path); encoder.AddFrame(bitmapAux, 0, 0, TimeSpan.FromMilliseconds(listFrames[i].Delay)); bitmapAux.Dispose(); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } stream.Position = 0; try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, false)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Encoding with paint.Net."); } } #endregion break; default: throw new Exception("Undefined Gif encoder type"); } #endregion break; case Export.Apng: #region Apng #region Cut/Paint Unchanged Pixels if (param.DetectUnchangedPixels) { Update(id, 0, FindResource("Encoder.Analyzing").ToString()); if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.A, param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); listFrames = ImageMethods.PaintTransparentAndCut(listFrames, color, id, tokenSource); } else { listFrames = ImageMethods.CutUnchanged(listFrames, id, tokenSource); } } else { var size = listFrames[0].Path.ScaledSize(); listFrames.ForEach(x => x.Rect = new Int32Rect(0, 0, (int)size.Width, (int)size.Height)); } #endregion #region Encoding using (var stream = new MemoryStream()) { var frameCount = listFrames.Count(x => x.HasArea); using (var encoder = new Apng(stream, frameCount, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { if (!listFrames[i].HasArea && param.DetectUnchangedPixels) { continue; } if (listFrames[i].Delay == 0) { listFrames[i].Delay = 10; } encoder.AddFrame(listFrames[i].Path, listFrames[i].Rect, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Apng Encoding"); } } #endregion #endregion break; case Export.Video: #region Video switch (param.VideoEncoder) { case VideoEncoderType.AviStandalone: #region Avi Standalone var image = listFrames[0].Path.SourceFrom(); if (File.Exists(param.Filename)) { File.Delete(param.Filename); } //1000 / listFrames[0].Delay using (var aviWriter = new AviWriter(param.Filename, param.Framerate, image.PixelWidth, image.PixelHeight, param.VideoQuality)) { var numImage = 0; foreach (var frame in listFrames) { using (var outStream = new MemoryStream()) { var bitImage = frame.Path.SourceFrom(); var enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitImage)); enc.Save(outStream); outStream.Flush(); using (var bitmap = new Bitmap(outStream)) aviWriter.AddFrame(bitmap, param.FlipVideo); } Update(id, numImage, string.Format(processing, numImage)); numImage++; #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } #endregion break; case VideoEncoderType.Ffmpg: #region Video using FFmpeg SetStatus(Status.Encoding, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.Path + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].Path) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding with FFmpeg.") { HelpLink = str } } ; #endregion break; default: throw new Exception("Undefined video encoder"); } #endregion break; default: throw new ArgumentOutOfRangeException(nameof(param)); } if (!tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Completed, id, param.Filename); } #region Upload if (param.Upload && File.Exists(param.Filename)) { /* * using (var w = new WebClient()) * { * var clientID = "15 digit key"; * w.Headers.Add("Authorization", "Client-ID " + clientID); * var values = new NameValueCollection { { "image", Convert.ToBase64String(File.ReadAllBytes(@""+filename)) }}; * var response = w.UploadValues("https://api.imgur.com/3/upload.xml", values); * var x = XDocument.Load(new MemoryStream(response)); * var link = x.Descendants().Where(n => n.Name == "link").FirstOrDefault(); * string href = link.Value; * } */ } #endregion #region Copy to clipboard if (param.CopyToClipboard && File.Exists(param.Filename)) { Dispatcher.Invoke(() => { try { var data = new DataObject(); switch (param.CopyType) { case CopyType.File: if (param.Type != Export.Video) { data.SetImage(param.Filename.SourceFrom()); } data.SetText(param.Filename, TextDataFormat.Text); data.SetFileDropList(new StringCollection { param.Filename }); break; case CopyType.FolderPath: data.SetText(Path.GetDirectoryName(param.Filename) ?? param.Filename, TextDataFormat.Text); break; case CopyType.Link: data.SetText(param.Filename, TextDataFormat.Text); //TODO: Link. break; default: data.SetText(param.Filename, TextDataFormat.Text); break; } Clipboard.SetDataObject(data, true); InternalSetCopy(id, true); } catch (Exception e) { LogWriter.Log(e, "It was not possible to copy the file."); InternalSetCopy(id, false, e); } }); } #endregion #region Execute commands if (param.ExecuteCommands && !string.IsNullOrWhiteSpace(param.PostCommands)) { var command = param.PostCommands.Replace("{p}", "\"" + param.Filename + "\"").Replace("{f}", "\"" + Path.GetDirectoryName(param.Filename) + "\""); var output = ""; try { foreach (var com in command.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { var procStartInfo = new ProcessStartInfo("cmd", "/c " + com) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = new Process()) { process.StartInfo = procStartInfo; process.Start(); var message = process.StandardOutput.ReadToEnd(); var error = process.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(error)) { throw new Exception(error); } process.WaitForExit(1000); } } InternalSetCommand(id, true, output); } catch (Exception e) { LogWriter.Log(e, "It was not possible to run the post encoding command."); InternalSetCommand(id, false, output, e); } } #endregion } catch (Exception ex) { LogWriter.Log(ex, "Encode"); SetStatus(Status.Error, id, null, false, ex); } finally { #region Delete Encoder Folder try { var encoderFolder = Path.GetDirectoryName(listFrames[0].Path); if (!string.IsNullOrEmpty(encoderFolder)) { if (Directory.Exists(encoderFolder)) { Directory.Delete(encoderFolder, true); } } } catch (Exception ex) { LogWriter.Log(ex, "Cleaning the Encode folder"); } #endregion GC.Collect(); } }
public static void ExtractAnimation(WzSubProperty parent, string savePath, bool apngFirstFrame) { List <Bitmap> bmpList = new List <Bitmap>(parent.WzProperties.Count); List <int> delayList = new List <int>(parent.WzProperties.Count); Point biggestPng = new Point(0, 0); Point SmallestEmptySpace = new Point(65535, 65535); Point MaximumPngMappingEndingPts = new Point(0, 0); foreach (WzImageProperty subprop in parent.WzProperties) { if (subprop is WzCanvasProperty) { //System.Drawing.PointF origin = ((WzCanvasProperty)subprop).GetCanvasOriginPosition(); WzPngProperty png = ((WzCanvasProperty)subprop).PngProperty; if (png.Height > biggestPng.Y) { biggestPng.Y = png.Height; } if (png.Width > biggestPng.X) { biggestPng.X = png.Width; } } } List <WzCanvasProperty> sortedProps = new List <WzCanvasProperty>(); foreach (WzImageProperty subprop in parent.WzProperties) { if (subprop is WzCanvasProperty) { sortedProps.Add((WzCanvasProperty)subprop); WzPngProperty png = ((WzCanvasProperty)subprop).PngProperty; System.Drawing.PointF origin = ((WzCanvasProperty)subprop).GetCanvasOriginPosition(); Point StartPoints = new Point(biggestPng.X - (int)origin.X, biggestPng.Y - (int)origin.Y); Point PngMapppingEndingPts = new Point(StartPoints.X + png.Width, StartPoints.Y + png.Height); if (StartPoints.X < SmallestEmptySpace.X) { SmallestEmptySpace.X = StartPoints.X; } if (StartPoints.Y < SmallestEmptySpace.Y) { SmallestEmptySpace.Y = StartPoints.Y; } if (PngMapppingEndingPts.X > MaximumPngMappingEndingPts.X) { MaximumPngMappingEndingPts.X = PngMapppingEndingPts.X; } if (PngMapppingEndingPts.Y > MaximumPngMappingEndingPts.Y) { MaximumPngMappingEndingPts.Y = PngMapppingEndingPts.Y; } } } sortedProps.Sort(new Comparison <WzCanvasProperty>(PropertySorter)); for (int i = 0; i < sortedProps.Count; i++) { WzCanvasProperty subprop = sortedProps[i]; if (i.ToString() != subprop.Name) { Warning.Error(string.Format(Properties.Resources.AnimError, i.ToString())); return; } Bitmap bmp = subprop.PngProperty.GetImage(false); System.Drawing.PointF origin = subprop.GetCanvasOriginPosition(); bmpList.Add(OptimizeBitmapTransparent(bmp, new WzVectorProperty("", origin.X, origin.Y), biggestPng, SmallestEmptySpace, MaximumPngMappingEndingPts)); WzIntProperty delayProp = (WzIntProperty)subprop["delay"]; int delay = 100; if (delayProp != null) { delay = delayProp.Value; } delayList.Add(delay); } Apng apngBuilder = new Apng(); if (apngFirstFrame) { apngBuilder.AddFrame(new Frame(CreateIncompatibilityFrame(new Size(bmpList[0].Width, bmpList[0].Height)), 1, 1)); } for (int i = 0; i < bmpList.Count; i++) { apngBuilder.AddFrame(new Frame(bmpList[i], getNumByDelay(delayList[i]), getDenByDelay(delayList[i]))); } apngBuilder.WriteApng(savePath, apngFirstFrame, true); }
private void Encode(List <FrameInfo> listFrames, int id, Parameters param, CancellationTokenSource tokenSource) { var processing = this.DispatcherStringResource("Encoder.Processing"); try { switch (param.Type) { case Export.Gif: #region Gif #region Cut/Paint Unchanged Pixels if (param.EncoderType == GifEncoderType.Legacy || param.EncoderType == GifEncoderType.ScreenToGif) { if (param.DetectUnchangedPixels) { Update(id, 0, FindResource("Encoder.Analyzing").ToString()); if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); listFrames = ImageMethods.PaintTransparentAndCut(listFrames, color, id, tokenSource); } else { listFrames = ImageMethods.CutUnchanged(listFrames, id, tokenSource); } } else { var size = listFrames[0].Path.ScaledSize(); listFrames.ForEach(x => x.Rect = new Int32Rect(0, 0, (int)size.Width, (int)size.Height)); } } #endregion switch (param.EncoderType) { case GifEncoderType.ScreenToGif: #region Improved encoding using (var stream = new MemoryStream()) { using (var encoder = new GifFile(stream, param.RepeatCount)) { encoder.UseGlobalColorTable = param.UseGlobalColorTable; encoder.TransparentColor = param.DummyColor; encoder.MaximumNumberColor = param.MaximumNumberColors; for (var i = 0; i < listFrames.Count; i++) { if (!listFrames[i].HasArea && param.DetectUnchangedPixels) { continue; } if (listFrames[i].Delay == 0) { listFrames[i].Delay = 10; } encoder.AddFrame(listFrames[i].Path, listFrames[i].Rect, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Improved Encoding"); } } #endregion break; case GifEncoderType.Legacy: #region Legacy Encoding using (var encoder = new AnimatedGifEncoder()) { if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); encoder.SetTransparent(color); encoder.SetDispose(1); //Undraw Method, "Leave". } encoder.Start(param.Filename); encoder.SetQuality(param.Quality); encoder.SetRepeat(param.RepeatCount); var numImage = 0; foreach (var frame in listFrames) { #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion if (!frame.HasArea && param.DetectUnchangedPixels) { continue; } var bitmapAux = new Bitmap(frame.Path); encoder.SetDelay(frame.Delay); encoder.AddFrame(bitmapAux, frame.Rect.X, frame.Rect.Y); bitmapAux.Dispose(); Update(id, numImage, string.Format(processing, numImage)); numImage++; } } #endregion break; case GifEncoderType.PaintNet: #region paint.NET encoding using (var stream = new MemoryStream()) { using (var encoder = new GifEncoder(stream, null, null, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { var bitmapAux = new Bitmap(listFrames[i].Path); encoder.AddFrame(bitmapAux, 0, 0, TimeSpan.FromMilliseconds(listFrames[i].Delay)); bitmapAux.Dispose(); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } stream.Position = 0; try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.BufferSize, false)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Encoding with paint.Net."); } } #endregion break; case GifEncoderType.FFmpeg: #region FFmpeg encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.Path + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].Path) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding the gif with FFmpeg.") { HelpLink = str } } ; #endregion break; case GifEncoderType.Gifski: #region Gifski encoding SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsGifskiPresent()) { throw new ApplicationException("Gifski not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } var outputPath = Path.GetDirectoryName(listFrames[0].Path); var fps = !param.ExtraParameters.Contains("--fps") ? "--fps " + (int)(1000d / listFrames.Average(x => x.Delay)) : ""; param.Command = $"{param.ExtraParameters} {fps} -o \"{param.Filename}\" \"{Path.Combine(outputPath, "*.png")}\""; var process2 = new ProcessStartInfo(UserSettings.All.GifskiLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro2 = Process.Start(process2); var str2 = pro2.StandardError.ReadToEnd(); var fileInfo2 = new FileInfo(param.Filename); if (!fileInfo2.Exists || fileInfo2.Length == 0) { throw new Exception("Error while encoding the gif with Gifski.") { HelpLink = str2 } } ; #endregion break; default: throw new Exception("Undefined Gif encoder type"); } #endregion break; case Export.Apng: #region Apng #region Cut/Paint Unchanged Pixels if (param.DetectUnchangedPixels) { Update(id, 0, FindResource("Encoder.Analyzing").ToString()); if (param.DummyColor.HasValue) { var color = Color.FromArgb(param.DummyColor.Value.A, param.DummyColor.Value.R, param.DummyColor.Value.G, param.DummyColor.Value.B); listFrames = ImageMethods.PaintTransparentAndCut(listFrames, color, id, tokenSource); } else { listFrames = ImageMethods.CutUnchanged(listFrames, id, tokenSource); } } else { var size = listFrames[0].Path.ScaledSize(); listFrames.ForEach(x => x.Rect = new Int32Rect(0, 0, (int)size.Width, (int)size.Height)); } #endregion #region Encoding using (var stream = new MemoryStream()) { var frameCount = listFrames.Count(x => x.HasArea); using (var encoder = new Apng(stream, frameCount, param.RepeatCount)) { for (var i = 0; i < listFrames.Count; i++) { if (!listFrames[i].HasArea && param.DetectUnchangedPixels) { continue; } if (listFrames[i].Delay == 0) { listFrames[i].Delay = 10; } encoder.AddFrame(listFrames[i].Path, listFrames[i].Rect, listFrames[i].Delay); Update(id, i, string.Format(processing, i)); #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } try { using (var fileStream = new FileStream(param.Filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096)) stream.WriteTo(fileStream); } catch (Exception ex) { SetStatus(Status.Error, id); LogWriter.Log(ex, "Apng Encoding"); } } #endregion #endregion break; case Export.Video: #region Video switch (param.VideoEncoder) { case VideoEncoderType.AviStandalone: #region Avi Standalone var image = listFrames[0].Path.SourceFrom(); if (File.Exists(param.Filename)) { File.Delete(param.Filename); } //1000 / listFrames[0].Delay using (var aviWriter = new AviWriter(param.Filename, param.Framerate, image.PixelWidth, image.PixelHeight, param.VideoQuality)) { var numImage = 0; foreach (var frame in listFrames) { using (var outStream = new MemoryStream()) { var bitImage = frame.Path.SourceFrom(); var enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitImage)); enc.Save(outStream); outStream.Flush(); using (var bitmap = new Bitmap(outStream)) aviWriter.AddFrame(bitmap, param.FlipVideo); } Update(id, numImage, string.Format(processing, numImage)); numImage++; #region Cancellation if (tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Canceled, id); break; } #endregion } } #endregion break; case VideoEncoderType.Ffmpg: #region Video using FFmpeg SetStatus(Status.Processing, id, null, true); if (!Util.Other.IsFfmpegPresent()) { throw new ApplicationException("FFmpeg not present."); } if (File.Exists(param.Filename)) { File.Delete(param.Filename); } #region Generate concat var concat = new StringBuilder(); foreach (var frame in listFrames) { concat.AppendLine("file '" + frame.Path + "'"); concat.AppendLine("duration " + (frame.Delay / 1000d).ToString(CultureInfo.InvariantCulture)); } var concatPath = Path.GetDirectoryName(listFrames[0].Path) ?? Path.GetTempPath(); var concatFile = Path.Combine(concatPath, "concat.txt"); if (!Directory.Exists(concatPath)) { Directory.CreateDirectory(concatPath); } if (File.Exists(concatFile)) { File.Delete(concatFile); } File.WriteAllText(concatFile, concat.ToString()); #endregion param.Command = string.Format(param.Command, concatFile, param.ExtraParameters.Replace("{H}", param.Height.ToString()).Replace("{W}", param.Width.ToString()), param.Filename); var process = new ProcessStartInfo(UserSettings.All.FfmpegLocation) { Arguments = param.Command, CreateNoWindow = true, ErrorDialog = false, UseShellExecute = false, RedirectStandardError = true }; var pro = Process.Start(process); var str = pro.StandardError.ReadToEnd(); var fileInfo = new FileInfo(param.Filename); if (!fileInfo.Exists || fileInfo.Length == 0) { throw new Exception("Error while encoding with FFmpeg.") { HelpLink = str } } ; #endregion break; default: throw new Exception("Undefined video encoder"); } #endregion break; default: throw new ArgumentOutOfRangeException(nameof(param)); } //If it was canceled, try deleting the file. if (tokenSource.Token.IsCancellationRequested) { if (File.Exists(param.Filename)) { File.Delete(param.Filename); } SetStatus(Status.Canceled, id); return; } #region Upload if (param.Upload && File.Exists(param.Filename)) { InternalUpdate(id, "Encoder.Uploading", true, true); try { //TODO: Make it less hardcoded. switch (param.UploadDestinationIndex) { case 0: //Imgur. using (var w = new WebClient()) { w.Headers.Add("Authorization", "Client-ID " + Secret.ImgurId); var values = new NameValueCollection { { "image", Convert.ToBase64String(File.ReadAllBytes(param.Filename)) } }; var response = w.UploadValues("https://api.imgur.com/3/upload.xml", values); var x = XDocument.Load(new MemoryStream(response)); var node = x.Descendants().FirstOrDefault(n => n.Name == "link"); var nodeHash = x.Descendants().FirstOrDefault(n => n.Name == "deletehash"); if (node == null) { throw new Exception("No link was provided by Imgur", new Exception(x.Document?.ToString() ?? "The document was null. :/")); } InternalSetUpload(id, true, node.Value, "https://imgur.com/delete/" + nodeHash?.Value); } break; case 1: //Gfycat. using (var client = new HttpClient()) { using (var res = client.PostAsync(@"https://api.gfycat.com/v1/gfycats", null).Result) { var result = res.Content.ReadAsStringAsync().Result; //{"isOk":true,"gfyname":"ThreeWordCode","secret":"15alphanumerics","uploadType":"filedrop.gfycat.com"} var ser = new JavaScriptSerializer(); if (!(ser.DeserializeObject(result) is Dictionary <string, object> thing)) { throw new Exception("It was not possible to get the gfycat name: " + res); } var name = thing["gfyname"] as string; using (var content = new MultipartFormDataContent()) { content.Add(new StringContent(name), "key"); content.Add(new ByteArrayContent(File.ReadAllBytes(param.Filename)), "file", name); using (var res2 = client.PostAsync("https://filedrop.gfycat.com", content).Result) { if (!res2.IsSuccessStatusCode) { throw new Exception("It was not possible to get the gfycat upload result: " + res2); } //{"task": "complete", "gfyname": "ThreeWordCode"} //{"progress": "0.03", "task": "encoding", "time": 10} //If the task is not yet completed, try waiting. var input2 = ""; while (!input2.Contains("complete")) { using (var res3 = client.GetAsync("https://api.gfycat.com/v1/gfycats/fetch/status/" + name).Result) { input2 = res3.Content.ReadAsStringAsync().Result; if (!res3.IsSuccessStatusCode) { throw new Exception("It was not possible to get the gfycat upload status: " + res3); } } if (!input2.Contains("complete")) { Thread.Sleep(1000); } } if (res2.IsSuccessStatusCode) { InternalSetUpload(id, true, "https://gfycat.com/" + name); } } } } } break; } } catch (Exception e) { LogWriter.Log(e, "It was not possible to run the post encoding command."); InternalSetUpload(id, false, null, null, e); } } #endregion #region Copy to clipboard if (param.CopyToClipboard && File.Exists(param.Filename)) { Dispatcher.Invoke(() => { try { var data = new DataObject(); switch (param.CopyType) { case CopyType.File: if (param.Type != Export.Video) { data.SetImage(param.Filename.SourceFrom()); } data.SetText(param.Filename, TextDataFormat.Text); data.SetFileDropList(new StringCollection { param.Filename }); break; case CopyType.FolderPath: data.SetText(Path.GetDirectoryName(param.Filename) ?? param.Filename, TextDataFormat.Text); break; case CopyType.Link: data.SetText(param.Filename, TextDataFormat.Text); //TODO: Link. break; default: data.SetText(param.Filename, TextDataFormat.Text); break; } Clipboard.SetDataObject(data, true); InternalSetCopy(id, true); } catch (Exception e) { LogWriter.Log(e, "It was not possible to copy the file."); InternalSetCopy(id, false, e); } }); } #endregion #region Execute commands if (param.ExecuteCommands && !string.IsNullOrWhiteSpace(param.PostCommands)) { InternalUpdate(id, "Encoder.Executing", true, true); var command = param.PostCommands.Replace("{p}", "\"" + param.Filename + "\"").Replace("{f}", "\"" + Path.GetDirectoryName(param.Filename) + "\""); var output = ""; try { foreach (var com in command.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)) { var procStartInfo = new ProcessStartInfo("cmd", "/c " + com) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = new Process()) { process.StartInfo = procStartInfo; process.Start(); var message = process.StandardOutput.ReadToEnd(); var error = process.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(message)) { output += message + Environment.NewLine; } if (!string.IsNullOrWhiteSpace(error)) { throw new Exception(error); } process.WaitForExit(1000); } } InternalSetCommand(id, true, command, output); } catch (Exception e) { LogWriter.Log(e, "It was not possible to run the post encoding command."); InternalSetCommand(id, false, command, output, e); } } #endregion if (!tokenSource.Token.IsCancellationRequested) { SetStatus(Status.Completed, id, param.Filename); } } catch (Exception ex) { LogWriter.Log(ex, "Encode"); SetStatus(Status.Error, id, null, false, ex); } finally { #region Delete Encoder Folder try { var encoderFolder = Path.GetDirectoryName(listFrames[0].Path); if (!string.IsNullOrEmpty(encoderFolder)) { if (Directory.Exists(encoderFolder)) { Directory.Delete(encoderFolder, true); } } } catch (Exception ex) { LogWriter.Log(ex, "Cleaning the Encode folder"); } #endregion GC.Collect(); } }