-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMainWindow.xaml.cs
267 lines (235 loc) · 9.97 KB
/
MainWindow.xaml.cs
1
2
3
4
5
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
99
100
101
102
103
104
105
106
107
108
109
110
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#region 3rd party software
// http://artof01.com/vrellis/works/AllAndOne.html https://www.youtube.com/watch?v=LnXhjzS_i48 Artwork of Petros Vrellis
// https://docs.pdfsharp.net https://github.com/empira/PDFsharp .NET library for processing PDF files
#endregion
#region " Imports definitions "
using Microsoft.Win32;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
#endregion
namespace ImageMerger
{
/// <summary>
/// Routines for hiding one or two images within three slides
/// </summary>
public partial class MainWindow : Window
{
#region " Variables definitions "
List<Image> images = []; // List of all images to process
int maxWidth = 0; // Width of the largest image in pixels
int maxHeight = 0; // Height of the largest image in pixels
Point point; // Point used to position the SpinningWheel control
CancellationToken token; // Token used to cancel the SpinningWheel thread
#endregion
/// <summary>
/// Program start routine
/// </summary>
public MainWindow()
{
InitializeComponent();
// Add all images to list
images = [Image00, Image01, Image02, Image03, Image04, Image05, Image06, Image07, Image08];
// Store source paths needed for possible images reset
foreach (Image image in images)
image.Tag = ((BitmapFrame)image.Source).Decoder.ToString();
}
/// <summary>
/// Merge images together
/// </summary>
/// <param name="sender">Interrupt <c>sender</c> object</param>
/// <param name="e">Interrupt event arguments</param>
public void MergeImages(object sender, RoutedEventArgs e)
{
// Show spinning wheel in a new thread
point = Image04.PointToScreen(new Point(0, 0));
CancellationTokenSource cts = new();
token = cts.Token;
Thread popupThread = new(new ThreadStart(ShowSpinningWheel));
popupThread.SetApartmentState(ApartmentState.STA);
popupThread.IsBackground = false;
popupThread.Start();
Mouse.OverrideCursor = Cursors.Wait;
// Scale all images to the largest width and height found, and convert them to CMYK32 format
FormatImages();
// Merge source images with merge images into target images
images = Merge.Images(images);
// Enable "Show Images" and "Save Images" buttons
ShowImagesButton.IsEnabled = true;
SaveImagesButton.IsEnabled = true;
// Cancel spinning wheel thread
cts.Cancel();
popupThread.Join();
Mouse.OverrideCursor = Cursors.Arrow;
}
/// <summary>
/// Display the spinning wheel in a popup
/// </summary>
public void ShowSpinningWheel()
{
try
{
// Display popup containing spinning wheel
Popup popup = new()
{
Child = new SpinningWheel(),
AllowsTransparency = true,
Placement = PlacementMode.Absolute,
HorizontalOffset = point.X * 0.68, // HACK: PointToScreen X-value is too high
VerticalOffset = point.Y * 0.72, // HACK: PointToScreen Y-value is too high
IsOpen = true
};
while (true)
{
popup.Refresh();
if (token.IsCancellationRequested)
{
// Request for cancellation received, cancel thread
popup.IsOpen = false;
throw new TaskCanceledException();
}
}
}
catch (OperationCanceledException err)
{
Debug.Write(err.ToString());
}
}
/// <summary>
/// Scale all images to the largest width and height found, and convert it to CMYK32 format
/// </summary>
public void FormatImages()
{
BitmapSource bitmap;
// Find the largest width and height
foreach (Image image in images)
{
bitmap = (BitmapSource)image.Source;
if (bitmap.PixelWidth > maxWidth)
maxWidth = bitmap.PixelWidth;
if (bitmap.PixelHeight > maxHeight)
maxHeight = bitmap.PixelHeight;
}
// Scale all images to the largest width and height found, and convert it to CMYK32 format
foreach (Image image in images)
{
// Scale image
bitmap = (BitmapSource)image.Source;
bitmap = new TransformedBitmap(bitmap,
new ScaleTransform(
(double)maxWidth / bitmap.PixelWidth,
(double)maxHeight / bitmap.PixelHeight));
image.Source = bitmap;
// Convert image to CMYK32 format
FormatConvertedBitmap newFormat = new();
newFormat.BeginInit();
newFormat.Source = (BitmapSource)image.Source; // Chain the objects together
newFormat.DestinationFormat = PixelFormats.Cmyk32; // Set the new format to CMYK32
newFormat.EndInit();
image.Source = newFormat;
}
}
/// <summary>
/// Display all merged images in an animation
/// </summary>
/// <param name="sender">Interrupt <c>sender</c> object</param>
/// <param name="e">Interrupt event arguments</param>
public void ShowImages(object sender, RoutedEventArgs e)
{
List<Image> imageList = [Image06, Image07, Image08];
Preview.Images(imageList);
}
/// <summary>
/// Save all merged images as PDF file
/// </summary>
/// <param name="sender">Interrupt <c>sender</c> object</param>
/// <param name="e">Interrupt event arguments</param>
public void SaveImages(object sender, RoutedEventArgs e)
{
List<Image> imageList = [Image06, Image07, Image08];
PDF.Save(imageList);
}
/// <summary>
/// Reset all images to their original contents
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void ResetImages(object sender, RoutedEventArgs e)
{
foreach (Image image in images)
{
BitmapImage bitmap = new();
bitmap.BeginInit();
bitmap.UriSource = new Uri((string)image.Tag); // Uri strings have been saved during program start
bitmap.EndInit();
image.Source = bitmap;
// Disable "Show Images" and "Save Images" buttons
ShowImagesButton.IsEnabled = false;
SaveImagesButton.IsEnabled = false;
}
}
/// <summary>
/// Load a new image and display it in control
/// </summary>
/// <param name="sender">Interrupt <c>sender</c> object</param>
/// <param name="e">Interrupt event arguments</param>
public void Image_Click(object sender, RoutedEventArgs e)
{
if (sender is Image image)
{
try
{
// Show open file dialog
OpenFileDialog openFileDialog = new()
{
Filter = "Image files (*.png;*.jpg;*.jpeg;*.gif;*.tiff)|*.png;*.jpg;*.jpeg;*.gif;*.tiff|All files (*.*)|*.*",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
};
if (openFileDialog.ShowDialog() == true)
{
// Load image
BitmapImage bitmap = new();
bitmap.BeginInit();
bitmap.UriSource = new Uri(openFileDialog.FileName);
bitmap.EndInit();
// Ensure image is square
if (bitmap.PixelWidth > bitmap.PixelHeight)
image.Source = new CroppedBitmap(bitmap, new Int32Rect((bitmap.PixelWidth - bitmap.PixelHeight) / 2, 0, bitmap.PixelHeight, bitmap.PixelHeight));
else if (bitmap.PixelHeight > bitmap.PixelWidth)
image.Source = new CroppedBitmap(bitmap, new Int32Rect(0, (bitmap.PixelHeight - bitmap.PixelWidth) / 2, bitmap.PixelWidth, bitmap.PixelWidth));
else
image.Source = bitmap;
// Disable "Show Images" and "Save Images" buttons
ShowImagesButton.IsEnabled = false;
SaveImagesButton.IsEnabled = false;
}
else
{
// File dialog was cancelled, load blank image
//image.Source = Image05.Source;
}
}
catch (Exception err)
{
Debug.Write(err.ToString());
}
}
}
}
/// <summary>
/// Extension method used to refresh the SpinningWheel, which keeps it spinning
/// </summary>
public static class ExtensionMethods
{
private static readonly Action EmptyDelegate = delegate { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
}