132

Using System.Drawing.Image.

If an image width or height exceed the maximum, it need to be resized proportionally . After resized it need to make sure that neither width or height still exceed the limit.

The Width and Height will be resized until it is not exceed to maximum and minimum automatically (biggest size possible) and also maintain the ratio.

Alex Aza
  • 76,499
  • 26
  • 155
  • 134
Sarawut Positwinyu
  • 4,974
  • 15
  • 54
  • 80
  • @Sarawut Positwinyu - But what aspect ratio do you want ? – Bibhu Jun 28 '11 at 05:26
  • What do you want to happen if an image can't be resized to a max and min of the height and width and the aspect ratio is maintained? – Conrad Frix Jun 28 '11 at 05:29
  • @Bibhu Is there many type of aspect ratio ? i don't know about that. I just want the image ratio to be similar as original image ratio ad possible. – Sarawut Positwinyu Jun 28 '11 at 05:33
  • @Sarawut Positwinyu - look at this wiki link for more about aspect ratio. http://en.wikipedia.org/wiki/Aspect_ratio_%28image%29 – Bibhu Jun 28 '11 at 05:35
  • @Conrad Frix Is there any case like that ? i really don't know but if it has really no way to maintain the ratio. to reduce the size to be less than maximum size is more important, there is no minimum size required. – Sarawut Positwinyu Jun 28 '11 at 05:37
  • @Bibhu Thankyou, i may misuse the word "aspect ratio" – Sarawut Positwinyu Jun 28 '11 at 05:38
  • 1
    @Sarawut Positwinyu You didn't misuse the term aspect ratio. Or if you did you're in [good company](http://windows.microsoft.com/en-US/windows7/Resize-a-picture-using-Paint) – Conrad Frix Jun 28 '11 at 05:50

3 Answers3

323

Like this?

public static void Test()
{
    using (var image = Image.FromFile(@"c:\logo.png"))
    using (var newImage = ScaleImage(image, 300, 400))
    {
        newImage.Save(@"c:\test.png", ImageFormat.Png);
    }
}

public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
    var ratioX = (double)maxWidth / image.Width;
    var ratioY = (double)maxHeight / image.Height;
    var ratio = Math.Min(ratioX, ratioY);

    var newWidth = (int)(image.Width * ratio);
    var newHeight = (int)(image.Height * ratio);

    var newImage = new Bitmap(newWidth, newHeight);

    using (var graphics = Graphics.FromImage(newImage))
        graphics.DrawImage(image, 0, 0, newWidth, newHeight);

    return newImage;
}
Alex Aza
  • 76,499
  • 26
  • 155
  • 134
  • 8
    @Alex nice use of Math.Min (I always forget about that one) – Conrad Frix Jun 28 '11 at 05:50
  • 5
    I'd suggest you use a using statement on the Graphics object at least to save some resources :) – Schalk Jun 28 '11 at 07:07
  • I just thinking about a case, i don't sure if it possible or not that after multiply with ratio the width Or height might still larger than max width or max height. – Sarawut Positwinyu Jun 28 '11 at 10:37
  • @Sarawut Positwinyu - Although I can't see it, the code could have a bug. Can you give an example, when it does not work? – Alex Aza Jun 28 '11 at 16:24
  • @Alex Aza, After experimenting your code and trying to make it cause a bug, i have found that it is impossible. The code is so thoughtful. – Sarawut Positwinyu Jun 29 '11 at 02:50
  • 4
    Also make sure you are using System.Drawing.Image if using asp.net. – Induster Aug 18 '12 at 18:52
  • Correct me If I am wrong.. But if the aspect ratio of the maxWidth and maxHeight is different than the original images aspect ratio, won't this deform the image? The calculation should caclulate a properly aspected resize within the bounding box (maxWidth/maxHeight). Which would result in an image that is smaller than the bounding box in almost all scenarios. This fits the bounding box no matter what, which is wrong most the time. – Ryan Mann Sep 30 '15 at 03:43
  • @AlexAza can you see this: http://stackoverflow.com/questions/33865130/out-of-memory-exception-while-uploading-multiple-images-asynchronously – Sandip Bantawa Dec 22 '15 at 15:13
  • @AlexAza, I've used your code and added var x = 0; var y = 0; if (newWidth > newHeight) { y = (newWidth - newHeight) / 2; } else if (newHeight > newWidth) { x = (newHeight - newWidth) / 2; } to center scaled image – Andrii Muzychuk Apr 15 '16 at 09:27
  • @AlexAza what about returning the image without saving? – Smith Aug 24 '16 at 17:46
  • 1
    @Smith - don't run Save method if you don't need to save the image. This is exactly what my ScaleImage method does - returns image without saving it. – Alex Aza Aug 25 '16 at 07:29
7

Much longer solution, but accounts for the following scenarios:

  1. Is the image smaller than the bounding box?
  2. Is the Image and the Bounding Box square?
  3. Is the Image square and the bounding box isn't
  4. Is the image wider and taller than the bounding box
  5. Is the image wider than the bounding box
  6. Is the image taller than the bounding box

    private Image ResizePhoto(FileInfo sourceImage, int desiredWidth, int desiredHeight)
    {
        //throw error if bouning box is to small
        if (desiredWidth < 4 || desiredHeight < 4)
            throw new InvalidOperationException("Bounding Box of Resize Photo must be larger than 4X4 pixels.");            
        var original = Bitmap.FromFile(sourceImage.FullName);
    
        //store image widths in variable for easier use
        var oW = (decimal)original.Width;
        var oH = (decimal)original.Height;
        var dW = (decimal)desiredWidth;
        var dH = (decimal)desiredHeight;
    
        //check if image already fits
        if (oW < dW && oH < dH)
            return original; //image fits in bounding box, keep size (center with css) If we made it bigger it would stretch the image resulting in loss of quality.
    
        //check for double squares
        if (oW == oH && dW == dH)
        {
            //image and bounding box are square, no need to calculate aspects, just downsize it with the bounding box
            Bitmap square = new Bitmap(original, (int)dW, (int)dH);
            original.Dispose();
            return square;
        }
    
        //check original image is square
        if (oW == oH)
        {
            //image is square, bounding box isn't.  Get smallest side of bounding box and resize to a square of that center the image vertically and horizontally with Css there will be space on one side.
            int smallSide = (int)Math.Min(dW, dH);
            Bitmap square = new Bitmap(original, smallSide, smallSide);
            original.Dispose();
            return square;
        }
    
        //not dealing with squares, figure out resizing within aspect ratios            
        if (oW > dW && oH > dH) //image is wider and taller than bounding box
        {
            var r = Math.Min(dW, dH) / Math.Min(oW, oH); //two dimensions so figure out which bounding box dimension is the smallest and which original image dimension is the smallest, already know original image is larger than bounding box
            var nH = oH * r; //will downscale the original image by an aspect ratio to fit in the bounding box at the maximum size within aspect ratio.
            var nW = oW * r;
            var resized = new Bitmap(original, (int)nW, (int)nH);
            original.Dispose();
            return resized;
        }
        else
        {
            if (oW > dW) //image is wider than bounding box
            {
                var r = dW / oW; //one dimension (width) so calculate the aspect ratio between the bounding box width and original image width
                var nW = oW * r; //downscale image by r to fit in the bounding box...
                var nH = oH * r;
                var resized = new Bitmap(original, (int)nW, (int)nH);
                original.Dispose();
                return resized;
            }
            else
            {
                //original image is taller than bounding box
                var r = dH / oH;
                var nH = oH * r;
                var nW = oW * r;
                var resized = new Bitmap(original, (int)nW, (int)nH);
                original.Dispose();
                return resized;
            }
        }
    }
    
wloescher
  • 4,503
  • 2
  • 25
  • 27
Ryan Mann
  • 5,178
  • 32
  • 42
5

Working Solution :

For Resize image with size lower then 100Kb

WriteableBitmap bitmap = new WriteableBitmap(140,140);
bitmap.SetSource(dlg.File.OpenRead());
image1.Source = bitmap;

Image img = new Image();
img.Source = bitmap;
WriteableBitmap i;

do
{
    ScaleTransform st = new ScaleTransform();
    st.ScaleX = 0.3;
    st.ScaleY = 0.3;
    i = new WriteableBitmap(img, st);
    img.Source = i;
} while (i.Pixels.Length / 1024 > 100);

More Reference at http://net4attack.blogspot.com/

user806084
  • 51
  • 1
  • 2