Krzysztof Radzimski

Pasja tworzenia

Bardzo duże obrazki, System.Drawing.Image i MemoryOutOfException

05 lipiec
Opublikował Krzysztof Radzimski 5. lipca 2011 10:34

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.

Kod metody poniżej:

    /// <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
          }
        }
      }
    }

Tagi: | Kategorie: Programowanie

Wyślij link na adres e-mail | Link do tego postu | RSSRSS comment feed