Welcome to DotNetManiac

Http Handler: Resize image handler

Date Revised: December 15 2009

Visual Studio 2008 | C#

Use an Http Image Handler to dynamically resize images in your asp.net web applications. I prefer this method as it provides high quality image resizing - see below for a live demo. I hate the Bitmap.GetThumbnailImage() method, it's only good for small thumbnails and the quality is not so great.

download Download code as text file

<%@ WebHandler Language="C#" Class="ImageHandler" %>
using System;
using System.Web;
 
/// <summary>
/// www.DotNetManiac.info
/// November 14 2005 - [created]
/// January 16 2005 - [revised]    
/// February 22 2006 - [revised]    
/// May 08 2006 - [revised]
/// August 18 2007 - [revised]
/// August 12 2009 - [revised]
/// November 24 2009 - [revised]
/// December 15 2009 - [revised]
/// 
/// Creates a dynamic image and returns it in the outputstream for an img src
/// This takes away the overhead of using an aspx page. Commonly used for 
/// creating thumbnail images or zooming in or out of images.
/// This implimentation works with JPG images only. 
/// 
/// Max image size is set to avoid performance issues.
/// 
/// If constrain proportions is set to true, then image will scale based on 
/// requested width. Otherwise image will be forced to requested dimensions
/// which might produce a slightly distorted image.
///    
/// Update on May 08 2006 
/// Allows constrain proportions to be used. If constrain 
/// proportions is set to true, then image will scale based on 
/// requested width. Otherwise image will be forced to requested dimensions
/// which might produce a slightly distorted image.
/// 
/// Update August 18 2006 - ported to C# 
/// changed the method of resizing to use a Graphics object which produces
/// a much better quality image regardless of dimensions while only having a 
/// slightly larger image size than the bitmap.GetThumbnailImage method.
/// Also general code cleanup
///     
/// Update August 12 2009
/// Code was reorganized for better maintainability. Use of USING blocks were added
/// for bitmap and graphic objects.
/// 
/// Update November 24 2009 - Added ability to rotate images 90, 180, and 270 degrees.
/// 
/// Update December 15 2009 - Fixed a bug that didn't resize portrait images properly.
/// </summary>
public class ImageHandler : IHttpHandler
{
    // incoming query string variables
    private string imagePath = string.Empty;        // path=    virtual path required
    private string imageWidth = "0";                // w=   0 means use original
    private string imageHeight = "0";               // h=
    private string imageQuality = "0";              // q=
    private string constrainProportions = "true";   // cp=  true or false
    private string rotation = "0";                  // r= rotation in degrees, optional, 0 is default.
 
    private const Int32 maxQuality = 100;
    private const Int32 minQuality = 30;
    private const string imageNotFound = "~/App_Images/image_not_found.jpg";
    private const string mimeType = "image/jpeg";
 
    public void ProcessRequest(HttpContext context)
    {
        // get query string parameters
        imagePath = context.Request.QueryString["path"];
        imageWidth = context.Request.QueryString["w"];
        imageHeight = context.Request.QueryString["h"];
        imageQuality = context.Request.QueryString["q"];
        constrainProportions = context.Request.QueryString["cp"];
        rotation = context.Request.QueryString["r"];
 
        // sets the HTTP MIME type of the output stream.
        // jpeg is default       
        context.Response.ContentType = mimeType;
 
        // clear all content output from the buffer stream
        context.Response.Clear();
 
        // response is cacheable by clients and shared (proxy) caches
        context.Response.Cache.SetCacheability(HttpCacheability.Public);
 
        // Buffer response so that page is sent
        // after processing is complete.
        context.Response.BufferOutput = true;
 
        if (System.IO.File.Exists(HttpContext.Current.Server.MapPath(imagePath)))
        {
            imagePath = HttpContext.Current.Server.MapPath(imagePath);
        }
        else
        {
            // displays an empty image holder or make sure you have a not found image
            imageQuality = "100";
            imagePath = HttpContext.Current.Server.MapPath(imageNotFound);
        }
 
        Int32 originalWidth = 0;
        Int32 originalHeight = 0;
        Int32 newWidth = 0;
        Int32 newHeight = 0;
        Int64 quality = 0;
 
        // set the quality of the jpeg image returned.. 30 (low) - 100 (high)
        if (Int64.TryParse(imageQuality, out quality))
        {
            if (quality > maxQuality) { quality = maxQuality; }
            if (quality < minQuality) { quality = minQuality; }
        }
        else
        { quality = maxQuality; }
 
        // ENCODING
        System.Drawing.Imaging.Encoder encoder =
          System.Drawing.Imaging.Encoder.Quality;
 
        System.Drawing.Imaging.EncoderParameter encoderParameter =
            new System.Drawing.Imaging.EncoderParameter(encoder, quality);
 
        // create an array with one parameter; quality
        System.Drawing.Imaging.EncoderParameters codecParams =
            new System.Drawing.Imaging.EncoderParameters(1);
        codecParams.Param[0] = encoderParameter;
 
        // Gets the codecs for this image type (jpeg)
        System.Drawing.Imaging.ImageCodecInfo codecInfo = getEncoderInfo();
 
        // Create a bitmap to hold new image, uses Bitmap.GetThumbnailImage
        using (System.Drawing.Bitmap bitmapOriginal = new System.Drawing.Bitmap(imagePath))
        {
            originalWidth = bitmapOriginal.Width;
            originalHeight = bitmapOriginal.Height;
 
            Int32.TryParse(imageWidth, out newWidth);
            Int32.TryParse(imageHeight, out newHeight);
 
            if (newWidth > originalWidth) { newWidth = originalWidth; }
            if (newHeight > originalHeight) { newHeight = originalHeight; }
 
            // Constrain Proportions provides scaling by width
            // False by default which forces width and height - when exact size required
            if (!string.IsNullOrEmpty(constrainProportions)
                && constrainProportions.Equals("true", StringComparison.OrdinalIgnoreCase))
            {
                newWidth = Convert.ToInt32(imageWidth);
                Double scaleFactor = 0.0;
                // scale the image based on the width
                if (originalWidth > originalHeight)
                {
                    // landscape
                    Int32 largestDimension = (Math.Max(originalWidth, originalHeight));
                    scaleFactor = ((Double)newWidth / (Double)largestDimension);
                }
                else
                {
                    // portrait
                    Int32 smallestDimension = (Math.Min(originalWidth, originalHeight));
                    scaleFactor = ((Double)newWidth / (Double)smallestDimension);
                }
                newHeight = Convert.ToInt32((originalHeight * scaleFactor));
            }
 
            // Create the new bitmap image
            using (System.Drawing.Bitmap bitmapNew =
                new System.Drawing.Bitmap(newWidth, newHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
            {
                // use the same resolution
                bitmapNew.SetResolution(bitmapOriginal.HorizontalResolution, bitmapOriginal.VerticalResolution);
 
                // Create the graphics object to draw the old bitmap on the new sized bitmap.
                using (System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage(bitmapNew))
                {
                    // resize using the highest possible quality for best results
                    graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
 
                    graphic.DrawImage(bitmapOriginal,
                        new System.Drawing.Rectangle(0, 0, newWidth, newHeight), // destination
                        new System.Drawing.Rectangle(0, 0, originalWidth, originalHeight), //source
                        System.Drawing.GraphicsUnit.Pixel); //using pixels
                }
 
                // Rotate the bitmap
                switch (Convert.ToInt32(rotation))
                {
                    case 90:
                        bitmapNew.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
                        break;
                    case 180:
                        bitmapNew.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
                        break;
                    case 270:
                        bitmapNew.RotateFlip(System.Drawing.RotateFlipType.Rotate270FlipNone);
                        break;
                    default:
                        // always show as original
                        bitmapNew.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipNone);
                        break;
                }
 
                // context.Response.OutputStream enables binary output to the outgoing HTTP content body.
                // codecInfo is the image type (jpeg)
                // codecParams is the quality
                bitmapNew.Save(context.Response.OutputStream, codecInfo, codecParams);
            }
        }
    }
 
    /// <summary>
    /// Return the codec info for jpeg images
    /// </summary>
    /// <param name="mimeType"></param>
    /// <returns></returns>
    private System.Drawing.Imaging.ImageCodecInfo getEncoderInfo()
    {
        System.Drawing.Imaging.ImageCodecInfo[] encoders;
        encoders = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
 
        Int32 i = 0;
        while (i < encoders.Length)
        {
            if (encoders[i].MimeType == mimeType)
            {
                return encoders[i];
            }
            i++;
        }
 
        return null;
    }
 
    public bool IsReusable
    { get { return false; } }
}

Live Demo

Original image size was 1024x768

The image was then scaled to 300x225, 200x150, and 100x75 with a quality setting of 50.

300 200 100
Welcome to DotNetManiac