Java 为什么BuffereImage需要超出其数据数组大小的内存?
我试图确定任何给定类型的\u INT\u ARGB将使用多少堆,这样,对于正在进行图像处理的程序,我可以根据提供给它的图像大小设置合理的最大堆 我编写了以下程序作为测试,然后用它来确定在没有Java 为什么BuffereImage需要超出其数据数组大小的内存?,java,heap,bufferedimage,Java,Heap,Bufferedimage,我试图确定任何给定类型的\u INT\u ARGB将使用多少堆,这样,对于正在进行图像处理的程序,我可以根据提供给它的图像大小设置合理的最大堆 我编写了以下程序作为测试,然后用它来确定在没有OutOfMemoryError的情况下运行的最小最大堆: import java.awt.image.BufferedImage; public class Test { public static void main(String[] args) { final int w = Intege
OutOfMemoryError
的情况下运行的最小最大堆:
import java.awt.image.BufferedImage;
public class Test {
public static void main(String[] args) {
final int w = Integer.parseInt(args[0]);
final int h = Integer.parseInt(args[1]);
final BufferedImage img =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
System.out.println((4*w*h) >> 20);
}
}
(打印的值是存储buffereImage
像素数据的int[]
的预期大小。)我希望找到的是所需的最大堆大小类似于x+c
,其中,x
是数据数组的大小,c
是一个常量,包含加载的类的大小、buffereImage
对象等。这是我发现的结果(所有值均以MB为单位):
4*w*h最小最大堆
----- ------------
5 -
10 15
20 31
40 61
80 121
160 241
1.5x
非常适合观测。(注意,我没有发现5MB图像的最小值。)我不明白我看到了什么。这些额外的字节是什么?Oracle的虚拟机中似乎有一个bug,它是在1.6.0_16和1.6.0_20之间引入的。您甚至可以将问题归结为分配一个int数组,因为问题不仅仅与BuffereImage有关
对于1.6.0_16,我需要至少413MB堆来分配一个包含100000000个元素的int数组,这似乎是合理的。对于1.6.0_20,相同的操作需要至少573 MB的堆空间,尽管分配阵列后实际使用的字节数只有大约400000000字节。进一步调查后,问题似乎是堆中的旧一代无法充分扩展以容纳映像的数据阵列,尽管堆中有足够的空闲内存
有关如何扩展旧一代的更多详细信息,请参阅。问题在于BuffereImage对象将图像以未复制的格式存储在内存中。有一个有效的解决方案:您可以将映像存储在硬盘上,而不必担心堆大小或物理内存限制。它最多可存储2147483647像素(或46340 x 46340像素)BigBuffereImage解决了这个问题 创建一个BigBuffereImage:
BigBufferedImage image = BigBufferedImage.create(
tempDir, width, height, type);
BigBufferedImage image = BigBufferedImage.create(
imagePath, tempDir, type);
package com.pulispace.mc.ui.panorama.util;
/*
* This class is part of MCFS (Mission Control - Flight Software) a development
* of Team Puli Space, official Google Lunar XPRIZE contestant.
* This class is released under Creative Commons CC0.
* @author Zsolt Pocze, Dimitry Polivaev
* Please like us on facebook, and/or join our Small Step Club.
* http://www.pulispace.com
* https://www.facebook.com/pulispace
* http://nyomdmegteis.hu/en/
*/
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.image.BandedSampleModel;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import sun.nio.ch.DirectBuffer;
public class BigBufferedImage extends BufferedImage {
private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
public static final int MAX_PIXELS_IN_MEMORY = 1024 * 1024;
public static BufferedImage create(int width, int height, int imageType) {
if (width * height > MAX_PIXELS_IN_MEMORY) {
try {
final File tempDir = new File(TMP_DIR);
return createBigBufferedImage(tempDir, width, height, imageType);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
return new BufferedImage(width, height, imageType);
}
}
public static BufferedImage create(File inputFile, int imageType) throws IOException {
try (ImageInputStream stream = ImageIO.createImageInputStream(inputFile);) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (readers.hasNext()) {
try {
ImageReader reader = readers.next();
reader.setInput(stream, true, true);
int width = reader.getWidth(reader.getMinIndex());
int height = reader.getHeight(reader.getMinIndex());
BufferedImage image = create(width, height, imageType);
int cores = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
int block = Math.min(MAX_PIXELS_IN_MEMORY / cores / width, (int) (Math.ceil(height / (double) cores)));
ExecutorService generalExecutor = Executors.newFixedThreadPool(cores);
List<Callable<ImagePartLoader>> partLoaders = new ArrayList<>();
for (int y = 0; y < height; y += block) {
partLoaders.add(new ImagePartLoader(
y, width, Math.min(block, height - y), inputFile, image));
}
generalExecutor.invokeAll(partLoaders);
generalExecutor.shutdown();
return image;
} catch (InterruptedException ex) {
Logger.getLogger(BigBufferedImage.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return null;
}
private static BufferedImage createBigBufferedImage(File tempDir, int width, int height, int imageType)
throws FileNotFoundException, IOException {
FileDataBuffer buffer = new FileDataBuffer(tempDir, width * height, 4);
ColorModel colorModel = null;
BandedSampleModel sampleModel = null;
switch (imageType) {
case TYPE_INT_RGB:
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8, 0},
false,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 3);
break;
case TYPE_INT_ARGB:
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8, 8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4);
break;
default:
throw new IllegalArgumentException("Unsupported image type: " + imageType);
}
SimpleRaster raster = new SimpleRaster(sampleModel, buffer, new Point(0, 0));
BigBufferedImage image = new BigBufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
return image;
}
private static class ImagePartLoader implements Callable<ImagePartLoader> {
private final int y;
private final BufferedImage image;
private final Rectangle region;
private final File file;
public ImagePartLoader(int y, int width, int height, File file, BufferedImage image) {
this.y = y;
this.image = image;
this.file = file;
region = new Rectangle(0, y, width, height);
}
@Override
public ImagePartLoader call() throws Exception {
Thread.currentThread().setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2);
try (ImageInputStream stream = ImageIO.createImageInputStream(file);) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (readers.hasNext()) {
ImageReader reader = readers.next();
reader.setInput(stream, true, true);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(region);
BufferedImage part = reader.read(0, param);
Raster source = part.getRaster();
WritableRaster target = image.getRaster();
target.setRect(0, y, source);
}
}
return ImagePartLoader.this;
}
}
private BigBufferedImage(ColorModel cm, SimpleRaster raster, boolean isRasterPremultiplied, Hashtable<?, ?> properties) {
super(cm, raster, isRasterPremultiplied, properties);
}
public void dispose() {
((SimpleRaster) getRaster()).dispose();
}
public static void dispose(RenderedImage image) {
if (image instanceof BigBufferedImage) {
((BigBufferedImage) image).dispose();
}
}
private static class SimpleRaster extends WritableRaster {
public SimpleRaster(SampleModel sampleModel, FileDataBuffer dataBuffer, Point origin) {
super(sampleModel, dataBuffer, origin);
}
public void dispose() {
((FileDataBuffer) getDataBuffer()).dispose();
}
}
private static final class FileDataBufferDeleterHook extends Thread {
static {
Runtime.getRuntime().addShutdownHook(new FileDataBufferDeleterHook());
}
private static final HashSet<FileDataBuffer> undisposedBuffers = new HashSet<>();
@Override
public void run() {
final FileDataBuffer[] buffers = undisposedBuffers.toArray(new FileDataBuffer[0]);
for (FileDataBuffer b : buffers) {
b.disposeNow();
}
}
}
private static class FileDataBuffer extends DataBuffer {
private final String id = "buffer-" + System.currentTimeMillis() + "-" + ((int) (Math.random() * 1000));
private File dir;
private String path;
private File[] files;
private RandomAccessFile[] accessFiles;
private MappedByteBuffer[] buffer;
public FileDataBuffer(File dir, int size) throws FileNotFoundException, IOException {
super(TYPE_BYTE, size);
this.dir = dir;
init();
}
public FileDataBuffer(File dir, int size, int numBanks) throws FileNotFoundException, IOException {
super(TYPE_BYTE, size, numBanks);
this.dir = dir;
init();
}
private void init() throws FileNotFoundException, IOException {
FileDataBufferDeleterHook.undisposedBuffers.add(this);
if (dir == null) {
dir = new File(".");
}
if (!dir.exists()) {
throw new RuntimeException("FileDataBuffer constructor parameter dir does not exist: " + dir);
}
if (!dir.isDirectory()) {
throw new RuntimeException("FileDataBuffer constructor parameter dir is not a directory: " + dir);
}
path = dir.getPath() + "/" + id;
File subDir = new File(path);
subDir.mkdir();
buffer = new MappedByteBuffer[banks];
accessFiles = new RandomAccessFile[banks];
files = new File[banks];
for (int i = 0; i < banks; i++) {
File file = files[i] = new File(path + "/bank" + i + ".dat");
final RandomAccessFile randomAccessFile = accessFiles[i] = new RandomAccessFile(file, "rw");
buffer[i] = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, getSize());
}
}
@Override
public int getElem(int bank, int i) {
return buffer[bank].get(i) & 0xff;
}
@Override
public void setElem(int bank, int i, int val) {
buffer[bank].put(i, (byte) val);
}
@Override
protected void finalize() throws Throwable {
dispose();
}
private void disposeNow() {
final MappedByteBuffer[] disposedBuffer = this.buffer;
this.buffer = null;
disposeNow(disposedBuffer);
}
public void dispose() {
final MappedByteBuffer[] disposedBuffer = this.buffer;
this.buffer = null;
new Thread() {
@Override
public void run() {
disposeNow(disposedBuffer);
}
}.start();
}
private void disposeNow(final MappedByteBuffer[] disposedBuffer) {
FileDataBufferDeleterHook.undisposedBuffers.remove(this);
if (disposedBuffer != null) {
for (MappedByteBuffer b : disposedBuffer) {
((DirectBuffer) b).cleaner().clean();
}
}
if (accessFiles != null) {
for (RandomAccessFile file : accessFiles) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
accessFiles = null;
}
if (files != null) {
for (File file : files) {
file.delete();
}
files = null;
}
if (path != null) {
new File(path).delete();
path = null;
}
}
}
}
将现有映像加载到BigBuffereImage:
BigBufferedImage image = BigBufferedImage.create(
tempDir, width, height, type);
BigBufferedImage image = BigBufferedImage.create(
imagePath, tempDir, type);
package com.pulispace.mc.ui.panorama.util;
/*
* This class is part of MCFS (Mission Control - Flight Software) a development
* of Team Puli Space, official Google Lunar XPRIZE contestant.
* This class is released under Creative Commons CC0.
* @author Zsolt Pocze, Dimitry Polivaev
* Please like us on facebook, and/or join our Small Step Club.
* http://www.pulispace.com
* https://www.facebook.com/pulispace
* http://nyomdmegteis.hu/en/
*/
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.image.BandedSampleModel;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import sun.nio.ch.DirectBuffer;
public class BigBufferedImage extends BufferedImage {
private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
public static final int MAX_PIXELS_IN_MEMORY = 1024 * 1024;
public static BufferedImage create(int width, int height, int imageType) {
if (width * height > MAX_PIXELS_IN_MEMORY) {
try {
final File tempDir = new File(TMP_DIR);
return createBigBufferedImage(tempDir, width, height, imageType);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
return new BufferedImage(width, height, imageType);
}
}
public static BufferedImage create(File inputFile, int imageType) throws IOException {
try (ImageInputStream stream = ImageIO.createImageInputStream(inputFile);) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (readers.hasNext()) {
try {
ImageReader reader = readers.next();
reader.setInput(stream, true, true);
int width = reader.getWidth(reader.getMinIndex());
int height = reader.getHeight(reader.getMinIndex());
BufferedImage image = create(width, height, imageType);
int cores = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
int block = Math.min(MAX_PIXELS_IN_MEMORY / cores / width, (int) (Math.ceil(height / (double) cores)));
ExecutorService generalExecutor = Executors.newFixedThreadPool(cores);
List<Callable<ImagePartLoader>> partLoaders = new ArrayList<>();
for (int y = 0; y < height; y += block) {
partLoaders.add(new ImagePartLoader(
y, width, Math.min(block, height - y), inputFile, image));
}
generalExecutor.invokeAll(partLoaders);
generalExecutor.shutdown();
return image;
} catch (InterruptedException ex) {
Logger.getLogger(BigBufferedImage.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return null;
}
private static BufferedImage createBigBufferedImage(File tempDir, int width, int height, int imageType)
throws FileNotFoundException, IOException {
FileDataBuffer buffer = new FileDataBuffer(tempDir, width * height, 4);
ColorModel colorModel = null;
BandedSampleModel sampleModel = null;
switch (imageType) {
case TYPE_INT_RGB:
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8, 0},
false,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 3);
break;
case TYPE_INT_ARGB:
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8, 8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4);
break;
default:
throw new IllegalArgumentException("Unsupported image type: " + imageType);
}
SimpleRaster raster = new SimpleRaster(sampleModel, buffer, new Point(0, 0));
BigBufferedImage image = new BigBufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
return image;
}
private static class ImagePartLoader implements Callable<ImagePartLoader> {
private final int y;
private final BufferedImage image;
private final Rectangle region;
private final File file;
public ImagePartLoader(int y, int width, int height, File file, BufferedImage image) {
this.y = y;
this.image = image;
this.file = file;
region = new Rectangle(0, y, width, height);
}
@Override
public ImagePartLoader call() throws Exception {
Thread.currentThread().setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2);
try (ImageInputStream stream = ImageIO.createImageInputStream(file);) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (readers.hasNext()) {
ImageReader reader = readers.next();
reader.setInput(stream, true, true);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(region);
BufferedImage part = reader.read(0, param);
Raster source = part.getRaster();
WritableRaster target = image.getRaster();
target.setRect(0, y, source);
}
}
return ImagePartLoader.this;
}
}
private BigBufferedImage(ColorModel cm, SimpleRaster raster, boolean isRasterPremultiplied, Hashtable<?, ?> properties) {
super(cm, raster, isRasterPremultiplied, properties);
}
public void dispose() {
((SimpleRaster) getRaster()).dispose();
}
public static void dispose(RenderedImage image) {
if (image instanceof BigBufferedImage) {
((BigBufferedImage) image).dispose();
}
}
private static class SimpleRaster extends WritableRaster {
public SimpleRaster(SampleModel sampleModel, FileDataBuffer dataBuffer, Point origin) {
super(sampleModel, dataBuffer, origin);
}
public void dispose() {
((FileDataBuffer) getDataBuffer()).dispose();
}
}
private static final class FileDataBufferDeleterHook extends Thread {
static {
Runtime.getRuntime().addShutdownHook(new FileDataBufferDeleterHook());
}
private static final HashSet<FileDataBuffer> undisposedBuffers = new HashSet<>();
@Override
public void run() {
final FileDataBuffer[] buffers = undisposedBuffers.toArray(new FileDataBuffer[0]);
for (FileDataBuffer b : buffers) {
b.disposeNow();
}
}
}
private static class FileDataBuffer extends DataBuffer {
private final String id = "buffer-" + System.currentTimeMillis() + "-" + ((int) (Math.random() * 1000));
private File dir;
private String path;
private File[] files;
private RandomAccessFile[] accessFiles;
private MappedByteBuffer[] buffer;
public FileDataBuffer(File dir, int size) throws FileNotFoundException, IOException {
super(TYPE_BYTE, size);
this.dir = dir;
init();
}
public FileDataBuffer(File dir, int size, int numBanks) throws FileNotFoundException, IOException {
super(TYPE_BYTE, size, numBanks);
this.dir = dir;
init();
}
private void init() throws FileNotFoundException, IOException {
FileDataBufferDeleterHook.undisposedBuffers.add(this);
if (dir == null) {
dir = new File(".");
}
if (!dir.exists()) {
throw new RuntimeException("FileDataBuffer constructor parameter dir does not exist: " + dir);
}
if (!dir.isDirectory()) {
throw new RuntimeException("FileDataBuffer constructor parameter dir is not a directory: " + dir);
}
path = dir.getPath() + "/" + id;
File subDir = new File(path);
subDir.mkdir();
buffer = new MappedByteBuffer[banks];
accessFiles = new RandomAccessFile[banks];
files = new File[banks];
for (int i = 0; i < banks; i++) {
File file = files[i] = new File(path + "/bank" + i + ".dat");
final RandomAccessFile randomAccessFile = accessFiles[i] = new RandomAccessFile(file, "rw");
buffer[i] = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, getSize());
}
}
@Override
public int getElem(int bank, int i) {
return buffer[bank].get(i) & 0xff;
}
@Override
public void setElem(int bank, int i, int val) {
buffer[bank].put(i, (byte) val);
}
@Override
protected void finalize() throws Throwable {
dispose();
}
private void disposeNow() {
final MappedByteBuffer[] disposedBuffer = this.buffer;
this.buffer = null;
disposeNow(disposedBuffer);
}
public void dispose() {
final MappedByteBuffer[] disposedBuffer = this.buffer;
this.buffer = null;
new Thread() {
@Override
public void run() {
disposeNow(disposedBuffer);
}
}.start();
}
private void disposeNow(final MappedByteBuffer[] disposedBuffer) {
FileDataBufferDeleterHook.undisposedBuffers.remove(this);
if (disposedBuffer != null) {
for (MappedByteBuffer b : disposedBuffer) {
((DirectBuffer) b).cleaner().clean();
}
}
if (accessFiles != null) {
for (RandomAccessFile file : accessFiles) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
accessFiles = null;
}
if (files != null) {
for (File file : files) {
file.delete();
}
files = null;
}
if (path != null) {
new File(path).delete();
path = null;
}
}
}
}
渲染图像的一部分:
part = image.getSubimage(x, y, width, height);
BigBuffereImage的实现:
BigBufferedImage image = BigBufferedImage.create(
tempDir, width, height, type);
BigBufferedImage image = BigBufferedImage.create(
imagePath, tempDir, type);
package com.pulispace.mc.ui.panorama.util;
/*
* This class is part of MCFS (Mission Control - Flight Software) a development
* of Team Puli Space, official Google Lunar XPRIZE contestant.
* This class is released under Creative Commons CC0.
* @author Zsolt Pocze, Dimitry Polivaev
* Please like us on facebook, and/or join our Small Step Club.
* http://www.pulispace.com
* https://www.facebook.com/pulispace
* http://nyomdmegteis.hu/en/
*/
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.image.BandedSampleModel;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import sun.nio.ch.DirectBuffer;
public class BigBufferedImage extends BufferedImage {
private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
public static final int MAX_PIXELS_IN_MEMORY = 1024 * 1024;
public static BufferedImage create(int width, int height, int imageType) {
if (width * height > MAX_PIXELS_IN_MEMORY) {
try {
final File tempDir = new File(TMP_DIR);
return createBigBufferedImage(tempDir, width, height, imageType);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
return new BufferedImage(width, height, imageType);
}
}
public static BufferedImage create(File inputFile, int imageType) throws IOException {
try (ImageInputStream stream = ImageIO.createImageInputStream(inputFile);) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (readers.hasNext()) {
try {
ImageReader reader = readers.next();
reader.setInput(stream, true, true);
int width = reader.getWidth(reader.getMinIndex());
int height = reader.getHeight(reader.getMinIndex());
BufferedImage image = create(width, height, imageType);
int cores = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
int block = Math.min(MAX_PIXELS_IN_MEMORY / cores / width, (int) (Math.ceil(height / (double) cores)));
ExecutorService generalExecutor = Executors.newFixedThreadPool(cores);
List<Callable<ImagePartLoader>> partLoaders = new ArrayList<>();
for (int y = 0; y < height; y += block) {
partLoaders.add(new ImagePartLoader(
y, width, Math.min(block, height - y), inputFile, image));
}
generalExecutor.invokeAll(partLoaders);
generalExecutor.shutdown();
return image;
} catch (InterruptedException ex) {
Logger.getLogger(BigBufferedImage.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return null;
}
private static BufferedImage createBigBufferedImage(File tempDir, int width, int height, int imageType)
throws FileNotFoundException, IOException {
FileDataBuffer buffer = new FileDataBuffer(tempDir, width * height, 4);
ColorModel colorModel = null;
BandedSampleModel sampleModel = null;
switch (imageType) {
case TYPE_INT_RGB:
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8, 0},
false,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 3);
break;
case TYPE_INT_ARGB:
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8, 8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4);
break;
default:
throw new IllegalArgumentException("Unsupported image type: " + imageType);
}
SimpleRaster raster = new SimpleRaster(sampleModel, buffer, new Point(0, 0));
BigBufferedImage image = new BigBufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
return image;
}
private static class ImagePartLoader implements Callable<ImagePartLoader> {
private final int y;
private final BufferedImage image;
private final Rectangle region;
private final File file;
public ImagePartLoader(int y, int width, int height, File file, BufferedImage image) {
this.y = y;
this.image = image;
this.file = file;
region = new Rectangle(0, y, width, height);
}
@Override
public ImagePartLoader call() throws Exception {
Thread.currentThread().setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2);
try (ImageInputStream stream = ImageIO.createImageInputStream(file);) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (readers.hasNext()) {
ImageReader reader = readers.next();
reader.setInput(stream, true, true);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(region);
BufferedImage part = reader.read(0, param);
Raster source = part.getRaster();
WritableRaster target = image.getRaster();
target.setRect(0, y, source);
}
}
return ImagePartLoader.this;
}
}
private BigBufferedImage(ColorModel cm, SimpleRaster raster, boolean isRasterPremultiplied, Hashtable<?, ?> properties) {
super(cm, raster, isRasterPremultiplied, properties);
}
public void dispose() {
((SimpleRaster) getRaster()).dispose();
}
public static void dispose(RenderedImage image) {
if (image instanceof BigBufferedImage) {
((BigBufferedImage) image).dispose();
}
}
private static class SimpleRaster extends WritableRaster {
public SimpleRaster(SampleModel sampleModel, FileDataBuffer dataBuffer, Point origin) {
super(sampleModel, dataBuffer, origin);
}
public void dispose() {
((FileDataBuffer) getDataBuffer()).dispose();
}
}
private static final class FileDataBufferDeleterHook extends Thread {
static {
Runtime.getRuntime().addShutdownHook(new FileDataBufferDeleterHook());
}
private static final HashSet<FileDataBuffer> undisposedBuffers = new HashSet<>();
@Override
public void run() {
final FileDataBuffer[] buffers = undisposedBuffers.toArray(new FileDataBuffer[0]);
for (FileDataBuffer b : buffers) {
b.disposeNow();
}
}
}
private static class FileDataBuffer extends DataBuffer {
private final String id = "buffer-" + System.currentTimeMillis() + "-" + ((int) (Math.random() * 1000));
private File dir;
private String path;
private File[] files;
private RandomAccessFile[] accessFiles;
private MappedByteBuffer[] buffer;
public FileDataBuffer(File dir, int size) throws FileNotFoundException, IOException {
super(TYPE_BYTE, size);
this.dir = dir;
init();
}
public FileDataBuffer(File dir, int size, int numBanks) throws FileNotFoundException, IOException {
super(TYPE_BYTE, size, numBanks);
this.dir = dir;
init();
}
private void init() throws FileNotFoundException, IOException {
FileDataBufferDeleterHook.undisposedBuffers.add(this);
if (dir == null) {
dir = new File(".");
}
if (!dir.exists()) {
throw new RuntimeException("FileDataBuffer constructor parameter dir does not exist: " + dir);
}
if (!dir.isDirectory()) {
throw new RuntimeException("FileDataBuffer constructor parameter dir is not a directory: " + dir);
}
path = dir.getPath() + "/" + id;
File subDir = new File(path);
subDir.mkdir();
buffer = new MappedByteBuffer[banks];
accessFiles = new RandomAccessFile[banks];
files = new File[banks];
for (int i = 0; i < banks; i++) {
File file = files[i] = new File(path + "/bank" + i + ".dat");
final RandomAccessFile randomAccessFile = accessFiles[i] = new RandomAccessFile(file, "rw");
buffer[i] = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, getSize());
}
}
@Override
public int getElem(int bank, int i) {
return buffer[bank].get(i) & 0xff;
}
@Override
public void setElem(int bank, int i, int val) {
buffer[bank].put(i, (byte) val);
}
@Override
protected void finalize() throws Throwable {
dispose();
}
private void disposeNow() {
final MappedByteBuffer[] disposedBuffer = this.buffer;
this.buffer = null;
disposeNow(disposedBuffer);
}
public void dispose() {
final MappedByteBuffer[] disposedBuffer = this.buffer;
this.buffer = null;
new Thread() {
@Override
public void run() {
disposeNow(disposedBuffer);
}
}.start();
}
private void disposeNow(final MappedByteBuffer[] disposedBuffer) {
FileDataBufferDeleterHook.undisposedBuffers.remove(this);
if (disposedBuffer != null) {
for (MappedByteBuffer b : disposedBuffer) {
((DirectBuffer) b).cleaner().clean();
}
}
if (accessFiles != null) {
for (RandomAccessFile file : accessFiles) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
accessFiles = null;
}
if (files != null) {
for (File file : files) {
file.delete();
}
files = null;
}
if (path != null) {
new File(path).delete();
path = null;
}
}
}
}
package com.pulispace.mc.ui.panorama.util;
/*
*本课程是MCFS(任务控制飞行软件)a开发的一部分
*普力空间团队的成员,谷歌月球XPRIZE的官方参赛者。
*该类在知识共享CC0下发布。
*@作者Zsolt Pocze,Dimitry Polivaev
*请在facebook上喜欢我们,和/或加入我们的小步俱乐部。
* http://www.pulispace.com
* https://www.facebook.com/pulispace
* http://nyomdmegteis.hu/en/
*/
导入java.awt.Point;
导入java.awt.Rectangle;
导入java.awt.color.ColorSpace;
导入java.awt.image.BandedSampleModel;
导入java.awt.image.buffereImage;
导入java.awt.image.ColorModel;
导入java.awt.image.ComponentColorModel;
导入java.awt.image.DataBuffer;
导入java.awt.image.graster;
导入java.awt.image.renderImage;
导入java.awt.image.SampleModel;
导入java.awt.image.WritableRaster;
导入java.io.File;
导入java.io.FileNotFoundException;
导入java.io.IOException;
导入java.io.RandomAccessFile;
导入java.nio.MappedByteBuffer;
导入java.nio.channels.FileChannel;
导入java.util.ArrayList;
导入java.util.HashSet;
导入java.util.Hashtable;
导入java.util.Iterator;
导入java.util.List;
导入java.util.concurrent.Callable;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.logging.Level;
导入java.util.logging.Logger;
导入javax.imageio.imageio;
导入javax.imageio.ImageReadParam;
导入javax.imageio.ImageReader;
导入javax.imageio.stream.ImageInputStream;
导入sun.nio.ch.DirectBuffer;
公共类BigBuffereImage扩展了BuffereImage{
私有静态最终字符串TMP_DIR=System.getProperty(“java.io.tmpdir”);
内存中的公共静态最终整数最大像素=1024*1024;
公共静态缓冲区映像创建(int-width、int-height、int-imageType){
如果(宽度*高度>内存中的最大像素){
试一试{
最终文件tempDir=新文件(TMP_DIR);
返回CreateBigBuffereImage(tempDir、宽度、高度、imageType);
}捕获(IOE异常){
抛出新的运行时异常(e);
}
}否则{
返回新的BuffereImage(宽度、高度、图像类型);
}
}
公共静态缓冲区映像创建(文件inputFile,int imageType)引发IOException{
try(ImageInputStream=ImageIO.createImageInputStream(inputFile);){
迭代器读卡器=ImageIO.getImageReaders(流);
if(readers.hasNext()){
试一试{
ImageReader=readers.next();
setInput(流、真、真);
int-width=reader.getWidth(reader.getMinIndex());
int height=reader.getHeight(reader.getMinIndex());
BuffereImage=创建(宽度、高度、图像类型);
int cores=Math.max(1,Runtime.getRuntime().availableProcessors()/2);
int block=Math.min(最大内存像素/内核/宽度,(int)(Math.ceil(高度/(双)内核));
ExecutorService generalExecutor=Executors.newFixedThreadPool(核心);
List partLoaders=new ArrayList();
对于(int y=0;y