Zmień rozmiar obrazu proporcjonalnie z ograniczeniami MaxHeight i MaxWidth

124

Korzystanie System.Drawing.Image.

Jeśli szerokość lub wysokość obrazu przekracza maksimum, należy proporcjonalnie zmienić jego rozmiar. Po zmianie rozmiaru należy upewnić się, że ani szerokość, ani wysokość nadal nie przekraczają limitu.

Szerokość i wysokość będą zmieniane do momentu, gdy nie zostaną automatycznie przekroczone do maksimum i minimum (największy możliwy rozmiar), a także utrzymają stosunek.

Sarawut Positwinyu
źródło
@Sarawut Positwinyu - Ale jaki współczynnik proporcji chcesz?
Bibhu
Co chcesz zrobić, jeśli nie można zmienić rozmiaru obrazu do maksymalnej i minimalnej wysokości i szerokości, a proporcje są zachowane?
Conrad Frix
@Bibhu Czy jest wiele typów współczynników proporcji? nie wiem o tym. Chcę tylko, aby proporcje obrazu były podobne do oryginalnych proporcji obrazu w reklamie.
Sarawut Positwinyu
@Sarawut Positwinyu - spójrz na to łącze wiki, aby uzyskać więcej informacji na temat współczynnika proporcji. en.wikipedia.org/wiki/Aspect_ratio_%28image%29
Bibhu
1
@Sarawut Positwinyu Nie nadużyłeś terminu współczynnik proporcji. A jeśli tak, to jesteś w dobrym towarzystwie
Conrad Frix

Odpowiedzi:

300

Lubię to?

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
źródło
7
@Alex dobre wykorzystanie Math.Min (zawsze o tym zapominam)
Conrad Frix
5
Sugerowałbym użycie instrukcji using na obiekcie Graphics przynajmniej w celu zaoszczędzenia części zasobów :)
Schalk
Właśnie myślę o przypadku, nie jestem pewien, czy to możliwe, czy nie, że po pomnożeniu przez współczynnik szerokość lub wysokość może nadal być większa niż maksymalna szerokość lub maksymalna wysokość.
Sarawut Positwinyu
4
Upewnij się również, że używasz System.Drawing.Image, jeśli używasz asp.net.
Induster
1
@Smith - nie uruchamiaj metody Save, jeśli nie musisz zapisywać obrazu. Dokładnie to robi moja metoda ScaleImage - zwraca obraz bez zapisywania go.
Alex Aza,
5

Rozwiązanie robocze:

W przypadku opcji Zmień rozmiar obrazu o rozmiarze mniejszym niż 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);

Więcej informacji na http://net4attack.blogspot.com/

user806084
źródło
5

Znacznie dłuższe rozwiązanie, ale uwzględnia następujące scenariusze:

  1. Czy obraz jest mniejszy niż obwiednia?
  2. Czy obraz i obwiednia są kwadratowe?
  3. Czy obraz jest kwadratowy, a obwiednia nie
  4. Czy obraz jest szerszy i wyższy niż obwiednia
  5. Czy obraz jest szerszy niż obwiednia
  6. Czy obraz jest wyższy niż obwiednia

    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
źródło
1
Myślę, że jest kilka literówek, w których używasz współczynnika do obliczenia nowej wysokości obrazu o zmienionym rozmiarze. Poprawna var nH = oH * r; Niepoprawnie: var nH = oW * r;
wloescher
Naprawiono, po prostu nigdy nie komentowałem.
Ryan Mann,