在Java中快速加载大图片的部分

4
我有一个应用程序需要展示一张非常大的图片(从磁盘上的png文件),而屏幕上只会显示图片的一小部分。然而,这个可见部分可以在大图中快速移动。
将整个图像一次性加载到BufferedImage中不是一个好主意,因为它可能有1万到10万像素宽,但是其在磁盘上的大小并不是很大(可能只有几MB),所以问题在于只加载需要显示的部分。
我尝试创建了一个如下所示的ImageReader:
FileImageInputStream is = new FileImageInputStream(imageFile);
ImageReader imageReader = ImageIO.getImageReaders(is).next();
ImageReader.setInput(is, false, true);
ImageReadParam readParameters = imageReader.getDefaultReadParam();

然后有一种获取子图像的方法,就是像这样:
private BufferedImage loadFrame(int x, int y, int w, int h) {
    readParameters.setSourceRegion(new Rectangle(x,y,w,h));
    try {
        return imageReader.read(0, readParameters);
    } catch (IOException ex) {
        return null;
    }
}

这个方法原则上可行,但速度太慢。所以在图像快速移动时,它会有很大的延迟。

我还尝试过事先拆分源图像,以便我有一堆小图像放在磁盘上,然后根据需要使用ImageIO.read(getImageFile(x,y))加载,其中getImageFile(x,y)将返回该位置的适当文件。这种方法实际上要快得多,而且完全可用。

所以我想我找到了解决办法,但这种方式似乎有些笨拙。除了需要对源图像进行一些准备外,它还需要大量的磁盘访问(虽然我想这可能已经被缓存了)。

所以我的问题是:最好的方法是什么?(为什么从磁盘加载图像比从ImageReader加载图像的一部分更快?)

1个回答

3
PNG是一种压缩格式,你不能只打开文件并寻找特定位置来开始读取区域(就像你可以在阅读文件头后使用位图进行的那样)。整个PNG需要被加载(解析/解压缩)然后才能开始提取它的区域。http://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header 如果你想牺牲磁盘空间以改善内存(RAM)的使用和性能...
- 你可以将图片分成网格块,并仅加载那些你需要用来构建用户视图的块。 - 例如:1x1.png、1x2.png、2x1.png、2x2.png——如果用户看着左上角,你只需要加载1x1.png等等。 - 你可以将图像转换为位图BMP,尽管图像在磁盘上会更大,但你将能够提取它的特定区域而无需处理整个文件。

好的。我想可能可以只解压PNG的一部分,但是仔细想想,设计一个能够像这样工作的压缩格式可能不容易或者不高效(而且显然PNG不是这样的格式)。感谢您的回答! - zephyr

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