ImageConverter - 将图像转换为特定图像格式,动态改变尺寸
ImageConverter 允许对图像进行缩放并将其保存为选定的图像格式。
引言
每周我都要在儿子凯文(7 岁)的足球比赛中拍摄几十张照片。其中大部分照片都会被发布到为他的球队创建的网站上。每周我都要进行重复性的任务,就是将图像(大多是 3072 x 2048)缩放到更友好的尺寸(例如:768 x 512)。为什么?只是为了那些没有 ADSL 或其他条件不好的人。
当然,任何重复的任务都可以自动化,于是第一个应用程序应运而生,用于按照我的意愿缩放图片。为了应对不同的源路径和目标路径,以及不同的缩放方式,该应用程序需要一个用户界面。在开发应用程序及其(简单但有效)用户界面的过程中,泛化应用程序的使用是显而易见的。下一步就是与大家分享,并希望大家觉得该应用程序或其中的某些代码片段有用。
使用 Image Converter
只需下载压缩的可执行文件,解压并打开。打开 ImageConverter 后,您可以通过按 打开... 按钮来打开要转换的图像文件。如果尚未设置,转换后图像的目标路径将自动设置为源路径。通过按 浏览... 按钮,您可以为转换后的图像选择一个替代路径。
在开始转换过程之前,您可以定义转换后的图像格式、图像的命名方式以及所需的图像尺寸。
要命名目标图像,您可以使用原始文件名,并添加目标图像格式对应的扩展名。如果存在同名文件,则会被覆盖。另一种选择是使用您定义的自定义前缀。每个目标图像文件将根据此前缀和一个序列号获得唯一的名称。
目标尺寸可以是一个指定的尺寸,这意味着原始图像将被拉伸。如果您只关心获得宽度或高度中最大值小于或等于指定最大值的图像,那么如果宽度或高度大于指定的最大值,图像将被缩放。第三种选择是通过指定的百分比缩放每个图像。
使用代码
应用程序的主要代码在 FormImageConverter.cs 中,而我使用的 Thumbnail
控件的代码在 Thumbnail.cs 中。
选择图像
通过调用标准 OpenFileDialog
实例的 ShowDialog
方法来选择要转换的图像。选定的图像文件通过 DisplayThumbnails
方法进行处理,该方法会释放所有仍然存在的 Thumbnail
控件。然后,为每个图像文件创建一个 Thumbnail
控件并将其添加到 panelThumbnails.Controls
。请注意,如果加载的文件不是(有效的)图像文件,则会抛出 OutOfMemoryException
,并且不会创建 Thumbnail
控件。
转换过程线程
由于我希望在转换过程中显示一些进度信息,因此转换过程在一个单独的线程中进行。该线程在 Convert 按钮的事件处理程序中创建并启动。
private void buttonConvert_Click(object sender, System.EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
_convertThread = new Thread(new ThreadStart(ConvertImageFiles));
_convertThread.Start();
}
ConvertImageFiles
方法负责确定目标图像的扩展名和图像格式,以及图像文件名。利用这些信息并知道目标图像的尺寸,就可以转换 Thumbnail
控件所代表的原始图像。
private void ConvertImageFiles()
{
this.Cursor = Cursors.WaitCursor;
_converting = true;
this.EnableControls();
try
{
...
// determine the target size
System.Drawing.Size targetSize = this.DetermineTargetSize(thumbnail.Image);
System.Drawing.Image targetImage = null;
try
{
// the new image is created and written based on
// the original, the target size and the desired image format
targetImage = new Bitmap(thumbnail.Image, targetSize);
targetImage.Save(targetFileName, targetImageFormat);
// invoke the delegate for updating progress information
this.Invoke(_imageInfoDelegate,
new object[] { thumbnail, targetFileName, targetSize });
targetImage.Dispose();
}
catch (System.Threading.ThreadAbortException e)
{
throw e;
}
catch {}
...
}
catch {}
finally
{
this.Cursor = Cursors.Default;
_converting = false;
this.EnableControls();
}
}
为了防止用户在转换过程中更改设置,主窗体上的控件在开始时被禁用,并且 Close 按钮变成 Cancel 按钮。按下 Cancel 按钮会导致线程被中止,通过调用 _convertThread.Abort
方法,这会引发一个 System.Threading.ThreadAbortException
。该异常被捕获并重新抛出,直到到达外层的 try
-catch
块,此时控件才能再次启用。
为了在转换过程中更新进度信息,使用主窗体上的 Invoke
方法执行一个委托。进度信息使用了缩略图、目标文件名和目标尺寸。这些对象被放入一个对象数组中,该数组保存了调用 DisplayImageInfo
方法的参数。
// the delegate
private delegate void ImageInfoDelegate(Thumbnail thumbnail,
string targetFile, System.Drawing.Size toSize);
private ImageInfoDelegate _imageInfoDelegate = null;
_imageInfoDelegate = new ImageInfoDelegate(DisplayImageInfo);
// invoke the delegate for updating progress information
this.Invoke(_imageInfoDelegate, new object[] { thumbnail,
targetFileName, targetSize });
启用/禁用控件
如前所述,主窗体上的各种控件在转换过程中被禁用,并且 Close 按钮变成了 Cancel 按钮。此外,如果没有选择图像或未定义目标路径,则 Convert 按钮被禁用。这是在 EnableControls
方法中实现的,该方法在查询条件发生变化时必须被调用。
private void EnableControls()
{
this.buttonConvert.Enabled =
!_converting &&
this.panelThumbnails.Controls.Count > 0 &&
this.labelTargetPathValue.Text.Trim() != "";
this.buttonBrowseTargetPath.Enabled = !_converting;
this.buttonOpen.Enabled = !_converting;
this.groupBoxOnConvert.Enabled = !_converting;
this.groupBoxTargetSize.Enabled = !_converting;
this.comboBoxTargetFormat.Enabled = !_converting;
this.buttonClose.Text = _converting ? "Cancel" : "Close";
}
缩略图控件
Thumbnail
控件是一个 UserControl
,上面放置了一个 Panel
,其中包含一个 PictureBox
和两个 Label
。PictureBox
用于显示缩略图,文件名的 Label
显示文件名,原始图像尺寸的 Label
显示原始图像尺寸。通过将控件的 DockPadding
设置为所有方向的 2 来获得控件周围的边框。PictureBox
和文件名 Label
之间的细线是底层 Panel
的 BackColor
'透出' 的结果。这是通过将两个 Label
设置为靠底部停靠,并让 PictureBox
占据剩余的空间,但高度减少一像素来实现的。
Thumbnail
控件的主要任务是为每个加载的图像文件显示缩略图,并提供对相应图像及其文件路径的便捷访问。文件路径被传递给构造函数,然后使用 FromFile
方法加载文件。
public Thumbnail(string filePath)
{
InitializeComponent();
_filePath = filePath;
try
{
_image = System.Drawing.Image.FromFile(_filePath);
}
catch (System.OutOfMemoryException e)
{
// file does not represent a valid image
throw e;
}
...
加载一个不代表(可识别)图像的文件会导致 OutOfMemoryException
,因此该异常被捕获并重新抛出,以通知创建缩略图的代码。
如果文件可加载,则会计算缩略图的尺寸并设置视觉反馈。
...
int max = Math.Min(this.pictureBox.Width, this.pictureBox.Height);
int width = _image.Width;
int height = _image.Height;
// determine the size for the thumbnail image
if (_image.Width > max || _image.Height > max)
{
if (_image.Width > _image.Height)
{
width = max;
height = (int) (_image.Height * max / _image.Width);
}
else
{
width = (int) (_image.Width * max / _image.Height);
height = max;
}
}
// set feedback information
this.pictureBox.Image = new Bitmap(_image, width, height);
this.labelFileName.Text = Path.GetFileName(_filePath);
this.labelImageSize.Text = string.Format("{0} x {1}",
_image.Size.Width, _image.Size.Height);
}
结论
我认为开发 ImageConverter 应用程序非常有成就感。当然,它为我在足球队网站上显示图片之前节省了大量调整图片尺寸的时间。但这个小应用程序也展示了开发通用 C# 应用程序的各个方面。这些方面包括创建用户控件以获得您想要的特定用户界面感觉、使用线程来提供进度信息以及利用某些内置 .NET 框架方法的强大功能。
历史
2004 年 9 月 8 日发布第一个版本。