WPF CroppedBitmap + RenderTargetBitmap = 事件处理程序泄漏问题和冻结问题

4

我想知道是否存在与CroppedBitmap和RenderTargetBitmap相关的.NET EventHandler泄漏问题。这让我深受困扰!

我的WPF应用程序以60fps的速度运行一个始终运行的渲染调用。我发现随着时间的推移,正在呈现的内容变得越来越慢。

经过大量调查,包括在Visual Studio中进行分析和使用ANTS内存分析器,我将其缩小到使用CroppedBitmap。

我有一些简单的问题演示代码,其中包括...

RenderTargetBitmap srcBitmap = new RenderTargetBitmap((int)scaledWidth, (int)scaledHeight, 96, 96,     PixelFormats.Default);
RenderTargetBitmap destBitmap = new RenderTargetBitmap((int)scaledWidth, (int)scaledHeight, 96, 96,     PixelFormats.Default);

<code loop>
{...
    DrawingVisual DV = new DrawingVisual();
    DrawingContext DC = DV.RenderOpen();

    var srcRec = new Int32Rect(x,y,w,h);
    var srcCrop = new CroppedBitmap(srcBitmap, srcRec);

    var destRec = new Rect(.....);
    DC.DrawImage(srcCrop, destRec);

    DC.Close();
    destBitmap.Render(DV);

    DC = null;
    DV = null;
...}

使用srcBitmap调用CroppedBitmap之后,会在srcBitmap上添加一个事件处理程序。具体来说,将CroppedBitmap.Source = srcBitmap设置为真时将添加此事件处理程序。
CroppedBitmap具有DownloadCompleted、DownloadFailed、DownloadProgress事件。我想猜测是DownloadCompleted事件。
通过检查事件处理程序数量(使用srcBitmap/RenderTargetBitmap上的 _downloadEvent 属性),每次调用CroppedBitmap时这个数量确实会增加一个,而且永远不会减少。我只能得出结论:CroppedBitmap正在向RenderTargetBitmap添加一个从未被删除的事件处理程序。运行一段时间后,RenderTargetBitmap上就会积累成千上万个事件处理程序,我认为这正是导致我的速度降低的原因。
我真的不知道该怎么办!图像被设置在简单的Image控件上,没有什么特别的。
我尝试着从发生这种情况的地方开始一步一步地跟踪到 .net 框架的深处,但当调试器开始进入优化过的 .net 库时,我就迷失了方向,无法继续挖掘。
位图的呈现很简单...
Content.Source = srcBitmap;
....
<Image x:Name="Content" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RenderOptions.BitmapScalingMode="LowQuality" Stretch="None" IsHitTestVisible="False"></Image>

为了明确起见,例如代码可以在没有渲染、没有绘制图像等情况下运行 - 只需要新的CroppedBitmap...代码才能显示这种行为。其他行以上是为了完整性而包含的。没有任何区别。
上面的示例是针对.NET Framework 4.7.2运行的,以确保最新版本存在此问题。
我还尝试了冻结CroppedBitmap,以防万一会有什么不同。显然没有。事实上,调用srcCrop.Freeze导致srcBitmap.isFrozen为true!嗯.....所以似乎CroppedBitmap对其被裁剪的位图进行了修改?
在上述方面找不到任何信息、帮助、文章等 :(
请注意,这不是RenderTargetBitmap泄漏内存,在支持论坛中您看到提及的是另外的问题。运行此代码而没有CroppedBitmap时它很好。我在几个地方使用RenderTargetBitmaps,所有的都很好(我已经按照那篇文章中描述的方式考虑了内存使用率的增加,并且我已经调用了GC.Collect等)。我还重复使用我的RenderTargetBitmaps,因此它们只被创建一次。(可能是为什么当应用多个CroppedBitmap时,它们的事件处理程序堆积起来的原因)。
我尝试使用反射器获取正在创建的事件处理程序,以便我可以手动删除它们,但是(尽管互联网上有各种示例)无法找到它们隐藏的位置。一定有某个地方!
任何人有什么想法可以修复这个问题吗?
提前感谢您的帮助...
1个回答

3

不必使用CroppedBitmap,您可以使用具有适当Viewbox的ImageBrush:

var viewbox = new Rect(
    x * 96 / srcBitmap.DpiX,
    y * 96 / srcBitmap.DpiY,
    w * 96 / srcBitmap.DpiX,
    h * 96 / srcBitmap.DpiY);

var srcBrush = new ImageBrush
{
    ImageSource = srcBitmap,
    Viewbox = viewbox,
    ViewboxUnits = BrushMappingMode.Absolute,
};

// instead of DC.DrawImage(srcCrop, destRec);
DC.DrawRectangle(srcBrush, null, destRec);

先生,您真是个明星!我尝试了这个方法,并且成功了。请注意,在性能基准测试方面,我没有看到任何与我之前的做法有什么不同。谢谢您。不过,我仍然非常想知道为什么CroppedBitmap会导致问题出现... - Martin B
不用谢。也许你想看一下.NET参考源中的CroppedBitmap源代码。 - Clemens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接