【问题标题】:Properly disposing of Bitmap object正确处理 Bitmap 对象
【发布时间】:2021-01-11 21:57:29
【问题描述】:

我在 C# Winforms 面板中绘制图像:

private void DrawPanel_Paint(object sender, PaintEventArgs e)
{
    DrawOnPanel(e.Graphics);
}

调用的方法从我的资源 (myImage) 中获取现有图像,将其提供给另一个方法,该方法调整图像大小并返回调整大小的图像以便绘制。

public static void DrawOnPanel(Graphics g)
{
    var _resizedImage = ResizeImage(Resources.myImage);
    g.DrawImage(_resizedImage, destX, destY);

    // ... several other images resized & manipulated then drawn

}

调整图片大小的功能是:

public Bitmap ResizeImage(Bitmap image)
{
    int scale = 3;
    var destImage= new Bitmap(image.Width * scale, image.Height * scale);
    destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
    using (var graphics = Graphics.FromImage(destImage))
    {
    graphics.CompositingMode = CompositingMode.SourceCopy;
    graphics.CompositingQuality = CompositingQuality.HighSpeed;
    graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
    graphics.SmoothingMode = SmoothingMode.HighSpeed;
    graphics.PixelOffsetMode = PixelOffsetMode.None;

    using var wrapMode = new ImageAttributes();
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
    }
    return destImage;
}

程序在其循环中不断调用 DrawPanel.Invalidate()。 每次调用 DrawPanel.Invalidate() 时,我都会检测到内存泄漏。在 GC 处理它之前,内存消耗正在稳步上升。虽然这不是一个破坏游戏的问题,但我仍然想知道我应该在哪里以及如何在上面的代码中处理我的对象。 我尝试在上述DrawOnPanel 方法中使用using var _resizedImage = ResizeImage(Resources.myImage);,但程序返回错误System.ArgumentException: 'Parameter is not valid.'。如果我删除 using 则没有错误。

【问题讨论】:

  • 请发布有意义的代码。此外,您不需要任何这些:将资源图像分配给字段对象或集合,然后使用图形对象绘制调整大小的图像。它有一个超载。表单关闭时处理图像。 -- 请记住,资源是一个工厂(如果它来自Properties.Resources),它会在您每次请求时创建一个新图像对象
  • but the program returns an error here the second time I invalidate the screen. twitter.com/marcgravell/status/1330430245384679424?s=20
  • 我们需要查看destImage 的声明位置。请分享minimal reproducible example
  • (对上面的代码做了一些更正,destImage在ResizeImage方法的开头声明。)问题出在DrawOnPanel方法中的using语句。当我运行程序时,它返回一个参数无效错误。我知道代码可以优化,但我想知道我应该在哪里处理对象 _resizedImage?

标签: c# winforms object bitmap dispose


【解决方案1】:

每次调用 ResizeImage 时,都会创建一个新的位图。一旦您知道不再需要此位图,就应该立即处理它。在 Graphics 上绘制后似乎不需要调整大小的图像。因此,我建议进行以下更改:

public static void DrawOnPanel(Graphics g)
{
    using (Image_resizedImage = ResizeImage(Resources.myImage))
    {
        g.DrawImage(_resizedImage, destX, destY);
    }
    // resizedImage is Disposed

    // ... several other images resized & manipulated then drawn
}

改进空间

每次调用DrawOnPanel 时,您当前的设计都会创建一个新的调整大小的位图。在我看来,Resources.myImage 的大部分时间都被调整大小,调整大小的位图将是相同的。为什么在影响调整后的 Bitmap 的参数改变之前不记住它?

如果我查看代码,您似乎总是从原始图像创建相同大小的图像。因此,您甚至可以将所有调整大小的图像放入一个字典中以便快速查找:

// get all Image resources that you will resize and put them in a Dictionary
// Key original Bitmap, Value: resized Bitmap
private Dictionary<Bitmap, BitMap> images = this.FetchImages()
    .ToDictionary(
       // Parameter keySelector: key is original image
       image => image,
       // Parameter valueSelector: value is the resized image
       imgage => ResizeImage(original));

现在显示调整大小的图像会更快:只需查找。

public static void DrawOnPanel(Graphics g)
{
    var _resizedImage = this.Images[Resources.myImage];
    g.DrawImage(_resizedImage, destX, destY);
   // ... several other images resized & manipulated then drawn
}

不要忘记在您的表单关闭时或最迟在您的表单被处置时处置位图:

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        foreach (var resizedImage in images.Values)
            resizedImage.Dispose();
    }
}

【讨论】:

  • 感谢您的具体建议!我使用using 语句处理我的图像,就像你写的那样,但我得到一个错误 System.ArgumentException: 'Parameter is not valid.'将鼠标悬停在 Visual Studio 中的 _resizedImage 上,它似乎已被释放。问题可能是该方法是静态的吗?至于您建议的其他改进,它们似乎是合理的,我会尝试实施它们,谢谢!问候
  • "问题可能在于方法是静态的吗?"好吧,我猜您已经尝试过使用非静态方法,并且您已经尝试过使用未调整大小的虚拟图像,不是吗?如果您只是在 using 语句中创建原始图像的副本怎么办?关于我输入此回复所花费的时间,您可以尝试很多事情
  • 顺便说一句:也可能是 Bitmap 已经被释放了。将调整大小的位图包装到位图的子类中并覆盖protected virtual void Dispose(bool)。下个断点看看 Dispose 什么时候被调用
  • 我已经尝试了您的前 2 个建议,但没有运气。我将尝试您建议的其他内容,并深入研究我的代码。问候
  • 请注意,从资源中获取的图像也可能每次都会创建一个新实例,因此您可能需要在其自己的 using 块中将 Resources.myImage 的任何使用更改为它自己的位图变量,以便using (Bitmap resBm = Resources.myImage) using (Image_resizedImage = ResizeImage(resBm)) { ... }.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-13
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
相关资源
最近更新 更多