Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ImageConverter - Converts images to a specific image format, changing sizes on the flow

0.00/5 (No votes)
13 Sep 2004 1  
ImageConverter allows images to be resized and written to a selected image format.

Sample Image - ImageConverter

Introduction

Every week I take dozens of pictures during the soccer games of Kevin, my 7 year old son. Most of these pictures are placed on a web site created for his team. And every week I find myself performing the ever recurring task of resizing the images (mostly 3072 x 2048) to something more friendly (e.g.: 768 x 512). Why? Just for those less fortunate that have no ADSL or so.

Of course, every repeating task is prone to be automated, and a first application was born to resize the pictures according to my wishes. To cope with different source and target paths, as well as different ways of resizing, the application needed a user interface. Working on the application and its (simple, yet effective) user interface, generalizing the use of the application is evident. The next step then is to share it with all of you and hope you find the application or some code snippet useful.

Using Image Converter

Just download the zipped executable, unzip it, and open it. After you have opened ImageConverter, you can open the image files that you want to convert by pressing the Open... button. The target path for the converted images is automatically set to the source path, if not set already. By pressing the Browse... button, you can select an alternative path for the converted images.

Before you start the conversion process, you can define the image format after conversion, you can define how the images should be named, and you can define the required image size.

For naming the target images, you can use the original name, and the corresponding extension for the target image format is added. If a file with the resulting name already exists, it will be overwritten. Another option is to use a prefix defined by you. Each target image file will get a unique name based on this prefix and a sequence number.

The target size can be a specified size, which means that the original image will be stretched. If you are just interested in obtaining images for which the largest of width or height is less than or equal to the specified maximum, the image will be scaled if either width or height is larger than the specified maximum. The third option is to scale each image by the specified percentage.

Using the code

The main code for the application is in FormImageConverter.cs whilst the code for the Thumbnail control I use is in Thumbnail.cs.

Selecting Images

The images to be converted are selected by invoking the ShowDialog method of a standard OpenFileDialog instance. The selected image files are processed by the DisplayThumbnails method, which disposes off any Thumbnail control still present. Then for each image file, a Thumbnail control is created and added to panelThumbnails.Controls. Note that if the file to be loaded is not a (valid) image file, an OutOfMemoryException is thrown and the Thumbnail control will not be created.

Conversion Process Thread

Because I want some progress information displayed during the conversion process, the conversion process takes place in another thread. The thread is created and started from the event handler for the Convert button.

private void buttonConvert_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    _convertThread = new Thread(new ThreadStart(ConvertImageFiles));
    _convertThread.Start();
}

The ConvertImageFiles method takes care of determining the extension and image format for the target image, as well as the name for the image file. Using this information and knowing the size for the target image, the original image as represented by the Thumbnail control can be converted.

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();
    }
}

To prevent the user from changing the settings during the conversion process, the controls on the main form are disabled at the start, and the Close button becomes a Cancel button. Pressing the Cancel button causes the thread to be aborted by calling _convertThread.Abort method, which causes a System.Threading.ThreadAbortException to be thrown. The exception is caught and re-thrown until the outer try-catch is reached, and the controls can be enabled again.

To update the progress information during the conversion process, a delegate is executed using the Invoke method on the main form. The progress information uses the thumbnail, the target file name, and the target size. These objects are put in an object array, which holds the arguments for the DisplayImageInfo method to be called.

// 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 });

Enabling/Disabling Controls

As noted, the various controls on the main form are disabled during the conversion process and the Close button changed into a Cancel button. Also, if no images have been selected or no target path defined, the Convert button is disabled. This is implemented in the EnableControls method, which has to be called every time the conditions queried in the method have changed.

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 Control

The Thumbnail control is a UserControl on which a Panel is placed that contains a PictureBox and two Labels. The PictureBox holds the thumbnail image, the file name Label, and the original image size Label. The border around the control is obtained by setting the control's DockPadding to 2 for all sides. The thin line between the PictureBox and the file name Label is the BackColor of the underlying Panel 'shining' through. This is accomplished by setting both Labels to dock on the bottom and by giving the PictureBox the size of the remaining space with the height set to just one pixel less.

Thumbnail Control

The main task of the Thumbnail control is to show a thumbnail for each loaded image file and to present easy access to the corresponding image and its file path. The file path is supplied to the constructor, and the file is loaded using the FromFile method.

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;
    }

    ...

Loading a file that does not represent a (recognizable) image causes an OutOfMemoryException, so the exception is caught and thrown again to inform the code constructing the thumbnail.

If it is loadable, the size for the thumbnail image is calculated and the visual feedback is set.

    ...

    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);
}

Conclusion

I found working on the ImageConverter application quite satisfying. Of course, it saves me a lot of time resizing pictures before displaying them on the soccer team's web site. But this small application also shows various aspects that can be found in developing a general C# application. Such aspects include the creation of user controls to get that specific user interface feeling you want, using threads for progress information, and utilizing the power of certain built-in .NET framework methods.

History

First version on September 8th, 2004.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here