WinForms/ c#:实现长可滚动的图像列表会增加内存

本文关键字:图像 列表 内存 增加 滚动 实现 WinForms | 更新日期: 2023-09-27 18:08:21

我尝试将jpg-images作为一长串图片框加载到表格布局面板中。图像列表应该平滑地滚动,即没有闪烁或可见的模糊或抖动。有大约300张图片,每张图片的大小约为150kb (600px x 850px)。

问题是,当将图像加载到面板中时,内存(RAM)比它应该的要大得多(考虑到图像文件的大小)。如果列表被加载,大约500Mb的内存被阻塞,但是图像应该需要大约300 x 150kb = 45MB。

如果我直接将图像加载到picturebox或作为MemoryStream,这并不重要:关于内存负载的结果是相同的。

所以,有什么东西使我的内存负载激增,达到了图像文件所需内存的10倍以上。

问题是:如何在不改变图像质量或图像大小的情况下将内存负载降低到100Mb左右。有人有好主意吗?

提前谢谢你

洛基

WinForms/ c#:实现长可滚动的图像列表会增加内存

内存中JPG图像的大小是未压缩图像所使用的内存。

MemorySize = w * h * (number of images) * (3 or 4 bytes)
             -> w, h is in pixels

不透明的图像为3字节或24位,透明的图像为4字节。它是每个通道(红,绿,蓝,透明)每像素8位。

在你的例子中,这是:

MemorySize = 600 * 850 * 300 * 3 = 459000000 bytes ~ 440 MB

将内存使用保持在100MB以下的唯一解决方案是只加载前70个图像,并在用户进一步向下滚动时加载进一步需要的图像,同时从顶部删除图像。

假设每一行有n个图像,并且k行是可见的。最好说首先加载第一个n * (k+2)图像。然后,当用户在一行下面滚动时,删除现在不可见的行中的图像并加载下一行的图像。为此,添加某种EventHandler来滚动事件。


在Google上搜索压缩内存位图,我发现了这个:
http://www.codeproject.com/KB/graphics/CompressibleImage.aspx

检查一下,它可以节省从磁盘加载图像的时间,然后你只在运行时解压缩它们,从而节省磁盘访问时间。

我现在正在一个示例项目中尝试这个类,并将使用结果更新答案。

更新2

我已经测试了CompressibleImage类,它工作得很好。但是当它重新压缩你的图像时,它已经是JPEG压缩的,它可能会导致一些质量损失。
为此,我为类添加了一个额外的构造函数,它将原始文件字节直接存储在类的压缩流变量中。因此,添加以下构造函数,它应该可以工作,而不会因为重新压缩而损失任何质量:

public CompressibleImage(string fileName, bool alreadyCompressed){
    if (alreadyCompressed)
        this.stream = new MemoryStream(File.ReadAllBytes(fileName));
    else
        this.decompressed = Image.FromFile(fileName);
}

你可以这样使用这个类:

// to load image in compressed format, where filename points to a JPG
// and the second argument as true tells that the image is already compressed
CompressibleImage ci = new CompressibleImage(filename, true);
// to display in a PictureBox
pictureBox1.Image = ci.GetDecompressedImage();
// to free image memory once out of view
pictureBox1.Image = null;
ci.ClearDecompressedImage();
// force Garbage Collector, do this after removing a whole row of images, 
// as by default GC is not called immediately but only when needed, so
// this forces GC and reclaims memory from just freed images immediately
GC.Collect();

试一试,看看在滚动时是否仍然有质量损失或口吃