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.
[Update 3: I've updated my code to use Windows Imaging Component for image resizing to avoid the issues with ASP.NET.]
[Update 2: Nathanael Jones has a fantastic comment to share his expertise in image resizing and the issues discussed in other comments about System.Drawing.]
[Update 1: As some people pointed out in comments, the use of the third approach in a Windows or ASP.NET service may cause problems as is documented on MSDN, however, you should rethink your implementation if you're performing a resize scneario inside a service. There should be better alternatives for doing that.]
To be fair, I can’t call it very high quality because it’s just high quality and there isn’t anything higher than high, however, as I’ll discuss later, since this has a higher quality than the existing techniques for high quality resizing, I’d call it very high quality to help search engines.
I’m the type of the lazy programmer who knows software engineering and doesn’t waste time reinventing the wheel, so I usually try to find solutions to existing classic problems when I’m writing code. Working on Hawraman, the engine powering Keyvan.TV, I needed to resize the poster image of videos dynamically to display them to users both on Keyvan.TV and on any third party site that uses the embedment code. Apparently, I had to create a controller that serves for dynamically resizing my images on fly and rendering them in the output. Looking for the existing solutions, I copy/pasted 4-5 different codes available on the internet, but none of them gave me a satisfactory result, so I had to invest more and write an improved code. Although I usually don’t publish posts that are already done by others, I guess this time I have to publish this one and share the code to help others when they’re searching for the solution. Yes, I know, it’s like a 1980’s blog post, but I’m an old guy!
Here I implement my image resizing techniques in an ASP.NET MVC controller, however, the logic is the same for applying this technique to other types of applications including ASP.NET Web Forms or desktop applications. I have to point that the first two approaches discussed below are discussed in more details by Gunnar Peipman, Mike Borozdin, and many others (with some minor modifications), however, these approaches don’t provide the high quality that you may need for many scenarios, or at least, they couldn’t serve for my purposes.
I implement my resizing techniques in different action methods in an ASP.NET MVC controller. These action methods receive the width and height of the destination image as their parameters. I can set up my routing to handle this controller, called ImageResizeController.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"ImageResizer",
"imageresize/{action}/{width}/{height}",
new { controller = "ImageResize", action = "Index", width = 400, height = 225 }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
The original image is a big image of my ugly and funny face that I recorded for the Pilot episode on Keyvan.TV as you see below (the original image is 1920×1080 pixels that you can find in the source code sample package). Later on, I try to resize this image to 400×225 pixels.

The very naïve approach to image resizing is to use the GetThumbnailImage function of System.Drawing.Image class.
public ActionResult Naive(int width, int height)
{
MemoryStream original = new MemoryStream
(System.IO.File.ReadAllBytes(Request.MapPath("~/content/original.jpg")));
MemoryStream result = new MemoryStream();
Image image = Image.FromStream(original);
// Don't do this to yourself!
Image thumbnail = image.GetThumbnailImage(width, height, null, IntPtr.Zero);
thumbnail.Save(result, image.RawFormat);
thumbnail.Dispose();
image.Dispose();
result.Position = 0;
return new FileStreamResult(result, "image/jpeg");
}
Of course, the output is as good as the efforts that you put into writing this code!

An improved version provides a higher-quality that is more acceptable than the naïve approach. This version uses the Graphics objects along with a Rectangle object and applies InterpolationMode, SmoothingMode, PixelOffsetMode, CompositionQuality properties of the Graphics object to set the quality.
public ActionResult Medium(int width, int height)
{
MemoryStream original = new MemoryStream
(System.IO.File.ReadAllBytes(Request.MapPath("~/content/original.jpg")));
MemoryStream result = new MemoryStream();
Image image = Image.FromStream(original);
Bitmap bitmap = new Bitmap(width, height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
Rectangle imageRectangle = new Rectangle(0, 0, width, height);
graphics.DrawImage(image, imageRectangle);
bitmap.Save(result, image.RawFormat);
graphics.Dispose();
bitmap.Dispose();
image.Dispose();
result.Position = 0;
return new FileStreamResult(result, "image/jpeg");
}
This improves the quality to some extent but it’s not very noticeable.

But the improved technique that produces very high quality resized images applies an ImageCodecInfo to use encoders to store the image in a very high quality.
public ActionResult High(int width, int height)
{
MemoryStream original = new MemoryStream
(System.IO.File.ReadAllBytes(Request.MapPath("~/content/original.jpg")));
MemoryStream result = new MemoryStream();
Image image = Image.FromStream(original);
Image thumbnail = new Bitmap(width, height);
Graphics graphics = Graphics.FromImage(thumbnail);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(image, 0, 0, width, height);
ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
EncoderParameters encoderParameters;
encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
thumbnail.Save(result, info[1], encoderParameters);
result.Position = 0;
return new FileStreamResult(result, "image/jpeg");
}
Here there are some similarities with the last technique and the main difference comes from the way the resized image is saved using an ImageCodecInfo array. I think the details are self-explanatory, so I don’t explain them here.
This technique renders a very high quality resized image that is similar to what you get from image editing programs.

In a classic post I reinvented the wheel to address the common problem of image resizing in .NET by aiming at producing very high quality images that you can get from photo editing software.
I implemented three different techniques for image resizing in an ASP.NET MVC application starting with a very basic and native approach, followed by an approach that produces medium quality images, and finally a technique that provides very high quality images by using image encoders to save the resized image.
I’ve uploaded the source code sample of this post, so you can download and test all the three techniques yourself.
diryboy
Aug 25, 2011 9:48 PM
#
thumbnail.Save(result, info[1], encoderParameters);
Keyvan Nayyeri
Aug 25, 2011 10:17 PM
#
The ImageCodeInfo array is set to ImageCodecInfo.GetImageEncoders which consists of the available codecs for images on the machine, however, what we need is the second codec which is the built-in codec for JPEG images (the first one is for BMP, third one is for GIF, and so on).
So info[1] is returning the second built-in encoder which is for JPEG images.
I hope this clarifies the code for you.
liviu
Aug 26, 2011 8:29 AM
#
Maybe the JPG is builtin the second default codec, but there is no guarantee for it.
Why not:
var jpgCodec = info.Where(x => x.FilenameExtension.Split(';').Contains("*.JPG")).FirstOrDefault();
Ryan O'Neill
Aug 26, 2011 8:40 AM
#
Or is the data from GetImageEncoders different in some way?
Keyvan Nayyeri
Aug 26, 2011 8:59 AM
#
As far as I know, ImageCodecInfo.GetImageEncoders returns the list of codecs starting with default ones on all machines by using the ImageCodeInfo class in GDI+, and the ordering is always the same. Never stumbled upon a counterexample, but if you can provide one, then you're right in querying the codec.
Thanks for pointing that, though.
Keyvan Nayyeri
Aug 26, 2011 9:03 AM
#
No, it's not a dumb question. The point is these two classes are different types. Image.Save method expects an ImageCodecInfo as its parameter but Imaging.ImageFormat.Jpeg is of ImageFormat type which is not compatible and convertible/castable, so the code doesn't work and you get error even at compile time.
Axel
Sep 03, 2011 5:35 PM
#
Keyvan Nayyeri
Sep 03, 2011 5:52 PM
#
Thanks for your link and the information, however, it is working for me in a typical scenario and it should work for many other people like me because many people don't want to do this in a Windows service.
Axel
Sep 03, 2011 5:57 PM
#
Keyvan Nayyeri
Sep 03, 2011 6:01 PM
#
I don't see a necessity to do this because the underlying structure of the code is totally supported in ASP.NET and works great. I've deployed this code to three different production scenarios and it doesn't have any issues.
Keyvan Nayyeri
Sep 03, 2011 6:04 PM
#
Perhaps your main issue is that you don't understand the difference between Windows and ASP.NET services and regular applications. In that case, it's hard to explain some simple principles to you!
Anders Borum
Sep 04, 2011 3:22 AM
#
"Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service. Attempting to use these classes from within one of these application types may produce unexpected problems, such as diminished service performance and run-time exceptions. For a supported alternative, see Windows Imaging Components."
Now, as a software architect, I was part of a team at a major client running a really large SharePoint 2007 farm with high traffic. Aside from the memory leaks native to SharePoint, we were also seing memory leaks that pointed to ASP.NET itself. We ran a case with Microsoft Premier Support on highest level and they said that we should definitely remove all references to System.Drawing in our projects. My thoughts on this were that it wouldn't solve the issue, and it didn't, but we were given clear directions from Microsoft that our scenario wasn't supported with references to System.Drawing.
Aside from that, I think your last comment comes across a bit rude. Then again, maybe it's just be.
Keyvan Nayyeri
Sep 04, 2011 6:43 AM
#
My response is just the same. Of course, I read that MSDN article. You need to be able to distinguish between a Windows and ASP.NET service with other types of applications. The article is clear that this problem is only happening with services, but what you don't see is the fact that use of the services is usually a bad practice. Most developers are lazy and use those services to make their job easier but this is what they get in return. Windows and ASP.NET services are problematic and it's not just the case for this scenario. If you don't follow the best practices, you can get such issues no matter how you implement your code.
The real question is why somebody should possibly implement an image resizing scenario in a service? To schedule that as a background task? Why not implementing a custom scheduling mechanism? This is already considered outdated with the parallel features in .NET 4.0.
I guess it's simple: the use of services is not a good idea for most of the cases and should be avoided. You use that, and this is what you get.
As of that comment, I guess that's a very good response to someone who sits at home, finds an article, makes a comment, and makes assumptions about the author with something like "second-guessing Microsoft" while he doesn't know the author at all.
All in all, thank you for your comment and your explanation. As I said, there are reasons why we have software engineering practices and development guidelines. Most of the times, they're associated with something technical behind the scenes. It's been a very long time that I haven't used services in my applications and I don't think I'm going to use them in the future again. .NET is mature and powerful enough to give me better mechanisms to achieve my goals.
Andres Goodman
Sep 05, 2011 2:46 PM
#
RussellH
Sep 19, 2011 11:08 AM
#
This link may shed some light on the issue. blogs.msdn.com/b/winformsue/archive/2007/05/14/what-does-not-supported-mean.aspx
"When we say they are not supported in services or ASP.NET applications (also a service), we mean exactly that; they are not supported."
The key words are "ASP.NET applications (also a service)."
The point is that ASP.NET itself runs as a windows "service". The wording can be confusing, but the bottom line is that any ASP.NET program is running as a windows service. The classes in System.Drawing expect the process to have a logged in user with a user interface.
Keyvan Nayyeri
Sep 19, 2011 11:32 AM
#
Thank you very much for your comment. Your comment makes sense to me but the problem is all these approaches are using System.Drawing and they're being used widely in ASP.NET applications (including several applications that I wrote myself). I may be missing a point here.
Nathanael Jones
Oct 16, 2011 4:08 PM
#
Any time a user suspects they are seeing any kind of instability in the software, I immediately set up a RDP or TeamViewer session and investigate, regardless of whether I think it's their fault or not. It's never once been related to System.Drawing. System.IO, yes, but not System.Drawing.
But that doesn't mean System.Drawing is safe. It just means that the ImageResizing.Net library uses it in a safe manner.
System.Drawing is a veritable minefield of bugs, memory leaks, handle leaks, and unexpected behavior. Nothing works as you'd expect it to. You have to buffer files in memory streams, wrap everything that implements IDisposable in a using clause (.Dispose() is not sufficient unless it's in a finally clause), and the default settings are never the correct ones.
So OF COURSE System.Drawing isn't supported. There is no way Microsoft could handle the support burden. Developers copy and paste code that doesn't even have a Dispose() call let alone a using(){} clause, then wonder why their server crashes like mad.
But you CAN use System.Drawing safely - it's just intrinsically dangerous if you don't know what you are doing (and the docs are worthless).
I've been compiling a list of the pitfalls related to image resizing and how to avoid them over the years - you can find them at http://nathanaeljones.com/163/20-image-resizing-pitfalls
@Keyvan - It may be a good idea to switch to using(){} clauses in your examples despite the fact they decrease readability. Lots of developers (many of whom may not speak English as a primary language) copy and past code without reading the article that contains it or any of the comments.
@Russell - Reading the article you linked to, it's clear what the author is saying. Microsoft will not support any use of System.Drawing in ASP.NET. That doesn't mean it will not work perfectly well if you use it in the perfect manner. You just won't get any support for it. WPF and WIC are also not supported, so you're left holding an empty bag if you decide not to do anything Microsoft doesn't provide support for.
Keyvan Nayyeri
Oct 16, 2011 4:49 PM
#
Thank you very much for your comment and sharing your thoughts and expertise.
I'll make sure more people will see this comment.
Leave a Comment