diff --git a/WzComparerR2.Avatar/AvatarCanvas.cs b/WzComparerR2.Avatar/AvatarCanvas.cs index 0c2f3509..4b3d3b90 100644 --- a/WzComparerR2.Avatar/AvatarCanvas.cs +++ b/WzComparerR2.Avatar/AvatarCanvas.cs @@ -949,9 +949,9 @@ private void CreateBone(Bone root, Tuple[] frameNodes, bo { continue; } - if (childNode.Text == "hairShade") + if (linkNode.Text == "hairShade") { - linkNode = childNode.FindNodeByPath("0"); + linkNode = linkNode.FindNodeByPath("0"); if (linkNode == null) { continue; diff --git a/WzComparerR2.Avatar/UI/AvatarForm.cs b/WzComparerR2.Avatar/UI/AvatarForm.cs index 6ab80d51..90514681 100644 --- a/WzComparerR2.Avatar/UI/AvatarForm.cs +++ b/WzComparerR2.Avatar/UI/AvatarForm.cs @@ -1466,18 +1466,22 @@ private void btnSaveAsGif_Click(object sender, EventArgs e) } else { + // get default encoder var config = ImageHandlerConfig.Default; - var encParams = AnimateEncoderFactory.GetEncoderParams(config.GifEncoder.Value); + using var encoder = AnimateEncoderFactory.CreateEncoder(config); + var cap = encoder.Compatibility; + + string extensionFilter = string.Join(";", cap.SupportedExtensions.Select(ext => $"*{ext}")); var dlg = new SaveFileDialog() { Title = "Save Avatar", - Filter = string.Format("{0} (*{1})|*{1}|All Files(*.*)|*.*", encParams.FileDescription, encParams.FileExtension), + Filter = string.Format("{0} (*{1})|*{1}|All Files(*.*)|*.*", encoder.Name, extensionFilter), FileName = string.Format("avatar{0}{1}{2}{3}", string.IsNullOrEmpty(avatar.ActionName) ? "" : ("_" + avatar.ActionName), string.IsNullOrEmpty(avatar.EmotionName) ? "" : ("_" + avatar.EmotionName), string.IsNullOrEmpty(avatar.TamingActionName) ? "" : ("_" + avatar.TamingActionName), - encParams.FileExtension) + cap.DefaultExtension) }; if (dlg.ShowDialog() != DialogResult.OK) @@ -1485,6 +1489,7 @@ private void btnSaveAsGif_Click(object sender, EventArgs e) return; } + string outputFileName = dlg.FileName; var actPlaying = new[] { bodyPlaying, emoPlaying, tamingPlaying }; var actFrames = new[] { cmbBodyFrame, cmbEmotionFrame, cmbTamingFrame } .Select((cmb, i) => @@ -1529,7 +1534,7 @@ private void btnSaveAsGif_Click(object sender, EventArgs e) var gifLayer = new GifLayer(); - if (aniCount == 1) + if (aniCount == 1 && !cap.IsFixedFrameRate) { int aniActIndex = Array.FindIndex(actPlaying, b => b); for (int fIdx = 0, fCnt = actFrames[aniActIndex].Length; fIdx < fCnt; fIdx++) @@ -1558,8 +1563,8 @@ private void btnSaveAsGif_Click(object sender, EventArgs e) { // more than 2 animating action parts, for simplicity, we use fixed frame delay. actFrames = actFrames.Concat(effectActFrames).ToArray(); - var aniLength = actFrames.Max(layer => layer == null ? 0 : layer.Sum(f => f.actionFrame.AbsoluteDelay)); - var aniDelay = 30; + int aniLength = actFrames.Max(layer => layer == null ? 0 : layer.Sum(f => f.actionFrame.AbsoluteDelay)); + int aniDelay = config.MinDelay; // pipeline functions IEnumerable RenderDelay() @@ -1658,7 +1663,7 @@ GifFrame ApplyFrame(int[] actionIndices, int delay) // build pipeline var step1 = RenderDelay(); var step2 = GetFrameActionIndices(step1); - var step3 = MergeFrames(step2); + var step3 = cap.IsFixedFrameRate ? step2 : MergeFrames(step2); var step4 = step3.Select(tp => ApplyFrame(tp.Item1, tp.Item2)); // run pipeline @@ -1710,27 +1715,24 @@ Brush CreateBackgroundBrush() } } - var bgBrush = CreateBackgroundBrush(); - using (var enc = AnimateEncoderFactory.CreateEncoder(dlg.FileName, clientRect.Width, clientRect.Height, config)) + using var bgBrush = CreateBackgroundBrush(); + encoder.Init(outputFileName, clientRect.Width, clientRect.Height); + foreach (IGifFrame gifFrame in gifLayer.Frames) { - foreach (IGifFrame gifFrame in gifLayer.Frames) + using (var bmp = new Bitmap(clientRect.Width, clientRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { - using (var bmp = new Bitmap(clientRect.Width, clientRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) + using (var g = Graphics.FromImage(bmp)) { - using (var g = Graphics.FromImage(bmp)) + // draw background + if (bgBrush != null) { - // draw background - if (bgBrush != null) - { - g.FillRectangle(bgBrush, 0, 0, bmp.Width, bmp.Height); - } - gifFrame.Draw(g, clientRect); + g.FillRectangle(bgBrush, 0, 0, bmp.Width, bmp.Height); } - enc.AppendFrame(bmp, Math.Max(10, gifFrame.Delay)); + gifFrame.Draw(g, clientRect); } + encoder.AppendFrame(bmp, Math.Max(cap.MinFrameDelay, gifFrame.Delay)); } } - bgBrush?.Dispose(); } } @@ -2115,7 +2117,8 @@ private void ExportAvatar(object sender, EventArgs e) } var config = ImageHandlerConfig.Default; - var encParams = AnimateEncoderFactory.GetEncoderParams(config.GifEncoder.Value); + using var encoder = AnimateEncoderFactory.CreateEncoder(config); + var cap = encoder.Compatibility; FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "Select a destination folder to export."; @@ -2146,14 +2149,15 @@ async Task ExportGif(string actionName) } } - string fileName = System.IO.Path.Combine(dlg.SelectedPath, actionName.Replace('\\', '.') + encParams.FileExtension); + string fileName = System.IO.Path.Combine(dlg.SelectedPath, actionName.Replace('\\', '.') + cap.DefaultExtension); var tasks = new List(); tasks.Add(Task.Run(() => { - GifEncoder enc = AnimateEncoderFactory.CreateEncoder(fileName, gif.GetRect().Width, gif.GetRect().Height, config); - gif.SaveGif(enc, fileName, Color.Transparent); + using var encoder = AnimateEncoderFactory.CreateEncoder(config); + encoder.Init(fileName, gif.GetRect().Width, gif.GetRect().Height); + gif.SaveGif(encoder, fileName, Color.Transparent); })); await Task.WhenAll(tasks); diff --git a/WzComparerR2.Common/CharaSim/Item.cs b/WzComparerR2.Common/CharaSim/Item.cs index 9e49585f..0f732874 100644 --- a/WzComparerR2.Common/CharaSim/Item.cs +++ b/WzComparerR2.Common/CharaSim/Item.cs @@ -15,9 +15,11 @@ public Item() this.Specs = new Dictionary(); this.CoreSpecs = new Dictionary(); this.AddTooltips = new List(); + this.Recipes = new List(); } public int Level { get; set; } + public int? DamageSkinID { get; set; } public string ConsumableFrom { get; set; } public string EndUseDate { get; set; } public string SamplePath { get; set; } @@ -28,6 +30,7 @@ public Item() public Dictionary Specs { get; private set; } public Dictionary CoreSpecs { get; private set; } public List AddTooltips { get; internal set; } // Additional Tooltips + public List Recipes { get; private set; } public bool Cash { @@ -97,6 +100,10 @@ public static Item CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) item.Level = Convert.ToInt32(subNode.Value); break; + case "damageSkinID": + item.DamageSkinID = Convert.ToInt32(subNode.Value); + break; + case "consumableFrom": item.ConsumableFrom = Convert.ToString(subNode.Value); break; @@ -248,8 +255,21 @@ public static Item CreateFromNode(Wz_Node node, GlobalFindNodeFunction findNode) { foreach (Wz_Node subNode in specNode.Nodes) { - ItemSpecType type; - if (Enum.TryParse(subNode.Text, out type)) + if (subNode.Text == "recipe") + { + if (subNode.Value == null && subNode.Nodes.Count > 0) + { + foreach (var recipeNode in subNode.Nodes) + { + item.Recipes.Add(recipeNode.GetValue()); + } + } + else + { + item.Recipes.Add(subNode.GetValue()); + } + } + else if(Enum.TryParse(subNode.Text, out ItemSpecType type)) { try { diff --git a/WzComparerR2.Common/Controls/FrmProgressDialog.Designer.cs b/WzComparerR2.Common/Controls/FrmProgressDialog.Designer.cs index 08e9d250..0ada7454 100644 --- a/WzComparerR2.Common/Controls/FrmProgressDialog.Designer.cs +++ b/WzComparerR2.Common/Controls/FrmProgressDialog.Designer.cs @@ -65,6 +65,7 @@ private void InitializeComponent() this.labelX1.Size = new System.Drawing.Size(278, 19); this.labelX1.TabIndex = 0; this.labelX1.Text = "Message"; + this.labelX1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.labelX1_MouseClick); // // progressBarX1 // diff --git a/WzComparerR2.Common/Controls/FrmProgressDialog.cs b/WzComparerR2.Common/Controls/FrmProgressDialog.cs index c40e3b30..80486695 100644 --- a/WzComparerR2.Common/Controls/FrmProgressDialog.cs +++ b/WzComparerR2.Common/Controls/FrmProgressDialog.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Windows.Forms; +using DevComponents.DotNetBar; namespace WzComparerR2.Controls { @@ -38,5 +39,16 @@ public int ProgressMax get { return this.progressBarX1.Maximum; } set { this.progressBarX1.Maximum = value; } } + + public string FullMessage { get; set; } + + private void labelX1_MouseClick(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Right) + { + Clipboard.SetText(this.FullMessage ?? this.Message); + ToastNotification.Show(this, "클립보드에 복사되었습니다.", 1000, eToastPosition.TopCenter); + } + } } } diff --git a/WzComparerR2.Common/Controls/ProgressDialogContext.cs b/WzComparerR2.Common/Controls/ProgressDialogContext.cs index 7b3dcdf8..9c253816 100644 --- a/WzComparerR2.Common/Controls/ProgressDialogContext.cs +++ b/WzComparerR2.Common/Controls/ProgressDialogContext.cs @@ -11,6 +11,7 @@ namespace WzComparerR2.Controls public interface IProgressDialogContext { string Message { get; set; } + string FullMessage { get; set; } int Progress { get; set; } int ProgressMin { get; set; } int ProgressMax { get; set; } @@ -46,6 +47,12 @@ public string Message set { this.dialog.Message = value; } } + public string FullMessage + { + get { return this.dialog.FullMessage; } + set { this.dialog.FullMessage = value; } + } + public int Progress { get { return this.dialog.Progress; } diff --git a/WzComparerR2.Common/BuildInApngEncoder.cs b/WzComparerR2.Common/Encoders/BuildInApngEncoder.cs similarity index 58% rename from WzComparerR2.Common/BuildInApngEncoder.cs rename to WzComparerR2.Common/Encoders/BuildInApngEncoder.cs index f771a5c7..1351e14c 100644 --- a/WzComparerR2.Common/BuildInApngEncoder.cs +++ b/WzComparerR2.Common/Encoders/BuildInApngEncoder.cs @@ -5,27 +5,43 @@ using System.Drawing; using System.Runtime.InteropServices; -namespace WzComparerR2.Common +namespace WzComparerR2.Encoders { public class BuildInApngEncoder : GifEncoder { - public BuildInApngEncoder(string fileName, int width, int height) - : base(fileName, width, height) + public BuildInApngEncoder() { - var err = apng_init(fileName, width, height, out this.handle); + } + + public bool OptimizeEnabled { get; set; } + + private IntPtr handle; + + public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() + { + IsFixedFrameRate = false, + MinFrameDelay = 1, + MaxFrameDelay = 655350, + FrameDelayStep = 1, + AlphaSupportMode = AlphaSupportMode.FullAlpha, + DefaultExtension = ".png", + SupportedExtensions = new[] { ".png" }, + }; + + public override void Init(string fileName, int width, int height) + { + base.Init(fileName, width, height); + + var err = apng_init(fileName, width, height, out handle); if (err != ApngError.Success) { throw new Exception($"APNG Error: {err}."); } } - private IntPtr handle; - - public bool OptimizeEnabled { get; set; } - public override void AppendFrame(IntPtr pBuffer, int delay) { - var err = apng_append_frame(this.handle, pBuffer, 0, 0, this.Width, this.Height, this.Width * 4, delay, this.OptimizeEnabled); + var err = apng_append_frame(handle, pBuffer, 0, 0, Width, Height, Width * 4, delay, OptimizeEnabled); if (err != ApngError.Success) { throw new Exception($"APNG Error: {err}."); @@ -36,8 +52,12 @@ protected override void Dispose(bool disposing) { if (disposing) { - apng_write_end(this.handle); - apng_destroy(ref this.handle); + if (handle != IntPtr.Zero) + { + apng_write_end(handle); + apng_destroy(ref handle); + handle = IntPtr.Zero; + } } base.Dispose(disposing); } @@ -52,7 +72,7 @@ enum ApngError : int }; [DllImport("libapng.dll")] - static extern ApngError apng_init([MarshalAs(UnmanagedType.LPWStr)]string fileName, int width, int height, out IntPtr ppEnc); + static extern ApngError apng_init([MarshalAs(UnmanagedType.LPWStr)] string fileName, int width, int height, out IntPtr ppEnc); [DllImport("libapng.dll")] static extern ApngError apng_append_frame(IntPtr pEnc, IntPtr pData, int x, int y, int width, int height, int stride, int delay_ms, bool optimize); [DllImport("libapng.dll")] diff --git a/WzComparerR2.Common/BuildInGifEncoder.cs b/WzComparerR2.Common/Encoders/BuildInGifEncoder.cs similarity index 73% rename from WzComparerR2.Common/BuildInGifEncoder.cs rename to WzComparerR2.Common/Encoders/BuildInGifEncoder.cs index 18cdd559..a419de91 100644 --- a/WzComparerR2.Common/BuildInGifEncoder.cs +++ b/WzComparerR2.Common/Encoders/BuildInGifEncoder.cs @@ -7,20 +7,27 @@ using System.Drawing.Imaging; using ImageManipulation; -namespace WzComparerR2.Common +namespace WzComparerR2.Encoders { public class BuildInGifEncoder : GifEncoder { - public BuildInGifEncoder(string fileName, int width, int height) - : base(fileName, width, height) + public BuildInGifEncoder() { - bWriter = new BinaryWriter(File.Create(fileName)); mStream = new MemoryStream(); quantizer = new OctreeQuantizer(255, 8); - - WriteHeader(); } + public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() + { + IsFixedFrameRate = false, + MinFrameDelay = 10, + MaxFrameDelay = 655350, + FrameDelayStep = 10, + AlphaSupportMode = AlphaSupportMode.OneBitAlpha, + DefaultExtension = ".gif", + SupportedExtensions = new[] { ".gif" }, + }; + private BinaryWriter bWriter; private MemoryStream mStream; private Quantizer quantizer; @@ -32,6 +39,14 @@ public BuildInGifEncoder(string fileName, int width, int height) 0x03,0x01,0x00,0x00,0x00};//循环信息 其他信息 private static readonly byte[] gifEnd = new byte[] { 0x3b };//结束信息 + public override void Init(string fileName, int width, int height) + { + base.Init(fileName, width, height); + + bWriter = new BinaryWriter(File.Create(fileName)); + WriteHeader(); + } + public override void AppendFrame(Bitmap image, int delay) { mStream.SetLength(0); @@ -43,12 +58,12 @@ public override void AppendFrame(Bitmap image, int delay) byte[] tempArray = mStream.GetBuffer(); // 781开始为Graphic Control Extension块 标志为21 F9 04 - tempArray[784] = (byte)0x09; //图像刷新时屏幕返回初始帧 貌似不打会bug 意味不明 测试用 + tempArray[784] = 0x09; //图像刷新时屏幕返回初始帧 貌似不打会bug 意味不明 测试用 delay = delay / 10; tempArray[785] = (byte)(delay & 0xff); tempArray[786] = (byte)(delay >> 8 & 0xff); //写入2字节的帧delay // 787为透明色索引 788为块结尾0x00 - tempArray[787] = (byte)0xff; + tempArray[787] = 0xff; // 789开始为Image Descriptor块 标志位2C // 790~793为帧偏移大小 默认为0 // 794~797为帧图像大小 默认他 @@ -62,9 +77,9 @@ public override void AppendFrame(Bitmap image, int delay) public override void AppendFrame(IntPtr pBuffer, int delay) { - using(var bmp = new Bitmap(Width, Height, Width *4, PixelFormat.Format32bppArgb, pBuffer)) + using (var bmp = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppArgb, pBuffer)) { - this.AppendFrame(bmp, delay); + AppendFrame(bmp, delay); } } @@ -83,8 +98,12 @@ protected override void Dispose(bool disposing) { if (disposing) { - bWriter.Write(gifEnd); - bWriter.Close(); + if (bWriter != null) + { + bWriter.Write(gifEnd); + bWriter.Close(); + bWriter = null; + } } if (mStream != null) diff --git a/WzComparerR2.Common/Encoders/FFmpegEncoder.cs b/WzComparerR2.Common/Encoders/FFmpegEncoder.cs new file mode 100644 index 00000000..f1622f56 --- /dev/null +++ b/WzComparerR2.Common/Encoders/FFmpegEncoder.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.IO; +using System.IO.Pipes; + +namespace WzComparerR2.Encoders +{ + public class FFmpegEncoder : GifEncoder + { + public static readonly string DefaultExecutionFileName = "ffmpeg"; + /// + /// Default ffmpeg argument format to encode mp4 with avc H.264 video format. + /// + public static readonly string DefaultArgumentFormat = @$"-y -f rawvideo -pixel_format bgra -s %w*%h -r 1000/%t -i ""%i"" -vf ""crop=trunc(iw/2)*2:trunc(ih/2)*2"" -vcodec libx264 -pix_fmt yuv420p ""%o"""; + + public static readonly string DefaultOutputFileExtension = ".mp4"; + + public FFmpegEncoder() + { + } + + public string FFmpegBinPath { get; set; } + public string FFmpegArgumentFormat { get; set; } + public string OutputFileExtension { get; set; } + + private Process ffmpegProc; + private NamedPipeServerStream server; + private CancellationTokenSource ffmpegProcExitedCts; + private StringBuilder ffmpegStdout = new StringBuilder(); + private StringBuilder ffmpegStderr = new StringBuilder(); + private bool disposed; + + public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() + { + IsFixedFrameRate = true, + MinFrameDelay = 1, + MaxFrameDelay = int.MaxValue, + FrameDelayStep = 1, + AlphaSupportMode = AlphaSupportMode.NoAlpha, + DefaultExtension = string.IsNullOrEmpty(OutputFileExtension) ? DefaultOutputFileExtension : OutputFileExtension, + SupportedExtensions = new[] { ".*" }, + }; + + public unsafe override void AppendFrame(IntPtr pBuffer, int delay) + { + if (server == null && ffmpegProc == null) + { + StartFFmpeg(delay).Wait(); + } + + int frameDataLen = Width * Height * 4; + using UnmanagedMemoryStream ms = new((byte*)pBuffer.ToPointer(), frameDataLen, frameDataLen, FileAccess.Read); + ms.CopyToAsync(server, 32768, ffmpegProcExitedCts?.Token ?? default).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + private async Task StartFFmpeg(int delay) + { + // create random pipe name + string pipeName = $"{nameof(FFmpegEncoder)}-{Process.GetCurrentProcess().Id}-{(uint)Environment.TickCount}"; + + // create named-pipe server and listen + NamedPipeServerStream server = new(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte); + var task1 = server.WaitForConnectionAsync(); + + // start ffmpeg + ProcessStartInfo psi = new() + { + FileName = string.IsNullOrEmpty(FFmpegBinPath) ? DefaultExecutionFileName : FFmpegBinPath, + Arguments = SubstituteParams(string.IsNullOrEmpty(FFmpegArgumentFormat) ? DefaultArgumentFormat : FFmpegArgumentFormat, + @$"\\.\pipe\{pipeName}", Width, Height, delay, FileName), + WorkingDirectory = Environment.CurrentDirectory, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + }; + Process ffmpegProc = new() + { + StartInfo = psi, + }; + ffmpegProc.OutputDataReceived += FFmpegProc_OutputDataReceived; + ffmpegProc.ErrorDataReceived += FFmpegProc_ErrorDataReceived; + ffmpegProc.Exited += FFmpegProc_Exited; + ffmpegProc.Start(); + ffmpegProc.BeginOutputReadLine(); + ffmpegProc.BeginErrorReadLine(); + await task1.ConfigureAwait(false); + + this.server = server; + this.ffmpegProc = ffmpegProc; + ffmpegProcExitedCts = new CancellationTokenSource(); + } + + private void FFmpegProc_OutputDataReceived(object sender, DataReceivedEventArgs e) + { + ffmpegStdout?.AppendLine(e.Data); + } + + private void FFmpegProc_ErrorDataReceived(object sender, DataReceivedEventArgs e) + { + ffmpegStderr?.AppendLine(e.Data); + } + + private void FFmpegProc_Exited(object sender, EventArgs e) + { + ffmpegProcExitedCts?.Cancel(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposed) + { + if (disposing) + { + if (server != null) + { + if (server.IsConnected) + { + server.Flush(); + server.Disconnect(); + } + server.Dispose(); + } + if (ffmpegProc != null) + { + if (!ffmpegProc.HasExited) + { + ffmpegProc.WaitForExit(); + } + ffmpegProc.Dispose(); + } + if (ffmpegProcExitedCts != null) + { + ffmpegProcExitedCts.Dispose(); + } + } + + ffmpegStdout = null; + ffmpegStderr = null; + disposed = true; + } + } + + private string SubstituteParams(string format, string inputFileName, int width, int height, int frameDelay, string outputFileName) + { + // %i: inputFileName + // %w: width + // %h: height + // %t: frameDelay + // %o: outputFileName + // %%: escape '%' char + return Regex.Replace(format, "%[iwhto%]", match => match.Value switch + { + "%i" => inputFileName, + "%w" => width.ToString(), + "%h" => height.ToString(), + "%t" => frameDelay.ToString(), + "%o" => outputFileName, + "%%" => "%", + _ => throw new FormatException($"Unknown format: {match.Value}") + }); + } + } +} diff --git a/WzComparerR2.Common/GifEncoder.cs b/WzComparerR2.Common/Encoders/GifEncoder.cs similarity index 62% rename from WzComparerR2.Common/GifEncoder.cs rename to WzComparerR2.Common/Encoders/GifEncoder.cs index ef5f460d..8014eb8b 100644 --- a/WzComparerR2.Common/GifEncoder.cs +++ b/WzComparerR2.Common/Encoders/GifEncoder.cs @@ -5,34 +5,46 @@ using System.Drawing; using System.Drawing.Imaging; -namespace WzComparerR2.Common +namespace WzComparerR2.Encoders { public abstract class GifEncoder : IDisposable { - protected GifEncoder(string fileName, int width, int height) + protected GifEncoder() { - this.FileName = fileName; - this.Width = width; - this.Height = height; } public string FileName { get; private set; } public int Width { get; private set; } public int Height { get; private set; } + public virtual string Name + { + get + { + return GetType().Name; + } + } + public abstract GifEncoderCompatibility Compatibility { get; } + + public virtual void Init(string fileName, int width, int height) + { + FileName = fileName; + Width = width; + Height = height; + } + public virtual void AppendFrame(Bitmap image, int delay) { BitmapData data = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - this.AppendFrame(data.Scan0, delay); + AppendFrame(data.Scan0, delay); image.UnlockBits(data); } public abstract void AppendFrame(IntPtr pBuffer, int delay); - public void Dispose() { - this.Dispose(true); + Dispose(true); GC.SuppressFinalize(this); } @@ -43,7 +55,7 @@ protected virtual void Dispose(bool disposing) ~GifEncoder() { - this.Dispose(false); + Dispose(false); } } } diff --git a/WzComparerR2.Common/Encoders/GifEncoderCompatibility.cs b/WzComparerR2.Common/Encoders/GifEncoderCompatibility.cs new file mode 100644 index 00000000..c9019ff4 --- /dev/null +++ b/WzComparerR2.Common/Encoders/GifEncoderCompatibility.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WzComparerR2.Encoders +{ + public class GifEncoderCompatibility + { + public GifEncoderCompatibility() + { + } + + public bool IsFixedFrameRate { get; set; } + public int MinFrameDelay { get; set; } + public int MaxFrameDelay { get; set; } + public int FrameDelayStep { get; set; } + public AlphaSupportMode AlphaSupportMode { get; set; } + public string DefaultExtension { get; set; } + public IReadOnlyList SupportedExtensions { get; set; } + } + + public enum AlphaSupportMode + { + NoAlpha, + OneBitAlpha, + FullAlpha + } +} diff --git a/WzComparerR2.Common/IndexGifEncoder.cs b/WzComparerR2.Common/Encoders/IndexGifEncoder.cs similarity index 61% rename from WzComparerR2.Common/IndexGifEncoder.cs rename to WzComparerR2.Common/Encoders/IndexGifEncoder.cs index 33825bb5..f70be0ca 100644 --- a/WzComparerR2.Common/IndexGifEncoder.cs +++ b/WzComparerR2.Common/Encoders/IndexGifEncoder.cs @@ -3,35 +3,48 @@ using System.Drawing.Imaging; using System.Runtime.InteropServices; -namespace WzComparerR2.Common +namespace WzComparerR2.Encoders { public class IndexGifEncoder : GifEncoder { - public IndexGifEncoder(string location, int width, int height) - : this(location, width, height, 255, Color.FromArgb(0)) + public IndexGifEncoder() { - - } - public IndexGifEncoder(string location, int width, int height, int max_color, Color back_color) - :base(location, width, height) - { - encoder_pointer = construct(location, width, height, max_color, back_color.ToArgb()); } private IntPtr encoder_pointer; + public override GifEncoderCompatibility Compatibility => new GifEncoderCompatibility() + { + IsFixedFrameRate = false, + MinFrameDelay = 10, + MaxFrameDelay = 655350, + FrameDelayStep = 10, + AlphaSupportMode = AlphaSupportMode.OneBitAlpha, + DefaultExtension = ".gif", + SupportedExtensions = new[] { ".gif" }, + }; + + public override void Init(string fileName, int width, int height) + { + base.Init(fileName, width, height); + + encoder_pointer = construct(fileName, width, height, 255, 0); + } public override void AppendFrame(IntPtr pBuffer, int delay) { encoder.append_frame(pBuffer, delay, encoder_pointer); } - protected override void Dispose(bool disposing) { if (disposing) { - encoder.destruct(encoder_pointer); + if (encoder_pointer != IntPtr.Zero) + { + encoder.destruct(encoder_pointer); + encoder_pointer = IntPtr.Zero; + } } } diff --git a/WzComparerR2.Common/Gif.cs b/WzComparerR2.Common/Gif.cs index 15c0769e..fb307b97 100644 --- a/WzComparerR2.Common/Gif.cs +++ b/WzComparerR2.Common/Gif.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Runtime.InteropServices; using System.Drawing; using System.Drawing.Imaging; -using ImageManipulation; - +using System.IO; +using WzComparerR2.Encoders; using WzComparerR2.WzLib; namespace WzComparerR2.Common diff --git a/WzComparerR2.MapRender/UI/Tooltip2.cs b/WzComparerR2.MapRender/UI/Tooltip2.cs index 6ca768b3..99b77c80 100644 --- a/WzComparerR2.MapRender/UI/Tooltip2.cs +++ b/WzComparerR2.MapRender/UI/Tooltip2.cs @@ -400,8 +400,8 @@ private TooltipContent DrawItem(GameTime gameTime, RenderEnv env, UIWorldMap.Map this.StringLinker?.StringNpc.TryGetValue(npcID, out sr); string npcText = sr?.Name ?? npcID.ToString(); var npcInfo = PluginManager.FindWz(string.Format("Npc/{0:D7}.img/info", npcID)); - var hide = npcInfo.Nodes["hide"].GetValueEx(0); - var hideName = npcInfo.Nodes["hideName"].GetValueEx(0); + var hide = npcInfo?.Nodes["hide"].GetValueEx(0); + var hideName = npcInfo?.Nodes["hideName"].GetValueEx(0); if ((hide != 0 || hideName != 0) && npcNames.Contains(npcText)) { npcNames[npcNames.IndexOf(npcText)] = ""; diff --git a/WzComparerR2/AnimateEncoderFactory.cs b/WzComparerR2/AnimateEncoderFactory.cs index 395e6efa..7be50884 100644 --- a/WzComparerR2/AnimateEncoderFactory.cs +++ b/WzComparerR2/AnimateEncoderFactory.cs @@ -1,90 +1,108 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using WzComparerR2.Common; using WzComparerR2.Config; +using WzComparerR2.Encoders; namespace WzComparerR2 { - public class AnimateEncoderFactory + public static class AnimateEncoderFactory { - private AnimateEncoderFactory() + static AnimateEncoderFactory() { - + registeredEncoders = new Dictionary(); + RegisterEncoders(); } - public static AnimateEncoderParams GetEncoderParams(int encoderID) + private static Dictionary registeredEncoders; + + private static void RegisterEncoders() { - switch (encoderID) + registeredEncoders.Add(0, new AnimateEncoderProvider + { + ID = 0, + Name = nameof(BuildInGifEncoder), + CreateEncoderCallback = () => new BuildInGifEncoder(), + }); + + registeredEncoders.Add(1, new AnimateEncoderProvider { - default: - case 0: - return new AnimateEncoderParams() - { - ID = 0, - EncoderType = typeof(BuildInGifEncoder), - FileExtension = ".gif", - FileDescription = "GIF", - SupportAlphaChannel = false, - }; + ID = 1, + Name = nameof(IndexGifEncoder), + CreateEncoderCallback = () => new IndexGifEncoder(), + }); - case 1: - return new AnimateEncoderParams() - { - ID = 1, - EncoderType = typeof(IndexGifEncoder), - FileExtension = ".gif", - FileDescription = "GIF", - SupportAlphaChannel = false, - }; + registeredEncoders.Add(2, new AnimateEncoderProvider + { + ID = 2, + Name = nameof(BuildInApngEncoder), + CreateEncoderCallback = () => new BuildInApngEncoder(), + ConfigureEncoderCallback = (encoder, config) => + { + encoder.OptimizeEnabled = config.PaletteOptimized; + } + }); - case 2: - return new AnimateEncoderParams() - { - ID = 2, - EncoderType = typeof(BuildInApngEncoder), - FileExtension = ".png", - FileDescription = "APNG", - SupportAlphaChannel = true, - }; - } + registeredEncoders.Add(3, new AnimateEncoderProvider + { + ID = 3, + Name = nameof(FFmpegEncoder), + CreateEncoderCallback = () => new FFmpegEncoder(), + ConfigureEncoderCallback = (encoder, config) => + { + encoder.FFmpegBinPath = config.FFmpegBinPath; + encoder.FFmpegArgumentFormat = config.FFmpegArgument; + encoder.OutputFileExtension = config.FFmpegOutputFileExtension; + } + }); + } + + public static GifEncoder CreateEncoder(ImageHandlerConfig config) + { + return CreateEncoder(config.GifEncoder, config); } - public static GifEncoder CreateEncoder(string fileName, int width, int height, ImageHandlerConfig config) + public static GifEncoder CreateEncoder(int id, ImageHandlerConfig config) { - switch (config.GifEncoder.Value) + if (!registeredEncoders.TryGetValue(id, out var provider)) { - default: - case 0: - { - var enc = new BuildInGifEncoder(fileName, width, height); - return enc; - } + throw new Exception($"Encoder ID {id} has not registered"); + } - case 1: - { - var enc = new IndexGifEncoder(fileName, width, height); - return enc; - } + var encoder = provider.CreateEncoder(); + provider.ConfigureEncoder(encoder, config); + return encoder; + } - case 2: - { - var enc = new BuildInApngEncoder(fileName, width, height); - enc.OptimizeEnabled = config.PaletteOptimized; - return enc; - } - } + public interface IAnimateEncoderProvider + { + GifEncoder CreateEncoder(); + void ConfigureEncoder(GifEncoder encoder, ImageHandlerConfig config); } - } - public class AnimateEncoderParams - { - public int ID { get; set; } - public Type EncoderType { get; set; } - public string FileExtension { get; set; } - public string FileDescription { get; set; } + public class AnimateEncoderProvider : IAnimateEncoderProvider where T : GifEncoder + { + public int ID { get; set; } + public string Name { get; set; } + public Func CreateEncoderCallback { get; set; } + public Action ConfigureEncoderCallback { get; set; } + + public GifEncoder CreateEncoder() + { + if (this.CreateEncoderCallback == null) + { + throw new ArgumentNullException(nameof(CreateEncoderCallback)); + } + + return this.CreateEncoderCallback(); + } - public bool SupportAlphaChannel { get; set; } + public void ConfigureEncoder(GifEncoder encoder, ImageHandlerConfig config) + { + if (this.ConfigureEncoderCallback != null) + { + this.ConfigureEncoderCallback((T)encoder, config); + } + } + } } -} \ No newline at end of file +} diff --git a/WzComparerR2/CharaSimControl/ItemTooltipRender2.cs b/WzComparerR2/CharaSimControl/ItemTooltipRender2.cs index 4a3a3c50..6f1d6a0e 100644 --- a/WzComparerR2/CharaSimControl/ItemTooltipRender2.cs +++ b/WzComparerR2/CharaSimControl/ItemTooltipRender2.cs @@ -61,8 +61,8 @@ public override Bitmap Render() //绘制道具 int picHeight; Bitmap itemBmp = RenderItem(out picHeight); - Bitmap recipeInfoBmp = null; - List recipeItemBmps = new List(); + List recipeInfoBmps = new(); + List recipeItemBmps = new(); Bitmap setItemBmp = null; Bitmap levelBmp = null; int levelHeight = 0; @@ -150,28 +150,31 @@ public override Bitmap Render() }; //图纸相关 - if (this.item.Specs.TryGetValue(ItemSpecType.recipe, out long recipeID)) + if (this.item.Recipes.Count > 0) { - long recipeSkillID = recipeID / 10000; - Recipe recipe = null; - //寻找配方 - Wz_Node recipeNode = PluginBase.PluginManager.FindWz(string.Format(@"Skill\Recipe_{0}.img\{1}", recipeSkillID, recipeID)); - if (recipeNode != null) + foreach (int recipeID in this.item.Recipes) { - recipe = Recipe.CreateFromNode(recipeNode); - } - //生成配方图像 - if (recipe != null) - { - if (this.LinkRecipeInfo) + long recipeSkillID = recipeID / 10000; + Recipe recipe = null; + //寻找配方 + Wz_Node recipeNode = PluginBase.PluginManager.FindWz(string.Format(@"Skill\Recipe_{0}.img\{1}", recipeSkillID, recipeID)); + if (recipeNode != null) { - recipeInfoBmp = RenderLinkRecipeInfo(recipe); + recipe = Recipe.CreateFromNode(recipeNode); } - - if (this.LinkRecipeItem) + //生成配方图像 + if (recipe != null) { - int itemID = recipe.MainTargetItemID; - AppendGearOrItem(itemID); + if (this.LinkRecipeInfo) + { + recipeInfoBmps.Add(RenderLinkRecipeInfo(recipe)); + } + + if (this.LinkRecipeItem) + { + int itemID = recipe.MainTargetItemID; + AppendGearOrItem(itemID); + } } } } @@ -208,35 +211,35 @@ public override Bitmap Render() //计算布局 Size totalSize = new Size(itemBmp.Width, picHeight); Point recipeInfoOrigin = Point.Empty; - List recipeItemOrigins = new List(); + Point recipeItemOrigin = Point.Empty; Point setItemOrigin = Point.Empty; Point levelOrigin = Point.Empty; if (recipeItemBmps.Count > 0) { - if (recipeInfoBmp != null) + // layout: + // item | recipeItem + // recipeInfo | + recipeItemOrigin.X = totalSize.Width; + totalSize.Width += recipeItemBmps.Max(bmp => bmp.Width); + + if (recipeInfoBmps.Count > 0) { - recipeItemOrigins.Add(new Point(totalSize.Width, 0)); - recipeInfoOrigin.X = itemBmp.Width - recipeInfoBmp.Width; + recipeInfoOrigin.X = itemBmp.Width - recipeInfoBmps.Max(bmp => bmp.Width); recipeInfoOrigin.Y = picHeight; - totalSize.Width += recipeItemBmps[0].Width; - totalSize.Height = Math.Max(picHeight + recipeInfoBmp.Height, recipeItemBmps[0].Height); + totalSize.Height = Math.Max(picHeight + recipeInfoBmps.Sum(bmp => bmp.Height), recipeItemBmps.Sum(bmp => bmp.Height)); } else { - int itemCnt = recipeItemBmps.Count; - for (int i = 0; i < itemCnt; ++i) - { - recipeItemOrigins.Add(new Point(totalSize.Width, 0)); - totalSize.Width += recipeItemBmps[i].Width; - totalSize.Height = Math.Max(picHeight, recipeItemBmps[i].Height); - } + totalSize.Height = Math.Max(picHeight, recipeItemBmps.Sum(bmp => bmp.Height)); } } - else if (recipeInfoBmp != null) + else if (recipeInfoBmps.Count > 0) { - totalSize.Width += recipeInfoBmp.Width; - totalSize.Height = Math.Max(picHeight, recipeInfoBmp.Height); + // layout: + // item | recipeInfo + totalSize.Width += recipeInfoBmps.Max(bmp => bmp.Width); + totalSize.Height = Math.Max(picHeight, recipeInfoBmps.Sum(bmp => bmp.Height)); recipeInfoOrigin.X = itemBmp.Width; } if (setItemBmp != null) @@ -272,20 +275,24 @@ public override Bitmap Render() } //绘制配方 - if (recipeInfoBmp != null) + if (recipeInfoBmps.Count > 0) { - g.DrawImage(recipeInfoBmp, recipeInfoOrigin.X, recipeInfoOrigin.Y, - new Rectangle(Point.Empty, recipeInfoBmp.Size), GraphicsUnit.Pixel); + for (int i = 0, y = recipeInfoOrigin.Y; i < recipeInfoBmps.Count; i++) + { + g.DrawImage(recipeInfoBmps[i], recipeInfoOrigin.X, y, + new Rectangle(Point.Empty, recipeInfoBmps[i].Size), GraphicsUnit.Pixel); + y += recipeInfoBmps[i].Height; + } } //绘制产出道具 if (recipeItemBmps.Count > 0) { - int itemCnt = recipeItemBmps.Count; - for (int i = 0; i < itemCnt; ++i) + for (int i = 0, y = recipeItemOrigin.Y; i < recipeItemBmps.Count; i++) { - g.DrawImage(recipeItemBmps[i], recipeItemOrigins[i].X, recipeItemOrigins[i].Y, + g.DrawImage(recipeItemBmps[i], recipeItemOrigin.X, y, new Rectangle(Point.Empty, recipeItemBmps[i].Size), GraphicsUnit.Pixel); + y += recipeItemBmps[i].Height; } } @@ -306,11 +313,10 @@ public override Bitmap Render() if (itemBmp != null) itemBmp.Dispose(); - if (recipeInfoBmp != null) - recipeInfoBmp.Dispose(); + if (recipeInfoBmps.Count > 0) + recipeInfoBmps.ForEach(bmp => bmp.Dispose()); if (recipeItemBmps.Count > 0) - foreach (Bitmap recipeItemBmp in recipeItemBmps) - recipeItemBmp.Dispose(); + recipeItemBmps.ForEach(bmp => bmp.Dispose()); if (setItemBmp != null) setItemBmp.Dispose(); if (levelBmp != null) @@ -320,7 +326,6 @@ public override Bitmap Render() return tooltip; } - private Bitmap RenderItem(out int picH) { bool isTranslateRequired = Translator.IsTranslateEnabled; @@ -812,7 +817,7 @@ private Bitmap RenderItem(out int picH) long minLev = 0, maxLev = 0; bool willDrawExp = item.Props.TryGetValue(ItemPropType.exp_minLev, out minLev) && item.Props.TryGetValue(ItemPropType.exp_maxLev, out maxLev); - if (!string.IsNullOrEmpty(descLeftAlign) || item.CoreSpecs.Count > 0 || item.Sample.Bitmap != null || item.SamplePath != null || willDrawNickTag || willDrawExp) + if (!string.IsNullOrEmpty(descLeftAlign) || item.CoreSpecs.Count > 0 || item.Sample.Bitmap != null || item.DamageSkinID != null || item.SamplePath != null || willDrawNickTag || willDrawExp) { if (picH < iconY + 84) { @@ -877,6 +882,17 @@ private Bitmap RenderItem(out int picH) picH += item.Sample.Bitmap.Height; picH += 2; } + else if (item.DamageSkinID != null) + { + Wz_Node sampleNode = PluginManager.FindWz($@"Etc\DamageSkin.img\{item.DamageSkinID}\sample"); + if (sampleNode != null) + { + BitmapOrigin sample = BitmapOrigin.CreateFromNode(sampleNode, PluginManager.FindWz); + g.DrawImage(sample.Bitmap, (tooltip.Width - sample.Bitmap.Width) / 2, picH); + picH += sample.Bitmap.Height; + picH += 2; + } + } if (item.SamplePath != null) { Wz_Node sampleNode = PluginManager.FindWz(item.SamplePath); @@ -960,12 +976,12 @@ private Bitmap RenderItem(out int picH) //绘制配方需求 - if (item.Specs.TryGetValue(ItemSpecType.recipe, out value)) + if (item.Recipes.Count > 0) { long reqSkill, reqSkillLevel; if (!item.Specs.TryGetValue(ItemSpecType.reqSkill, out reqSkill)) { - reqSkill = value / 10000 * 10000; + reqSkill = item.Recipes[0] / 10000 * 10000; } if (!item.Specs.TryGetValue(ItemSpecType.reqSkillLevel, out reqSkillLevel)) diff --git a/WzComparerR2/Comparer/EasyComparer.cs b/WzComparerR2/Comparer/EasyComparer.cs index bd5a0d98..964b1c3c 100644 --- a/WzComparerR2/Comparer/EasyComparer.cs +++ b/WzComparerR2/Comparer/EasyComparer.cs @@ -858,6 +858,7 @@ protected virtual string OutputNodeValue(string fullPath, Wz_Node value, int col string colName = col == 0 ? "new" : (col == 1 ? "old" : col.ToString()); string fileName = fullPath.Replace('\\', '.'); string suffix = "_" + colName + ".png"; + string canvas = "_Canvas"; if (this.HashPngFileName) { @@ -880,11 +881,21 @@ protected virtual string OutputNodeValue(string fullPath, Wz_Node value, int col } fileName = fileName + suffix; + string outputDirName = new DirectoryInfo(outputDir).Name; + bool isCanvas = fileName.Contains(canvas); + if (isCanvas) + { + outputDir = Path.Combine(outputDir, canvas); + if (!Directory.Exists(outputDir)) + { + Directory.CreateDirectory(outputDir); + } + } using (Bitmap bmp = png.ExtractPng()) { bmp.Save(Path.Combine(outputDir, fileName), System.Drawing.Imaging.ImageFormat.Png); } - return string.Format("", new DirectoryInfo(outputDir).Name, WebUtility.UrlEncode(fileName)); + return string.Format("", isCanvas ? Path.Combine(outputDirName, canvas) : outputDirName, WebUtility.UrlEncode(fileName)); } else { diff --git a/WzComparerR2/Config/ImageHandlerConfig.cs b/WzComparerR2/Config/ImageHandlerConfig.cs index ccf1fa79..8b035c18 100644 --- a/WzComparerR2/Config/ImageHandlerConfig.cs +++ b/WzComparerR2/Config/ImageHandlerConfig.cs @@ -102,6 +102,27 @@ public ConfigItem OverlayRectColor get { return (ConfigItem)this["overlayRectColor"]; } set { this["overlayRectColor"] = value; } } + + [ConfigurationProperty("ffmpegBinPath")] + public ConfigItem FFmpegBinPath + { + get { return (ConfigItem)this["ffmpegBinPath"]; } + set { this["ffmpegBinPath"] = value; } + } + + [ConfigurationProperty("ffmpegArgument")] + public ConfigItem FFmpegArgument + { + get { return (ConfigItem)this["ffmpegArgument"]; } + set { this["ffmpegArgument"] = value; } + } + + [ConfigurationProperty("ffmpegOutputFileExtension")] + public ConfigItem FFmpegOutputFileExtension + { + get { return (ConfigItem)this["ffmpegOutputFileExtension"]; } + set { this["ffmpegOutputFileExtension"] = value; } + } } public enum ImageBackgroundType diff --git a/WzComparerR2/FrmGifSetting.Designer.cs b/WzComparerR2/FrmGifSetting.Designer.cs index b79d5fd4..3921f2a5 100644 --- a/WzComparerR2/FrmGifSetting.Designer.cs +++ b/WzComparerR2/FrmGifSetting.Designer.cs @@ -29,16 +29,18 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmGifSetting)); + DevComponents.DotNetBar.SuperTooltipInfo superTooltipInfo2 = new DevComponents.DotNetBar.SuperTooltipInfo(); + DevComponents.DotNetBar.SuperTooltipInfo superTooltipInfo3 = new DevComponents.DotNetBar.SuperTooltipInfo(); + DevComponents.DotNetBar.SuperTooltipInfo superTooltipInfo1 = new DevComponents.DotNetBar.SuperTooltipInfo(); this.colorPickerButton1 = new DevComponents.DotNetBar.ColorPickerButton(); this.labelX1 = new DevComponents.DotNetBar.LabelX(); this.checkBoxX1 = new DevComponents.DotNetBar.Controls.CheckBoxX(); - this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); - this.buttonX2 = new DevComponents.DotNetBar.ButtonX(); this.labelX2 = new DevComponents.DotNetBar.LabelX(); this.comboBoxEx1 = new DevComponents.DotNetBar.Controls.ComboBoxEx(); this.comboItem1 = new DevComponents.Editors.ComboItem(); this.comboItem2 = new DevComponents.Editors.ComboItem(); this.comboItem6 = new DevComponents.Editors.ComboItem(); + this.comboItem7 = new DevComponents.Editors.ComboItem(); this.labelX3 = new DevComponents.DotNetBar.LabelX(); this.slider1 = new DevComponents.DotNetBar.Controls.Slider(); this.labelX4 = new DevComponents.DotNetBar.LabelX(); @@ -61,10 +63,34 @@ private void InitializeComponent() this.comboItem3 = new DevComponents.Editors.ComboItem(); this.comboItem4 = new DevComponents.Editors.ComboItem(); this.comboItem5 = new DevComponents.Editors.ComboItem(); + this.superTabControl1 = new DevComponents.DotNetBar.SuperTabControl(); + this.superTabControlPanel3 = new DevComponents.DotNetBar.SuperTabControlPanel(); + this.labelX13 = new DevComponents.DotNetBar.LabelX(); + this.textBoxX3 = new DevComponents.DotNetBar.Controls.TextBoxX(); + this.labelX12 = new DevComponents.DotNetBar.LabelX(); + this.buttonX3 = new DevComponents.DotNetBar.ButtonX(); + this.textBoxX2 = new DevComponents.DotNetBar.Controls.TextBoxX(); + this.labelX11 = new DevComponents.DotNetBar.LabelX(); + this.textBoxX1 = new DevComponents.DotNetBar.Controls.TextBoxX(); + this.labelX10 = new DevComponents.DotNetBar.LabelX(); + this.superTabItem3 = new DevComponents.DotNetBar.SuperTabItem(); + this.superTabControlPanel1 = new DevComponents.DotNetBar.SuperTabControlPanel(); + this.superTabItem1 = new DevComponents.DotNetBar.SuperTabItem(); + this.superTabControlPanel2 = new DevComponents.DotNetBar.SuperTabControlPanel(); this.checkBoxX3 = new DevComponents.DotNetBar.Controls.CheckBoxX(); + this.superTabItem2 = new DevComponents.DotNetBar.SuperTabItem(); + this.panelEx1 = new DevComponents.DotNetBar.PanelEx(); + this.buttonX2 = new DevComponents.DotNetBar.ButtonX(); + this.buttonX1 = new DevComponents.DotNetBar.ButtonX(); this.panelExMosaic.SuspendLayout(); this.panelExColor.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.integerInput1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).BeginInit(); + this.superTabControl1.SuspendLayout(); + this.superTabControlPanel3.SuspendLayout(); + this.superTabControlPanel1.SuspendLayout(); + this.superTabControlPanel2.SuspendLayout(); + this.panelEx1.SuspendLayout(); this.SuspendLayout(); // // colorPickerButton1 @@ -89,7 +115,7 @@ private void InitializeComponent() this.labelX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX1.Location = new System.Drawing.Point(11, 13); this.labelX1.Name = "labelX1"; - this.labelX1.Size = new System.Drawing.Size(99, 16); + this.labelX1.Size = new System.Drawing.Size(44, 18); this.labelX1.TabIndex = 0; this.labelX1.Text = "Color"; // @@ -102,46 +128,22 @@ private void InitializeComponent() this.checkBoxX1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.checkBoxX1.Location = new System.Drawing.Point(11, 36); this.checkBoxX1.Name = "checkBoxX1"; - this.checkBoxX1.Size = new System.Drawing.Size(163, 16); + this.checkBoxX1.Size = new System.Drawing.Size(109, 18); this.checkBoxX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX1.TabIndex = 2; this.checkBoxX1.Text = "Transparent Background"; // - // buttonX1 - // - this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; - this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; - this.buttonX1.Location = new System.Drawing.Point(93, 276); - this.buttonX1.Name = "buttonX1"; - this.buttonX1.Size = new System.Drawing.Size(75, 23); - this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; - this.buttonX1.TabIndex = 10; - this.buttonX1.Text = "Confirm"; - this.buttonX1.Click += new System.EventHandler(this.buttonX1_Click); - // - // buttonX2 - // - this.buttonX2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; - this.buttonX2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; - this.buttonX2.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.buttonX2.Location = new System.Drawing.Point(189, 276); - this.buttonX2.Name = "buttonX2"; - this.buttonX2.Size = new System.Drawing.Size(75, 23); - this.buttonX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; - this.buttonX2.TabIndex = 11; - this.buttonX2.Text = "Cancel"; - this.buttonX2.Click += new System.EventHandler(this.buttonX2_Click); - // // labelX2 // this.labelX2.AutoSize = true; + this.labelX2.BackColor = System.Drawing.Color.Transparent; // // // this.labelX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; - this.labelX2.Location = new System.Drawing.Point(12, 199); + this.labelX2.Location = new System.Drawing.Point(16, 207); this.labelX2.Name = "labelX2"; - this.labelX2.Size = new System.Drawing.Size(56, 16); + this.labelX2.Size = new System.Drawing.Size(44, 18); this.labelX2.TabIndex = 5; this.labelX2.Text = "Filename"; // @@ -150,13 +152,15 @@ private void InitializeComponent() this.comboBoxEx1.DisplayMember = "Text"; this.comboBoxEx1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxEx1.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.comboBoxEx1.FormattingEnabled = true; this.comboBoxEx1.ItemHeight = 15; this.comboBoxEx1.Items.AddRange(new object[] { this.comboItem1, this.comboItem2, - this.comboItem6}); - this.comboBoxEx1.Location = new System.Drawing.Point(76, 170); + this.comboItem6, + this.comboItem7}); + this.comboBoxEx1.Location = new System.Drawing.Point(100, 178); this.comboBoxEx1.Name = "comboBoxEx1"; this.comboBoxEx1.Size = new System.Drawing.Size(129, 21); this.comboBoxEx1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; @@ -174,6 +178,10 @@ private void InitializeComponent() // this.comboItem6.Text = "APNG Encoder"; // + // comboItem7 + // + this.comboItem7.Text = "FFmpeg Encoder"; + // // labelX3 // this.labelX3.AutoSize = true; @@ -183,7 +191,7 @@ private void InitializeComponent() this.labelX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX3.Location = new System.Drawing.Point(11, 61); this.labelX3.Name = "labelX3"; - this.labelX3.Size = new System.Drawing.Size(87, 16); + this.labelX3.Size = new System.Drawing.Size(93, 16); this.labelX3.TabIndex = 3; this.labelX3.Text = "MinAlphaMixed"; // @@ -208,29 +216,33 @@ private void InitializeComponent() // labelX4 // this.labelX4.AutoSize = true; + this.labelX4.BackColor = System.Drawing.Color.Transparent; // // // this.labelX4.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; - this.labelX4.Location = new System.Drawing.Point(12, 12); + this.labelX4.Location = new System.Drawing.Point(16, 20); this.labelX4.Name = "labelX4"; - this.labelX4.Size = new System.Drawing.Size(68, 16); + this.labelX4.Size = new System.Drawing.Size(31, 18); this.labelX4.TabIndex = 0; this.labelX4.Text = "Background"; // // rdoMosaic // this.rdoMosaic.AutoSize = true; + this.rdoMosaic.BackColor = System.Drawing.Color.Transparent; // // // this.rdoMosaic.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.rdoMosaic.CheckBoxStyle = DevComponents.DotNetBar.eCheckBoxStyle.RadioButton; - this.rdoMosaic.Location = new System.Drawing.Point(12, 122); + this.rdoMosaic.Location = new System.Drawing.Point(16, 130); this.rdoMosaic.Name = "rdoMosaic"; - this.rdoMosaic.Size = new System.Drawing.Size(64, 16); + this.rdoMosaic.Size = new System.Drawing.Size(76, 18); this.rdoMosaic.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; - this.superTooltip1.SetSuperTooltip(this.rdoMosaic, new DevComponents.DotNetBar.SuperTooltipInfo("", "", "Adds a mosaic background to a GIF.", null, null, DevComponents.DotNetBar.eTooltipColor.System)); + superTooltipInfo2.BodyText = "Adds a mosaic background to a GIF."; + superTooltipInfo2.Color = DevComponents.DotNetBar.eTooltipColor.System; + this.superTooltip1.SetSuperTooltip(this.rdoMosaic, superTooltipInfo2); this.rdoMosaic.TabIndex = 3; this.rdoMosaic.Text = "Mosaic"; this.rdoMosaic.CheckedChanged += new System.EventHandler(this.rdoMosaic_CheckedChanged); @@ -247,7 +259,7 @@ private void InitializeComponent() this.panelExMosaic.Controls.Add(this.colorPickerButton2); this.panelExMosaic.DisabledBackColor = System.Drawing.Color.Empty; this.panelExMosaic.Enabled = false; - this.panelExMosaic.Location = new System.Drawing.Point(76, 97); + this.panelExMosaic.Location = new System.Drawing.Point(100, 105); this.panelExMosaic.Name = "panelExMosaic"; this.panelExMosaic.Size = new System.Drawing.Size(256, 66); this.panelExMosaic.Style.Alignment = System.Drawing.StringAlignment.Center; @@ -268,7 +280,7 @@ private void InitializeComponent() this.labelX7.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX7.Location = new System.Drawing.Point(11, 42); this.labelX7.Name = "labelX7"; - this.labelX7.Size = new System.Drawing.Size(62, 16); + this.labelX7.Size = new System.Drawing.Size(60, 18); this.labelX7.TabIndex = 4; this.labelX7.Text = "Block Size"; // @@ -281,7 +293,7 @@ private void InitializeComponent() this.labelX6.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX6.Location = new System.Drawing.Point(104, 14); this.labelX6.Name = "labelX6"; - this.labelX6.Size = new System.Drawing.Size(44, 16); + this.labelX6.Size = new System.Drawing.Size(38, 18); this.labelX6.TabIndex = 2; this.labelX6.Text = "Color 2"; // @@ -294,7 +306,7 @@ private void InitializeComponent() this.labelX5.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.labelX5.Location = new System.Drawing.Point(11, 14); this.labelX5.Name = "labelX5"; - this.labelX5.Size = new System.Drawing.Size(44, 16); + this.labelX5.Size = new System.Drawing.Size(38, 18); this.labelX5.TabIndex = 0; this.labelX5.Text = "Color 1"; // @@ -354,7 +366,7 @@ private void InitializeComponent() this.panelExColor.Controls.Add(this.slider1); this.panelExColor.DisabledBackColor = System.Drawing.Color.Empty; this.panelExColor.Enabled = false; - this.panelExColor.Location = new System.Drawing.Point(76, 6); + this.panelExColor.Location = new System.Drawing.Point(100, 14); this.panelExColor.Name = "panelExColor"; this.panelExColor.Size = new System.Drawing.Size(256, 86); this.panelExColor.Style.Alignment = System.Drawing.StringAlignment.Center; @@ -369,34 +381,38 @@ private void InitializeComponent() // rdoColor // this.rdoColor.AutoSize = true; + this.rdoColor.BackColor = System.Drawing.Color.Transparent; // // // this.rdoColor.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.rdoColor.CheckBoxStyle = DevComponents.DotNetBar.eCheckBoxStyle.RadioButton; - this.rdoColor.Location = new System.Drawing.Point(12, 51); + this.rdoColor.Location = new System.Drawing.Point(16, 59); this.rdoColor.Name = "rdoColor"; - this.rdoColor.Size = new System.Drawing.Size(57, 16); + this.rdoColor.Size = new System.Drawing.Size(51, 18); this.rdoColor.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; - this.superTooltip1.SetSuperTooltip(this.rdoColor, new DevComponents.DotNetBar.SuperTooltipInfo("", "", "Adds transparency or a solid color to a GIF.", null, null, DevComponents.DotNetBar.eTooltipColor.System)); + superTooltipInfo3.BodyText = "Adds transparency or a solid color to a GIF."; + superTooltipInfo3.Color = DevComponents.DotNetBar.eTooltipColor.System; + this.superTooltip1.SetSuperTooltip(this.rdoColor, superTooltipInfo3); this.rdoColor.TabIndex = 1; this.rdoColor.Text = "Color"; this.rdoColor.CheckedChanged += new System.EventHandler(this.rdoColor_CheckedChanged); // // superTooltip1 // - this.superTooltip1.DefaultTooltipSettings = new DevComponents.DotNetBar.SuperTooltipInfo("", "", "", null, null, DevComponents.DotNetBar.eTooltipColor.Gray); + this.superTooltip1.DefaultTooltipSettings = superTooltipInfo1; // // checkBoxX2 // this.checkBoxX2.AutoSize = true; + this.checkBoxX2.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX2.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; - this.checkBoxX2.Location = new System.Drawing.Point(12, 223); + this.checkBoxX2.Location = new System.Drawing.Point(16, 231); this.checkBoxX2.Name = "checkBoxX2"; - this.checkBoxX2.Size = new System.Drawing.Size(144, 16); + this.checkBoxX2.Size = new System.Drawing.Size(164, 18); this.checkBoxX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; this.checkBoxX2.TabIndex = 8; this.checkBoxX2.Text = "Save each frame as PNG"; @@ -404,13 +420,14 @@ private void InitializeComponent() // labelX8 // this.labelX8.AutoSize = true; + this.labelX8.BackColor = System.Drawing.Color.Transparent; // // // this.labelX8.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; - this.labelX8.Location = new System.Drawing.Point(12, 248); + this.labelX8.Location = new System.Drawing.Point(16, 256); this.labelX8.Name = "labelX8"; - this.labelX8.Size = new System.Drawing.Size(37, 16); + this.labelX8.Size = new System.Drawing.Size(44, 18); this.labelX8.TabIndex = 8; this.labelX8.Text = "Delay"; // @@ -423,25 +440,25 @@ private void InitializeComponent() this.integerInput1.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; this.integerInput1.ButtonFreeText.Shortcut = DevComponents.DotNetBar.eShortcut.F2; this.integerInput1.Increment = 10; - this.integerInput1.Location = new System.Drawing.Point(76, 245); + this.integerInput1.Location = new System.Drawing.Point(100, 253); this.integerInput1.MaxValue = 1000; this.integerInput1.MinValue = 10; this.integerInput1.Name = "integerInput1"; this.integerInput1.ShowUpDown = true; - this.integerInput1.Size = new System.Drawing.Size(80, 21); this.integerInput1.TabIndex = 9; this.integerInput1.Value = 10; // // labelX9 // this.labelX9.AutoSize = true; + this.labelX9.BackColor = System.Drawing.Color.Transparent; // // // this.labelX9.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; - this.labelX9.Location = new System.Drawing.Point(12, 174); + this.labelX9.Location = new System.Drawing.Point(16, 182); this.labelX9.Name = "labelX9"; - this.labelX9.Size = new System.Drawing.Size(50, 16); + this.labelX9.Size = new System.Drawing.Size(44, 18); this.labelX9.TabIndex = 16; this.labelX9.Text = "Encoder"; // @@ -450,13 +467,14 @@ private void InitializeComponent() this.comboBoxEx2.DisplayMember = "Text"; this.comboBoxEx2.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.comboBoxEx2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxEx2.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.comboBoxEx2.FormattingEnabled = true; this.comboBoxEx2.ItemHeight = 15; this.comboBoxEx2.Items.AddRange(new object[] { this.comboItem3, this.comboItem4, this.comboItem5}); - this.comboBoxEx2.Location = new System.Drawing.Point(76, 197); + this.comboBoxEx2.Location = new System.Drawing.Point(100, 205); this.comboBoxEx2.Name = "comboBoxEx2"; this.comboBoxEx2.Size = new System.Drawing.Size(129, 21); this.comboBoxEx2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; @@ -474,39 +492,295 @@ private void InitializeComponent() // this.comboItem5.Text = "WZ Path"; // + // superTabControl1 + // + // + // + // + // + // + // + this.superTabControl1.ControlBox.CloseBox.Name = ""; + // + // + // + this.superTabControl1.ControlBox.MenuBox.Name = ""; + this.superTabControl1.ControlBox.Name = ""; + this.superTabControl1.ControlBox.SubItems.AddRange(new DevComponents.DotNetBar.BaseItem[] { + this.superTabControl1.ControlBox.MenuBox, + this.superTabControl1.ControlBox.CloseBox}); + this.superTabControl1.Controls.Add(this.superTabControlPanel1); + this.superTabControl1.Controls.Add(this.superTabControlPanel3); + this.superTabControl1.Controls.Add(this.superTabControlPanel2); + this.superTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.superTabControl1.Location = new System.Drawing.Point(0, 0); + this.superTabControl1.Name = "superTabControl1"; + this.superTabControl1.ReorderTabsEnabled = true; + this.superTabControl1.SelectedTabFont = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Bold); + this.superTabControl1.SelectedTabIndex = 0; + this.superTabControl1.Size = new System.Drawing.Size(511, 311); + this.superTabControl1.TabAlignment = DevComponents.DotNetBar.eTabStripAlignment.Left; + this.superTabControl1.TabFont = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129))); + this.superTabControl1.TabIndex = 30; + this.superTabControl1.Tabs.AddRange(new DevComponents.DotNetBar.BaseItem[] { + this.superTabItem1, + this.superTabItem2, + this.superTabItem3}); + this.superTabControl1.Text = "superTabControl1"; + // + // superTabControlPanel3 + // + this.superTabControlPanel3.Controls.Add(this.labelX13); + this.superTabControlPanel3.Controls.Add(this.textBoxX3); + this.superTabControlPanel3.Controls.Add(this.labelX12); + this.superTabControlPanel3.Controls.Add(this.buttonX3); + this.superTabControlPanel3.Controls.Add(this.textBoxX2); + this.superTabControlPanel3.Controls.Add(this.labelX11); + this.superTabControlPanel3.Controls.Add(this.textBoxX1); + this.superTabControlPanel3.Controls.Add(this.labelX10); + this.superTabControlPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.superTabControlPanel3.Location = new System.Drawing.Point(124, 0); + this.superTabControlPanel3.Name = "superTabControlPanel3"; + this.superTabControlPanel3.Size = new System.Drawing.Size(387, 311); + this.superTabControlPanel3.TabIndex = 0; + this.superTabControlPanel3.TabItem = this.superTabItem3; + this.superTabControlPanel3.Visible = false; + // + // labelX13 + // + this.labelX13.AutoSize = true; + this.labelX13.BackColor = System.Drawing.Color.Transparent; + // + // + // + this.labelX13.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.labelX13.ForeColor = System.Drawing.SystemColors.ControlDarkDark; + this.labelX13.Location = new System.Drawing.Point(16, 207); + this.labelX13.Name = "labelX13"; + this.labelX13.TabIndex = 18; + this.labelX13.Text = "Argument Pattern:
\r\n  %i Input File Name
\r\n  %o Output File Name
\r\n  %w Input Image Width(px)
\r\n  %h Output Image Height(px)
\r\n  %t Delay(ms)"; + // + // textBoxX3 + // + this.textBoxX3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + // + // + // + this.textBoxX3.Border.Class = "TextBoxBorder"; + this.textBoxX3.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.textBoxX3.Location = new System.Drawing.Point(129, 180); + this.textBoxX3.Name = "textBoxX3"; + this.textBoxX3.PreventEnterBeep = true; + this.textBoxX3.Size = new System.Drawing.Size(75, 21); + this.textBoxX3.TabIndex = 17; + // + // labelX12 + // + this.labelX12.AutoSize = true; + this.labelX12.BackColor = System.Drawing.Color.Transparent; + // + // + // + this.labelX12.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.labelX12.Location = new System.Drawing.Point(16, 184); + this.labelX12.Name = "labelX12"; + this.labelX12.TabIndex = 16; + this.labelX12.Text = "File Extension"; + // + // buttonX3 + // + this.buttonX3.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; + this.buttonX3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonX3.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; + this.buttonX3.Location = new System.Drawing.Point(335, 14); + this.buttonX3.Name = "buttonX3"; + this.buttonX3.Size = new System.Drawing.Size(24, 24); + this.buttonX3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; + this.buttonX3.Symbol = ""; + this.buttonX3.SymbolSize = 12F; + this.buttonX3.TabIndex = 15; + this.buttonX3.Click += new System.EventHandler(this.buttonX3_Click); + // + // textBoxX2 + // + this.textBoxX2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + // + // + // + this.textBoxX2.Border.Class = "TextBoxBorder"; + this.textBoxX2.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.textBoxX2.Location = new System.Drawing.Point(129, 46); + this.textBoxX2.Multiline = true; + this.textBoxX2.Name = "textBoxX2"; + this.textBoxX2.PreventEnterBeep = true; + this.textBoxX2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBoxX2.Size = new System.Drawing.Size(201, 124); + this.textBoxX2.TabIndex = 14; + // + // labelX11 + // + this.labelX11.AutoSize = true; + this.labelX11.BackColor = System.Drawing.Color.Transparent; + // + // + // + this.labelX11.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.labelX11.Location = new System.Drawing.Point(16, 50); + this.labelX11.Name = "labelX11"; + this.labelX11.TabIndex = 13; + this.labelX11.Text = "Use FFmpeg"; + // + // textBoxX1 + // + this.textBoxX1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + // + // + // + this.textBoxX1.Border.Class = "TextBoxBorder"; + this.textBoxX1.Border.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.textBoxX1.Location = new System.Drawing.Point(129, 16); + this.textBoxX1.Name = "textBoxX1"; + this.textBoxX1.PreventEnterBeep = true; + this.textBoxX1.Size = new System.Drawing.Size(201, 21); + this.textBoxX1.TabIndex = 12; + // + // labelX10 + // + this.labelX10.AutoSize = true; + this.labelX10.BackColor = System.Drawing.Color.Transparent; + // + // + // + this.labelX10.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; + this.labelX10.Location = new System.Drawing.Point(16, 20); + this.labelX10.Name = "labelX10"; + this.labelX10.TabIndex = 10; + this.labelX10.Text = "FFmpeg Path"; + // + // superTabItem3 + // + this.superTabItem3.AttachedControl = this.superTabControlPanel3; + this.superTabItem3.GlobalItem = false; + this.superTabItem3.Name = "superTabItem3"; + this.superTabItem3.Text = "FFmpegEncoder"; + // + // superTabControlPanel1 + // + this.superTabControlPanel1.Controls.Add(this.comboBoxEx2); + this.superTabControlPanel1.Controls.Add(this.labelX9); + this.superTabControlPanel1.Controls.Add(this.labelX2); + this.superTabControlPanel1.Controls.Add(this.integerInput1); + this.superTabControlPanel1.Controls.Add(this.comboBoxEx1); + this.superTabControlPanel1.Controls.Add(this.labelX8); + this.superTabControlPanel1.Controls.Add(this.rdoMosaic); + this.superTabControlPanel1.Controls.Add(this.checkBoxX2); + this.superTabControlPanel1.Controls.Add(this.panelExColor); + this.superTabControlPanel1.Controls.Add(this.rdoColor); + this.superTabControlPanel1.Controls.Add(this.panelExMosaic); + this.superTabControlPanel1.Controls.Add(this.labelX4); + this.superTabControlPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.superTabControlPanel1.Location = new System.Drawing.Point(124, 0); + this.superTabControlPanel1.Name = "superTabControlPanel1"; + this.superTabControlPanel1.Size = new System.Drawing.Size(387, 311); + this.superTabControlPanel1.TabIndex = 1; + this.superTabControlPanel1.TabItem = this.superTabItem1; + // + // superTabItem1 + // + this.superTabItem1.AttachedControl = this.superTabControlPanel1; + this.superTabItem1.GlobalItem = false; + this.superTabItem1.Name = "superTabItem1"; + this.superTabItem1.Text = "Common"; + // + // superTabControlPanel2 + // + this.superTabControlPanel2.Controls.Add(this.checkBoxX3); + this.superTabControlPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.superTabControlPanel2.Location = new System.Drawing.Point(124, 0); + this.superTabControlPanel2.Name = "superTabControlPanel2"; + this.superTabControlPanel2.Size = new System.Drawing.Size(387, 311); + this.superTabControlPanel2.TabIndex = 0; + this.superTabControlPanel2.TabItem = this.superTabItem2; + this.superTabControlPanel2.Visible = false; + // // checkBoxX3 // this.checkBoxX3.AutoSize = true; + this.checkBoxX3.BackColor = System.Drawing.Color.Transparent; // // // this.checkBoxX3.BackgroundStyle.CornerType = DevComponents.DotNetBar.eCornerType.Square; - this.checkBoxX3.Location = new System.Drawing.Point(212, 174); + this.checkBoxX3.Location = new System.Drawing.Point(16, 20); this.checkBoxX3.Name = "checkBoxX3"; - this.checkBoxX3.Size = new System.Drawing.Size(76, 16); + this.checkBoxX3.Size = new System.Drawing.Size(168, 18); this.checkBoxX3.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; - this.checkBoxX3.TabIndex = 23; + this.checkBoxX3.TabIndex = 24; this.checkBoxX3.Text = "Optimize (APNG)"; // + // superTabItem2 + // + this.superTabItem2.AttachedControl = this.superTabControlPanel2; + this.superTabItem2.GlobalItem = false; + this.superTabItem2.Name = "superTabItem2"; + this.superTabItem2.Text = "APngEncoder"; + // + // panelEx1 + // + this.panelEx1.CanvasColor = System.Drawing.SystemColors.Control; + this.panelEx1.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; + this.panelEx1.Controls.Add(this.buttonX2); + this.panelEx1.Controls.Add(this.buttonX1); + this.panelEx1.DisabledBackColor = System.Drawing.Color.Empty; + this.panelEx1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panelEx1.Location = new System.Drawing.Point(0, 311); + this.panelEx1.Name = "panelEx1"; + this.panelEx1.Size = new System.Drawing.Size(511, 30); + this.panelEx1.Style.Alignment = System.Drawing.StringAlignment.Center; + this.panelEx1.Style.BackColor1.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground; + this.panelEx1.Style.BackColor2.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBackground2; + this.panelEx1.Style.Border = DevComponents.DotNetBar.eBorderType.SingleLine; + this.panelEx1.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder; + this.panelEx1.Style.ForeColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelText; + this.panelEx1.Style.GradientAngle = 90; + this.panelEx1.TabIndex = 38; + // + // buttonX2 + // + this.buttonX2.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; + this.buttonX2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonX2.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; + this.buttonX2.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonX2.Location = new System.Drawing.Point(442, 4); + this.buttonX2.Name = "buttonX2"; + this.buttonX2.Size = new System.Drawing.Size(60, 23); + this.buttonX2.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; + this.buttonX2.TabIndex = 1; + this.buttonX2.Text = "Cancel"; + // + // buttonX1 + // + this.buttonX1.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton; + this.buttonX1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonX1.ColorTable = DevComponents.DotNetBar.eButtonColor.OrangeWithBackground; + this.buttonX1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonX1.Location = new System.Drawing.Point(375, 4); + this.buttonX1.Name = "buttonX1"; + this.buttonX1.Size = new System.Drawing.Size(60, 23); + this.buttonX1.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled; + this.buttonX1.TabIndex = 0; + this.buttonX1.Text = "Confirm"; + // // FrmGifSetting // this.CancelButton = this.buttonX2; - this.ClientSize = new System.Drawing.Size(344, 311); - this.Controls.Add(this.checkBoxX3); - this.Controls.Add(this.comboBoxEx2); - this.Controls.Add(this.labelX9); - this.Controls.Add(this.integerInput1); - this.Controls.Add(this.labelX8); - this.Controls.Add(this.checkBoxX2); - this.Controls.Add(this.rdoColor); - this.Controls.Add(this.panelExColor); - this.Controls.Add(this.panelExMosaic); - this.Controls.Add(this.rdoMosaic); - this.Controls.Add(this.labelX4); - this.Controls.Add(this.comboBoxEx1); - this.Controls.Add(this.labelX2); - this.Controls.Add(this.buttonX2); - this.Controls.Add(this.buttonX1); + this.ClientSize = new System.Drawing.Size(510, 341); + this.Controls.Add(this.superTabControl1); + this.Controls.Add(this.panelEx1); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; @@ -519,8 +793,16 @@ private void InitializeComponent() this.panelExColor.ResumeLayout(false); this.panelExColor.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.integerInput1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.superTabControl1)).EndInit(); + this.superTabControl1.ResumeLayout(false); + this.superTabControlPanel3.ResumeLayout(false); + this.superTabControlPanel3.PerformLayout(); + this.superTabControlPanel1.ResumeLayout(false); + this.superTabControlPanel1.PerformLayout(); + this.superTabControlPanel2.ResumeLayout(false); + this.superTabControlPanel2.PerformLayout(); + this.panelEx1.ResumeLayout(false); this.ResumeLayout(false); - this.PerformLayout(); } @@ -529,8 +811,6 @@ private void InitializeComponent() private DevComponents.DotNetBar.ColorPickerButton colorPickerButton1; private DevComponents.DotNetBar.LabelX labelX1; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX1; - private DevComponents.DotNetBar.ButtonX buttonX1; - private DevComponents.DotNetBar.ButtonX buttonX2; private DevComponents.DotNetBar.LabelX labelX2; private DevComponents.DotNetBar.Controls.ComboBoxEx comboBoxEx1; private DevComponents.Editors.ComboItem comboItem1; @@ -558,6 +838,25 @@ private void InitializeComponent() private DevComponents.Editors.ComboItem comboItem4; private DevComponents.Editors.ComboItem comboItem5; private DevComponents.Editors.ComboItem comboItem6; + private DevComponents.DotNetBar.SuperTabControl superTabControl1; + private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel1; + private DevComponents.DotNetBar.SuperTabItem superTabItem1; + private DevComponents.DotNetBar.PanelEx panelEx1; + private DevComponents.DotNetBar.ButtonX buttonX2; + private DevComponents.DotNetBar.ButtonX buttonX1; + private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel2; + private DevComponents.DotNetBar.SuperTabItem superTabItem2; + private DevComponents.DotNetBar.SuperTabControlPanel superTabControlPanel3; + private DevComponents.DotNetBar.SuperTabItem superTabItem3; private DevComponents.DotNetBar.Controls.CheckBoxX checkBoxX3; + private DevComponents.DotNetBar.LabelX labelX10; + private DevComponents.DotNetBar.ButtonX buttonX3; + private DevComponents.DotNetBar.Controls.TextBoxX textBoxX2; + private DevComponents.DotNetBar.LabelX labelX11; + private DevComponents.DotNetBar.Controls.TextBoxX textBoxX1; + private DevComponents.Editors.ComboItem comboItem7; + private DevComponents.DotNetBar.LabelX labelX12; + private DevComponents.DotNetBar.Controls.TextBoxX textBoxX3; + private DevComponents.DotNetBar.LabelX labelX13; } } \ No newline at end of file diff --git a/WzComparerR2/FrmGifSetting.cs b/WzComparerR2/FrmGifSetting.cs index c9965573..b39474f1 100644 --- a/WzComparerR2/FrmGifSetting.cs +++ b/WzComparerR2/FrmGifSetting.cs @@ -130,6 +130,39 @@ public bool PaletteOptimized set { checkBoxX3.Checked = value; } } + public string FFmpegBinPath + { + get { return textBoxX1.Text; } + set { textBoxX1.Text = value; } + } + + public string FFmpegArgument + { + get { return textBoxX2.Text; } + set { textBoxX2.Text = value; } + } + + public string FFmpegDefaultExtension + { + get { return textBoxX3.Text; } + set { textBoxX3.Text = value; } + } + + public string FFmpegBinPathHint + { + set { textBoxX1.WatermarkText = value; } + } + + public string FFmpegArgumentHint + { + set { textBoxX2.WatermarkText = value; } + } + + public string FFmpegDefaultExtensionHint + { + set { textBoxX3.WatermarkText = value; } + } + public void Load(ImageHandlerConfig config) { this.SavePngFramesEnabled = config.SavePngFramesEnabled; @@ -145,6 +178,10 @@ public void Load(ImageHandlerConfig config) this.MosaicBlockSize = config.MosaicInfo.BlockSize; this.PaletteOptimized = config.PaletteOptimized; + + this.FFmpegBinPath = config.FFmpegBinPath; + this.FFmpegArgument = config.FFmpegArgument; + this.FFmpegDefaultExtension = config.FFmpegOutputFileExtension; } public void Save(ImageHandlerConfig config) @@ -162,18 +199,10 @@ public void Save(ImageHandlerConfig config) config.MosaicInfo.BlockSize = this.MosaicBlockSize; config.PaletteOptimized = this.PaletteOptimized; - } - - private void buttonX1_Click(object sender, EventArgs e) - { - this.DialogResult = DialogResult.OK; - this.Close(); - } - private void buttonX2_Click(object sender, EventArgs e) - { - this.DialogResult = DialogResult.Cancel; - this.Close(); + config.FFmpegBinPath = this.FFmpegBinPath; + config.FFmpegArgument = this.FFmpegArgument; + config.FFmpegOutputFileExtension = this.FFmpegDefaultExtension; } private void slider1_ValueChanged(object sender, EventArgs e) @@ -192,5 +221,16 @@ private void rdoMosaic_CheckedChanged(object sender, EventArgs e) panelExMosaic.Enabled = rdoMosaic.Checked; } + private void buttonX3_Click(object sender, EventArgs e) + { + OpenFileDialog dlg = new(); + dlg.Title = "FFmpeg.exe: Select File Path"; + dlg.Filter = "ffmpeg.exe|*.exe|*.*|*.*"; + dlg.FileName = this.FFmpegBinPath; + if (dlg.ShowDialog(this) == DialogResult.OK) + { + this.FFmpegBinPath = dlg.FileName; + } + } } } \ No newline at end of file diff --git a/WzComparerR2/FrmGifSetting.resx b/WzComparerR2/FrmGifSetting.resx index 48ecb57e..fc1199f5 100644 --- a/WzComparerR2/FrmGifSetting.resx +++ b/WzComparerR2/FrmGifSetting.resx @@ -117,29 +117,152 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 - JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAKklEQVQ4T2P4//8/RRhCAClyMIoBIJoU - PGrAqAEgPBwNIAfDDSAf/2cAALEslYfUgx+eAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACqSURBVDhPrZDLDYQwFMQoiiMHSqAUOqUUSngrxwk8oSir + /VjKZRgPhOlfxJfnIvZ9j/M84x106OKoShzHEeu6Dkd4RocujqqUwmgky4CjKiWE3shTBhxVqbHkkZ4M + OKpS4xuEZVnKecqAoyo1vuHN8zyXk6/TwFGVGkv+7HydDI6q1Lj/w3ojOKpSwp7ceI7gqMpQbuQRHFWJ + bduGcoMOXRxVacGn51em6QU0qy4TqeJTawAAAABJRU5ErkJggg== + + 1047, 54 + + + 1140, 54 + + + 1259, 54 + + + 1352, 54 + + + 1483, 54 + + + 17, 91 + + + 106, 91 + + + 199, 91 + - 17, 17 + 1422, 91 + + + 914, 54 + + + 413, 91 + + + 506, 91 + + + 599, 91 + + + 692, 91 + + + 781, 91 - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 - JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAKklEQVQ4T2P4//8/RRhCAClyMIoBIJoU - PGrAqAEgPBwNIAfDDSAf/2cAALEslYfUgx+eAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACqSURBVDhPrZDLDYQwFMQoiiMHSqAUOqUUSngrxwk8oSir + /VjKZRgPhOlfxJfnIvZ9j/M84x106OKoShzHEeu6Dkd4RocujqqUwmgky4CjKiWE3shTBhxVqbHkkZ4M + OKpS4xuEZVnKecqAoyo1vuHN8zyXk6/TwFGVGkv+7HydDI6q1Lj/w3ojOKpSwp7ceI7gqMpQbuQRHFWJ + bduGcoMOXRxVacGn51em6QU0qy4TqeJTawAAAABJRU5ErkJggg== + + 937, 91 + - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 - JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAKklEQVQ4T2P4//8/RRhCAClyMIoBIJoU - PGrAqAEgPBwNIAfDDSAf/2cAALEslYfUgx+eAAAAAElFTkSuQmCC + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACqSURBVDhPrZDLDYQwFMQoiiMHSqAUOqUUSngrxwk8oSir + /VjKZRgPhOlfxJfnIvZ9j/M84x106OKoShzHEeu6Dkd4RocujqqUwmgky4CjKiWE3shTBhxVqbHkkZ4M + OKpS4xuEZVnKecqAoyo1vuHN8zyXk6/TwFGVGkv+7HydDI6q1Lj/w3ojOKpSwp7ceI7gqMpQbuQRHFWJ + bduGcoMOXRxVacGn51em6QU0qy4TqeJTawAAAABJRU5ErkJggg== + + 1093, 91 + + + 1217, 91 + + + 795, 54 + + + 702, 54 + + + 1032, 17 + + + 173, 17 + + + 266, 17 + + + 397, 17 + + + 17, 54 + + + 195, 54 + + + 373, 54 + + + 546, 17 + + + 724, 17 + + + 824, 17 + + + 932, 17 + + + 1158, 17 + + + 594, 54 + + + 1262, 17 + + + 1362, 17 + + + 1470, 17 + + + 492, 54 + + + 1318, 91 + + + 309, 91 + \ No newline at end of file diff --git a/WzComparerR2/MainForm.cs b/WzComparerR2/MainForm.cs index 8a374bcb..bc1dcbff 100644 --- a/WzComparerR2/MainForm.cs +++ b/WzComparerR2/MainForm.cs @@ -2,31 +2,32 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; -using System.Drawing; using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; -using System.Linq; -using System.IO; using System.Xml; +using static Microsoft.Xna.Framework.MathHelper; using Timer = System.Timers.Timer; -using System.Threading; -using System.Threading.Tasks; +using DevComponents.AdvTree; using DevComponents.DotNetBar; using DevComponents.DotNetBar.Controls; -using DevComponents.AdvTree; -using WzComparerR2.WzLib; -using WzComparerR2.Common; -using WzComparerR2.CharaSimControl; -using WzComparerR2.PluginBase; + +using WzComparerR2.Animation; using WzComparerR2.CharaSim; +using WzComparerR2.CharaSimControl; +using WzComparerR2.Common; using WzComparerR2.Comparer; -using WzComparerR2.Controls; -using WzComparerR2.Rendering; using WzComparerR2.Config; -using WzComparerR2.Animation; -using static Microsoft.Xna.Framework.MathHelper; +using WzComparerR2.Controls; +using WzComparerR2.Encoders; +using WzComparerR2.PluginBase; +using WzComparerR2.WzLib; namespace WzComparerR2 { @@ -710,6 +711,9 @@ private void buttonItemGifSetting_Click(object sender, EventArgs e) { FrmGifSetting frm = new FrmGifSetting(); frm.Load(ImageHandlerConfig.Default); + frm.FFmpegBinPathHint = FFmpegEncoder.DefaultExecutionFileName; + frm.FFmpegArgumentHint = FFmpegEncoder.DefaultArgumentFormat; + frm.FFmpegDefaultExtensionHint = FFmpegEncoder.DefaultOutputFileExtension; if (frm.ShowDialog() == DialogResult.OK) { ConfigManager.Reload(); @@ -857,12 +861,13 @@ private void OnSavePngFile(Frame frame) private void OnSaveGifFile(AnimationItem aniItem, bool options) { var config = ImageHandlerConfig.Default; - var encParams = AnimateEncoderFactory.GetEncoderParams(config.GifEncoder.Value); + using var encoder = AnimateEncoderFactory.CreateEncoder(config); + var cap = encoder.Compatibility; string aniName = this.cmbItemAniNames.SelectedItem as string; string aniFileName = pictureBoxEx1.PictureName + (string.IsNullOrEmpty(aniName) ? "" : ("." + aniName)) - + encParams.FileExtension; + + cap.DefaultExtension; if (config.AutoSaveEnabled) { @@ -879,8 +884,8 @@ private void OnSaveGifFile(AnimationItem aniItem, bool options) else { var dlg = new SaveFileDialog(); - - dlg.Filter = string.Format("{0} (*{1})|*{1}|All Files (*.*)|*.*", encParams.FileDescription, encParams.FileExtension); + string extensionFilter = string.Join(";", cap.SupportedExtensions.Select(ext => $"*{ext}")); + dlg.Filter = string.Format("{0} (*{1})|*{1}|All Files (*.*)|*.*", encoder.Name, extensionFilter); dlg.FileName = aniFileName; if (dlg.ShowDialog() != DialogResult.OK) @@ -891,7 +896,7 @@ private void OnSaveGifFile(AnimationItem aniItem, bool options) } var clonedAniItem = (AnimationItem)aniItem.Clone(); - if (this.pictureBoxEx1.SaveAsGif(clonedAniItem, aniFileName, config, options)) + if (this.pictureBoxEx1.SaveAsGif(clonedAniItem, aniFileName, config, encoder, options)) { labelItemStatus.Text = "Image saved: " + aniFileName; } diff --git a/WzComparerR2/PictureBoxEx.cs b/WzComparerR2/PictureBoxEx.cs index 32c4824c..d184c4b5 100644 --- a/WzComparerR2/PictureBoxEx.cs +++ b/WzComparerR2/PictureBoxEx.cs @@ -1,20 +1,21 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.IO; +using System.Linq; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Xna.Framework; -using WzComparerR2.WzLib; -using WzComparerR2.Controls; using WzComparerR2.Animation; -using WzComparerR2.Rendering; -using WzComparerR2.Config; using WzComparerR2.Common; +using WzComparerR2.Config; +using WzComparerR2.Controls; +using WzComparerR2.Encoders; +using WzComparerR2.Rendering; +using WzComparerR2.WzLib; namespace WzComparerR2 { @@ -298,14 +299,19 @@ public void AdjustPosition() } } - public bool SaveAsGif(AnimationItem aniItem, string fileName, ImageHandlerConfig config, bool options) + public bool SaveAsGif(AnimationItem aniItem, string fileName, ImageHandlerConfig config, GifEncoder encoder, bool showOptions) { var rec = new AnimationRecoder(this.GraphicsDevice); + var cap = encoder.Compatibility; rec.Items.Add(aniItem); int length = rec.GetMaxLength(); - int delay = Math.Max(10, config.MinDelay); - var timeline = rec.GetGifTimeLine(delay, 655350); + int delay = Math.Max(cap.MinFrameDelay, config.MinDelay); + int[] timeline = null; + if (!cap.IsFixedFrameRate) + { + timeline = rec.GetGifTimeLine(delay, cap.MaxFrameDelay); + } // calc available canvas area rec.ResetAll(); @@ -337,7 +343,7 @@ public bool SaveAsGif(AnimationItem aniItem, string fileName, ImageHandlerConfig OutputHeight = bounds.Height, }; - if (options) + if (showOptions) { var frmOptions = new FrmGifClipOptions() { @@ -413,8 +419,7 @@ public bool SaveAsGif(AnimationItem aniItem, string fileName, ImageHandlerConfig } // select encoder - GifEncoder enc = AnimateEncoderFactory.CreateEncoder(fileName, targetSize.X, targetSize.Y, config); - var encParams = AnimateEncoderFactory.GetEncoderParams(config.GifEncoder.Value); + encoder.Init(fileName, targetSize.X, targetSize.Y); // pipeline functions IEnumerable> MergeFrames(IEnumerable> frames) @@ -432,7 +437,7 @@ IEnumerable> MergeFrames(IEnumerable> fram prevFrame = currentFrame; prevDelay = currentDelay; } - else if (memcmp(prevFrame, currentFrame, (IntPtr)prevFrame.Length) == 0) + else if (prevFrame.AsSpan().SequenceEqual(currentFrame.AsSpan())) { prevDelay += currentDelay; } @@ -499,7 +504,7 @@ IEnumerable ClipTimeline(int[] _timeline) async Task ApplyFrame(byte[] frameData, int frameDelay) { byte[] gifData = null; - if (!encParams.SupportAlphaChannel && config.BackgroundType.Value == ImageBackgroundType.Transparent) + if (cap.AlphaSupportMode != AlphaSupportMode.FullAlpha && config.BackgroundType.Value == ImageBackgroundType.Transparent) { using (var rt2 = rec.GetGifTexture(config.BackgroundColor.Value.ToXnaColor(), config.MinMixedAlpha)) { @@ -523,16 +528,18 @@ async Task ApplyFrame(byte[] frameData, int frameDelay) tasks.Add(Task.Run(() => { string pngFileName = Path.Combine(framesDirName, $"{prevTime}_{prevTime + frameDelay}.png"); - unsafe + GCHandle gcHandle = GCHandle.Alloc(frameData, GCHandleType.Pinned); + try { - fixed (byte* pFrameBuffer = frameData) + using (var bmp = new System.Drawing.Bitmap(targetSize.X, targetSize.Y, targetSize.X * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, gcHandle.AddrOfPinnedObject())) { - using (var bmp = new System.Drawing.Bitmap(targetSize.X, targetSize.Y, targetSize.X * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, new IntPtr(pFrameBuffer))) - { - bmp.Save(pngFileName, System.Drawing.Imaging.ImageFormat.Png); - } + bmp.Save(pngFileName, System.Drawing.Imaging.ImageFormat.Png); } } + finally + { + gcHandle.Free(); + } })); } @@ -541,12 +548,15 @@ async Task ApplyFrame(byte[] frameData, int frameDelay) { // TODO: only for gif here? frameDelay = Math.Max(10, (int)(Math.Round(frameDelay / 10.0) * 10)); - unsafe + + GCHandle gcHandle = GCHandle.Alloc(frameData, GCHandleType.Pinned); + try { - fixed (byte* pGifBuffer = gifData) - { - enc.AppendFrame(new IntPtr(pGifBuffer), frameDelay); - } + encoder.AppendFrame(gcHandle.AddrOfPinnedObject(), frameDelay); + } + finally + { + gcHandle.Free(); } })); @@ -557,7 +567,7 @@ async Task ApplyFrame(byte[] frameData, int frameDelay) async Task RenderJob(IProgressDialogContext context, CancellationToken cancellationToken) { - bool isCompareAndMergeFrames = timeline == null; + bool isCompareAndMergeFrames = timeline == null && !cap.IsFixedFrameRate; // build pipeline IEnumerable delayEnumerator = timeline == null ? RenderDelay() : ClipTimeline(timeline); @@ -607,13 +617,20 @@ async Task RenderJob(IProgressDialogContext context, CancellationToken cancellat } catch (Exception ex) { - context.Message = $"Error: {ex.Message}"; + if (ex is AggregateException aggrEx && aggrEx.InnerExceptions.Count == 1) + { + context.Message = $"Error: {aggrEx.InnerExceptions[0].Message}"; + } + else + { + context.Message = $"Error: {ex.Message}"; + } + context.FullMessage = ex.ToString(); throw; } finally { rec.End(); - enc.Dispose(); this.IsPlaying = isPlaying; } } @@ -705,10 +722,5 @@ private void UpdateInfoText() aniItem.Length); } } - - - [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern int memcmp(byte[] b1, byte[] b2, IntPtr count); - } } \ No newline at end of file