Feedback

Bilder proportional Skalieren

Sprache: C#

Bertrand Le Roy hat in einem Blogartikel (http://bit.ly/6o4ziC) gezeigt, dass die Bildmanipulationen mit WPF bis zu 3x schneller arbeiten als die Funktionen aus dem Namensraum System.Drawing (GDI+). Auch die Dateigrößen sind geringer bei gleicher oder sogar besserer Bildqualität. Der hier gezeigten Methode übergibt man zum einen den Stream mit dem Bild (bsp. aus den POST-Daten einer ASP.NET Anwendung), die gewünschte Maximalgröße (Breite und Höhe in Pixeln bei 92 DPI) und den zu verwendenden Algorithmus für die Neuberechnung. Hier empfiehlt sich eigentlich immer "[b]BitmapScalingMode.HighQuality[/b]" bzw. "[b]BitmapScalingMode.Fant[/b]" (beide gleichwertig, ersteres existiert noch aus Kompatibilitätsgründen). Als Ergebnis bekommt man wieder einen Stream zurück, den man beispielsweise als Datei speichern kann. [b]Beispiel für ASP.NET MVC:[/b] [code] HttpPostedFileBase posted = Request.Files[file]; string fn = Guid.NewGuid() + ".png"; FileStream fs = System.IO.File.Create("~/upload/" + fn); Resize(posted.InputStream, 100, 100, BitmapScalingMode.Fant).WriteTo(fs); fs.Close(); posted.InputStream.Close(); [/code]
using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public static class ImgScale {
  /// <summary>
  /// Resizes the specified image stream
  /// </summary>
  /// <param name="imageStream">The source image stream</param>
  /// <param name="maxWidth">maximum width of the scaled image</param>
  /// <param name="maxHeight">maximum height of the scaled image</param>
  /// <param name="scalingMode">rendering mode</param>
  /// <returns></returns>
  public static Stream Resize(Stream imageStream, int maxWidth, int maxHeight, BitmapScalingMode scalingMode) {
    // Lade den ersten (und meistens einzigen) Frame aus dem Stream der Bilddatei und gib den Algorithmus zum Neuberechnen vor
    BitmapDecoder imgDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.None, BitmapCacheOption.Default);
    BitmapFrame img = imgDecoder.Frames[0];
    DrawingGroup group = new DrawingGroup();
    RenderOptions.SetBitmapScalingMode(group, scalingMode);
    
    // Berechne die Verhältnisse des Originalbildes zur gewünschten Maximalgröße und bestimme so welche der beiden übergebenen
    // Werte übernommen, und welcher skaliert werden muss
    float xP = maxWidth / (float)img.Width;
    float yP = maxHeight / (float)img.Height;
    if (xP > yP)
      maxWidth = Convert.ToInt32(img.Width * yP);
    else
      maxHeight = Convert.ToInt32(img.Height * xP);


    // Lege eine neue Grafik mit den berechneten Maßen an und berechne das Originalbild neu
    group.Children.Add(new ImageDrawing(img, new Rect(0, 0, maxWidth, maxHeight)));
    DrawingVisual targetVisual = new DrawingVisual();
    DrawingContext targetContext = targetVisual.RenderOpen();
    targetContext.DrawDrawing(group);
    RenderTargetBitmap target = new RenderTargetBitmap(maxWidth, maxHeight, 96, 96, PixelFormats.Default); // 96 DPI, Standart-Bildschirmauflösung
    targetContext.Close();
    target.Render(targetVisual);
    BitmapFrame targetFrame = BitmapFrame.Create(target);
    
    // Der PNG-Encode bietet sich in den meisten Fällen an, da er die beste Qualität bietet
    // GIF hat gerade bei Fotos eine zu niedrige Farbanzahl und JPEG hat eine verlustbehaftete Bildkompression
    PngBitmapEncoder targetEncoder = new PngBitmapEncoder();
    
    targetEncoder.Frames.Add(targetFrame);
    Stream output = null;
    targetEncoder.Save(output);
    return output;
  }
}
using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public static class ImgScale {
  /// <summary>
  /// Resizes the specified image stream
  /// </summary>
  /// <param name="imageStream">The source image stream</param>
  /// <param name="maxWidth">maximum width of the scaled image</param>
  /// <param name="maxHeight">maximum height of the scaled image</param>
  /// <param name="scalingMode">rendering mode</param>
  /// <returns></returns>
  public static Stream Resize(Stream imageStream, int maxWidth, int maxHeight, BitmapScalingMode scalingMode) {
    // Lade den ersten (und meistens einzigen) Frame aus dem Stream der Bilddatei und gib den Algorithmus zum Neuberechnen vor
    BitmapDecoder imgDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.None, BitmapCacheOption.Default);
    BitmapFrame img = imgDecoder.Frames[0];
    DrawingGroup group = new DrawingGroup();
    RenderOptions.SetBitmapScalingMode(group, scalingMode);
    
    // Berechne die Verhältnisse des Originalbildes zur gewünschten Maximalgröße und bestimme so welche der beiden übergebenen
    // Werte übernommen, und welcher skaliert werden muss
    float xP = maxWidth / (float)img.Width;
    float yP = maxHeight / (float)img.Height;
    if (xP > yP)
      maxWidth = Convert.ToInt32(img.Width * yP);
    else
      maxHeight = Convert.ToInt32(img.Height * xP);


    // Lege eine neue Grafik mit den berechneten Maßen an und berechne das Originalbild neu
    group.Children.Add(new ImageDrawing(img, new Rect(0, 0, maxWidth, maxHeight)));
    DrawingVisual targetVisual = new DrawingVisual();
    DrawingContext targetContext = targetVisual.RenderOpen();
    targetContext.DrawDrawing(group);
    RenderTargetBitmap target = new RenderTargetBitmap(maxWidth, maxHeight, 96, 96, PixelFormats.Default); // 96 DPI, Standart-Bildschirmauflösung
    targetContext.Close();
    target.Render(targetVisual);
    BitmapFrame targetFrame = BitmapFrame.Create(target);
    
    // Der PNG-Encode bietet sich in den meisten Fällen an, da er die beste Qualität bietet
    // GIF hat gerade bei Fotos eine zu niedrige Farbanzahl und JPEG hat eine verlustbehaftete Bildkompression
    PngBitmapEncoder targetEncoder = new PngBitmapEncoder();
    
    targetEncoder.Frames.Add(targetFrame);
    Stream output = null;
    targetEncoder.Save(output);
    return output;
  }
}