Creating great thumbnails in ASP.NET

The built in function for creating thumbnails in ASP.NET is extremely convenient and very simple to implement.

int width = 190;
int height = 190;
Bitmap source = new Bitmap("c:\someimage.gif");
System.Drawing.Image thumb = source.GetThumbnailImage(width,height,null,IntPtr.Zero);

 original (31.7k)

The trouble is that it produces relatively poor quality results and excessively large file sizes. The thumbnails tend to look very muddy when using this route, but many times it's good enough for whatever your needs may be.

An Alternative

The alternative that I use regularly involves redrawing the image using the System.Drawing.Graphics library. It is very simple to implement but produces superior results if for no other reason than its file size. The following is the standard function I use for creating thumbnails.

public static Bitmap CreateThumbnail(Bitmap source, int thumbWi, int thumbHi, bool maintainAspect)
        {
            // return the source image if it's smaller than the designated thumbnail
            if (source.Width < thumbWi && source.Height < thumbHi) return source;

            System.Drawing.Bitmap ret = null;
            try
            {
                int wi, hi;

                wi = thumbWi;
                hi = thumbHi;

                if (maintainAspect)
                {
                    // maintain the aspect ratio despite the thumbnail size parameters
                    if (source.Width > source.Height)
                    {
                        wi = thumbWi;
                        hi = (int)(source.Height * ((decimal)thumbWi / source.Width));
                    }
                    else
                    {
                        hi = thumbHi;
                        wi = (int)(source.Width * ((decimal)thumbHi / source.Height));
                    }
                }

                // original code that creates lousy thumbnails
                // System.Drawing.Image ret = source.GetThumbnailImage(wi,hi,null,IntPtr.Zero);
                ret = new Bitmap(wi, hi);
                using (Graphics g = Graphics.FromImage(ret))
                {
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.FillRectangle(Brushes.White, 0, 0, wi, hi);
                    g.DrawImage(source, 0, 0, wi, hi);
                }
            }
            catch
            {
                ret = null;
            }

            return ret;
        }

aG8klKbxhE-h43x2_tLGsw (10.5k)

This function is handy because it's parameters include a flag for maintaining the aspect ratio of the image along with the thumbnail size you would like. The thumbnail magic happens in this portion of the code:

                using (Graphics g = Graphics.FromImage(ret))
                {
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.FillRectangle(Brushes.White, 0, 0, wi, hi);
                    g.DrawImage(source, 0, 0, wi, hi);
                }

This method is slightly slower but the results are hard to ignore as illustrated by the comparison below:

(31.7k)original aG8klKbxhE-h43x2_tLGsw (10.5k)

Pretty nice improvement in both file size and quality I would say, but....

We can do even better

Now we can add into the mix some JPEG compression and really optimize the results. I won't pretend to fully understand how the JPEG compression code below works, but it sure does the trick.

                //Configure JPEG Compression Engine
                System.Drawing.Imaging.EncoderParameters encoderParams = new System.Drawing.Imaging.EncoderParameters();
                long[] quality = new long[1];
                quality[0] = 75;
                System.Drawing.Imaging.EncoderParameter encoderParam = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
                encoderParams.Param[0] = encoderParam;

                System.Drawing.Imaging.ImageCodecInfo[] arrayICI = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
                System.Drawing.Imaging.ImageCodecInfo jpegICI = null;
                for (int x = 0; x < arrayICI.Length; x++)
                {
                    if (arrayICI[x].FormatDescription.Equals("JPEG"))
                    {
                        jpegICI = arrayICI[x];
                        break;
                    }
                }

This code will set up the encoderParameters needed for saving the new compressed thumbnail. The quality[0] value is where you set the compression level. I've had success going as low as a value of 40 for some applications, but when quality is a major requirement I find 75 to do very well. To use this engine you would execute the JPEG Compression code before you save your thumbnail, then use its encoderParamaters as a parameter when saving. For example:

	System.Drawing.Image myThumbnail = CreateThumbnail(myBitmap,Width,Height,false);                

	//Configure JPEG Compression Engine
                System.Drawing.Imaging.EncoderParameters encoderParams = new System.Drawing.Imaging.EncoderParameters();
                long[] quality = new long[1];
                quality[0] = 75;
                System.Drawing.Imaging.EncoderParameter encoderParam = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
                encoderParams.Param[0] = encoderParam;

                System.Drawing.Imaging.ImageCodecInfo[] arrayICI = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
                System.Drawing.Imaging.ImageCodecInfo jpegICI = null;
                for (int x = 0; x < arrayICI.Length; x++)
                {
                    if (arrayICI[x].FormatDescription.Equals("JPEG"))
                    {
                        jpegICI = arrayICI[x];
                        break;
                    }
                }
	
	myThumbnail.Save(Path.Combine(SavePathThumb, fileName), jpegICI, encoderParams);
                myThumbnail.Dispose();

compressed (2.39k)

Which still looks pretty darn good for 2.39k.

Conclusion and final comparison

Here is the final comparison between the 3 thumbnails going from largest file size to smallest:

original aG8klKbxhE-h43x2_tLGsw compressed

Largest = 31.7k

Uncompressed redraw = 10.5k (67% smaller)

Compressed redraw = 2.39k (92% smaller)

It's hard to ignore those results. The source code for the thumbnail function and the JPEG compression engine are below.

ThumbnailGenerator.cs (1.97 kb)

JPEGCompressionConfig.cs (969.00 bytes)

kick it on DotNetKicks.com


Search

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008