【问题标题】:WPF/C# Updating (and disposing of) Bitmap/BitmapSource in ValueChanged event?WPF/C# 在 ValueChanged 事件中更新(和处理)位图/位图源?
【发布时间】:2014-10-27 09:52:24
【问题描述】:

我有一个小型图像编辑程序,它可以打开图像并对其进行处理。在这种情况下,我正在尝试调整像素的亮度。问题不在于像素正确,而是为我的滑块处理在 ValueChanged 事件中创建的 Bitmap/BitmapSources。

所以基本上用户会点击一个按钮,它会打开一个带有滑块的新窗口。这个新窗口会复制原始位图以供以后使用。

当滑块的值发生变化时,它会从原始的Bitmap中创建一个新的Bitmap,相应的亮度增加,并由此创建一个新的BitmapSource,并更新图像控件的源。即使我使用的是“使用”语句,滑动这么长时间后我仍然会出现“内存不足”异常。

知道这是为什么吗?有没有更好的解决方法可以达到同样的效果?我已包含以下代码:

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();
        }
    }
}

我想知道的另一件事是,是否有更好的方法来更改 MainWindow 的控件而无需将其作为参数传递,或者这是否是此类事情的首选方法?谢谢!

【问题讨论】:

  • 所以我发现没有这一行是导致我们的问题的原因:if (this.NewBitmap != null) { this.NewBitmap.Dispose(); } 但是,现在我收到错误“系统中发生'System.Runtime.InteropServices.ExternalException'类型的异常。 Drawing.dll 但未在用户代码中处理;附加信息:GDI+ 中出现一般错误。”来自以下行:BitmapSource m = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
  • 在将新的 BitmapSource 或 Bitmap 设置为任何内容之前,请先尝试处理原始的。正如我所见,您不使用任何 BrightnessWindow::Bitmap 字段。
  • 很好,我是对的。 :)
  • 但是第二个错误呢?它不是很明确:/
  • 在 DEBUG 菜单下,选择“Exceptions”,然后从出现的对话框中,在“Common Language Runtime Exceptions”的“Thrown”列中放置一个复选框。单击确定,然后再次尝试调试。

标签: c# wpf


【解决方案1】:

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

每次使用 bitmap.GetHbitmap 时,都会创建一个 gdibitmap 的新实例。根据 msdn 链接,“您负责调用 GDI DeleteObject 方法来释放 GDI 位图对象使用的内存。有关 GDI 位图的详细信息,请参阅 Windows GDI 文档中的位图。”

当你完成它时,我会将那个人拉出一个变量并调用 deleteobject ,否则你只是创建n gdibitmaps 其中n = number of times you move the slider 并且永远不会处理它们。

编辑:突出显示该行:

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

    }

【讨论】:

  • 在研究您的答案后,我发现了这个:link,它帮助我正确处理了对象。编译器无法识别 DeleteObject 方法,因此希望这可以帮助遇到同样问题的其他人。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多