.NET Image Scaling in CSharp

94Tr One feature of .NET that I use regularly is image scaling.  I typically use this on web sites that need image upload capabilities.  I assume the user is going to send me an image that is significantly bigger than what I could use and then scale it down before storing it.

I also use this feature so that I only have to store one image.  If I need it at a different size, I can scale it prior to sending it down to the browser.  In this case, I implement .NET page caching so that I don’t have to run the scaling code every time the image is requested.

Image scaling really isn’t that hard.  Here’s how it’s done.

Supporting Stuff

I do image scaling so much that I created a class for it.  It has a supporting property called OriginalImage that is of type System.Drawing.Image that accepts the original image object.  It is supported by a member variable named m_Image that you will see in the rest of my code.

It also has a supporting method named GetEncoderInfo() that returns image Encoder information based on the MIME type that is passed in to it.  Here’s that code:

public System.Drawing.Imaging.ImageCodecInfo 
    GetEncoderInfo(String mimeType)
{
    int i;
    ImageCodecInfo[] encoders;
    encoders = ImageCodecInfo.GetImageEncoders();
    for (i = 0; i < encoders.Length; ++i)
    {
        if (encoders[i].MimeType == mimeType)
            return encoders[i];
    }
    return null;
}

The main method that does the scaling has the following signature

public byte[] GenerateThumbNail( 
    int Width, int Height)
{
}

Notice that I’m returning a byte array.  This is because the information is either going to be streamed back to the web browser or stored into a field in the database.  I find storing images in the database much easier to manage than storing them on the file system since I don’t have to keep track of file names.  It’s a personal preference thing.  You could use the byte array to store to the file system if you wanted.

Scaling

The width and height parameters take the rectangle I want the image to fit into.  I’ve also written my code so that if the parameter is zero, it is ignored.  That is, if we pass in a width of zero and a height of 20, the image is scaled so that it is always 20 pixels wide and the height will be scaled so the image maintains the same aspect ratio.

Here’s the code that determines the scaling ratio

int OriginalWidth = m_Image.Width;
int OriginalHeight = m_Image.Height;
double scale=0.0;

// Scale the image.
// if maxHeight = 0, scale to maxWidth
if (Height == 0)
{
    scale = (double)Width / (double)m_Image.Width;
}
    // if maxWidth = 0, scale to maxHeight
else if (Width == 0)
{
    scale = (double)Height / (double)m_Image.Height;
}
    // if maxHeight >= maxWidth /w (scale by maxWidth)
else if (((double)Height) >= m_Image.Height * 
    ((double)Width) / ((double)m_Image.Width))
{
    scale = (double)Width / (double)m_Image.Width;
}
    // if maxWidth > maxHeight/h (scale by maxHeight)
else
{
    scale = (double)Height / (double)m_Image.Height;
}

Height = Convert.ToInt32(m_Image.Height * scale);
Width = Convert.ToInt32(m_Image.Width * scale);

That was probably the hardest part of the code to write.  Getting the math right is what makes scaling difficult–the rest is just API calls.

Next we create a Bitmap object and turn that into a Graphic object.  The properties I set after creating the Graphic object are the property settings that I’ve found produce the best final image.

The last line of code draws the original image into the Graphic object scaled to the new size.

Graphics graphic = Graphics.FromImage(returnBitmap);
graphic.CompositingMode = 
    System.Drawing.Drawing2D.CompositingMode.SourceOver;
graphic.CompositingQuality = 
    System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphic.SmoothingMode = 
    System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphic.InterpolationMode = 
    System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphic.PixelOffsetMode = 
    System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;

graphic.DrawImage(m_Image, new Rectangle(0, 0, Width, Height));

Convert To Image Type and Return

This final bit of code turns the image into a jpg file, stores the image into a memory stream and then converts the memory stream to the byte array that the method returns.  I compress at 99 because that’s what consistently gives me the best image in the end.

EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
System.Drawing.Imaging.Encoder myEncoder;
long Compression = 99L;

myEncoder = 
    System.Drawing.Imaging.Encoder.Quality;
myEncoderParameters = 
    new EncoderParameters(1);
myEncoderParameter = 
    new EncoderParameter(myEncoder, Compression);
myEncoderParameters.Param[0] = 
    myEncoderParameter;

System.IO.MemoryStream ms =        
    new System.IO.MemoryStream();
returnBitmap.Save(ms, 
    GetEncoderInfo("image/jpeg"), 
    myEncoderParameters);
m_Image.Dispose();
returnBitmap.Dispose();
graphic.Dispose();

byte[] returnArray = ms.GetBuffer();
ms.Close();
        
return returnArray;

Related Post

  • Storing An Image To a Database in .NETStoring An Image To a Database in .NET Several weeks ago I mentioned that I store the images that the user uploads to the system into the database. Some of you have expressed an interest in how I do that.  So I plan to cover th...
  • Upload a File via WebRequest Using CSharpUpload a File via WebRequest Using CSharp I got this question a couple of weeks ago but just never had the time to put into answering fully.  But today I have some extra time due to the fact that I’m under-booked with projects. Th...
  • CSharp adds the var keyword!CSharp adds the var keyword! There have been several new features added to the CSharp language that will significantly reduce the amount of code that ends up in our source files.  It will not significantly reduce the amount of...
  • How Generics WorkHow Generics Work One of my fundamental beliefs about programming is that the best programmers understand what's actually happening under the hood. This is why it is beneficial to look at the code for the .NET fram...
  • Templated E-Mail using .NETTemplated E-Mail using .NET One thing I’m pretty consistent about is letting the computer do most of my work for me.  As a “programmer” I really don’t like to program.  I prefer to solve problems. You’ve already ...