我尝试将jpg图像作为一个长图片框列表加载到表格布局面板中。图像列表应该可以平滑地滚动,即没有闪烁或可见的模糊或抖动。大约有300张图像,每张图像的大小约为150kb(600px x 850px)。
问题在于,当将图像加载到面板中时,存储器(RAM)应该被烧得更多(被认为是图像文件的大小)。如果列表已加载,则大约500Mb在内存中被阻止,但图像应该需要大约300 x 150kb = 45MB。
如果我将图像直接加载到图片框中或作为MemoryStream加载无关紧要:关于内存加载的结果是相同的。
所以,有些事情正在炸毁我的内存负载,其程度是图像文件所需的10倍以上。
问题是:如何在不改变图像质量或图像尺寸的情况下将内存负载降至100Mb以下。有没有人有个好主意?
非常感谢您提前
ROCKIE
答案 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();
尝试一下,看看滚动中是否仍然存在质量下降或口吃