Redimensione a imagem proporcionalmente com as restrições MaxHeight e MaxWidth

124

Usando System.Drawing.Image.

Se a largura ou altura da imagem exceder o máximo, ela deverá ser redimensionada proporcionalmente. Depois de redimensionado, é necessário garantir que nem a largura nem a altura ainda excedam o limite.

A Largura e a Altura serão redimensionadas até que não sejam excedidas para o máximo e o mínimo automaticamente (maior tamanho possível) e também manterão a proporção.

Sarawut Positwinyu
fonte
@Sarawut Positwinyu - Mas qual proporção você deseja?
Bibhu
O que você deseja que aconteça se uma imagem não puder ser redimensionada para um máximo e um mínimo de altura e largura e a proporção for mantida?
Conrad Frix
@Bibhu Há muitos tipos de proporção? eu não sei sobre isso Eu só quero que a proporção da imagem seja semelhante à possível proporção da imagem original.
Sarawut Positwinyu
@Sarawut Positwinyu - veja este link do wiki para saber mais sobre a proporção. en.wikipedia.org/wiki/Aspect_ratio_%28image%29
Bibhu 28/06
1
@Sarawut Positwinyu Você não usou o termo proporção de forma incorreta. Ou se você fez você está em boa companhia
Conrad Frix

Respostas:

300

Como isso?

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
fonte
7
Utilização agradável @Alex de Math.min (eu sempre esquecer que um)
Conrad Frix
5
Eu sugiro que você use uma instrução usando o objeto Graphics, pelo menos, para salvar alguns recursos :)
Schalk
Eu só estou pensando em um caso, não sei se é possível ou não que, depois de multiplicar com a proporção, a largura ou a altura ainda possam ser maiores que a largura máxima ou a altura máxima.
Sarawut Positwinyu
4
Verifique também se você está usando System.Drawing.Image se estiver usando asp.net.
Induster
1
@ Smith - não execute o método Save se não precisar salvar a imagem. É exatamente isso que meu método ScaleImage faz - retorna a imagem sem salvá-la.
precisa
5

Solução de trabalho:

Para redimensionar imagem com tamanho inferior a 100 KB

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);

Mais referência em http://net4attack.blogspot.com/

user806084
fonte
5

Solução muito mais longa, mas é responsável pelos seguintes cenários:

  1. A imagem é menor que a caixa delimitadora?
  2. A imagem e a caixa delimitadora estão quadradas?
  3. O quadrado Imagem e a caixa delimitadora não
  4. A imagem é mais larga e mais alta que a caixa delimitadora
  5. A imagem é mais larga que a caixa delimitadora
  6. A imagem é mais alta que a caixa delimitadora

    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;
            }
        }
    }
Ryan Mann
fonte
1
Eu acho que existem alguns erros de digitação nos quais você está usando a proporção para calcular a nova altura da imagem redimensionada. Var nH correto = oH * r; Incorreto: var nH = oW * r;
Wloescher 21/07
Corrigido, apenas nunca comentado.
Ryan Mann