├── .gitattributes ├── .gitignore ├── BatchImageProcessor.CLI ├── BatchImageProcessor.CLI.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs ├── BatchImageProcessor.Model ├── BatchImageProcessor.Model.csproj ├── File.cs ├── Folder.cs ├── Interface │ ├── IFile.cs │ ├── IFolderable.cs │ ├── IFolderableHost.cs │ └── IIoObject.cs ├── IoObject.cs ├── Model.cs ├── Properties │ ├── Annotations.cs │ └── AssemblyInfo.cs ├── StaticImageUtils.cs ├── Types │ ├── AdjustmentOptions.cs │ ├── CropOptions.cs │ ├── Enums │ │ ├── Alignment.cs │ │ ├── ColorType.cs │ │ ├── Format.cs │ │ ├── InterpolationMode.cs │ │ ├── NameType.cs │ │ ├── RawHighlightMode.cs │ │ ├── ResizeMode.cs │ │ ├── Rotation.cs │ │ ├── WatermarkType.cs │ │ └── WhiteBalanceMode.cs │ ├── ModelProgressUpdate.cs │ ├── OptionSet.cs │ ├── OutputOptions.cs │ ├── RawOptions.cs │ ├── ResizeOptions.cs │ └── WatermarkOptions.cs └── Utility │ └── ExtensionMethods.cs ├── BatchImageProcessor.sln ├── BatchImageProcessor ├── App.cs ├── App.xaml ├── BatchImageProcessor.csproj ├── Controls │ ├── SplitButton.cs │ ├── SplitButton.xaml │ └── VirtualizingWrapPanel.cs ├── EntryPoint.cs ├── Exec │ └── dcraw.exe ├── GlobalSuppressions.cs ├── Images │ ├── 24 │ │ └── image-export.png │ ├── 32 │ │ ├── cross.png │ │ └── image-transition.png │ ├── ImageRotator.ico │ ├── ImageRotator.png │ ├── arrow-045.png │ ├── arrow-090.png │ ├── arrow-135.png │ ├── arrow-180.png │ ├── arrow-225.png │ ├── arrow-270.png │ ├── arrow-315.png │ ├── arrow-circle-180.png │ ├── arrow-circle-ccwise.png │ ├── arrow-circle-cwise.png │ ├── arrow-noturn--auto.png │ ├── arrow-noturn.png │ ├── arrow-transition.png │ ├── arrow.png │ ├── bin.png │ ├── circle.png │ ├── color-adjustment--cross.png │ ├── color-adjustment.png │ ├── cross.png │ ├── edit.png │ ├── folder--plus.png │ ├── folder-import.png │ ├── folder.png │ ├── image--pencil.png │ ├── image-crop--cross.png │ ├── image-crop.png │ ├── image-export.png │ ├── image-greyscale.png │ ├── image-import.png │ ├── image-resize-actual--cross.png │ ├── image-resize-actual.png │ ├── image-resize-exact.png │ ├── image-resize.png │ ├── image-rotate--asterisk.png │ ├── image-rotate.png │ ├── image-saturation--cross.png │ ├── image-saturation.png │ ├── image-sepia.png │ ├── image-vertical.png │ ├── image.png │ ├── information.png │ ├── refresh.png │ ├── selection-exclude.png │ ├── selection-select.png │ ├── selection.png │ ├── ui-check-box-uncheck.png │ ├── ui-check-box.png │ ├── ui-check-boxes.png │ ├── ui-text-field.png │ └── wrench-screwdriver.png ├── Native │ ├── DwmApiInteropt.cs │ ├── HookEventArgs.cs │ ├── HookType.cs │ ├── LocalWindowsHook.cs │ ├── Natives.cs │ ├── ShellContextMenu.cs │ └── ShellContextMenuException.cs ├── Properties │ ├── Annotations.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ └── image-export.ico ├── View │ ├── AboutBox.cs │ ├── AboutBox.xaml │ ├── FileOptions.xaml │ ├── FileOptions.xaml.cs │ ├── MainWindow.cs │ ├── MainWindow.xaml │ ├── RawOptions.xaml │ ├── RawOptions.xaml.cs │ ├── RenameFileDialog.cs │ └── RenameFileDialog.xaml └── ViewModel │ ├── Converters │ ├── BooleanConverter.cs │ ├── BooleanToEffectConverter.cs │ ├── BooleanToOpacityConverter.cs │ ├── BooleanToVisibilityConverter.cs │ ├── EnumBooleanConverter.cs │ ├── EnumStringConverter.cs │ └── EnumVisibilityConverter.cs │ ├── EnumStringConverter.cs │ ├── EnumVisibilityConverter.cs │ ├── Extensions │ └── EnumerationExtension.cs │ ├── FileCollectionView.cs │ ├── FileWrapper.cs │ ├── FolderCollectionView.cs │ ├── FolderWrapper.cs │ ├── Services │ └── NavigationService.cs │ ├── TypeConverter.cs │ ├── ViewModel.cs │ └── WeakThumbnail.cs └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | *.sln.ide 9 | *.DotSettings 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | [Pp]ackages/ 20 | 21 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 22 | !packages/*/build/ 23 | 24 | # MSTest test Results 25 | [Tt]est[Rr]esult*/ 26 | [Bb]uild[Ll]og.* 27 | 28 | *_i.c 29 | *_p.c 30 | *.ilk 31 | *.meta 32 | *.obj 33 | *.pch 34 | *.pdb 35 | *.pgc 36 | *.pgd 37 | *.rsp 38 | *.sbr 39 | *.tlb 40 | *.tli 41 | *.tlh 42 | *.tmp 43 | *.tmp_proj 44 | *.log 45 | *.vspscc 46 | *.vssscc 47 | .builds 48 | *.pidb 49 | *.log 50 | *.scc 51 | 52 | # Visual C++ cache files 53 | ipch/ 54 | *.aps 55 | *.ncb 56 | *.opensdf 57 | *.sdf 58 | *.cachefile 59 | 60 | # Visual Studio profiler 61 | *.psess 62 | *.vsp 63 | *.vspx 64 | 65 | # Guidance Automation Toolkit 66 | *.gpState 67 | 68 | # ReSharper is a .NET coding add-in 69 | _ReSharper*/ 70 | *.[Rr]e[Ss]harper 71 | 72 | # TeamCity is a build add-in 73 | _TeamCity* 74 | 75 | # DotCover is a Code Coverage Tool 76 | *.dotCover 77 | 78 | # NCrunch 79 | *.ncrunch* 80 | .*crunch*.local.xml 81 | 82 | # Installshield output folder 83 | [Ee]xpress/ 84 | 85 | # DocProject is a documentation generator add-in 86 | DocProject/buildhelp/ 87 | DocProject/Help/*.HxT 88 | DocProject/Help/*.HxC 89 | DocProject/Help/*.hhc 90 | DocProject/Help/*.hhk 91 | DocProject/Help/*.hhp 92 | DocProject/Help/Html2 93 | DocProject/Help/html 94 | 95 | # Click-Once directory 96 | publish/ 97 | 98 | # Publish Web Output 99 | *.Publish.xml 100 | 101 | # NuGet Packages Directory 102 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 103 | #packages/ 104 | 105 | # Windows Azure Build Output 106 | csx 107 | *.build.csdef 108 | 109 | # Windows Store app package directory 110 | AppPackages/ 111 | 112 | # Others 113 | sql/ 114 | *.Cache 115 | ClientBin/ 116 | [Ss]tyle[Cc]op.* 117 | ~$* 118 | *~ 119 | *.dbmdl 120 | *.[Pp]ublish.xml 121 | *.pfx 122 | *.publishsettings 123 | 124 | # RIA/Silverlight projects 125 | Generated_Code/ 126 | 127 | # Backup & report files from converting an old project file to a newer 128 | # Visual Studio version. Backup files are not needed, because we have git ;-) 129 | _UpgradeReport_Files/ 130 | Backup*/ 131 | UpgradeLog*.XML 132 | UpgradeLog*.htm 133 | 134 | # SQL Server files 135 | App_Data/*.mdf 136 | App_Data/*.ldf 137 | 138 | 139 | #LightSwitch generated files 140 | GeneratedArtifacts/ 141 | _Pvt_Extensions/ 142 | ModelManifest.xml 143 | 144 | # ========================= 145 | # Windows detritus 146 | # ========================= 147 | 148 | # Windows image file caches 149 | Thumbs.db 150 | ehthumbs.db 151 | 152 | # Folder config file 153 | Desktop.ini 154 | 155 | # Recycle Bin used on file shares 156 | $RECYCLE.BIN/ 157 | 158 | # Mac desktop service store files 159 | .DS_Store 160 | 161 | # NuGet stuff 162 | packages/Extended.Wpf.Toolkit.2.2.1/Extended.Wpf.Toolkit.2.2.1.nupkg 163 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net35/WPFToolkit.dll 164 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net35/Xceed.Wpf.Toolkit.dll 165 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/de/Xceed.Wpf.AvalonDock.resources.dll 166 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/es/Xceed.Wpf.AvalonDock.resources.dll 167 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/fr/Xceed.Wpf.AvalonDock.resources.dll 168 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/hu/Xceed.Wpf.AvalonDock.resources.dll 169 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/it/Xceed.Wpf.AvalonDock.resources.dll 170 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/pt-BR/Xceed.Wpf.AvalonDock.resources.dll 171 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/ro/Xceed.Wpf.AvalonDock.resources.dll 172 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/ru/Xceed.Wpf.AvalonDock.resources.dll 173 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/sv/Xceed.Wpf.AvalonDock.resources.dll 174 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/Xceed.Wpf.AvalonDock.dll 175 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/Xceed.Wpf.AvalonDock.Themes.Aero.dll 176 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/Xceed.Wpf.AvalonDock.Themes.Metro.dll 177 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/Xceed.Wpf.AvalonDock.Themes.VS2010.dll 178 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/Xceed.Wpf.DataGrid.dll 179 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/Xceed.Wpf.Toolkit.dll 180 | packages/Extended.Wpf.Toolkit.2.2.1/lib/net40/zh-Hans/Xceed.Wpf.AvalonDock.resources.dll 181 | packages/Extended.Wpf.Toolkit.2.2.1/tools/install.ps1 182 | 183 | .vs/ 184 | .tool-versions 185 | -------------------------------------------------------------------------------- /BatchImageProcessor.CLI/BatchImageProcessor.CLI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows 4 | Exe 5 | bipcli 6 | true 7 | win-x64 8 | false 9 | 10 | 11 | true 12 | BatchImageProcessor.CLI 13 | BatchImageProcessor.CLI 14 | Copyright © Sidneys1 2024 15 | 2.0.1 16 | 2.0.1.0 17 | 2.0.1.0 18 | 19 | 20 | false 21 | 22 | 23 | true 24 | none 25 | Off 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BatchImageProcessor.CLI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | using BatchImageProcessor.Model.Types; 7 | using BatchImageProcessor.Model.Types.Enums; 8 | using NDesk.Options; 9 | using OptionSet = NDesk.Options.OptionSet; 10 | 11 | namespace BatchImageProcessor.CLI 12 | { 13 | internal class Program : IProgress 14 | { 15 | private static void Main(string[] args) 16 | { 17 | var showHelp = false; 18 | var x = new Model.Types.OptionSet(); 19 | var manifest = string.Empty; 20 | var did_resize = false; 21 | 22 | var fontsize = 12f; 23 | var fontname = "Calibri"; 24 | 25 | #region Option Definitions 26 | 27 | var p = new OptionSet 28 | { 29 | {"man=", "A {manifest} file", o => manifest = o}, 30 | 31 | { 32 | "rotate=", "A {rotation} transform.\n0=None,\n1-3=Clockwise 90-180-270,\n4=Portrait,\n5=Landscape", 33 | (int o) => x.Rotation = (Rotation) o 34 | }, 35 | 36 | #region Resize Flags 37 | 38 | { 39 | "resize=", "A {resize} transform.\n(None|Smaller|Larger|Exact)", 40 | o => { 41 | ResizeMode r; 42 | if (Enum.TryParse(o, true, out r)) 43 | x.ResizeOptions.ResizeMode = r; 44 | 45 | x.EnableResize = x.ResizeOptions.ResizeMode != ResizeMode.None; 46 | } 47 | }, 48 | { 49 | "rwidth=", "Resize {width}, in pixels.", 50 | (int o) => { 51 | x.ResizeOptions.ResizeWidth = o; 52 | did_resize = true; 53 | } 54 | }, 55 | { 56 | "rheight=", "Resize {height}, in pixels.", 57 | (int o) => { 58 | x.ResizeOptions.ResizeHeight = o; 59 | did_resize = true; 60 | } 61 | }, 62 | { 63 | "a|noaspect", "Disables automatic aspect\nratio matching when resizing.", 64 | o => x.ResizeOptions.UseAspectRatio = o == null 65 | }, 66 | 67 | #endregion 68 | 69 | #region Crop Flags 70 | 71 | {"c|crop", "Enables cropping.", o => x.EnableCrop = o != null}, 72 | {"cwidth=", "Crop {width}, in pixels.", (int o) => x.CropOptions.CropWidth = o}, 73 | {"cheight=", "Crop {height}, in pixels.", (int o) => x.CropOptions.CropHeight = o}, 74 | { 75 | "calign=", "Crop {alignment}.\n0 1 2\n3 4 5\n6 7 8", 76 | (int o) => x.CropOptions.CropAlignment = (Alignment) o 77 | }, 78 | 79 | #endregion 80 | 81 | #region Watermark Flags 82 | 83 | {"w|watermark", "Enables watermarking.", o => x.EnableWatermark = o != null}, 84 | { 85 | "wtype=", "Watermark {type}.\n(text|image)", o => 86 | { 87 | WatermarkType wt; 88 | if (Enum.TryParse(o, true, out wt)) x.WatermarkOptions.WatermarkType = wt; 89 | } 90 | }, 91 | {"wtext=", "Watermark {text}, in quotes.", o => x.WatermarkOptions.WatermarkText = o}, 92 | {"wfile=", "Watermark image file{path}.", o => x.WatermarkOptions.WatermarkImagePath = o}, 93 | {"wfont=", "Watermark {font} name.", o => fontname = o}, 94 | {"wsize=", "Watermark font {size}, in pts.", (float o) => fontsize = o}, 95 | {"wopac=", "Watermark {opacity}.", (double o) => x.WatermarkOptions.WatermarkOpacity = o}, 96 | {"wcolor", "Image watermarks in color.", o => x.WatermarkOptions.WatermarkGreyscale = o != null}, 97 | { 98 | "walign=", "Watermark {alignment}.\n0 1 2\n3 4 5\n6 7 8", 99 | (int o) => x.WatermarkOptions.WatermarkAlignment = (Alignment) o 100 | }, 101 | 102 | #endregion 103 | 104 | #region Adjustments Flags 105 | 106 | {"brightness=", "Brightness {value}.\nE.g. 0.8=80%", (double o) => x.AdjustmentOptions.ColorBrightness = o}, 107 | {"contrast=", "Contrast {value} %.\nE.g. 0.8=80%", (double o) => x.AdjustmentOptions.ColorContrast = o}, 108 | {"gamma=", "Gamma {value}.\nMin=0.1, Max=5.0\nE.g. 0.8=80%", (double o) => x.AdjustmentOptions.ColorGamma = o}, 109 | { 110 | "smode=", "Saturation {mode}.\n0=Saturation\n1=Greyscale\n2=Sepia", 111 | (int o) => x.AdjustmentOptions.ColorType = (ColorType) o 112 | }, 113 | {"saturation=", "Saturation {value}.\nE.g. 0.8=80%", (double o) => x.AdjustmentOptions.ColorSaturation = o}, 114 | 115 | #endregion 116 | 117 | #region Output Flags 118 | 119 | { 120 | "o|output=", "Output directory {path}, in quotes.\nNot specifying this outputs\nto current working directory.", 121 | o => x.OutputOptions.OutputPath = o 122 | }, 123 | { 124 | "naming=", "Output naming {method}.\n(Original|Numbered|Custom)", o => 125 | { 126 | NameType t; 127 | if (Enum.TryParse(o, true, out t)) x.OutputOptions.NameOption = t; 128 | } 129 | }, 130 | { 131 | "customname=", 132 | "Naming Template, in quotes.\nUse these identifiers:\n{{o}} = Original Filename\n{{w}} = Output Width\n{{h}} = Output Height\nE.g. \"{{o}} - {{w}}x{{h}}\" might result in\n\"DSCF001 - 800x600.jpg\"", 133 | o => x.OutputOptions.OutputTemplate = o 134 | }, 135 | { 136 | "format=", "Output format, defaults to Jpg.\n(Jpg|Png|Bmp|Gif|Tiff)", o => 137 | { 138 | Format f; 139 | if (Enum.TryParse(o, true, out f)) x.OutputOptions.OutputFormat = f; 140 | } 141 | }, 142 | { 143 | "jquality=", "Jpeg quality {value}.\nDefaults to 0.95.\nE.g. 0.8 = 80%", 144 | (double o) => x.OutputOptions.JpegQuality = o 145 | }, 146 | 147 | #endregion 148 | 149 | {"?|help", "Show this message and exit", o => showHelp = o != null} 150 | }; 151 | 152 | #endregion 153 | 154 | if (args.Length == 0) 155 | { 156 | ShowHelp(p); 157 | return; 158 | } 159 | 160 | List extra; 161 | try 162 | { 163 | extra = p.Parse(args); 164 | } 165 | catch (OptionException e) 166 | { 167 | Console.WriteLine("bipcli:"); 168 | Console.WriteLine(e.Message); 169 | Console.WriteLine("Try 'BatchImageProcessor --help' for more information."); 170 | return; 171 | } 172 | 173 | if (showHelp) 174 | { 175 | ShowHelp(p); 176 | return; 177 | } 178 | 179 | if (!x.EnableResize && did_resize) 180 | { 181 | var fg = Console.ForegroundColor; 182 | Console.ForegroundColor = ConsoleColor.Yellow; 183 | Console.WriteLine("Warning: resize dimension(s) specified, but no --resize mode set."); 184 | Console.ForegroundColor = fg; 185 | } 186 | 187 | var files = new List(); 188 | 189 | x.WatermarkOptions.WatermarkFont = new Font(fontname, fontsize); 190 | 191 | if ((extra == null || !extra.Any()) && string.IsNullOrEmpty(manifest)) return; 192 | 193 | var badfiles = extra?.Where(o => !File.Exists(o)).ToList(); 194 | if (badfiles?.Count > 0) 195 | { 196 | Console.WriteLine("Bad Filename(s):"); 197 | badfiles.ForEach(o => Console.Write("\t\"{0}\"", o)); 198 | } 199 | if (extra != null && extra.Any()) 200 | files.AddRange(extra); 201 | else 202 | { 203 | if (!File.Exists(manifest)) 204 | Console.WriteLine("Manifest does not exist!"); 205 | using (var r = File.OpenText(manifest)) 206 | { 207 | while (!r.EndOfStream) 208 | { 209 | var s = r.ReadLine()?.Replace("\"", ""); 210 | if (File.Exists(s)) 211 | files.Add(s); 212 | } 213 | } 214 | } 215 | 216 | if (string.IsNullOrWhiteSpace(x.OutputOptions.OutputPath)) 217 | x.OutputOptions.OutputPath = new DirectoryInfo(".").FullName; 218 | 219 | var mod = new Model.Model(x, files); 220 | mod.Process(new Program()).Wait(); 221 | } 222 | 223 | private static void ShowHelp(OptionSet p) 224 | { 225 | Console.WriteLine("Command line usage: BatchImageProcessor [OPTIONS]+ in_files"); 226 | Console.WriteLine("\tProcesses in_files with specified options."); 227 | Console.WriteLine("\tOutputs to --output or current directory."); 228 | Console.WriteLine("\tIf no input file is specified, a manifest is required."); 229 | Console.WriteLine(); 230 | Console.WriteLine("Options:"); 231 | p.WriteOptionDescriptions(Console.Out); 232 | } 233 | 234 | public void Report(ModelProgressUpdate value) 235 | { 236 | lock (this) 237 | { 238 | var s = $@"Done {value.Done} out of {value.Total}"; 239 | 240 | Console.SetCursorPosition(0, Console.CursorTop > 1 ? Console.CursorTop - 2 : 0); 241 | Console.WriteLine(s); 242 | 243 | var max = Console.WindowWidth - 2; 244 | 245 | var val = (float) value.Done/value.Total; 246 | Console.WriteLine(@" " + new string('|', val > 0 ? (int) (max*val) : 1)); 247 | } 248 | } 249 | } 250 | } -------------------------------------------------------------------------------- /BatchImageProcessor.CLI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | [assembly: AssemblyTrademark("")] 4 | [assembly: AssemblyCulture("")] 5 | 6 | // Setting ComVisible to false makes the types in this assembly not visible 7 | // to COM components. If you need to access a type in this assembly from 8 | // COM, set the ComVisible attribute to true on that type. 9 | [assembly: ComVisible(false)] 10 | 11 | // The following GUID is for the ID of the typelib if this project is exposed to COM 12 | [assembly: Guid("293511ac-5e5d-4182-833d-7e802796891c")] 13 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/BatchImageProcessor.Model.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows 4 | Library 5 | true 6 | win-x64 7 | 8 | 9 | true 10 | BatchImageProcessor.Model 11 | BatchImageProcessor.Model 12 | Copyright © Sidneys1 2024 13 | 2.0.1 14 | 2.0.1.0 15 | 2.0.1.0 16 | 17 | 18 | false 19 | 20 | 21 | true 22 | none 23 | Off 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/File.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using BatchImageProcessor.Model.Interface; 3 | using BatchImageProcessor.Model.Types; 4 | using BatchImageProcessor.Model.Types.Enums; 5 | 6 | namespace BatchImageProcessor.Model 7 | { 8 | // [System.Runtime.Versioning.SupportedOSPlatform("windows")] 9 | public class File : IoObject, IFolderable, IFile 10 | { 11 | #region Variables 12 | 13 | public OptionSet Options { get; set; } = new OptionSet {Rotation = Rotation.Default}; 14 | public bool Selected { get; set; } = true; 15 | 16 | public RawOptions RawOptions { get; set; } = null; 17 | public bool IsRaw { get; set; } = false; 18 | public static readonly ISet InvalidCharacters = new HashSet(System.IO.Path.GetInvalidFileNameChars()); 19 | 20 | #endregion 21 | 22 | public new string Name 23 | { 24 | get { return base.Name; } 25 | set { base.Name = value; } 26 | } 27 | 28 | public bool IsValidName { get; set; } 29 | 30 | public File(string path) : base(path) 31 | { 32 | Options.OutputOptions.OutputFormat = Format.Default; 33 | } 34 | 35 | #region Properties 36 | 37 | 38 | public int ImageNumber { get; set; } 39 | public string OutputPath { get; set; } 40 | 41 | #endregion 42 | } 43 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Folder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.IO; 5 | using System.Linq; 6 | using BatchImageProcessor.Model.Interface; 7 | 8 | namespace BatchImageProcessor.Model 9 | { 10 | public class Folder : IFolderable, IFolderableHost 11 | { 12 | public static readonly ISet InvalidCharacters = new HashSet(Path.GetInvalidPathChars()); 13 | public string Name { get; set; } 14 | public ObservableCollection Files { get; } = new ObservableCollection(); 15 | 16 | public bool ContainsFile(string p) 17 | { 18 | return Files.Any(o => (o is Folder) 19 | ? string.Equals(((Folder)o).Name, p, StringComparison.Ordinal) 20 | : string.Equals(((File)o).Name, p, StringComparison.Ordinal) 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Interface/IFile.cs: -------------------------------------------------------------------------------- 1 | using BatchImageProcessor.Model.Types; 2 | 3 | namespace BatchImageProcessor.Model.Interface 4 | { 5 | public interface IFile : IIoObject 6 | { 7 | bool Selected { get; set; } 8 | 9 | OptionSet Options { get; set; } 10 | 11 | int ImageNumber { get; set; } 12 | string OutputPath { get; set; } 13 | 14 | bool IsRaw { get; set; } 15 | RawOptions RawOptions { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Interface/IFolderable.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Interface 2 | { 3 | public interface IFolderable 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Interface/IFolderableHost.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace BatchImageProcessor.Model.Interface 4 | { 5 | public interface IFolderableHost 6 | { 7 | string Name { get; set; } 8 | ObservableCollection Files { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Interface/IIoObject.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Interface 2 | { 3 | public interface IIoObject 4 | { 5 | string Name { get; } 6 | string Path { get; } 7 | bool IsFile { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/IoObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Runtime.CompilerServices; 5 | using BatchImageProcessor.Model.Annotations; 6 | using BatchImageProcessor.Model.Interface; 7 | 8 | namespace BatchImageProcessor.Model 9 | { 10 | public abstract class IoObject : INotifyPropertyChanged, IDisposable, IIoObject 11 | { 12 | private readonly FileSystemWatcher _watcher; 13 | private string _path; 14 | private string _name; 15 | 16 | protected IoObject(string path) 17 | { 18 | var file = System.IO.File.Exists(path); 19 | if (!(file || Directory.Exists(path))) 20 | throw new FileNotFoundException($@"File/folder at ""{path}"" does not exist."); 21 | 22 | IsFile = file; 23 | Path = path; 24 | 25 | Name = GetName(path); 26 | 27 | 28 | var directoryInfo = new FileInfo(Path).Directory; 29 | if (directoryInfo != null) 30 | _watcher = new FileSystemWatcher(IsFile ? directoryInfo.FullName : Path); 31 | if (IsFile) 32 | _watcher.Filter = System.IO.Path.GetFileName(path); 33 | 34 | _watcher.Renamed += watcher_Renamed; 35 | _watcher.Deleted += Watcher_Deleted; 36 | _watcher.EnableRaisingEvents = true; 37 | } 38 | 39 | protected IoObject() 40 | { 41 | } 42 | 43 | public string Name 44 | { 45 | get { return _name; } 46 | set { _name = value; PropChanged(); } 47 | } 48 | 49 | public string Path 50 | { 51 | get { return _path; } 52 | set 53 | { 54 | _path = value; 55 | // ReSharper disable once ExplicitCallerInfoArgument 56 | PropChanged(nameof(Name)); 57 | PropChanged(); 58 | } 59 | } 60 | 61 | public bool IsFile { get; set; } 62 | 63 | public void Dispose() 64 | { 65 | _watcher.Dispose(); 66 | } 67 | 68 | private static void Watcher_Deleted(object sender, FileSystemEventArgs e) 69 | { 70 | } 71 | 72 | private void watcher_Renamed(object sender, RenamedEventArgs e) 73 | { 74 | if (!IsFile) return; 75 | Path = e.FullPath; 76 | _watcher.Filter = e.Name; 77 | } 78 | public static string GetName(string path) 79 | { 80 | if (Directory.Exists(path) && 81 | (System.IO.File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory) 82 | return System.IO.Path.GetFileName(path); 83 | if (System.IO.File.Exists(path)) 84 | return System.IO.Path.GetFileNameWithoutExtension(path); 85 | throw new FileNotFoundException($@"File/folder at ""{path}"" does not exist."); 86 | } 87 | 88 | public event PropertyChangedEventHandler PropertyChanged; 89 | 90 | [NotifyPropertyChangedInvocator] 91 | protected virtual void PropChanged([CallerMemberName] string propertyName = null) 92 | { 93 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Model.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Drawing; 5 | using System.Drawing.Imaging; 6 | using System.Globalization; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using BatchImageProcessor.Model.Interface; 12 | using BatchImageProcessor.Model.Types; 13 | using BatchImageProcessor.Model.Types.Enums; 14 | 15 | namespace BatchImageProcessor.Model 16 | { 17 | [System.Runtime.Versioning.SupportedOSPlatform("windows")] 18 | public class Model 19 | { 20 | private int _totalImages; 21 | private int _doneImages; 22 | 23 | 24 | public bool Cancel = false; 25 | 26 | 27 | public bool OutputSet { get; set; } = false; 28 | 29 | public ObservableCollection Folders { get; } = new ObservableCollection(); 30 | 31 | private readonly bool _console; 32 | public OptionSet Options { get; } = new OptionSet(); 33 | 34 | public Model() { } 35 | 36 | public Model(OptionSet x, List files) 37 | { 38 | var rootFolder = new Folder(); 39 | Folders.Add(rootFolder); 40 | 41 | Options = x; 42 | _console = true; 43 | 44 | files.ForEach(o => rootFolder.Files.Add(new File(o))); 45 | } 46 | 47 | public async Task Process(IProgress progress = null) 48 | { 49 | _totalImages = 0; 50 | _doneImages = 0; 51 | 52 | if (_console) 53 | QueueItems(Folders[0], Options.OutputOptions.OutputPath, progress); 54 | else 55 | await Task.Factory.StartNew(() => QueueItems(Folders[0], Options.OutputOptions.OutputPath, progress)); 56 | } 57 | 58 | private void QueueItems(IFolderableHost folderWrapper, string path, IProgress progress = null) 59 | { 60 | var enumerable = folderWrapper.Files.OfType().Where(wrapper => wrapper.Selected); 61 | var fileWrappers = enumerable as List ?? enumerable.ToList(); 62 | fileWrappers.ForEach(o => 63 | { 64 | o.OutputPath = path; 65 | o.ImageNumber = _totalImages++; 66 | }); 67 | 68 | progress?.Report(new ModelProgressUpdate(_totalImages, _doneImages)); 69 | 70 | Parallel.ForEach(fileWrappers, o => ProcessImage(o, progress)); 71 | 72 | var enumerable1 = folderWrapper.Files.OfType(); 73 | var list = enumerable1 as List ?? enumerable1.ToList(); 74 | list.ForEach(fold => QueueItems(fold, Path.Combine(path, fold.Name))); 75 | } 76 | 77 | private void ProcessImage(IFile w, IProgress progress = null) 78 | { 79 | if (Cancel) 80 | { 81 | Interlocked.Decrement(ref _totalImages); 82 | progress?.Report(new ModelProgressUpdate(_totalImages, _doneImages)); 83 | return; 84 | } 85 | 86 | if (w != null) 87 | { 88 | var outFmt = w.Options.OutputOptions.OutputFormat == Format.Default ? Options.OutputOptions.OutputFormat : w.Options.OutputOptions.OutputFormat; 89 | 90 | // Load Image 91 | Image b = null; 92 | try 93 | { 94 | b = StaticImageUtils.LoadImage(w); 95 | } 96 | catch (Exception e) 97 | { 98 | Console.Error.WriteLine(e); 99 | } 100 | 101 | if (b == null) 102 | { 103 | Interlocked.Decrement(ref _totalImages); 104 | progress?.Report(new ModelProgressUpdate(_totalImages, _doneImages)); 105 | return; 106 | } 107 | 108 | #region Process Steps 109 | 110 | var clone = new Bitmap(b.Width, b.Height, PixelFormat.Format32bppPArgb); 111 | using (var gr = Graphics.FromImage(clone)) 112 | { 113 | gr.DrawImage(b, new Rectangle(0, 0, clone.Width, clone.Height)); 114 | } 115 | b.Dispose(); 116 | b = clone; 117 | 118 | if (w.Options.Rotation != Rotation.Default || Options.EnableRotation) 119 | b.RotateImage(w.Options.EnableRotation ? w.Options.Rotation : Options.Rotation); 120 | 121 | if (Options.EnableResize || w.Options.EnableResize) 122 | b = b.ResizeImage(w.Options.EnableResize ? w.Options.ResizeOptions : Options.ResizeOptions); 123 | 124 | if (Options.EnableCrop || w.Options.EnableCrop) 125 | b = b.CropImage(w.Options.EnableCrop ? w.Options.CropOptions : Options.CropOptions); 126 | 127 | if (Options.EnableWatermark || w.Options.EnableWatermark) 128 | b.WatermarkImage(w.Options.EnableWatermark ? w.Options.WatermarkOptions : Options.WatermarkOptions); 129 | 130 | b = b.AdjustImage(w.Options.EnableAdjustments ? w.Options.AdjustmentOptions : Options.AdjustmentOptions); 131 | 132 | #endregion 133 | 134 | // Filename 135 | var name = GenerateFilename(w, b); 136 | 137 | // Output Path 138 | var outpath = GenerateOutputPath(w, name, outFmt); 139 | 140 | #region Select Encoder 141 | 142 | var encoder = ImageFormat.Jpeg; 143 | // Save 144 | switch (outFmt) 145 | { 146 | case Format.Png: 147 | encoder = ImageFormat.Png; 148 | break; 149 | case Format.Gif: 150 | encoder = ImageFormat.Gif; 151 | break; 152 | case Format.Tiff: 153 | encoder = ImageFormat.Tiff; 154 | break; 155 | case Format.Bmp: 156 | encoder = ImageFormat.Bmp; 157 | break; 158 | } 159 | 160 | #endregion 161 | 162 | if (outFmt == Format.Jpg) 163 | { 164 | var myEncoderParameters = new EncoderParameters(1); 165 | var myencoder = Encoder.Quality; 166 | var myEncoderParameter = new EncoderParameter(myencoder, (long)(Options.OutputOptions.JpegQuality * 100)); 167 | myEncoderParameters.Param[0] = myEncoderParameter; 168 | b.Save(outpath, StaticImageUtils.GetEncoder(encoder), myEncoderParameters); 169 | } 170 | else 171 | b.Save(outpath, encoder); 172 | b.Dispose(); 173 | outpath.Close(); 174 | } 175 | 176 | Interlocked.Increment(ref _doneImages); 177 | progress?.Report(new ModelProgressUpdate(_totalImages, _doneImages)); 178 | } 179 | 180 | private FileStream GenerateOutputPath(IFile w, string name, Format outputFormat) 181 | { 182 | var ext = ".jpg"; 183 | switch (outputFormat) 184 | { 185 | case Format.Png: 186 | ext = ".png"; 187 | break; 188 | case Format.Gif: 189 | ext = ".gif"; 190 | break; 191 | case Format.Tiff: 192 | ext = ".tiff"; 193 | break; 194 | case Format.Bmp: 195 | ext = ".bmp"; 196 | break; 197 | } 198 | 199 | var outpath = Path.Combine(w.OutputPath, name + ext); 200 | if (!Directory.Exists(w.OutputPath)) 201 | Directory.CreateDirectory(w.OutputPath); 202 | 203 | var outpathFormat = outpath.Replace(ext, " ({0})" + ext); 204 | 205 | FileStream ret; 206 | lock (this) 207 | { 208 | if (System.IO.File.Exists(outpath)) 209 | { 210 | var i = 0; 211 | while (System.IO.File.Exists(string.Format(outpathFormat, ++i))) 212 | { 213 | } 214 | outpath = string.Format(outpathFormat, i); 215 | } 216 | 217 | ret = System.IO.File.Create(outpath); 218 | } 219 | return ret; 220 | } 221 | 222 | private string GenerateFilename(IFile w, Image b) 223 | { 224 | string name = null; 225 | switch (Options.OutputOptions.NameOption) 226 | { 227 | case NameType.Original: 228 | name = w.Name; 229 | break; 230 | case NameType.Numbered: 231 | name = w.ImageNumber.ToString(CultureInfo.InvariantCulture); 232 | break; 233 | case NameType.Custom: 234 | name = Options.OutputOptions.OutputTemplate; 235 | name = name.Replace("{o}", w.Name); 236 | name = name.Replace("{w}", b.Width.ToString(CultureInfo.InvariantCulture)); 237 | name = name.Replace("{h}", b.Height.ToString(CultureInfo.InvariantCulture)); 238 | break; 239 | } 240 | return name; 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | [assembly: AssemblyTrademark("")] 4 | [assembly: AssemblyCulture("")] 5 | 6 | // Setting ComVisible to false makes the types in this assembly not visible 7 | // to COM components. If you need to access a type in this assembly from 8 | // COM, set the ComVisible attribute to true on that type. 9 | [assembly: ComVisible(false)] 10 | 11 | // The following GUID is for the ID of the typelib if this project is exposed to COM 12 | [assembly: Guid("7165d238-5543-40fb-a74f-f792368be597")] 13 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/StaticImageUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing; 4 | using System.Drawing.Drawing2D; 5 | using System.Drawing.Imaging; 6 | using System.Drawing.Text; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Windows.Media.Imaging; 10 | using BatchImageProcessor.Model.Interface; 11 | using BatchImageProcessor.Model.Types; 12 | using BatchImageProcessor.Model.Types.Enums; 13 | using InterpolationMode = BatchImageProcessor.Model.Types.Enums.InterpolationMode; 14 | using Rotation = BatchImageProcessor.Model.Types.Enums.Rotation; 15 | 16 | namespace BatchImageProcessor.Model 17 | { 18 | [System.Runtime.Versioning.SupportedOSPlatform("windows")] 19 | public static class StaticImageUtils 20 | { 21 | public static ImageCodecInfo GetEncoder(ImageFormat format) 22 | { 23 | var imageCodecInfo = ImageCodecInfo.GetImageDecoders(); 24 | return imageCodecInfo.FirstOrDefault(codec => codec.FormatID == format.Guid); 25 | } 26 | 27 | public static Image GetRawImageData(IFile file, string execDcrawExe) 28 | { 29 | var f = new FileInfo(execDcrawExe); 30 | var args = "-c -T"; 31 | 32 | if (file.RawOptions != null) 33 | { 34 | switch (file.RawOptions.WhiteBalance) 35 | { 36 | case WhiteBalanceMode.Camera: 37 | args += " -w"; 38 | break; 39 | case WhiteBalanceMode.Average: 40 | args += " -a"; 41 | break; 42 | case WhiteBalanceMode.AverageArea: 43 | args += $" -A {file.RawOptions.WhiteBalanceLeft} {file.RawOptions.WhiteBalanceTop} {file.RawOptions.WhiteBalanceWidth} {file.RawOptions.WhiteBalanceHeight}"; 44 | break; 45 | case WhiteBalanceMode.Custom: 46 | args += $" -r {file.RawOptions.WhiteBalanceMul0} {file.RawOptions.WhiteBalanceMul1} {file.RawOptions.WhiteBalanceMul2} {file.RawOptions.WhiteBalanceMul3} "; 47 | break; 48 | } 49 | 50 | switch (file.RawOptions.InterpolationMode) 51 | { 52 | case InterpolationMode.Grayscale: 53 | args += " -d"; 54 | break; 55 | case InterpolationMode.GrayscaleUnscaled: 56 | args += " -D"; 57 | break; 58 | case InterpolationMode.GrayscaleUnscaledNoCrop: 59 | args += " -E"; 60 | break; 61 | case InterpolationMode.Halfsize: 62 | args += " -h"; 63 | break; 64 | case InterpolationMode.Q0: 65 | args += " -q 0"; 66 | break; 67 | case InterpolationMode.Q1: 68 | args += " -q 1"; 69 | break; 70 | case InterpolationMode.Q2: 71 | args += " -q 2"; 72 | break; 73 | case InterpolationMode.Q3: 74 | args += " -q 3"; 75 | break; 76 | case InterpolationMode.FourColor: 77 | args += " -f"; 78 | break; 79 | } 80 | 81 | if (file.RawOptions.Cleanup) 82 | args += $" -m {file.RawOptions.CleanupPasses}"; 83 | 84 | if (file.RawOptions.FixedWhiteLevel) 85 | args += " -W"; 86 | 87 | if (Math.Abs(file.RawOptions.Brighness - 1.0) > 0.01) 88 | args += $" -b {file.RawOptions.Brighness}"; 89 | 90 | if (file.RawOptions.DisableAutoOrient) 91 | args += " -t 0"; 92 | 93 | if (file.RawOptions.NoStretch) 94 | args += " -j"; 95 | } 96 | 97 | var startInfo = new ProcessStartInfo(f.FullName) 98 | { 99 | Arguments = $"{args} \"{file.Path}\"", 100 | RedirectStandardOutput = true, 101 | UseShellExecute = false, 102 | CreateNoWindow = true 103 | }; 104 | var process = Process.Start(startInfo); 105 | 106 | if (process == null) return null; 107 | 108 | try 109 | { 110 | return Image.FromStream(process.StandardOutput.BaseStream, true, true); 111 | } 112 | catch (Exception) 113 | { 114 | // ignored 115 | } 116 | 117 | return null; 118 | } 119 | 120 | #region Image Processing 121 | 122 | public static void RotateImage(this Image b, Rotation rotation) 123 | { 124 | switch (rotation) 125 | { 126 | case Rotation.Clockwise: 127 | b.RotateFlip(RotateFlipType.Rotate90FlipNone); 128 | break; 129 | case Rotation.CounterClockwise: 130 | b.RotateFlip(RotateFlipType.Rotate270FlipNone); 131 | break; 132 | case Rotation.UpsideDown: 133 | b.RotateFlip(RotateFlipType.Rotate180FlipNone); 134 | break; 135 | case Rotation.Portrait: 136 | if (b.Width > b.Height) 137 | b.RotateFlip(RotateFlipType.Rotate270FlipNone); 138 | break; 139 | case Rotation.Landscape: 140 | if (b.Width < b.Height) 141 | b.RotateFlip(RotateFlipType.Rotate90FlipNone); 142 | break; 143 | } 144 | } 145 | 146 | public static Image ResizeImage(this Image b, ResizeOptions options) 147 | { 148 | var newSize = b.Size; 149 | var targetSize = new Size(options.ResizeWidth, options.ResizeHeight); 150 | 151 | if (options.UseAspectRatio) 152 | { 153 | if (b.Width > b.Height) // landscape 154 | { 155 | targetSize = new Size( 156 | targetSize.Width > targetSize.Height ? targetSize.Width : targetSize.Height, 157 | targetSize.Width > targetSize.Height ? targetSize.Height : targetSize.Width); 158 | } 159 | else if (b.Height > b.Width) // Portrait 160 | { 161 | targetSize = new Size( 162 | targetSize.Height > targetSize.Width ? targetSize.Width : targetSize.Height, 163 | targetSize.Height > targetSize.Width ? targetSize.Height : targetSize.Width); 164 | } 165 | } 166 | 167 | switch (options.ResizeMode) 168 | { 169 | case ResizeMode.Smaller: 170 | if (b.Width > targetSize.Width || b.Height > targetSize.Height) 171 | { 172 | var ratioX = targetSize.Width / (double)b.Width; 173 | var ratioY = targetSize.Height / (double)b.Height; 174 | // use whichever multiplier is smaller 175 | var ratio = ratioX < ratioY ? ratioX : ratioY; 176 | 177 | // now we can get the new height and width 178 | var newHeight = Convert.ToInt32(b.Height * ratio); 179 | var newWidth = Convert.ToInt32(b.Width * ratio); 180 | 181 | newSize = new Size(newWidth, newHeight); 182 | } 183 | break; 184 | case ResizeMode.Larger: 185 | if (b.Width < targetSize.Width || b.Height < targetSize.Height) 186 | { 187 | var ratioX = targetSize.Width / (double)b.Width; 188 | var ratioY = targetSize.Height / (double)b.Height; 189 | // use whichever multiplier is larger 190 | var ratio = ratioX > ratioY ? ratioX : ratioY; 191 | 192 | // now we can get the new height and width 193 | var newHeight = Convert.ToInt32(b.Height * ratio); 194 | var newWidth = Convert.ToInt32(b.Width * ratio); 195 | 196 | newSize = new Size(newWidth, newHeight); 197 | } 198 | break; 199 | case ResizeMode.Exact: 200 | if (b.Size != targetSize) 201 | { 202 | newSize = new Size(targetSize.Width, targetSize.Height); 203 | } 204 | break; 205 | } 206 | 207 | if (b.Size == newSize) return b; 208 | var one = b; 209 | Image two = new Bitmap(b, newSize); 210 | one.Dispose(); 211 | return two; 212 | } 213 | 214 | public static Image CropImage(this Image b, CropOptions options) 215 | { 216 | var cropSize = new Size(options.CropWidth, options.CropHeight); 217 | 218 | if (cropSize.Width > b.Width || cropSize.Height > b.Height) 219 | cropSize = new Size( 220 | cropSize.Width > b.Width ? b.Width : cropSize.Width, 221 | cropSize.Height > b.Height ? b.Height : cropSize.Height); 222 | 223 | int x = 0, y = 0; 224 | 225 | switch (options.CropAlignment) 226 | { 227 | case Alignment.Middle_Left: 228 | case Alignment.Middle_Center: 229 | case Alignment.Middle_Right: 230 | y = ((b.Height / 2) - (cropSize.Height / 2)); 231 | break; 232 | 233 | case Alignment.Bottom_Left: 234 | case Alignment.Bottom_Center: 235 | case Alignment.Bottom_Right: 236 | y = (b.Height - cropSize.Height); 237 | break; 238 | } 239 | 240 | switch (options.CropAlignment) 241 | { 242 | case Alignment.Top_Center: 243 | case Alignment.Middle_Center: 244 | case Alignment.Bottom_Center: 245 | x = ((b.Width / 2) - (cropSize.Width / 2)); 246 | break; 247 | 248 | case Alignment.Top_Right: 249 | case Alignment.Middle_Right: 250 | case Alignment.Bottom_Right: 251 | x = b.Width - cropSize.Width; 252 | break; 253 | } 254 | 255 | var r = new Rectangle(new Point(x, y), cropSize); 256 | 257 | Image ret = new Bitmap(cropSize.Width, cropSize.Height); 258 | 259 | using (var g = Graphics.FromImage(ret)) 260 | g.DrawImage(b, new Rectangle(Point.Empty, cropSize), r, GraphicsUnit.Pixel); 261 | 262 | b.Dispose(); 263 | return ret; 264 | } 265 | 266 | public static void WatermarkImage(this Image b, WatermarkOptions options) 267 | { 268 | using (var g = Graphics.FromImage(b)) 269 | { 270 | var align = options.WatermarkAlignment; 271 | var opac = options.WatermarkOpacity; 272 | var margin = new SizeF(20, 20); 273 | if (options.WatermarkType == WatermarkType.Text) 274 | { 275 | var text = options.WatermarkText; 276 | var font = options.WatermarkFont; 277 | 278 | var tSize = g.MeasureString(text, font); 279 | 280 | var tPos = new PointF(); 281 | 282 | switch (align) 283 | { 284 | case Alignment.Top_Left: 285 | case Alignment.Top_Center: 286 | case Alignment.Top_Right: 287 | tPos.Y = margin.Height; 288 | break; 289 | case Alignment.Middle_Left: 290 | case Alignment.Middle_Center: 291 | case Alignment.Middle_Right: 292 | tPos.Y = (b.Height / 2f) - (tSize.Height / 2f); 293 | break; 294 | case Alignment.Bottom_Left: 295 | case Alignment.Bottom_Center: 296 | case Alignment.Bottom_Right: 297 | tPos.Y = b.Height - margin.Height - tSize.Height; 298 | break; 299 | } 300 | 301 | switch (align) 302 | { 303 | case Alignment.Top_Left: 304 | case Alignment.Middle_Left: 305 | case Alignment.Bottom_Left: 306 | tPos.X = margin.Width; 307 | break; 308 | case Alignment.Top_Center: 309 | case Alignment.Middle_Center: 310 | case Alignment.Bottom_Center: 311 | tPos.X = (b.Width / 2f) - (tSize.Width / 2f); 312 | break; 313 | case Alignment.Top_Right: 314 | case Alignment.Middle_Right: 315 | case Alignment.Bottom_Right: 316 | tPos.X = b.Width - margin.Width - tSize.Width; 317 | break; 318 | } 319 | 320 | using (var brush = new SolidBrush(Color.FromArgb((int)(255 * opac), Color.White))) 321 | { 322 | g.SmoothingMode = SmoothingMode.HighQuality; 323 | g.TextRenderingHint = TextRenderingHint.AntiAlias; 324 | g.DrawString(text, font, brush, tPos.X, tPos.Y); 325 | brush.Color = Color.FromArgb((int)(255 * opac), Color.Black); 326 | g.DrawString(text, font, brush, tPos.X - (font.SizeInPoints / 24) - 1, tPos.Y - (font.SizeInPoints / 24) - 1); 327 | } 328 | } 329 | else 330 | { 331 | var path = options.WatermarkImagePath; 332 | 333 | if (!System.IO.File.Exists(path)) 334 | return; 335 | 336 | var grey = options.WatermarkGreyscale; 337 | 338 | float tl = 1f, tc = 0f, tr = 0f, ml = 0f, mc = 1f, mr = 0f, bl = 0f, bc = 0f, br = 1f; 339 | 340 | const float rwgt = .3086f; 341 | const float gwgt = .6094f; 342 | const float bwgt = .0820f; 343 | 344 | if (grey) 345 | { 346 | tl = tc = tr = rwgt; 347 | ml = mc = mr = gwgt; 348 | bl = bc = br = bwgt; 349 | } 350 | 351 | float[][] ptsArray = 352 | { 353 | new[] {tl, tc, tr, 0f, 0f}, // RED 354 | new[] {ml, mc, mr, 0f, 0f}, // GREEN 355 | new[] {bl, bc, br, 0f, 0f}, // BLUE 356 | new[] {0f, 0f, 0f, 1f, 0f}, // Alpha 357 | new[] {0, 0, 0, 0f, 1f} 358 | }; 359 | 360 | var imageAttributes = new ImageAttributes(); 361 | imageAttributes.ClearColorMatrix(); 362 | var colorMatrix = new ColorMatrix(ptsArray) { Matrix33 = (float)opac }; 363 | imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 364 | 365 | var i = Image.FromFile(path); 366 | var tPos = new PointF(); 367 | switch (align) 368 | { 369 | case Alignment.Top_Left: 370 | case Alignment.Top_Center: 371 | case Alignment.Top_Right: 372 | tPos.Y = margin.Height; 373 | break; 374 | case Alignment.Middle_Left: 375 | case Alignment.Middle_Center: 376 | case Alignment.Middle_Right: 377 | tPos.Y = (b.Height / 2f) - (i.Height / 2f); 378 | break; 379 | case Alignment.Bottom_Left: 380 | case Alignment.Bottom_Center: 381 | case Alignment.Bottom_Right: 382 | tPos.Y = b.Height - margin.Height - i.Height; 383 | break; 384 | } 385 | 386 | switch (align) 387 | { 388 | case Alignment.Top_Left: 389 | case Alignment.Middle_Left: 390 | case Alignment.Bottom_Left: 391 | tPos.X = margin.Width; 392 | break; 393 | case Alignment.Top_Center: 394 | case Alignment.Middle_Center: 395 | case Alignment.Bottom_Center: 396 | tPos.X = (b.Width / 2f) - (i.Width / 2f); 397 | break; 398 | case Alignment.Top_Right: 399 | case Alignment.Middle_Right: 400 | case Alignment.Bottom_Right: 401 | tPos.X = b.Width - margin.Width - i.Width; 402 | break; 403 | } 404 | 405 | g.DrawImage(i, new Rectangle((int)tPos.X, (int)tPos.Y, i.Width, i.Height), 0, 0, i.Width, i.Height, 406 | GraphicsUnit.Pixel, imageAttributes); 407 | } 408 | } 409 | } 410 | 411 | public static Image AdjustImage(this Image image, AdjustmentOptions options) 412 | { 413 | var sat = (float)options.ColorSaturation; 414 | 415 | float tl = 1f, tc = 0f, tr = 0f, ml = 0f, mc = 1f, mr = 0f, bl = 0f, bc = 0f, br = 1f; 416 | 417 | const float rwgt = .3086f; 418 | const float gwgt = .6094f; 419 | const float bwgt = .0820f; 420 | 421 | switch (options.ColorType) 422 | { 423 | case ColorType.Greyscale: 424 | tl = tc = tr = rwgt; 425 | ml = mc = mr = gwgt; 426 | bl = bc = br = bwgt; 427 | break; 428 | case ColorType.Sepia: 429 | tl = .393f; 430 | tc = .349f; 431 | tr = .272f; 432 | ml = .769f; 433 | mc = .686f; 434 | mr = .534f; 435 | bl = .189f; 436 | bc = .168f; 437 | br = .131f; 438 | break; 439 | case ColorType.Saturation: 440 | tl = (1f - sat) * rwgt + sat; 441 | tc = tr = (1f - sat) * rwgt; 442 | 443 | mc = (1f - sat) * gwgt + sat; 444 | ml = mr = (1f - sat) * gwgt; 445 | 446 | br = (1f - sat) * bwgt + sat; 447 | bl = bc = (1f - sat) * bwgt; 448 | break; 449 | } 450 | 451 | tl *= (float)options.ColorContrast; 452 | mc *= (float)options.ColorContrast; 453 | br *= (float)options.ColorContrast; 454 | 455 | float[][] ptsArray = 456 | { 457 | new[] {tl, tc, tr, 0f, 0f}, // RED 458 | new[] {ml, mc, mr, 0f, 0f}, // GREEN 459 | new[] {bl, bc, br, 0f, 0f}, // BLUE 460 | new[] {0f, 0f, 0f, 1f, 0f}, // Alpha 461 | new[] {(float)options.ColorBrightness - 1f, (float)options.ColorBrightness - 1f, (float)options.ColorBrightness - 1f, 0f, 1f} 462 | }; 463 | 464 | var imageAttributes = new ImageAttributes(); 465 | imageAttributes.ClearColorMatrix(); 466 | imageAttributes.SetColorMatrix(new ColorMatrix(ptsArray), ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 467 | imageAttributes.SetGamma((float)options.ColorGamma, ColorAdjustType.Bitmap); 468 | 469 | using (var g = Graphics.FromImage(image)) 470 | { 471 | g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height), 0, 0, image.Width, image.Height, 472 | GraphicsUnit.Pixel, imageAttributes); 473 | } 474 | 475 | return image; 476 | } 477 | 478 | private static readonly string[] RawExts = { 479 | ".3fr", ".ari", ".arw", ".bay", ".crw", ".cr2", ".cap", ".dcs", ".dcr", ".dng", ".drf", ".eip", ".erf", ".fff", 480 | ".iiq", ".k25", ".kdc", ".mdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm", ".orf", ".pef", ".ptx", ".pxn", 481 | ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw", ".x3f" 482 | }; 483 | public static Image LoadImage(IFile file) 484 | { 485 | var f = new FileInfo(file.Path); 486 | return !RawExts.Contains(f.Extension, StringComparer.OrdinalIgnoreCase) ? Image.FromFile(file.Path, true) : GetRawImageData(file, ".\\Exec\\dcraw.exe"); 487 | } 488 | 489 | #endregion 490 | 491 | public static BitmapSource GetSource(this Bitmap bit) 492 | { 493 | var bitmapImage = new BitmapImage(); 494 | using (var memory = new MemoryStream()) 495 | { 496 | bit.Save(memory, ImageFormat.Png); 497 | memory.Position = 0; 498 | 499 | bitmapImage.BeginInit(); 500 | bitmapImage.StreamSource = memory; 501 | bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 502 | bitmapImage.EndInit(); 503 | } 504 | return bitmapImage; 505 | } 506 | } 507 | } 508 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/AdjustmentOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BatchImageProcessor.Model.Types.Enums; 3 | 4 | namespace BatchImageProcessor.Model.Types 5 | { 6 | [Serializable] 7 | public class AdjustmentOptions 8 | { 9 | public ColorType ColorType { get; set; } = ColorType.Saturation; 10 | public double ColorBrightness { get; set; } = 1.0; 11 | public double ColorContrast { get; set; } = 1.0; 12 | public double ColorGamma { get; set; } = 1.0; 13 | public double ColorSaturation { get; set; } = 1.0; 14 | } 15 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/CropOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BatchImageProcessor.Model.Types.Enums; 3 | 4 | namespace BatchImageProcessor.Model.Types 5 | { 6 | [Serializable] 7 | public class CropOptions 8 | { 9 | public Alignment CropAlignment { get; set; } = Alignment.Middle_Center; 10 | public int CropWidth { get; set; } = 800; 11 | public int CropHeight { get; set; } = 600; 12 | } 13 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/Alignment.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum Alignment 4 | { 5 | // ReSharper disable once InconsistentNaming 6 | Top_Left, 7 | // ReSharper disable once InconsistentNaming 8 | Top_Center, 9 | // ReSharper disable once InconsistentNaming 10 | Top_Right, 11 | 12 | // ReSharper disable once InconsistentNaming 13 | Middle_Left, 14 | // ReSharper disable once InconsistentNaming 15 | Middle_Center, 16 | // ReSharper disable once InconsistentNaming 17 | Middle_Right, 18 | 19 | // ReSharper disable once InconsistentNaming 20 | Bottom_Left, 21 | // ReSharper disable once InconsistentNaming 22 | Bottom_Center, 23 | // ReSharper disable once InconsistentNaming 24 | Bottom_Right 25 | } 26 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/ColorType.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum ColorType 4 | { 5 | Saturation, 6 | Greyscale, 7 | Sepia 8 | } 9 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/Format.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum Format 4 | { 5 | Default, 6 | Jpg, 7 | Png, 8 | Gif, 9 | Tiff, 10 | Bmp 11 | } 12 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/InterpolationMode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace BatchImageProcessor.Model.Types.Enums 4 | { 5 | public enum InterpolationMode 6 | { 7 | Default, 8 | Grayscale, 9 | [Description("Greyscale (Unscaled Pixel Values)")] 10 | GrayscaleUnscaled, 11 | [Description("Greyscale (Unscaled Pixel Values, Don't Crop Masked Pixels")] 12 | GrayscaleUnscaledNoCrop, 13 | [Description("Halfsized Color Image")] 14 | Halfsize, 15 | [Description("Bilinear Interpolation")] 16 | Q0, 17 | [Description("VNG (Variable Number of Gradients) Interpolation")] 18 | Q1, 19 | [Description("PPG (Patterned Pixel Grouping) Interpolation")] 20 | Q2, 21 | [Description("AHD (Adaptive Homogeneity-Directed) Interpolation")] 22 | Q3, 23 | [Description("Interpolate RGB as Four Colors")] 24 | FourColor 25 | } 26 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/NameType.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum NameType 4 | { 5 | Original, 6 | Numbered, 7 | Custom 8 | } 9 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/RawHighlightMode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace BatchImageProcessor.Model.Types.Enums 4 | { 5 | public enum RawHighlightMode 6 | { 7 | Clipped, 8 | Unclipped, 9 | Blend, 10 | [Description("Reconstruct (Whites)")] 11 | ReconstructHi=3, 12 | [Description("Reconstruct (Balance)")] 13 | ReconstructMed =5, 14 | [Description("Reconstruct (Colors)")] 15 | ReconstructLo =7 16 | } 17 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/ResizeMode.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum ResizeMode 4 | { 5 | None, 6 | Smaller, 7 | Larger, 8 | Exact 9 | } 10 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/Rotation.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum Rotation 4 | { 5 | None, 6 | Clockwise, 7 | UpsideDown, 8 | CounterClockwise, 9 | Portrait, 10 | Landscape, 11 | Default 12 | } 13 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/WatermarkType.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types.Enums 2 | { 3 | public enum WatermarkType 4 | { 5 | Text, 6 | Image 7 | } 8 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/Enums/WhiteBalanceMode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace BatchImageProcessor.Model.Types.Enums 4 | { 5 | public enum WhiteBalanceMode 6 | { 7 | Default, 8 | Camera, 9 | Average, 10 | [Description("Average (Rectangular Area)")] 11 | AverageArea, 12 | Custom 13 | } 14 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/ModelProgressUpdate.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Model.Types 2 | { 3 | public struct ModelProgressUpdate 4 | { 5 | public int Total; 6 | public int Done; 7 | 8 | public ModelProgressUpdate(int total, int done) 9 | { 10 | Total = total; 11 | Done = done; 12 | } 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/OptionSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BatchImageProcessor.Model.Types.Enums; 3 | 4 | namespace BatchImageProcessor.Model.Types 5 | { 6 | // [System.Runtime.Versioning.SupportedOSPlatform("windows")] 7 | public class OptionSet 8 | { 9 | #region Enabled 10 | 11 | public bool EnableRotation { get; set; } = false; 12 | public bool EnableResize { get; set; } = false; 13 | public bool EnableCrop { get; set; } = false; 14 | public bool EnableWatermark { get; set; } = false; 15 | public bool EnableAdjustments { get; set; } = false; 16 | 17 | #endregion 18 | 19 | public Rotation Rotation { get; set; } = Rotation.None; 20 | 21 | public WatermarkOptions WatermarkOptions { get; } = new WatermarkOptions(); 22 | 23 | public OutputOptions OutputOptions { get; } = new OutputOptions(); 24 | 25 | public CropOptions CropOptions { get; } = new CropOptions(); 26 | 27 | public AdjustmentOptions AdjustmentOptions { get; } = new AdjustmentOptions(); 28 | 29 | public ResizeOptions ResizeOptions { get; } = new ResizeOptions(); 30 | } 31 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/OutputOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BatchImageProcessor.Model.Types.Enums; 3 | 4 | namespace BatchImageProcessor.Model.Types 5 | { 6 | [Serializable] 7 | public class OutputOptions 8 | { 9 | public string OutputPath { get; set; } = string.Empty; 10 | public string OutputTemplate { get; set; } = string.Empty; 11 | public NameType NameOption { get; set; } = NameType.Original; 12 | public double JpegQuality { get; set; } = 0.95; 13 | public Format OutputFormat { get; set; } = Format.Jpg; 14 | } 15 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/RawOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BatchImageProcessor.Model.Types.Enums; 3 | 4 | namespace BatchImageProcessor.Model.Types 5 | { 6 | [Serializable] 7 | public class RawOptions 8 | { 9 | #region Color Options 10 | 11 | public WhiteBalanceMode WhiteBalance { get; set; } 12 | public int WhiteBalanceLeft { get; set; } 13 | public int WhiteBalanceTop { get; set; } 14 | public int WhiteBalanceWidth { get; set; } = 1; 15 | public int WhiteBalanceHeight { get; set; } = 1; 16 | 17 | public double WhiteBalanceMul0 { get; set; } 18 | public double WhiteBalanceMul1 { get; set; } 19 | public double WhiteBalanceMul2 { get; set; } 20 | public double WhiteBalanceMul3 { get; set; } 21 | 22 | #endregion 23 | 24 | #region Interpolation Options 25 | 26 | public InterpolationMode InterpolationMode { get; set; } 27 | public bool Cleanup { get; set; } = false; 28 | public int CleanupPasses { get; set; } = 1; 29 | 30 | #endregion 31 | 32 | #region Output Options 33 | 34 | public bool FixedWhiteLevel { get; set; } = false; 35 | public double Brighness { get; set; } = 1.0; 36 | 37 | public bool DisableAutoOrient { get; set; } = false; 38 | public bool NoStretch { get; set; } = false; 39 | 40 | public string Info { get; set; } 41 | 42 | #endregion 43 | 44 | public RawOptions(string info) 45 | { 46 | Info = info; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/ResizeOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BatchImageProcessor.Model.Types.Enums; 3 | 4 | namespace BatchImageProcessor.Model.Types 5 | { 6 | [Serializable] 7 | public class ResizeOptions 8 | { 9 | public ResizeMode ResizeMode { get; set; } = ResizeMode.Smaller; 10 | public int ResizeWidth { get; set; } = 800; 11 | public int ResizeHeight { get; set; } = 600; 12 | public bool UseAspectRatio { get; set; } = true; 13 | } 14 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Types/WatermarkOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using BatchImageProcessor.Model.Types.Enums; 4 | 5 | namespace BatchImageProcessor.Model.Types 6 | { 7 | // [Serializable, System.Runtime.Versioning.SupportedOSPlatform("windows")] 8 | public class WatermarkOptions 9 | { 10 | [System.Runtime.Versioning.SupportedOSPlatform("windows")] 11 | private Font _watermarkFont; 12 | 13 | public WatermarkOptions() 14 | { 15 | WatermarkFont = new Font("Calibri", 12f); 16 | } 17 | 18 | ~WatermarkOptions() 19 | { 20 | _watermarkFont?.Dispose(); 21 | } 22 | 23 | public bool WatermarkGreyscale { get; set; } = true; 24 | public string WatermarkText { get; set; } = string.Empty; 25 | public string WatermarkImagePath { get; set; } = string.Empty; 26 | public Alignment WatermarkAlignment { get; set; } = Alignment.Bottom_Right; 27 | public double WatermarkOpacity { get; set; } = 0.7; 28 | public WatermarkType WatermarkType { get; set; } = WatermarkType.Text; 29 | public string WatermarkFontString => $"{WatermarkFont.FontFamily.Name}, {WatermarkFont.SizeInPoints}pt"; 30 | 31 | [System.Runtime.Versioning.SupportedOSPlatform("windows")] 32 | public Font WatermarkFont 33 | { 34 | get { return _watermarkFont; } 35 | set 36 | { 37 | _watermarkFont?.Dispose(); 38 | _watermarkFont = value; 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /BatchImageProcessor.Model/Utility/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using System.ArrayExtensions; 4 | 5 | namespace System 6 | { 7 | public static class ObjectExtensions 8 | { 9 | private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); 10 | 11 | public static bool IsPrimitive(this Type type) 12 | { 13 | if (type == typeof(String)) return true; 14 | return (type.IsValueType & type.IsPrimitive); 15 | } 16 | 17 | public static Object Copy(this Object originalObject) 18 | { 19 | return InternalCopy(originalObject, new Dictionary(new ReferenceEqualityComparer())); 20 | } 21 | private static Object InternalCopy(Object originalObject, IDictionary visited) 22 | { 23 | if (originalObject == null) return null; 24 | var typeToReflect = originalObject.GetType(); 25 | if (IsPrimitive(typeToReflect)) return originalObject; 26 | if (visited.ContainsKey(originalObject)) return visited[originalObject]; 27 | if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null; 28 | var cloneObject = CloneMethod.Invoke(originalObject, null); 29 | if (typeToReflect.IsArray) 30 | { 31 | var arrayType = typeToReflect.GetElementType(); 32 | if (IsPrimitive(arrayType) == false) 33 | { 34 | Array clonedArray = (Array)cloneObject; 35 | clonedArray.MultiDimForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices)); 36 | } 37 | 38 | } 39 | visited.Add(originalObject, cloneObject); 40 | CopyFields(originalObject, visited, cloneObject, typeToReflect); 41 | RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect); 42 | return cloneObject; 43 | } 44 | 45 | private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect) 46 | { 47 | if (typeToReflect.BaseType != null) 48 | { 49 | RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType); 50 | CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate); 51 | } 52 | } 53 | 54 | private static void CopyFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func filter = null) 55 | { 56 | foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags)) 57 | { 58 | if (filter != null && filter(fieldInfo) == false) continue; 59 | if (IsPrimitive(fieldInfo.FieldType)) continue; 60 | var originalFieldValue = fieldInfo.GetValue(originalObject); 61 | var clonedFieldValue = InternalCopy(originalFieldValue, visited); 62 | fieldInfo.SetValue(cloneObject, clonedFieldValue); 63 | } 64 | } 65 | public static T Copy(this T original) 66 | { 67 | return (T)Copy((Object)original); 68 | } 69 | } 70 | 71 | public class ReferenceEqualityComparer : EqualityComparer 72 | { 73 | public override bool Equals(object x, object y) 74 | { 75 | return ReferenceEquals(x, y); 76 | } 77 | public override int GetHashCode(object obj) 78 | { 79 | if (obj == null) return 0; 80 | return obj.GetHashCode(); 81 | } 82 | } 83 | 84 | namespace ArrayExtensions 85 | { 86 | public static class ArrayExtensions 87 | { 88 | public static void MultiDimForEach(this Array array, Action action) 89 | { 90 | if (array.LongLength == 0) return; 91 | ArrayTraverse walker = new ArrayTraverse(array); 92 | do action(array, walker.Position); 93 | while (walker.Step()); 94 | } 95 | } 96 | 97 | internal class ArrayTraverse 98 | { 99 | public int[] Position; 100 | private int[] maxLengths; 101 | 102 | public ArrayTraverse(Array array) 103 | { 104 | maxLengths = new int[array.Rank]; 105 | for (int i = 0; i < array.Rank; ++i) 106 | { 107 | maxLengths[i] = array.GetLength(i) - 1; 108 | } 109 | Position = new int[array.Rank]; 110 | } 111 | 112 | public bool Step() 113 | { 114 | for (int i = 0; i < Position.Length; ++i) 115 | { 116 | if (Position[i] < maxLengths[i]) 117 | { 118 | Position[i]++; 119 | for (int j = 0; j < i; j++) 120 | { 121 | Position[j] = 0; 122 | } 123 | return true; 124 | } 125 | } 126 | return false; 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /BatchImageProcessor.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.22609.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BatchImageProcessor", "BatchImageProcessor\BatchImageProcessor.csproj", "{A57FEBB7-72BF-4794-AB8F-DB1A71989C0D}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BatchImageProcessor.Model", "BatchImageProcessor.Model\BatchImageProcessor.Model.csproj", "{7165D238-5543-40FB-A74F-F792368BE597}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BatchImageProcessor.CLI", "BatchImageProcessor.CLI\BatchImageProcessor.CLI.csproj", "{293511AC-5E5D-4182-833D-7E802796891C}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {A57FEBB7-72BF-4794-AB8F-DB1A71989C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {A57FEBB7-72BF-4794-AB8F-DB1A71989C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {A57FEBB7-72BF-4794-AB8F-DB1A71989C0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {A57FEBB7-72BF-4794-AB8F-DB1A71989C0D}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {7165D238-5543-40FB-A74F-F792368BE597}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {7165D238-5543-40FB-A74F-F792368BE597}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {7165D238-5543-40FB-A74F-F792368BE597}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {7165D238-5543-40FB-A74F-F792368BE597}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {293511AC-5E5D-4182-833D-7E802796891C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {293511AC-5E5D-4182-833D-7E802796891C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {293511AC-5E5D-4182-833D-7E802796891C}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {293511AC-5E5D-4182-833D-7E802796891C}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /BatchImageProcessor/App.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor 2 | { 3 | /// 4 | /// Interaction logic for App.xaml 5 | /// 6 | public partial class App 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /BatchImageProcessor/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | Batch Image Processor 7 | 8 | -------------------------------------------------------------------------------- /BatchImageProcessor/BatchImageProcessor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows 4 | WinExe 5 | Batch Image Processor 6 | Borne Programming 7 | 3 8 | 0.2.0.%2a 9 | true 10 | true 11 | true 12 | win-x64 13 | true 14 | true 15 | false 16 | false 17 | 18 | 19 | true 20 | BatchImageProcessor.CLI 21 | BatchImageProcessor.CLI 22 | Copyright © Sidneys1 2024 23 | 2.0.1 24 | 2.0.1.0 25 | 2.0.1.0 26 | 27 | 28 | false 29 | 30 | 31 | true 32 | none 33 | Off 34 | 35 | 36 | Images\ImageRotator.ico 37 | 38 | 39 | BatchImageProcessor.EntryPoint 40 | 41 | 42 | 5FF27F20160E587AD4BF8F9CD3DA2F4E1483EE95 43 | 44 | 45 | BatchImageProcessor_TemporaryKey.pfx 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | true 55 | 56 | 57 | en-US 58 | False 59 | False 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | False 99 | Microsoft .NET Framework 4.5.1 %28x86 and x64%29 100 | true 101 | 102 | 103 | False 104 | .NET Framework 3.5 SP1 Client Profile 105 | false 106 | 107 | 108 | False 109 | .NET Framework 3.5 SP1 110 | false 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | PreserveNewest 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 4.6.0 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | True 241 | True 242 | Settings.settings 243 | 244 | 245 | 246 | 247 | SettingsSingleFileGenerator 248 | Settings.Designer.cs 249 | 250 | 251 | -------------------------------------------------------------------------------- /BatchImageProcessor/Controls/SplitButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using System.Windows.Input; 6 | 7 | namespace BatchImageProcessor.Controls 8 | { 9 | /// 10 | /// Interaction logic for SplitButton.xaml 11 | /// 12 | public partial class SplitButton 13 | { 14 | public SplitButton() 15 | { 16 | InitializeComponent(); 17 | DataContext = this; 18 | } 19 | 20 | public event Action Click = delegate { }; 21 | 22 | private void Button_Click_1(object sender, RoutedEventArgs e) 23 | { 24 | // TODO ContextMenu is no longer supported. Use ContextMenuStrip instead. For more details see https://docs.microsoft.com/en-us/dotnet/core/compatibility/winforms#removed-controls 25 | if (ContextMenu == null) return; 26 | ContextMenu.IsEnabled = true; 27 | ContextMenu.PlacementTarget = (sender as Button); 28 | ContextMenu.Placement = PlacementMode.Bottom; 29 | ContextMenu.IsOpen = true; 30 | } 31 | 32 | private void Canvas_MouseUp(object sender, MouseButtonEventArgs e) 33 | { 34 | Click(this, new EventArgs()); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /BatchImageProcessor/Controls/SplitButton.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /BatchImageProcessor/EntryPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Windows.Media; 7 | using BatchImageProcessor.Model.Types; 8 | using BatchImageProcessor.View; 9 | using NDesk.Options; 10 | using Xceed.Wpf.Toolkit; 11 | using OptionSet = NDesk.Options.OptionSet; 12 | 13 | namespace BatchImageProcessor 14 | { 15 | public class EntryPoint : IProgress 16 | { 17 | [STAThread] 18 | public static void Main(string[] args) 19 | { 20 | var noShaders = false; 21 | var noAero = false; 22 | List extra=null; 23 | if (args != null && args.Length > 0) 24 | { 25 | var showHelp = false; 26 | var p = new OptionSet 27 | { 28 | {"?|help", "Show this message and exit", o => showHelp = o != null}, 29 | 30 | {"s|noshaders", "Disables shaders in the GUI", o => noShaders = o != null}, 31 | {"e|noaero", "Disables Windows Aero extensions", o => noAero = o != null} 32 | }; 33 | 34 | try 35 | { 36 | extra = p.Parse(args); 37 | } 38 | catch (OptionException e) 39 | { 40 | var b = new StringBuilder("BatchImageProcessor: \r\n"); 41 | b.AppendLine(e.Message); 42 | b.AppendLine("Try 'BatchImageProcessor --help' for more information."); 43 | return; 44 | } 45 | 46 | if (showHelp) 47 | { 48 | ShowHelp(p); 49 | return; 50 | } 51 | } 52 | var app = new App(); 53 | app.Run(new MainWindow(noShaders, noAero, extra?.Where(File.Exists).ToArray(), extra?.Where(Directory.Exists).ToArray())); 54 | } 55 | 56 | static void ShowHelp(OptionSet p) 57 | { 58 | var b = new StringBuilder(); 59 | var s = new StringWriter(b); 60 | 61 | s.WriteLine("Desktop GUI usage: BatchImageProcessor [-s -a]"); 62 | s.WriteLine("\tShows a GUI interface for Batch Image Processor."); 63 | s.WriteLine("\tFor CLI usage, see 'bipcli --help'"); 64 | s.WriteLine(); 65 | s.WriteLine("Options:"); 66 | p.WriteOptionDescriptions(s); 67 | 68 | var m = new MessageBox { FontFamily = new FontFamily("Courier New"), Text = b.ToString(), Width = 600 }; 69 | m.ShowDialog(); 70 | } 71 | 72 | public void Report(ModelProgressUpdate value) 73 | { 74 | lock (this) 75 | { 76 | var s = $@"Done {value.Done} out of {value.Total}"; 77 | 78 | Console.SetCursorPosition((Console.WindowWidth/2) - (s.Length/2), Console.WindowHeight/2); 79 | Console.WriteLine(s); 80 | 81 | var max = Console.WindowWidth - 2; 82 | 83 | var val = (float) value.Done/value.Total; 84 | Console.WriteLine(@" " + new string('|', val > 0 ? (int) (max*val) : 1)); 85 | Console.Out.Flush(); 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /BatchImageProcessor/Exec/dcraw.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Exec/dcraw.exe -------------------------------------------------------------------------------- /BatchImageProcessor/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/GlobalSuppressions.cs -------------------------------------------------------------------------------- /BatchImageProcessor/Images/24/image-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/24/image-export.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/32/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/32/cross.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/32/image-transition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/32/image-transition.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/ImageRotator.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/ImageRotator.ico -------------------------------------------------------------------------------- /BatchImageProcessor/Images/ImageRotator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/ImageRotator.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-045.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-045.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-090.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-090.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-135.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-180.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-225.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-225.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-270.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-315.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-315.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-circle-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-circle-180.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-circle-ccwise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-circle-ccwise.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-circle-cwise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-circle-cwise.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-noturn--auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-noturn--auto.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-noturn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-noturn.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow-transition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow-transition.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/arrow.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/bin.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/circle.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/color-adjustment--cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/color-adjustment--cross.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/color-adjustment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/color-adjustment.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/cross.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/edit.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/folder--plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/folder--plus.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/folder-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/folder-import.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/folder.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image--pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image--pencil.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-crop--cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-crop--cross.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-crop.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-export.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-greyscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-greyscale.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-import.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-resize-actual--cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-resize-actual--cross.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-resize-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-resize-actual.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-resize-exact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-resize-exact.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-resize.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-rotate--asterisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-rotate--asterisk.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-rotate.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-saturation--cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-saturation--cross.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-saturation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-saturation.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-sepia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-sepia.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image-vertical.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/image.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/information.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/refresh.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/selection-exclude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/selection-exclude.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/selection-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/selection-select.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/selection.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/ui-check-box-uncheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/ui-check-box-uncheck.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/ui-check-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/ui-check-box.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/ui-check-boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/ui-check-boxes.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/ui-text-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/ui-text-field.png -------------------------------------------------------------------------------- /BatchImageProcessor/Images/wrench-screwdriver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Images/wrench-screwdriver.png -------------------------------------------------------------------------------- /BatchImageProcessor/Native/DwmApiInteropt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security; 4 | 5 | namespace BatchImageProcessor.Native 6 | { 7 | #region Structures 8 | 9 | /// 10 | /// Defines a structure containing margin dimensions. 11 | /// 12 | [StructLayout(LayoutKind.Sequential)] 13 | public struct Margins 14 | { 15 | public int cxLeftWidth, 16 | cxRightWidth, 17 | cyTopHeight, 18 | cyBottomHeight; 19 | } 20 | 21 | #endregion 22 | 23 | public static class DwmApiInterop 24 | { 25 | #region Constants 26 | 27 | public const int HTCLIENT = 0x1; 28 | public const int HTCAPTION = 0x2; 29 | 30 | public const int WM_NCHITTEST = 0x84; 31 | public const int WM_DWMCOMPOSITIONCHANGED = 0x31E; 32 | 33 | #endregion 34 | 35 | #region Helper methods 36 | 37 | /// 38 | /// Determines whether the cursor is on the client area. 39 | /// 40 | /// The window handle that is receiving and processing the window message. 41 | /// The window message. 42 | /// Additional message information. 43 | /// Additional message information. 44 | /// true if the cursor is on the client area; otherwise, false. 45 | public static bool IsOnClientArea(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam) 46 | { 47 | if (uMsg != WM_NCHITTEST) return false; 48 | return DefWindowProc(hWnd, uMsg, wParam, lParam).ToInt32() == HTCLIENT; 49 | } 50 | 51 | #endregion 52 | 53 | #region Managed wrapper methods 54 | 55 | /// 56 | /// Determines whether desktop composition is enabled on the client system. 57 | /// 58 | /// true if desktop composition is enabled; otherwise, false. 59 | public static bool IsCompositionEnabled() 60 | { 61 | var isEnabled = false; 62 | NativeMethods.DwmIsCompositionEnabled(ref isEnabled); 63 | return isEnabled; 64 | } 65 | 66 | /// 67 | /// Extends the window frame into the client area. 68 | /// 69 | /// The window handle whose client area to extend the window frame into. 70 | /// The amount of window frame to extend around the client area. 71 | /// S_OK on success; otherwise, an HRESULT error code. 72 | public static int ExtendFrameIntoClientArea(IntPtr hWnd, ref Margins margins) 73 | { 74 | return NativeMethods.DwmExtendFrameIntoClientArea(hWnd, ref margins); 75 | } 76 | 77 | /// 78 | /// Invokes the default window message procedure for the given window handle. 79 | /// 80 | /// The window handle that is receiving and processing the window message. 81 | /// The window message. 82 | /// Additional message information. 83 | /// Additional message information. 84 | /// The result of the message processing, which depends on the message. 85 | public static IntPtr DefWindowProc(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam) 86 | { 87 | return NativeMethods.DefWindowProc(hWnd, uMsg, wParam, lParam); 88 | } 89 | 90 | #endregion 91 | } 92 | 93 | [SuppressUnmanagedCodeSecurity] 94 | internal static class NativeMethods 95 | { 96 | [DllImport("dwmapi.dll")] 97 | internal static extern void DwmIsCompositionEnabled(ref bool isEnabled); 98 | 99 | [DllImport("dwmapi.dll")] 100 | internal static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins margins); 101 | 102 | [DllImport("user32.dll")] 103 | internal static extern IntPtr DefWindowProc(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /BatchImageProcessor/Native/HookEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BatchImageProcessor.Native 4 | { 5 | public class HookEventArgs : EventArgs 6 | { 7 | public int HookCode; // Hook code 8 | public IntPtr LParam; // LPARAM argument 9 | public IntPtr WParam; // WPARAM argument 10 | } 11 | } -------------------------------------------------------------------------------- /BatchImageProcessor/Native/HookType.cs: -------------------------------------------------------------------------------- 1 | namespace BatchImageProcessor.Native 2 | { 3 | public enum HookType 4 | { 5 | WhJournalRecord = 0, 6 | WhJournalplayback = 1, 7 | WhKeyboard = 2, 8 | WhGetmessage = 3, 9 | WhCallwndproc = 4, 10 | WhCbt = 5, 11 | WhSysmsgfilter = 6, 12 | WhMouse = 7, 13 | WhHardware = 8, 14 | WhDebug = 9, 15 | WhShell = 10, 16 | WhForegroundidle = 11, 17 | WhCallwndprocret = 12, 18 | WhKeyboardLl = 13, 19 | WhMouseLl = 14 20 | } 21 | } -------------------------------------------------------------------------------- /BatchImageProcessor/Native/LocalWindowsHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Threading; 4 | 5 | namespace BatchImageProcessor.Native 6 | { 7 | public class LocalWindowsHook 8 | { 9 | // ************************************************************************ 10 | // Filter function delegate 11 | public delegate void HookEventHandler(object sender, HookEventArgs e); 12 | 13 | public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam); 14 | 15 | // ************************************************************************ 16 | 17 | // ************************************************************************ 18 | // Internal properties 19 | protected HookProc MFilterFunc; 20 | protected IntPtr MHhook = IntPtr.Zero; 21 | protected HookType MHookType; 22 | // ************************************************************************ 23 | 24 | // ************************************************************************ 25 | // Event delegate 26 | 27 | // ************************************************************************ 28 | 29 | // ************************************************************************ 30 | // Class constructor(s) 31 | public LocalWindowsHook(HookType hook) 32 | { 33 | MHookType = hook; 34 | MFilterFunc = CoreHookProc; 35 | } 36 | 37 | public LocalWindowsHook(HookType hook, HookProc func) 38 | { 39 | MHookType = hook; 40 | MFilterFunc = func; 41 | } 42 | 43 | public event HookEventHandler HookInvoked; 44 | 45 | protected void OnHookInvoked(HookEventArgs e) 46 | { 47 | HookInvoked?.Invoke(this, e); 48 | } 49 | 50 | // ************************************************************************ 51 | 52 | // ************************************************************************ 53 | // Default filter function 54 | protected int CoreHookProc(int code, IntPtr wParam, IntPtr lParam) 55 | { 56 | if (code < 0) 57 | return CallNextHookEx(MHhook, code, wParam, lParam); 58 | 59 | // Let clients determine what to do 60 | var e = new HookEventArgs {HookCode = code, WParam = wParam, LParam = lParam}; 61 | OnHookInvoked(e); 62 | 63 | // Yield to the next hook in the chain 64 | return CallNextHookEx(MHhook, code, wParam, lParam); 65 | } 66 | 67 | // ************************************************************************ 68 | 69 | // ************************************************************************ 70 | // Install the hook 71 | public void Install() 72 | { 73 | MHhook = SetWindowsHookEx( 74 | MHookType, 75 | MFilterFunc, 76 | IntPtr.Zero, 77 | //AppDomain.GetCurrentThreadId(), 78 | Thread.CurrentThread.ManagedThreadId); 79 | } 80 | 81 | // ************************************************************************ 82 | 83 | // ************************************************************************ 84 | // Uninstall the hook 85 | public void Uninstall() 86 | { 87 | UnhookWindowsHookEx(MHhook); 88 | } 89 | 90 | #region Win32 Imports 91 | 92 | // ************************************************************************ 93 | // Win32: SetWindowsHookEx() 94 | [DllImport("user32.dll")] 95 | protected static extern IntPtr SetWindowsHookEx(HookType code, 96 | HookProc func, 97 | IntPtr hInstance, 98 | int threadId); 99 | 100 | // ************************************************************************ 101 | 102 | // ************************************************************************ 103 | // Win32: UnhookWindowsHookEx() 104 | [DllImport("user32.dll")] 105 | protected static extern int UnhookWindowsHookEx(IntPtr hhook); 106 | 107 | // ************************************************************************ 108 | 109 | // ************************************************************************ 110 | // Win32: CallNextHookEx() 111 | [DllImport("user32.dll")] 112 | protected static extern int CallNextHookEx(IntPtr hhook, 113 | int code, IntPtr wParam, IntPtr lParam); 114 | 115 | // ************************************************************************ 116 | 117 | #endregion 118 | } 119 | } -------------------------------------------------------------------------------- /BatchImageProcessor/Native/Natives.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace BatchImageProcessor.Native 4 | { 5 | class Natives 6 | { 7 | [DllImport("kernel32.dll")] 8 | public static extern bool AllocConsole(); 9 | 10 | [DllImport("kernel32.dll", SetLastError = true)] 11 | public static extern bool FreeConsole(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BatchImageProcessor/Native/ShellContextMenuException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BatchImageProcessor.Native 4 | { 5 | [Serializable] 6 | public class ShellContextMenuException : Exception 7 | { 8 | /// Default contructor 9 | public ShellContextMenuException() 10 | { 11 | } 12 | 13 | /// Constructor with message 14 | /// Message 15 | public ShellContextMenuException(string message) 16 | : base(message) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /BatchImageProcessor/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Select a Folder to Import 122 | For import folder browser dialog 123 | 124 | 125 | Select a folder to Output to 126 | Folder ouput browser dialog description 127 | 128 | 129 | Import Images... 130 | Import image dialog title 131 | 132 | 133 | Select Watermark Image... 134 | 135 | 136 | Image Files|*.jpg;*.jpeg;*.png 137 | 138 | 139 | Output Folder 140 | 141 | 142 | Rename Folder 143 | 144 | 145 | This folder has folders within it. Would you like to add those as well? 146 | 147 | 148 | Import Folder 149 | 150 | 151 | Name New Folder 152 | 153 | 154 | OK 155 | 156 | 157 | Cancel 158 | 159 | 160 | New Folder 161 | 162 | 163 | <No Path Set> 164 | 165 | 166 | Override Rotation 167 | 168 | 169 | Use Default Settings 170 | 171 | 172 | 270° Rotation 173 | 174 | 175 | No Rotation 176 | 177 | 178 | 180° Rotation 179 | 180 | 181 | Portrait 182 | 183 | 184 | Landscape 185 | 186 | 187 | Folders 188 | 189 | 190 | Import Image(s)... 191 | 192 | 193 | Add New Folder... 194 | 195 | 196 | Import Folder(s)... 197 | 198 | 199 | Remove Item 200 | 201 | 202 | Rename Folder 203 | 204 | 205 | Remove Folder 206 | 207 | 208 | Files 209 | 210 | 211 | Enable Selected Items 212 | 213 | 214 | Disable Selected Items 215 | 216 | 217 | Select All 218 | 219 | 220 | Select None 221 | 222 | 223 | Default Settings 224 | 225 | 226 | Manually GC all Generations 227 | 228 | 229 | About 230 | 231 | 232 | Enable/Disable Rotation 233 | 234 | 235 | Rotate 236 | 237 | 238 | Enable/Disable Resizing 239 | 240 | 241 | Resize 242 | 243 | 244 | Enable/Disable Cropping 245 | 246 | 247 | Crop 248 | 249 | 250 | Enable/Disable Watermarking 251 | 252 | 253 | Watermark 254 | 255 | 256 | Output 257 | 258 | 259 | Rotation Settings 260 | 261 | 262 | Rotate All Images: 263 | 264 | 265 | 90° Rotation 266 | 267 | 268 | Resize Settings 269 | 270 | 271 | Resize Mode: 272 | 273 | 274 | Larger Than 275 | 276 | 277 | Smaller Than 278 | 279 | 280 | Exact Size 281 | 282 | 283 | Smart Sizing: 284 | 285 | 286 | Automatically swaps the width and height of the resize to match the longer and shorter dimension of the image. 287 | 288 | 289 | Use Aspect Ratio 290 | 291 | 292 | Size: 293 | 294 | 295 | Width 296 | 297 | 298 | Height 299 | 300 | 301 | Crop Settings 302 | 303 | 304 | Crop down to: 305 | 306 | 307 | Width: 308 | 309 | 310 | Height: 311 | 312 | 313 | Watermark Settings 314 | 315 | 316 | Type 317 | 318 | 319 | Text 320 | 321 | 322 | Image 323 | 324 | 325 | Text Options 326 | 327 | 328 | Text: 329 | 330 | 331 | Font: 332 | 333 | 334 | Image Options 335 | 336 | 337 | Image File: 338 | 339 | 340 | Convert to Greyscale 341 | 342 | 343 | Other Options 344 | 345 | 346 | Opacity: 347 | 348 | 349 | Output Settings 350 | 351 | 352 | Output Location: 353 | 354 | 355 | File Naming Mode: 356 | 357 | 358 | Original Filename 359 | 360 | 361 | Numbered Sequentially 362 | 363 | 364 | Custom Template 365 | 366 | 367 | Custom Template Settings: 368 | 369 | 370 | Template: 371 | 372 | 373 | Example: 374 | 375 | 376 | Repalcement Codes: 377 | {o} - Original Filename 378 | {w} - Output Width 379 | {h} - Output Height 380 | 381 | 382 | Start Processing 383 | 384 | 385 | Invert Enabled State of Selected Items 386 | 387 | 388 | Invert Selection 389 | 390 | 391 | Enable/Disable Adjustments 392 | 393 | 394 | Adjustments 395 | 396 | 397 | Adjustment Settings 398 | 399 | 400 | {o} - Processed 401 | 402 | 403 | <No File Set> 404 | 405 | 406 | Watermark Text 407 | 408 | 409 | Output Folder 410 | 411 | 412 | This folder has folders within it. Would you like to add those as well? 413 | 414 | 415 | Import Folder 416 | 417 | 418 | Name New Folder 419 | 420 | 421 | Rename Folder 422 | 423 | 424 | Override Output Format 425 | 426 | 427 | Default Output Format 428 | 429 | 430 | JPEG (*.jpg) 431 | 432 | 433 | Portable Network Graphics (*.png) 434 | 435 | 436 | Bitmap (*.bmp) 437 | 438 | 439 | Graphics Interchange Format (*.gif) 440 | 441 | 442 | Tagged Image File Format (*.tiff) 443 | 444 | 445 | Override Resize 446 | 447 | 448 | Override Crop 449 | 450 | 451 | Override Watermark 452 | 453 | 454 | Override Adjustments 455 | 456 | 457 | Remove Image 458 | 459 | 460 | Enable/Disable This Image 461 | 462 | 463 | Resize Overridden 464 | 465 | 466 | Crop Overridden 467 | 468 | 469 | Watermark Overridden 470 | 471 | 472 | Adjustments Overridden 473 | 474 | 475 | Crop alignment: 476 | 477 | 478 | Top Left 479 | 480 | 481 | Top Center 482 | 483 | 484 | Top Right 485 | 486 | 487 | Middle Left 488 | 489 | 490 | Middle Center 491 | 492 | 493 | Middle Right 494 | 495 | 496 | Bottom Left 497 | 498 | 499 | Bottom Center 500 | 501 | 502 | Bottom Right 503 | 504 | 505 | Opacity 506 | 507 | 508 | 0% 509 | 510 | 511 | 100% 512 | 513 | 514 | Watermark Alignment 515 | 516 | 517 | Brightness 518 | 519 | 520 | 200% 521 | 522 | 523 | Contrast 524 | 525 | 526 | 500% 527 | 528 | 529 | Gamma 530 | 531 | 532 | 10% 533 | 534 | 535 | Adjust Saturation 536 | 537 | 538 | Convert to Greyscale 539 | 540 | 541 | Convert to Sepia 542 | 543 | 544 | Saturation 545 | 546 | 547 | 0% 548 | 549 | 550 | File Format 551 | 552 | 553 | JPEG Quality 554 | 555 | 556 | completed of 557 | 558 | 559 | Stop 560 | 561 | 562 | (Output Location not Set) 563 | 564 | 565 | 566 | ..\Resources\image-export.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 567 | 568 | 569 | Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif 570 | 571 | 572 | Raw Image Files|*.3fr;*.ari;*.arw;*.bay;*.crw;*.cr2;*.cap;*.dcs;*.dcr;*.dng;*.drf;*.eip;*.erf;*.fff;*.iiq;*.k25;*.kdc;*.mdc;*.mef;*.mos;*.mrw;*.nef;*.nrw;*.obm;*.orf;*.pef;*.ptx;*.pxn;*.r3d;*.raf;*.raw;*.rwl;*.rw2;*.rwz;*.sr2;*.srf;*.srw;*.x3f 573 | 574 | 575 | Open with paint.net 576 | 577 | 578 | Override Settings... 579 | 580 | 581 | Rename Output 582 | 583 | -------------------------------------------------------------------------------- /BatchImageProcessor/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace BatchImageProcessor.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BatchImageProcessor/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BatchImageProcessor/Resources/image-export.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sidneys1/BatchImageProcessor/800386d5e999aa20d9d8e14563473142a6c8deaf/BatchImageProcessor/Resources/image-export.ico -------------------------------------------------------------------------------- /BatchImageProcessor/View/AboutBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Windows; 5 | using BatchImageProcessor.ViewModel.Services; 6 | 7 | namespace BatchImageProcessor.View 8 | { 9 | /// 10 | /// Interaction logic for AboutBox.xaml 11 | /// 12 | public partial class AboutBox 13 | { 14 | public AboutBox() 15 | { 16 | InitializeComponent(); 17 | 18 | VersionTxt.Text = AssemblyVersion; 19 | CopyrightTxt.Text = AssemblyCopyright; 20 | CompanyTxt.Text = AssemblyCompany; 21 | DescriptionTxt.SetValue(NavigationService.TextProperty, AssemblyDescription); 22 | //DescriptionTxt.Text = AssemblyDescription; 23 | } 24 | 25 | private void Window_Loaded(object sender, RoutedEventArgs e) 26 | { 27 | var args = Environment.GetCommandLineArgs(); 28 | 29 | if (args.Length > 1 && args.Contains("-noshaders")) 30 | { 31 | Resources["dse"] = null; 32 | } 33 | } 34 | 35 | private void okBtn_Click(object sender, RoutedEventArgs e) 36 | { 37 | Close(); 38 | } 39 | 40 | #region Assembly Attribute Accessors 41 | 42 | public string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString(); 43 | 44 | public string AssemblyDescription 45 | { 46 | get 47 | { 48 | var attributes = Assembly.GetExecutingAssembly() 49 | .GetCustomAttributes(typeof (AssemblyDescriptionAttribute), false); 50 | return attributes.Length == 0 ? "" : ((AssemblyDescriptionAttribute) attributes[0]).Description; 51 | } 52 | } 53 | 54 | public string AssemblyCopyright 55 | { 56 | get 57 | { 58 | var attributes = Assembly.GetExecutingAssembly() 59 | .GetCustomAttributes(typeof (AssemblyCopyrightAttribute), false); 60 | return attributes.Length == 0 ? "" : ((AssemblyCopyrightAttribute) attributes[0]).Copyright; 61 | } 62 | } 63 | 64 | public string AssemblyCompany 65 | { 66 | get 67 | { 68 | var attributes = Assembly.GetExecutingAssembly() 69 | .GetCustomAttributes(typeof (AssemblyCompanyAttribute), false); 70 | return attributes.Length == 0 ? "" : ((AssemblyCompanyAttribute) attributes[0]).Company; 71 | } 72 | } 73 | 74 | #endregion 75 | } 76 | } -------------------------------------------------------------------------------- /BatchImageProcessor/View/AboutBox.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 49 |