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

Resize Images to Size Limit

0.00/5 (No votes)
3 Jun 2013 1  
This tip describes how to resize an existing image (e.g. uploaded by a user) so it does not exceed a defined size limit.

Introduction

To enable customers to upload images which exceed the maximum upload size given by the system, it became necessary to scale down large images until the given size limit was no longer exceeded. Since I did not find any articles about this problem, I decided to write one myself to maybe help others struggling with a similar problem.

Initialization

Before we can start the resizing process, we need to obtain the source path of the uploaded image, the destination path (which is very likely the same path) and the maximum allowed size given by your system. All these values are initialized in the constructor:

private int allowedFileSizeInByte;
private string sourcePath;
private string destinationPath;

public ImageResizer(int allowedSize, string sourcePath, string destinationPath)
{
	allowedFileSizeInByte = allowedSize;
	this.sourcePath = sourcePath;
	this.destinationPath = destinationPath;
}

Scaling Images

Let's start with the function which does the scaling. I use a Bitmap as the source image and a scale factor which is applied to change the width and height of the image. Later, we will see how to calculate this scale factor. Since the following code is pretty straight forward, I won't go into further details:

public Bitmap ScaleImage(Bitmap image, double scale)
{
	int newWidth = (int)(image.Width * scale);
	int newHeight = (int)(image.Height * scale);

	Bitmap result = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);
	result.SetResolution(image.HorizontalResolution, image.VerticalResolution);

	using (Graphics g = Graphics.FromImage(result))
	{
		g.InterpolationMode = InterpolationMode.HighQualityBicubic;
		g.CompositingQuality = CompositingQuality.HighQuality;
		g.SmoothingMode = SmoothingMode.HighQuality;
		g.PixelOffsetMode = PixelOffsetMode.HighQuality;

		g.DrawImage(image, 0, 0, result.Width, result.Height);
	}
	return result;
}   

Calculating the Scale Factor

The scale factor for the file size can be approximated by dividing the allowed file size by the file size of the image. This simply describes by which amount the file size has to decrease to reach the size limit. Since we generally don't know anything about the compression of the uploaded image, we cannot exactly predict by which factor we have to scale the width and height of our image. However, we know that the file size of an image will increment quadratically if we increase its width and height, since the file size is approximately calculated by multiplying width and height. Since we want to decrease the file size of the picture, we simply have to calculate the square root of the scale factor calculated above and use this value as a parameter for our ScaleImage function. To clarify this process, take a look at the following function which will be called by the client to perform the resizing:

public void ScaleImage()
{
	using (MemoryStream ms = new MemoryStream())
	{
		using (FileStream fs = new FileStream(sourcePath, FileMode.Open))
		{
			Bitmap bmp = (Bitmap)Image.FromStream(fs);
			SaveTemporary(bmp, ms, 100);

			while (ms.Length > allowedFileSizeInByte)
			{
				double scale = Math.Sqrt
				((double)allowedFileSizeInByte / (double)ms.Length);
				ms.SetLength(0);
				bmp = ScaleImage(bmp, scale);
				SaveTemporary(bmp, ms, 100);
			}

			if (bmp != null)
				bmp.Dispose();
			SaveImageToFile(ms);
		}
	}
}	

As you can see, we use a while MemoryStream to save the uploaded image in memory instead of the hard disk during the resizing process. The Bitmap variable bmp contains the image to be resized which is obtained through a FileStream. Inside the while loop, the image is resized using the scale factor and the new image is saved back into the MemoryStream using the SaveTemporary function we will see in a minute. This while loop is applied until the size limit is not exceeded anymore. We need to iterate here, because our scale factor is really only an approximation and depending on the image it might not provide enough scaling when calling ScaleImage for the first time.

Other Helper Functions

Finally, let's take a look at the helper functions used by the scaling process:

private void SaveTemporary(Bitmap bmp, MemoryStream ms, int quality)
{
	EncoderParameter qualityParam = new EncoderParameter
		(System.Drawing.Imaging.Encoder.Quality, quality);
	var codec = GetImageCodecInfo();
	var encoderParams = new EncoderParameters(1);
	encoderParams.Param[0] = qualityParam;
	bmp.Save(ms, codec, encoderParams);
}

private void SaveImageToFile(MemoryStream ms)
{
	byte[] data = ms.ToArray();

	using (FileStream fs = new FileStream(destinationPath, FileMode.Create))
	{
		fs.Write(data, 0, data.Length);
	}
}

private ImageCodecInfo GetImageCodecInfo()
{
	FileInfo fi = new FileInfo(sourcePath);

	switch (fi.Extension)
	{
		case ".bmp": return ImageCodecInfo.GetImageEncoders()[0];
		case ".jpg":
		case ".jpeg": return ImageCodecInfo.GetImageEncoders()[1];
		case ".gif": return ImageCodecInfo.GetImageEncoders()[2];
		case ".tiff": return ImageCodecInfo.GetImageEncoders()[3];
		case ".png": return ImageCodecInfo.GetImageEncoders()[4];
		default: return null;
	}
}

The SaveTemporary function is used to temporarily save the image into the provided MemoryStream during each iteration. It uses .NET EncoderParameters and ImageCodecInfo to get high quality compression rates which are nearly as good as the compression rates provided by Image editing software. Using the EncoderParameters and ImageCodecInfo in the call to bmp.Save provides better result than using the save function which takes a stream and an ImageFormat instance as its only parameters, so I advise you to use this version to get the best results. The SaveImageToFile function is finally used to write the MemoryStream, which contains the resized image, to its final location on the hard disk.

Summary

Hopefully you find this code useful and can apply it to solve similar problems. I appreciate any feedback tips and suggestions.

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