The other day, I was given the requirement to be able to dynamically resize a JPEG image server-side before rendering it down to the browser, thus reducing unnecessary bandwidth usage between the server and the client.
Clearly it would be more efficient to store the original image in the different sizes required, as resizing the images on-the-fly would put an unnecessary additional load on the server, however in this case it wasn't an option
Firstly we are going to need the System.Drawing
and System.Drawing.Drawing2D
namespaces:
// C#
using System.Drawing;
using System.Drawing.Drawing2D;
' Visual Basic
Imports System.Drawing
Imports System.Drawing.Drawing2D
Secondly the signature for our method which is going to do the resizing:
// C#
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
}
' Visual Basic
Public Shared Function ResizeImage(ByVal image As Image, ByVal size As Size, Optional ByVal preserveAspectRatio As Boolean = True) As Image
End Function
As you can see, the method takes two mandatory parameters: the image to be resized and the new size it is to be resized to. An optional third parameter specifies whether to preserve the image's original aspect ratio.
If we are not going to preserve the aspect ratio of the original image, then we simply set the height and width of the new image accordingly. However, if we do wish to preserve the aspect ratio, then the situation is a little more complex: Firstly we need to calculate the percentage difference, in each axis (height and width), between the original image and the desired size. Then we use whichever difference is the smaller to calculate the new height and width of the new image:
// C#
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = image.Width;
int originalHeight = image.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
' Visual Basic
Dim newWidth As Integer
Dim newHeight As Integer
If preserveAspectRatio Then
Dim originalWidth As Integer = image.Width
Dim originalHeight As Integer = image.Height
Dim percentWidth As Single = CSng(size.Width) / CSng(originalWidth)
Dim percentHeight As Single = CSng(size.Height) / CSng(originalHeight)
Dim percent As Single = If(percentHeight < percentWidth, percentHeight, percentWidth)
newWidth = CInt(originalWidth * percent)
newHeight = CInt(originalHeight * percent)
Else
newWidth = size.Width
newHeight = size.Height
End If
Next, we create a blank bitmap using our new dimensions:
// C#
Image newImage = new Bitmap(newWidth, newHeight);
' Visual Basic
Dim newImage As Image = New Bitmap(newWidth, newHeight)
Finally, we use the graphics handle of the new bitmap to draw the original image onto our new bitmap and return it:
// C#
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
' Visual Basic
Using graphicsHandle As Graphics = Graphics.FromImage(newImage)
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight)
End Using
Return newImage
You can experiment with the interpolation mode to vary the quality of the resized image. Personally, I found HighQualityBicubic
to give the best results. The code for the complete method is as follows:
// C#
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = image.Width;
int originalHeight = image.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
Image newImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
}
' Visual Basic
Public Shared Function ResizeImage(ByVal image As Image, ByVal size As Size, Optional ByVal preserveAspectRatio As Boolean = True) As Image
Dim newWidth As Integer
Dim newHeight As Integer
If preserveAspectRatio Then
Dim originalWidth As Integer = image.Width
Dim originalHeight As Integer = image.Height
Dim percentWidth As Single = CSng(size.Width) / CSng(originalWidth)
Dim percentHeight As Single = CSng(size.Height) / CSng(originalHeight)
Dim percent As Single = If(percentHeight < percentWidth, percentHeight, percentWidth)
newWidth = CInt(originalWidth * percent)
newHeight = CInt(originalHeight * percent)
Else
newWidth = size.Width
newHeight = size.Height
End If
Dim newImage As Image = New Bitmap(newWidth, newHeight)
Using graphicsHandle As Graphics = Graphics.FromImage(newImage)
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight)
End Using
Return newImage
End Function
We can then call the method at an appropriate point to resize the image. In this example, I simply read the original image from disk and then save the resized image to a MemoryStream
for use later in the application:
// C#
Image original = Image.FromFile(@"C:\path\to\some.jpg");
Image resized = ResizeImage(original, new Size(1024, 768));
MemoryStream memStream = new MemoryStream();
resized.Save(memStream, ImageFormat.Jpeg);
' Visual Basic
Dim original As Image = Image.FromFile("C:\path\to\some.jpg")
Dim resized As Image = ResizeImage(original, New Size(1024, 768))
Dim memStream As MemoryStream = New MemoryStream()
resized.Save(memStream, ImageFormat.Jpeg)
Thanks, it's very nice described example!
ReplyDelete