Jak niektórym wiadomo, wspaniała klasa System.Drawing.Image nie lubi operacji na zbyt dużych obrazkach. Czytałem, że jest to wina tworzenia pliku Thumbs.db w katalogach użytkownika. Jednak próba operacji na plikach umieszczonych w innych katalogach dawała ten sam objaw … czyli MemoryOutOfException. Pastwiłem się nad plikiem PNG 9921x14034 zawierający miejscowy plan zagospodarowania przestrzennego.
Próba zmniejszenia za pomocą System.Drawing.Image nie uda się ponieważ problem pojawia się już na etapie System.Drawing.Image.FromFile(…);
Z pomocą przyszła mi porada zalecająca wykorzystanie WPF-owych klas do obsługi obrazów. Tam wspomniane błędy związane z konsumpcją pamięci nie występują.
Operacja polega na pobraniu danych obrazu za pomocą BitmapDecoder, zmiana rozmiaru do wielkości 1920x1200 i zapisanie pliku z tymi ustawieniami za pomocą BitmapEncoder.
/// <summary>
/// Inserts the image from file.
/// </summary>
public void InsertImageFromFile() {
const int MAX_WIDTH = 1920;
const int MAX_HEIGHT = 1200;
using (OpenFileDialog dlg = new OpenFileDialog()
{
DefaultExt = "*.jpg",
Filter = "Wszystkie pliki graficzne(*.bmp;*.jpg;*.gif;*.png)|*.bmp;*.jpg;*.gif;*.png"
}) {
if (dlg.ShowDialog() == DialogResult.OK) {
var ext = Path.GetExtension(dlg.FileName).Remove(".").Trim();
var uri = new Uri(dlg.FileName);
var mustResize = false;
var orginSize = System.Drawing.Size.Empty;
System.Windows.Media.Imaging.BitmapDecoder decoder = null;
switch (ext) {
case "jpg":
case "jpeg": {
decoder = new System.Windows.Media.Imaging.JpegBitmapDecoder(uri,
System.Windows.Media.Imaging.BitmapCreateOptions.None,
System.Windows.Media.Imaging.BitmapCacheOption.Default);
break;
}
case "png": {
decoder = new System.Windows.Media.Imaging.PngBitmapDecoder(uri,
System.Windows.Media.Imaging.BitmapCreateOptions.None,
System.Windows.Media.Imaging.BitmapCacheOption.Default);
break;
}
case "gif": {
decoder = new System.Windows.Media.Imaging.GifBitmapDecoder(uri,
System.Windows.Media.Imaging.BitmapCreateOptions.None,
System.Windows.Media.Imaging.BitmapCacheOption.Default);
break;
}
case "bmp": {
decoder = new System.Windows.Media.Imaging.BmpBitmapDecoder(uri,
System.Windows.Media.Imaging.BitmapCreateOptions.None,
System.Windows.Media.Imaging.BitmapCacheOption.Default);
break;
}
}
if (decoder != null) {
if (decoder.Frames != null && decoder.Frames.Count > 0) {
orginSize = new System.Drawing.Size(decoder.Frames[0].PixelWidth,
decoder.Frames[0].PixelHeight);
mustResize = decoder.Frames[0].PixelWidth > MAX_WIDTH ||
decoder.Frames[0].PixelHeight > MAX_HEIGHT;
}
}
if (mustResize) {
var b = new System.Windows.Media.Imaging.BitmapImage();
b.BeginInit();
if (orginSize.Width > MAX_WIDTH) {
b.DecodePixelWidth = MAX_WIDTH;
}
else if (orginSize.Height > MAX_HEIGHT) {
b.DecodePixelHeight = MAX_HEIGHT;
}
b.UriSource = uri;
b.EndInit();
var tempPath = Path.Combine(Path.GetTempPath(), String.Empty.GenerateId() +
Path.GetExtension(dlg.FileName));
using (FileStream fs = new FileStream(tempPath, FileMode.Create)) {
System.Windows.Media.Imaging.BitmapEncoder encoder = null;
switch (ext) {
case "jpg":
case "jpeg": {
encoder = new System.Windows.Media.Imaging.JpegBitmapEncoder();
break;
}
case "png": {
encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
break;
}
case "bmp": {
encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
break;
}
case "gif": {
encoder = new System.Windows.Media.Imaging.GifBitmapEncoder();
break;
}
}
if (encoder != null) {
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(b));
encoder.Save(fs);
}
}
if (File.Exists(tempPath)) {
// tu robimy coś z plikiem
// jak zrobimy to usuwamy plik tymczasowy
try {
File.Delete(tempPath);
}
catch { }
}
}
else {
var image = System.Drawing.Image.FromFile(dlg.FileName);
// coś dalej robimy
}
}
}
}