WinForms / C#:实现长可滚动的图像列表会炸毁内存

时间:2011-09-13 10:51:41

标签: c#

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

问题在于,当将图像加载到面板中时,存储器(RAM)应该被烧得更多(被认为是图像文件的大小)。如果列表已加载,则大约500Mb在内存中被阻止,但图像应该需要大约300 x 150kb = 45MB。

如果我将图像直接加载到图片框中或作为MemoryStream加载无关紧要:关于内存加载的结果是相同的。

所以,有些事情正在炸毁我的内存负载,其程度是图像文件所需的10倍以上。

问题是:如何在不改变图像质量或图像尺寸的情况下将内存负载降至100Mb以下。有没有人有个好主意?

非常感谢您提前

ROCKIE

1 个答案:

答案 0 :(得分:7)

内存中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)图像。然后,当用户在一行下方滚动时,删除现在不可见行中的图像并加载下一行/ s图像。为此,添加某种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();

尝试一下,看看滚动中是否仍然存在质量下降或口吃