65.9K
CodeProject 正在变化。 阅读更多。
Home

ImageConverter - 将图像转换为特定图像格式,动态改变尺寸

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (23投票s)

2004年9月14日

CPOL

5分钟阅读

viewsIcon

108989

downloadIcon

4706

ImageConverter 允许对图像进行缩放并将其保存为选定的图像格式。

Sample Image - 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 和两个 LabelPictureBox 用于显示缩略图,文件名的 Label 显示文件名,原始图像尺寸的 Label 显示原始图像尺寸。通过将控件的 DockPadding 设置为所有方向的 2 来获得控件周围的边框。PictureBox 和文件名 Label 之间的细线是底层 PanelBackColor '透出' 的结果。这是通过将两个 Label 设置为靠底部停靠,并让 PictureBox 占据剩余的空间,但高度减少一像素来实现的。

Thumbnail Control

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 日发布第一个版本。

© . All rights reserved.