diff --git a/UoFiddler.Controls/Classes/AnimatedFrameListExtensions.cs b/UoFiddler.Controls/Classes/AnimatedFrameListExtensions.cs index bfa01ca1..479a5c8e 100644 --- a/UoFiddler.Controls/Classes/AnimatedFrameListExtensions.cs +++ b/UoFiddler.Controls/Classes/AnimatedFrameListExtensions.cs @@ -25,6 +25,11 @@ public static (Point, Size) GetAnimationDetails(this IEnumerable var drawCenter = new Point(int.MinValue, int.MinValue); foreach (var frame in frames) { + if (frame?.Bitmap == null) + { + continue; + } + drawCenter.X = Math.Max(drawCenter.X, frame.Center.X); drawCenter.Y = Math.Max(drawCenter.Y, frame.Center.Y + frame.Bitmap.Height); } @@ -34,14 +39,27 @@ public static (Point, Size) GetAnimationDetails(this IEnumerable var outputSize = new Size(0, 0); foreach (var frame in frames) { - var location = new Point(drawCenter.X - frame.Center.X, drawCenter.Y - frame.Center.Y - frame.Bitmap.Height); + if (frame?.Bitmap == null) + { + continue; + } + + var location = frame.GetAlignedLocation(drawCenter); outputSize.Width = Math.Max(outputSize.Width, location.X + frame.Bitmap.Width); outputSize.Height = Math.Max(outputSize.Height, location.Y + frame.Bitmap.Height); } - + return (drawCenter, outputSize); } + /// + /// Where to draw so its center lands on . + /// + public static Point GetAlignedLocation(this AnimatedFrame frame, Point drawCenter) + { + return new Point(drawCenter.X - frame.Center.X, drawCenter.Y - frame.Center.Y - frame.Bitmap.Height); + } + public static void ToGif(this IEnumerable frames, string outputFile, bool looping = true, int delay = 150, bool showFrameBounds = false) { var (drawCenter, outputSize) = frames.GetAnimationDetails(); @@ -57,7 +75,7 @@ public static void ToGif(this IEnumerable frames, string outputFi using var target = new Bitmap(outputSize.Width, outputSize.Height); using var g = Graphics.FromImage(target); - var location = new Point(drawCenter.X - frame.Center.X, drawCenter.Y - frame.Center.Y - frame.Bitmap.Height); + var location = frame.GetAlignedLocation(drawCenter); g.DrawImage(frame.Bitmap, location); diff --git a/UoFiddler.Controls/UserControls/AnimationListControl.Designer.cs b/UoFiddler.Controls/UserControls/AnimationListControl.Designer.cs index f921979c..f3cd59f1 100644 --- a/UoFiddler.Controls/UserControls/AnimationListControl.Designer.cs +++ b/UoFiddler.Controls/UserControls/AnimationListControl.Designer.cs @@ -65,6 +65,8 @@ private void InitializeComponent() asPngToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); asAnimatedGifToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); asAnimatedGifnoLoopingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + alignFramesSeparator = new System.Windows.Forms.ToolStripSeparator(); + alignFramesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); listView1 = new ListViewWithScrollbar(); contextMenuStrip3 = new System.Windows.Forms.ContextMenuStrip(components); exportFrameToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -312,7 +314,7 @@ private void InitializeComponent() // // extractAnimationToolStripMenuItem // - extractAnimationToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { asBmpToolStripMenuItem1, asTiffToolStripMenuItem1, asJpgToolStripMenuItem1, asPngToolStripMenuItem1, asAnimatedGifToolStripMenuItem, asAnimatedGifnoLoopingToolStripMenuItem }); + extractAnimationToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { asBmpToolStripMenuItem1, asTiffToolStripMenuItem1, asJpgToolStripMenuItem1, asPngToolStripMenuItem1, asAnimatedGifToolStripMenuItem, asAnimatedGifnoLoopingToolStripMenuItem, alignFramesSeparator, alignFramesToolStripMenuItem }); extractAnimationToolStripMenuItem.Name = "extractAnimationToolStripMenuItem"; extractAnimationToolStripMenuItem.Size = new System.Drawing.Size(173, 22); extractAnimationToolStripMenuItem.Text = "Export Animation.."; @@ -358,6 +360,19 @@ private void InitializeComponent() asAnimatedGifnoLoopingToolStripMenuItem.Size = new System.Drawing.Size(227, 22); asAnimatedGifnoLoopingToolStripMenuItem.Text = "As animated Gif (no looping)"; asAnimatedGifnoLoopingToolStripMenuItem.Click += OnClickExtractAnimGifNoLooping; + // + // alignFramesSeparator + // + alignFramesSeparator.Name = "alignFramesSeparator"; + alignFramesSeparator.Size = new System.Drawing.Size(224, 6); + // + // alignFramesToolStripMenuItem + // + alignFramesToolStripMenuItem.CheckOnClick = true; + alignFramesToolStripMenuItem.Name = "alignFramesToolStripMenuItem"; + alignFramesToolStripMenuItem.Size = new System.Drawing.Size(227, 22); + alignFramesToolStripMenuItem.Text = "Align Frames on One Canvas"; + alignFramesToolStripMenuItem.ToolTipText = "Bmp/Tiff/Jpg/Png: draw every frame on one canvas, anchored at its center like the Gif export, so frames don't jump. Png and Tiff get a transparent background."; // // listView1 // @@ -729,6 +744,8 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem tryToFindNewGraphicsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem asAnimatedGifToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem asAnimatedGifnoLoopingToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator alignFramesSeparator; + private System.Windows.Forms.ToolStripMenuItem alignFramesToolStripMenuItem; private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage2; diff --git a/UoFiddler.Controls/UserControls/AnimationListControl.cs b/UoFiddler.Controls/UserControls/AnimationListControl.cs index 3cc2df5c..fe6ba8f1 100644 --- a/UoFiddler.Controls/UserControls/AnimationListControl.cs +++ b/UoFiddler.Controls/UserControls/AnimationListControl.cs @@ -1174,23 +1174,47 @@ private void ExportAnimationFrames(ImageFormat imageFormat) string fileExtension = Utils.GetFileExtensionFor(imageFormat); string fileName = Path.Combine(Options.OutputPath, $"{what} {Utils.FormatExportId(_currentSelect)}"); - for (int i = 0; i < MainPictureBox.Frames?.Count; ++i) + var frames = MainPictureBox.Frames; + if (frames == null || frames.Count == 0) { - var frameBitmap = MainPictureBox.Frames[i].Bitmap; - using (Bitmap newBitmap = new Bitmap(frameBitmap.Width, frameBitmap.Height)) + return; + } + + // Aligned: every frame on one canvas, anchored at its center like the Gif export + bool align = alignFramesToolStripMenuItem.Checked; + var (drawCenter, canvasSize) = align ? frames.GetAnimationDetails() : (Point.Empty, Size.Empty); + if (align && (canvasSize.Width <= 0 || canvasSize.Height <= 0)) + { + return; + } + + // Bmp and Jpg have no alpha + bool transparent = align && (imageFormat.Equals(ImageFormat.Png) || imageFormat.Equals(ImageFormat.Tiff)); + + for (int i = 0; i < frames.Count; ++i) + { + var frame = frames[i]; + if (frame?.Bitmap == null) + { + continue; + } + + Size size = align ? canvasSize : frame.Bitmap.Size; + Point location = align ? frame.GetAlignedLocation(drawCenter) : Point.Empty; + using (Bitmap newBitmap = new Bitmap(size.Width, size.Height)) { using (Graphics newGraph = Graphics.FromImage(newBitmap)) { - newGraph.FillRectangle(Brushes.White, 0, 0, newBitmap.Width, newBitmap.Height); - newGraph.DrawImage(frameBitmap, new Point(0, 0)); - newGraph.Save(); + newGraph.Clear(transparent ? Color.Transparent : Color.White); + newGraph.DrawImage(frame.Bitmap, location); } newBitmap.Save($"{fileName}-{i}.{fileExtension}", imageFormat); } } - FileSavedDialog.Show(FindForm(), Options.OutputPath, $"Files with following format {fileName}-X.{fileExtension} saved successfully."); + string aligned = align ? $", aligned on a {canvasSize.Width}x{canvasSize.Height} canvas," : string.Empty; + FileSavedDialog.Show(FindForm(), Options.OutputPath, $"Files with following format {fileName}-X.{fileExtension}{aligned} saved successfully."); } private void OnClickExportFrameBmp(object sender, EventArgs e)