0

I have a small image editing program that opens an image and does fancy stuff to it. In this case, I'm trying to adjust the brightness of the pixels. The problem isn't getting the pixels right, its disposing the Bitmap/BitmapSources being created in the ValueChanged event for my slider.

So basically the user will click a button and it will open up a new window with a slider. This new window makes a copy of the original bitmap for later use.

When the slider's value changes, it will create a new Bitmap from the original with the corresponding brightness increase, create a new BitmapSource from this, and update the image control's source. Even though I'm using a 'using' statement, I still get an "out of memory" exception after sliding for so long.

Any idea why this is? Is there a better workaround that can achieve the same thing? I have included the code below:

using Imagin.Slideshow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Imagin
{
    /// <summary>
    /// Interaction logic for BrightnessWindow.xaml
    /// </summary>
    public partial class BrightnessWindow : Window
    {

        public int Increase;

        private System.Drawing.Bitmap Bitmap;
        private System.Drawing.Bitmap NewBitmap;

        private MainWindow ParentWindow;

        public BrightnessWindow(MainWindow m)
        {

            InitializeComponent();

            this.ParentWindow = m;

            this.Bitmap = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone();

        }

        private void slider1_ValueChanged(object sender, RoutedEventArgs e) 
        {

            using (System.Drawing.Bitmap b = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone(new RectangleF() { Width = (int)this.ParentWindow.Document.Bitmap.Width, Height = (int)this.ParentWindow.Document.Bitmap.Height, X = 0, Y = 0 }, this.ParentWindow.Document.Bitmap.PixelFormat))
            {
                this.ParentWindow.SetPixels(b, AdjustmentTypes.Brightness, Convert.ToInt32(this.slider1.Value));
                BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                this.ParentWindow.image1.Source = m;
                this.NewBitmap = (System.Drawing.Bitmap)b.Clone();
            }

        }

        private void applyButton_Click(object sender, RoutedEventArgs e)
        {
            if (this.NewBitmap != null)
            {
                this.ParentWindow.Document.Bitmap = this.NewBitmap;
            }
            this.Close();
        }

        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.ParentWindow.Document.Refresh();

            this.Close();
        }
    }
}

Another thing I was wondering is if there is a better way to alter the MainWindow's controls without having to pass it as a parameter, or is this the preferred approach for that kind of thing? Thanks!

  • So I figured out that not having this line was causing our issue: `if (this.NewBitmap != null) { this.NewBitmap.Dispose(); }` However, now I get the error "An exception of type 'System.Runtime.InteropServices.ExternalException' occurred in System.Drawing.dll but was not handled in user code; Additional information: A generic error occurred in GDI+." from the following line: `BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());` –  Oct 26 '14 at 14:26
  • Before setting new BitmapSource or Bitmap to anything try dispose first the original one. As i see you don't use for anything BrightnessWindow::Bitmap field. – Peter Kiss Oct 26 '14 at 14:26
  • Good i was right. :) – Peter Kiss Oct 26 '14 at 14:33
  • But what about the second error? It isn't being very explicit :/ –  Oct 26 '14 at 15:05
  • Go under the DEBUG menu, select "Exceptions", and from the dialog that comes up, place a check box in the "Thrown" column for "Common Language Runtime Exceptions". Click OK, and then try debugging again. – Peter Kiss Oct 26 '14 at 15:18

1 Answers1

0

http://msdn.microsoft.com/en-us/library/1dz311e4%28v=vs.110%29.aspx

Every time you use bitmap.GetHbitmap you create a new instance of a gdibitmap. Per the msdn link, "You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object. For more information about GDI bitmaps, see Bitmaps in the Windows GDI documentation."

I'd pull that guy out to a variable and call deleteobject on it when you're done with it, otherwise you're just creating n gdibitmaps where n = number of times you move the slider and never disposing of them.

Edit: Highlighting the line:

//Somewhere in the class
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

    private void slider1_ValueChanged(object sender, RoutedEventArgs e) 
    {

        using (System.Drawing.Bitmap b = (System.Drawing.Bitmap)this.ParentWindow.Document.Bitmap.Clone(new RectangleF() { Width = (int)this.ParentWindow.Document.Bitmap.Width, Height = (int)this.ParentWindow.Document.Bitmap.Height, X = 0, Y = 0 }, this.ParentWindow.Document.Bitmap.PixelFormat))
        {
            this.ParentWindow.SetPixels(b, AdjustmentTypes.Brightness, Convert.ToInt32(this.slider1.Value));
            IntPtr hbitmap = b.GetHbitmap(); //create variable so we don't orphan the object
            BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); //use variable
            this.ParentWindow.image1.Source = m;
            this.NewBitmap = (System.Drawing.Bitmap)b.Clone();
            DeleteObject(hbitmap); //delete gdi object
        }

    }
C Bauer
  • 5,003
  • 4
  • 33
  • 62
  • 1
    Upon researching your answer, I came across this: [link](http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak), which helped me properly dispose of object. Compiler didn't recognize DeleteObject method, so hopefully this helps someone else who runs into the same problem. Thanks! –  Oct 27 '14 at 00:48