【发布时间】:2016-07-08 05:20:23
【问题描述】:
我一直在使用 32 位 C# WPF 应用程序,该应用程序在 ListBox 中显示大量大图像(在许多情况下为 1080p)。问题是在我的 C# 对象(我已经绑定到)中保留一个 BitmapSource 对象会显着增加内存,因为我创建的 BitmapSource 的字节在渲染之前被复制/复制。如果我保留 BitmapSource 对象以便重用它或在其他地方重新显示它,我最终会由于渲染前复制而产生原始图像字节的多个副本。更具体地说,CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) 在渲染之前被调用。带有堆栈跟踪的内存/堆分析证实了在渲染之前复制字节的想法。
我创建的唯一“解决方法”,每次需要时都会生成 BitmapSource,如下所示:
ImageData data = _backendImage.getData();
SWIGTYPE_p_unsigned_char rawData = _backendImage.getRawData();
IntPtr dataPointer = SWIGTYPE_p_unsigned_char.getCPtr(rawData).Handle;
GC.Collect(); // forces garbage collection on not-displayed images
return Utilities.GenerateBitmapSource((int)_backendImage.getWidth(), (int)_backendImage.getHeight(), _backendImage.getByteOrder(), dataPointer, data.Count);
最后一行是我自己的函数来实际生成一个 BitmapSource 对象,超出了这个问题的范围。
解决方法的性能非常差,因为在每次渲染到 ListBox 之前,我要做的不仅仅是一份,而是两份数据副本(一份进入 BitmapSource,一份用于渲染)。保留 BitmapSource 会删除所有重复的复制操作,但会占用大量内存。
这是我的 ListBox XAML:
<ListBox Name="SlideShowListBox" ItemsSource="{Binding SlideData.PreviewData}"
SelectedIndex="{Binding SelectedIndex}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
SelectionMode="Extended"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel Orientation="Horizontal" Margin="0, 2.5, 0, 2.5">
<Label Content="{Binding data.ImageNumber}" VerticalAlignment="Top" Width="30" HorizontalContentAlignment="Right" Margin="0,-6.5,0,0"/>
<Grid>
<Image Source="{Binding data.ImageThumbnail}" RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="Top" HorizontalAlignment="Left"
Name="ListImage"
MaxWidth="{Binding ElementName=ListBoxItemSizer,
Path=ActualWidth, Converter={ikriv:MathConverter}, ConverterParameter=(x - 20)}">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding data.IsHidden}" Value="True">
<Setter Property="Opacity" Value="0.5"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Grid>
</VirtualizingStackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
问题:当我已经将所有字节存储在 RAM 中并在图像上调用了.Freeze() 时,有什么方法可以防止 WPF 在渲染之前复制字节?我想要一份副本我的图像字节在 RAM 中:不多也不少。
可能相关:.NET Memory issues loading ~40 images, memory not reclaimed -- 似乎不相关,因为我是从原始字节构建 BitmapSource 对象,而不是(文字)流对象。
编辑:有趣的说明——我在两个不同的屏幕上的 2 个不同的 ListBox 项目中显示这些 BitmapSource 项目。如果我将对象保留在周围,则 RAM 使用量只会在 第一次 渲染 BitmapSource 时增加,而不会在后续渲染时增加,无论 BitmapSource 出现在哪个屏幕或 ListBox 上。
【问题讨论】: