diff --git a/Controls/Metadata.xaml b/Controls/Metadata.xaml index df5d61b..8c219fc 100644 --- a/Controls/Metadata.xaml +++ b/Controls/Metadata.xaml @@ -4,34 +4,84 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DolphinDynamicInputTextureCreator.Controls" + xmlns:converters="clr-namespace:DolphinDynamicInputTextureCreator.ValueConverters" mc:Ignorable="d" - Height="220" Width="320"> - + Height="200" Width="320"> + + + + + + - - + + + + + - - - - - - - - - - - - - + diff --git a/DolphinDynamicInputTexture/Data/DynamicInputPack.cs b/DolphinDynamicInputTexture/Data/DynamicInputPack.cs index b02a6e2..63864b4 100644 --- a/DolphinDynamicInputTexture/Data/DynamicInputPack.cs +++ b/DolphinDynamicInputTexture/Data/DynamicInputPack.cs @@ -121,8 +121,8 @@ public string GameID /// export directory public void ExportToLocation(string location) { - WriteJson(Path.Combine(location, GeneratedJsonName + ".json")); WriteImages(location); + WriteJson(Path.Combine(location, GeneratedJsonName + ".json")); WriteGameID(location); } @@ -153,6 +153,7 @@ private void WriteImages(string location) { //exports the images for the output_textures WriteImage(location, texture); + foreach (HostDevice device in texture.HostDevices) { foreach (HostKey key in device.HostKeys) @@ -164,7 +165,7 @@ private void WriteImages(string location) } } - private void WriteImage(string location, Interfaces.IImage image) + private void WriteImage(string location, IImage image) { // Unlikely that we get here but skip textures that don't exist if (!File.Exists(image.TexturePath)) @@ -174,13 +175,29 @@ private void WriteImage(string location, Interfaces.IImage image) image.RelativeTexturePath = CheckRelativeTexturePath(image); string output_location = Path.Combine(location, image.RelativeTexturePath); + //create the directory when it does not exist. + Directory.CreateDirectory(Path.GetDirectoryName(output_location)); + + //Use External Scaling if necessary. + if (image is DynamicInputTexture texture) + { + if (DynamicInputTextureEvents.DynamicInputTextureExportProcessor != null) + { + DynamicInputTextureEvents.DynamicInputTextureExportProcessor.Invoke(output_location, texture); + if (File.Exists(output_location)) + { + texture.TexturePath = output_location; + return; + } + } + } + // Prevents the file from trying to overwrite itself. if (Path.GetFullPath(output_location) == Path.GetFullPath(image.TexturePath)) return; //write the image const bool overwrite = true; - Directory.CreateDirectory(Path.GetDirectoryName(output_location)); File.Copy(image.TexturePath, output_location, overwrite); } diff --git a/DolphinDynamicInputTexture/Data/DynamicInputTexture.cs b/DolphinDynamicInputTexture/Data/DynamicInputTexture.cs index 4e6aed0..3b3804c 100644 --- a/DolphinDynamicInputTexture/Data/DynamicInputTexture.cs +++ b/DolphinDynamicInputTexture/Data/DynamicInputTexture.cs @@ -1,5 +1,6 @@ using DolphinDynamicInputTexture.Interfaces; using Newtonsoft.Json; +using System; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Drawing; @@ -191,14 +192,40 @@ public double ImageHeightScaling #region Update + /// + /// reads the image size and adjusts the regions if necessary. + /// private void UpdateImageWidthHeight() { if (File.Exists(_texture_path)) { using (var bmp = new Bitmap(_texture_path)) { - ImageHeight = bmp.Height; - ImageWidth = bmp.Width; + if (ImageHeight > 0 && ImageWidth > 0) + { + double width_scale = (double)bmp.Width / ImageWidth; + double height_scale = (double)bmp.Height / ImageHeight; + + //When scaling up, the scale must be set directly. + if (width_scale >= 1) ImageWidth = bmp.Width; + if (height_scale >= 1) ImageHeight = bmp.Height; + + foreach (InputRegion region in Regions) + { + region.RegionRect.X *= width_scale; + region.RegionRect.Width *= width_scale; + region.RegionRect.Y *= height_scale; + region.RegionRect.Height *= height_scale; + } + + ImageWidth = bmp.Width; + ImageHeight = bmp.Height; + } + else + { + ImageHeight = bmp.Height; + ImageWidth = bmp.Width; + } } } } diff --git a/DolphinDynamicInputTexture/Data/DynamicInputTextureEvents.cs b/DolphinDynamicInputTexture/Data/DynamicInputTextureEvents.cs index addea0a..ac2d587 100644 --- a/DolphinDynamicInputTexture/Data/DynamicInputTextureEvents.cs +++ b/DolphinDynamicInputTexture/Data/DynamicInputTextureEvents.cs @@ -11,5 +11,18 @@ public static class DynamicInputTextureEvents public delegate bool ImageNotExistAction(Interfaces.IImage image, string details); + /// + /// Allows editing the terxtures during exporting. + /// + /// Path where the new texture should be saved + /// texture that should be scaled + /// + public delegate bool ExternalScalingProcesses(string savepath, DynamicInputTexture dynamicinputtexture); + + /// + /// Allows editing the DynamicInputTextures during exporting. + /// + public static ExternalScalingProcesses DynamicInputTextureExportProcessor { get; set; } + } } diff --git a/ViewModels/Dialogs.cs b/ViewModels/Dialogs.cs index b47fca6..9ae7e38 100644 --- a/ViewModels/Dialogs.cs +++ b/ViewModels/Dialogs.cs @@ -125,6 +125,8 @@ public static bool DialogExportToLocation(in DynamicInputPackViewModel pack) if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { pack.ExportToLocation(dialog.SelectedPath); + // Updating the user interface in case the image file has changed. + pack.Textures.Select(pack.Textures.Selected); return true; } return false; diff --git a/ViewModels/DynamicInputPackViewModel.cs b/ViewModels/DynamicInputPackViewModel.cs index 04b11d7..9a95162 100644 --- a/ViewModels/DynamicInputPackViewModel.cs +++ b/ViewModels/DynamicInputPackViewModel.cs @@ -118,7 +118,28 @@ public InputRegion SelectedRegion } private InputRegion _selected_region; + /// + /// Options used to rescale textures during export. + /// + public ExportTextureScalingViewModel ExportTextureScaling + { + get + { + if (_export_texture_scaling == null) + { + _export_texture_scaling = new ExportTextureScalingViewModel(); + if (Textures.ValidSelection) + { + ExportTextureScaling.SelectedScalingFactor = (int)Textures.Selected.ImageWidthScaling; + } + } + return _export_texture_scaling; + } + } + public ExportTextureScalingViewModel _export_texture_scaling; + #endregion + #region Commands #region SelectedTexture diff --git a/ViewModels/ExportTextureScalingViewModel.cs b/ViewModels/ExportTextureScalingViewModel.cs new file mode 100644 index 0000000..967a007 --- /dev/null +++ b/ViewModels/ExportTextureScalingViewModel.cs @@ -0,0 +1,120 @@ +using DolphinDynamicInputTexture.Data; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace DolphinDynamicInputTextureCreator.ViewModels +{ + public class ExportTextureScalingViewModel : Other.PropertyChangedBase + { + /// + /// Possible texture scaling modes. + /// + internal enum Modes + { + None, NearestNeighbor, Bicubic, Bilinear + } + + /// + /// The currently selected export texture scaling mode. + /// + internal Modes SelectedScalingMode + { + get => _selected_scaling_mode; + set + { + _selected_scaling_mode = value; + SetExportTextureScaling(); + OnPropertyChanged(nameof(SelectedScalingMode)); + } + } + private Modes _selected_scaling_mode = Modes.None; + + /// + /// Scaling factor that the exported image should have. + /// + public int SelectedScalingFactor { get; set; } = 4; + + #region UI Helper + + public string[] ScalingModesHelper + { + get => Enum.GetNames(typeof(Modes)); + } + + public string SelectedScalingModeHelper + { + get => SelectedScalingMode.ToString(); + set + { + SelectedScalingMode = Enum.Parse(value); + OnPropertyChanged(nameof(SelectedScalingModeHelper)); + } + } + + public int[] ScalingFactorHelper => new int[] { 1, 2, 3, 4, 5, 6, 8, 10 }; + + #endregion UI Helper + + /// + /// set the export scale for each texture. + /// + private void SetExportTextureScaling() + { + switch (SelectedScalingMode) + { + case Modes.None: + DynamicInputTextureEvents.DynamicInputTextureExportProcessor = null; + break; + + case Modes.NearestNeighbor: + case Modes.Bicubic: + case Modes.Bilinear: + DynamicInputTextureEvents.DynamicInputTextureExportProcessor = DefaultScalingProcessor; + break; + + default: + break; + } + } + + public bool DefaultScalingProcessor(string savepath, DynamicInputTexture dynamicinputtexture) + { + int Scaling = SelectedScalingFactor; + + //Should we use scaling? + if (Scaling == dynamicinputtexture.ImageWidthScaling) return false; + + //The actual scaling. + using (Bitmap newImage = new Bitmap(dynamicinputtexture.HashProperties.ImageWidth * Scaling, dynamicinputtexture.HashProperties.ImageHeight * Scaling)) + { + using (Bitmap Image = new Bitmap(dynamicinputtexture.TexturePath)) + using (Graphics graphics = Graphics.FromImage(newImage)) + { + switch (SelectedScalingMode) + { + case Modes.NearestNeighbor: + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + break; + + case Modes.Bicubic: + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + break; + + case Modes.Bilinear: + graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + break; + + default: + break; + } + + graphics.DrawImage(Image, 0, 0, newImage.Width, newImage.Height); + } + newImage.Save(savepath, System.Drawing.Imaging.ImageFormat.Png); + } + + return true; + } + } +} \ No newline at end of file