76

I want to scale a System.Drawing.Bitmap to at least less than some fixed width and height. This is to generate thumbnails for an image gallery on a website, so I want to keep the aspect ratio the same.

I have some across quite a few solutions but none seem to really do what I need; they revolve around scaling based on keeping the width or the height the same but not changing both.

An example:

If I have a 4272 by 2848 image and I want to scale it to a size of 1024 by 768, then the resulting image should be 1024 by 683 and padded (with a black border) to 1024 by 768.

How can I do this with images larger than the required size and smaller than the require sized and also pad images which don't come out to the exact size I need once scaled?

Michael J. Gray
  • 9,784
  • 6
  • 38
  • 67
  • Why can't you just do the math to calculate the other dimension? – Cody Gray - on strike May 04 '12 at 07:55
  • @CodyGray What "other" dimension exactly? It's a 2 dimensional image that needs to be scaled down while maintaining its aspect ratio. I've tried just taking the aspect ratio and figuring out a common denominator for the width and height that's close to the target size, and it never worked right. – Michael J. Gray May 04 '12 at 08:02

3 Answers3

197

The bitmap constructor has resizing built in.

Bitmap original = (Bitmap)Image.FromFile("DSC_0002.jpg");
Bitmap resized = new Bitmap(original,new Size(original.Width/4,original.Height/4));
resized.Save("DSC_0002_thumb.jpg");

http://msdn.microsoft.com/en-us/library/0wh0045z.aspx

If you want control over interpolation modes see this post.

WHol
  • 2,206
  • 2
  • 13
  • 16
  • 2
    Thank you. This helped a lot. I know this is a bit of an old answer, but is there a reason why my new image is fuzzy looking? Do I need to set any interpolation modes? – theGreenCabbage Jan 08 '14 at 18:18
  • 2
    Doesn't this just draw the original bitmap inside a new smaller bitmap? A part of the image should just be cut of. – DerpyNerd Oct 02 '14 at 17:02
  • 5
    *Doesn't this just draw the original bitmap inside a new smaller bitmap?* The link to TFM is right there: *Initializes a new instance of the Bitmap class from the specified existing image, **scaled to the specified size**.* – ta.speot.is Mar 08 '15 at 10:37
69

Target parameters:

float width = 1024;
float height = 768;
var brush = new SolidBrush(Color.Black);

Your original file:

var image = new Bitmap(file);

Target sizing (scale factor):

float scale = Math.Min(width / image.Width, height / image.Height);

The resize including brushing canvas first:

var bmp = new Bitmap((int)width, (int)height);
var graph = Graphics.FromImage(bmp);

// uncomment for higher quality output
//graph.InterpolationMode = InterpolationMode.High;
//graph.CompositingQuality = CompositingQuality.HighQuality;
//graph.SmoothingMode = SmoothingMode.AntiAlias;

var scaleWidth = (int)(image.Width * scale);
var scaleHeight = (int)(image.Height * scale);

graph.FillRectangle(brush, new RectangleF(0, 0, width, height));
graph.DrawImage(image, ((int)width - scaleWidth)/2, ((int)height - scaleHeight)/2, scaleWidth, scaleHeight);

And don't forget to do a bmp.Save(filename) to save the resulting file.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
yamen
  • 15,390
  • 3
  • 42
  • 52
  • 3
    This creates a solid black image. – Michael J. Gray May 04 '12 at 08:30
  • Works perfectly for me, no need to downvote unless you're sure you've found the issue. Remove the `FillRectangle` line and see what you get. Just tested again and it works fine. I'm on .NET 4.0 if that makes a difference. – yamen May 04 '12 at 08:35
  • It doesn't work if the target dimensions are both less than the image's dimensions. `scale` becomes 0.0. It also won't preserve the aspect ratio at all, from what I can tell. – Michael J. Gray May 04 '12 at 08:39
  • 4
    Change `var scale` to `float scale`. I'm guessing somewhere in your code your `width` and `height` are perhaps declared as `int` which causes problems. It works just fine if you follow what I've given closely. The whole point of `scale` is to preserve aspect ratio. Remove the -1 when you're done. – yamen May 04 '12 at 08:41
  • It looks "better" now with changing things to floats, yes. That was an issue. I suppose that's one reason I rarely use `var` personally! I noticed it and was about to respond and then saw your comment. Anyway, it produces essentially a black bar across the bottom of some images instead of the border I mentioned. Is there a way to make it a center scaling? Center scaling was the main problem I was having. – Michael J. Gray May 04 '12 at 08:44
  • 11
    I know this is old, but this helped me, so thankyou. However, I wrapped **graph** in a using block as it implements IDisposable. – JMK Feb 08 '13 at 16:28
  • 3
    I see this is old too, but just though i'd mention Graphics is Disposable(), so don't forget to put that graphics object into a using statement – iedoc Jul 06 '15 at 21:13
  • Yeah, what @yamen said. When I replaced width with Bounds.Width of my control I double checked and that value is an int. That'll throw things off by a lot. So typecast it before, like so, `float scale = Math.Min((float)Bounds.Width / image.Width, (float)Bounds.Height / image.Height);` No need to typecast the image.Width and image.Height which is also an int, because float / int is a float. – Brad Moore Sep 21 '15 at 00:36
  • I wish I can add +2 for the maths. – digitalman Aug 07 '16 at 12:04
  • 3
    Is there a specific reason for using the `FillRectangle` method over the `Clear` method? This would save having to create a brush and disposing it. – RobIII Sep 15 '17 at 10:06
4

Just to add to yamen's answer, which is perfect for images but not so much for text.

If you are trying to use this to scale text, like say a Word document (which is in this case in bytes from Word Interop), you will need to make a few modifications or you will get giant bars on the side.

May not be perfect but works for me!

using (MemoryStream ms = new MemoryStream(wordBytes))
{
    float width = 3840;
    float height = 2160;
    var brush = new SolidBrush(Color.White);

    var rawImage = Image.FromStream(ms);
    float scale = Math.Min(width / rawImage.Width, height / rawImage.Height);
    var scaleWidth  = (int)(rawImage.Width  * scale);
    var scaleHeight = (int)(rawImage.Height * scale);
    var scaledBitmap = new Bitmap(scaleWidth, scaleHeight);

    Graphics graph = Graphics.FromImage(scaledBitmap);
    graph.InterpolationMode = InterpolationMode.High;
    graph.CompositingQuality = CompositingQuality.HighQuality;
    graph.SmoothingMode = SmoothingMode.AntiAlias;
    graph.FillRectangle(brush, new RectangleF(0, 0, width, height));
    graph.DrawImage(rawImage, new Rectangle(0, 0 , scaleWidth, scaleHeight));

    scaledBitmap.Save(fileName, ImageFormat.Png);
    return scaledBitmap;
}
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Trent
  • 41
  • 2