├── .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