I'm Keyvan Nayyeri, a 28 years old software engineer working at Match.Com and living in Dallas, Texas.
I have a Master’s degree in computer science and a bachelor's degree in applied mathematics. I’m also known to be a technical author with several technical publications in the form of books and articles. Besides, I'm an open source enthusiast and have coordinated or contributed to several projects. Currently, I maintain my projects on GitHub.
As a content provider on the internet, not only I publish on this technical blog, but also I'm a podcaster and publish audio podcasts on Mash This.
Trying to maintain a healthy and active lifestyle, I'm a pescetarianist and exercise almost everyday. I’m an avid runner, soccer defender, and tennis player. I also have an interest in fashion.
Last year I had a blog post on very high quality image resizing in .NET that received mixed types of comments about its applicability in ASP.NET scenarios due to a memory leak. Some experienced people in the field said it works, and some others said it doesn’t. I was half-way convinced that it works until recently when I moved that code to GitHub as an open source project and Scott Galloway, a former program manager at ASP.NET team, told me that they had discussions about this at Microsoft back to the old days, and he recommended me to use Windows Imaging Component (WIC) as a better alternative that works everywhere. Getting that line, I updated my codebase to use this approach and it sounds like it works perfectly. This blog post by Bertrand Le Roy was a good start point for me.
The process of image resizing in WIC consists of creating a BitmapDecoder to decode the incoming image and then taking out the appropriate BitmapFrame. The next step is to use a TransformedBitmap object to resize the image (the size part), and then use an appropriate encoder (i.e., PngBitmapEncoder, JpegBitmapEncoder, or BmpBitmapEncoder) to encode the result. As a middle step, the quality of the image resizing process can be set by customizing the resized BitmapFrame and before encoding it using different algorithms specified through BitmapScalingMode enumerator.
It would be easier to see some code in action. I grab some parts of the code from my ImageResizer4DotNet project on GitHub to demonstrate this scenario. Here I try to resize an image with a low quality and store it as a PNG file.
public static MemoryStream LowPng(MemoryStream original, int width, int height)
{
BitmapFrame newphoto = GetBitmapFrame(original, width, height, BitmapScalingMode.LowQuality);
return GetPngStream(newphoto);
}
Here I get the original image as a MemoryStream and then pass it to GetBitmapFrame which does the resizing and returns the appropriate BitmapFrame that I can pass to GetPngStream in order to encode as a PNG image.
But what is going on inside GetBitmapFrame function? It does all the resizing based on the width, height, and resizing algorithm that is passed to it.
private static BitmapFrame GetBitmapFrame(MemoryStream original, int width, int height, BitmapScalingMode mode)
{
BitmapDecoder photoDecoder = BitmapDecoder.Create(original, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
BitmapFrame photo = photoDecoder.Frames[0];
TransformedBitmap target = new TransformedBitmap(
photo,
new ScaleTransform(
width / photo.Width * 96 / photo.DpiX,
height / photo.Height * 96 / photo.DpiY,
0, 0));
BitmapFrame thumbnail = BitmapFrame.Create(target);
BitmapFrame newPhoto = Resize(thumbnail, width, height, mode);
return newPhoto;
}
Here first the original image is decoded and the BitmapFrame is selected, then a transformation is done to change the size of the frame and pass it to Resize function to apply the appropriate quality and image resizing algorithm.
private static BitmapFrame Resize(BitmapFrame photo, int width, int height, BitmapScalingMode scalingMode)
{
DrawingGroup group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, scalingMode);
group.Children.Add(new ImageDrawing(photo, new Rect(0, 0, width, height)));
DrawingVisual targetVisual = new DrawingVisual();
DrawingContext targetContext = targetVisual.RenderOpen();
targetContext.DrawDrawing(group);
RenderTargetBitmap target = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
targetContext.Close();
target.Render(targetVisual);
BitmapFrame targetFrame = BitmapFrame.Create(target);
return targetFrame;
}
The code here consists of several components that work together to customize the quality of the resized frame, however, the key part is the use of BitmapScalingMode enumerator which provides different algorithms. The important point to note is that not all the values in this enumerator are distinct and some of them have different names with the same values.
And the last piece of this code is the GetPngStream function that encodes the resized frame with the appropriate encoder (PNG in this case).
private static MemoryStream GetPngStream(BitmapFrame photo)
{
MemoryStream result = new MemoryStream();
PngBitmapEncoder targetEncoder = new PngBitmapEncoder();
targetEncoder.Frames.Add(photo);
targetEncoder.Save(result);
return result;
}
This code is simple. A PngBitmapEncoder is created and the frame is added to its frames collection, then its state is saved.
In order to test this code, I create a console application and applies the above components in action.
static void Main(string[] args)
{
Console.Title = "Image Resizer for .NET";
string filePath = @"C:\ImageResizer4DotNet\ImageResize4DotNet.Test\Original.jpg";
Console.WriteLine("Enter the original image path:");
string temp = Console.ReadLine();
if (!string.IsNullOrEmpty(temp))
filePath = temp;
// Low quality PNG
MemoryStream original = new MemoryStream(System.IO.File.ReadAllBytes(filePath));
MemoryStream resizedStreamLow = Resizer.LowPng(original, 400, 225);
File.WriteAllBytes("resizedlow.png", resizedStreamLow.ToArray());
Console.WriteLine("Done!");
Console.ReadLine();
}
Here is the original image (it’s already resized to fit in the blog post):

And here is the resized PNG image with low quality:

The full source code of this project that includes different quality modes and storing formats along with a test project is hosted as an open source project on GitHub and there is also a NuGet package that you can use in your projects.
Nathanael Jones
May 30, 2012 11:37 AM
#
The (original) ImageResizer supports both WIC and GDI+. Nuget package ImageResizer.Wic.
Hamidreza
Aug 28, 2012 8:47 AM
#
Thanks for ur post. the original photo size is 67KB and re-sized one is 161KB.
Is there any way to reduce it? in PNG format i mean.
Leave a Comment