Image Resizing and Cropping in C#

So the other day I put up a c# wrapper for ffmpeg. The wrapper I built is a part of my own asset storage system which I guess I will probably be putting up in chunks over the next few weeks. Today I have decided to put up my image resizing tools for your use.

Resize Now or Resize Later?

There are a couple theories to image resizing, should the image be resized at the point it gets uploaded, or at the point it gets sent out to the client?

Each method has advantages and disadvantages. Resizing at upload is good because it speeds up time to delivery as the processor has already done the resizing, additionally, the image is only resized once, vs. if the image is resized every time it is downloaded, it could be resized countless times. The problem with resizing at upload, however, is that it is now resized — you can’t un-resize it. Resizing at download then, is more processor intensive as it is resized every time someone requests a different size (one could probably get around some of this with caching, but the no matter how you slice it, it is still more processor intensive). The nice thing is that based on querystring or, however you pass the resizer data, you get a customized image size.

I have my own theory on this. If one has a ton of storage space (which most of us do have available to us now adays) it makes sense to resize in some basic sizes at upload while keeping the original as well for future resizing, if needed. Most good graphic designers break all their pages up into grids of magic 3rds anyways and would, generally, have ideas about their perfect image sizes for thumbnails, swatches, large previews, etc..

In another post I will detail how I get all of these different sizes and what not stored for use, but — for now — I will just give you the tools with which to do resizing and cropping in one spot.


//Overload for crop that default starts top left of the image.
public static System.Drawing.Image CropImage(System.Drawing.Image Image, int Height, int Width)
{
	return CropImage(Image, Height, Width, 0,0);
}

//The crop image sub
public static System.Drawing.Image CropImage(System.Drawing.Image Image, int Height, int Width, int StartAtX, int StartAtY)
{
	Image outimage;
	MemoryStream mm = null;
	try
	{
		//check the image height against our desired image height
		if (Image.Height < Height) {
			Height = Image.Height;
		}
		
		if (Image.Width < Width) {
			Width = Image.Width;
		}
		
		//create a bitmap window for cropping
		Bitmap bmPhoto = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
		bmPhoto.SetResolution(72, 72);
		
		//create a new graphics object from our image and set properties
		Graphics grPhoto = Graphics.FromImage(bmPhoto);
		grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
		grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
		grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
		
		//now do the crop
		grPhoto.DrawImage(Image, new Rectangle(0, 0, Width, Height), StartAtX, StartAtY, Width, Height, GraphicsUnit.Pixel);
		
		// Save out to memory and get an image from it to send back out the method.
		mm = new MemoryStream();
		bmPhoto.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
		Image.Dispose();
		bmPhoto.Dispose();
		grPhoto.Dispose();
		outimage = Image.FromStream(mm);

		return outimage;
	}
	catch (Exception ex)
	{
		throw new Exception("Error cropping image, the error was: " + ex.Message);
	}
}

//Hard resize attempts to resize as close as it can to the desired size and then crops the excess
public static System.Drawing.Image HardResizeImage(int Width, int Height, System.Drawing.Image Image)
{
	int width = Image.Width;
	int height = Image.Height;
	Image resized = null;
	if (Width > Height)
	{
		resized = ResizeImage(Width, Width, Image);
	}
	else
	{
		resized = ResizeImage(Height, Height, Image);
	}
	Image output = CropImage(resized, Height, Width);
	//return the original resized image
	return output;
}

//Image resizing
public static System.Drawing.Image ResizeImage(int maxWidth, int maxHeight, System.Drawing.Image Image)
{
	int width = Image.Width;
	int height = Image.Height;
	if (width > maxWidth || height > maxHeight)
	{
		//The flips are in here to prevent any embedded image thumbnails -- usually from cameras
		//from displaying as the thumbnail image later, in other words, we want a clean
		//resize, not a grainy one.
		Image.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
		Image.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);

		float ratio = 0;
		if (width > height)
		{
			ratio = (float)width / (float)height;
			width = maxWidth;
			height = Convert.ToInt32(Math.Round((float)width / ratio));
		}
		else
		{
			ratio = (float)height / (float)width;
			height = maxHeight;
			width = Convert.ToInt32(Math.Round((float)height / ratio));
		}

		//return the resized image
		return Image.GetThumbnailImage(width, height, null, IntPtr.Zero);
	}
	//return the original resized image
	return Image;
}

Extenders

Yesterday I posted on extenders after seeing this great post http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc. It strikes me that so much of what we do can be thrown into extenders. The code above, for example could really simply be used as extenders for you existing System.Drawing.Imaging objects.

public static string Crop(this System.Drawing.Imaging ThisImage, int maxHeight, int maxWidth)
{
    return CropImage(ThisImage, maxHeight, maxWidth);
}

public static string Resize(this System.Drawing.Imaging ThisImage, int Height, int Width)
{
    return ResizeImage(Width, Height, ThisImage);
}

public static string HardResize(this System.Drawing.Imaging ThisImage, int Height, int Width)
{
    return HardResizeImage(Width, Height, ThisImage);
}
Advertisements

5 responses to this post.

  1. Posted by chengjinchuan on May 7, 2011 at 5:27 pm

    can you send the image crop project to me, I’m a fresh student.thanks

    Reply

  2. Posted by furawh on August 8, 2011 at 9:57 am

    very helpfull and easy code

    Reply

  3. Posted by mark foley on February 1, 2012 at 1:26 am

    Fantastic post – thanks for that! Quick note: for the extensions, the type should probably be this System.Drawing.Image not .Imaging. Cheers

    Reply

  4. Found this today, and it’s awesome! The only thing I have to comment about is when using HardResize there is a quite significant quality loss.

    Do you know why?

    Thanks in advance 🙂

    Reply

  5. Posted by Rafael on April 26, 2012 at 2:33 pm

    Very Nice! Thank You! Loved the HardResizeImage Method! 😀

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: