Monday, October 13, 2008

Resizing images

It was one of those days again. You build someone a .NET library to convert WMF images inside an OLE package to a clean JPEG file from MS Access and the first thing they say is, "Can it resize the image?". I'll spare you the why and skip right to the how :-).

First thing to think about is how you want this resizing to behave. You could resize just for size, but in our case we wanted to save space. Also we wanted to keep the aspect ratio and we only wanted to shrink images, not make them larger.

Keeping aspect ratio meens that only one of the dimensions can be specified. In our case this was always going to be the height, which would be specified in millimeters.

The process
It takes the following steps to resize an image based on a new height:
  1. Calculate the vertical resolution of the image in pixels/mm
  2. Calculate the new height
  3. Calculate the factor by which the image is being sized
  4. Calculate the horizontal resluotion in pixels/mm
  5. Calculate the new width based on the old one, the horizontal resolution and the sizing factor

The code
So let's dive into the code. First thing we need is a field and a constant to store some values in:

private const double mmPerInch = 25.4;
private double _sizingFactor;

The first step is to calculate the vertical resolution of the image in pixels/mm:

double pixelsPerMm = image.VerticalResolution / mmPerInch;

Then we can calculate the new height, taking into account that we don't want to change heights that are smaller then the preferred height:

if (image.Height / pixelsPerMm <= height)
_sizingFactor = 1;
return image.Height;
double newHeight = pixelsPerMm * height;

After this we can calculate the factor by which the image is being sized:

_sizingFactor = height / (image.Height / pixelsPerMm);

Then we calculate the horizontal resluotion in pixels/mm:

if (_sizingFactor == 1)
return image.Width;
double pixelsPerMm = image.HorizontalResolution / mmPerInch;

And we calculate the new width based on the old one, the horizontal resolution and the sizing factor:

double newWidthMm = (image.Width / pixelsPerMm) * _sizingFactor;
double newWidth = newWidthMm * pixelsPerMm;

Finally, resizing the image is easy. I've wrapped the code in some methods and constructed a Size construct:

int newHeight = CalculateHeight(image, height);
int newWidth = CalculateAspectRatioWidth(image);
return new Size(newWidth, newHeight);

Then we can resize an image like this:

Bitmap outputBitmap = new Bitmap(bitmap, CalculateNewSizeByHeight(bitmap, height));

No comments:

Post a Comment