Могу ли я сохранить огромный PNG, если бы все это не было в памяти?
Я сохраняю очень большой PNG (25 МБ или около того) с Java. Проблема в том, что при ее создании он использует 3 гигабайта памяти, что не идеально, поскольку оно сильно замедляет работу систем с низкой памятью.
Код, с которым я работаю, требует объединения набора черепичных изображений в одно изображение; Другими словами, у меня есть девять изображений (PNG):
A1 A2 A3
B1 B2 B3
C1 C2 C3
которые необходимо объединить в одно изображение.
Код, который я использую, следующий:
image = new BufferedImage(width, height, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// draw the 9 images on here at their proper positions...
// save image
g2d.dispose();
File file = getOutputFile();
ImageIO.write(image, "png", file);
Есть ли способ сделать и сохранить изображение, не имея всего изображения в памяти?
Edit:
Чтобы рисовать изображения, я делаю это в цикле:
BufferedImage tile = ImageIO.read(new File("file.png"));
g2d.drawImage(tile, x, y, w, h);
Это повторяется много раз (обычно около 25x25, но иногда и больше), поэтому, если есть небольшая утечка памяти, это может вызвать проблемы.
Ответы
Ответ 1
ImageIO.write(image, "png", file);
внутренне использует com.sun.imageio.plugins.png.PNGImageWriter. Этот метод и этот автор ожидают, что изображение будет отображаемым изображением, но написание PNG выполняется "группами", поэтому вы можете создать подкласс RenderedImage, который генерирует запрошенные полосы сложного большого изображения, когда писатель запрашивает эти полосы для изображения.
Из класса PNGImageWriter:
private void encodePass(ImageOutputStream os,
RenderedImage image,
int xOffset, int yOffset,
int xSkip, int ySkip) throws IOException {
// (...)
for (int row = minY + yOffset; row < minY + height; row += ySkip) {
Rectangle rect = new Rectangle(minX, row, width, 1); // <--- *1
Raster ras = image.getData(rect); // <--- *2
* 2 Я думаю, что это единственное место, где автор читает пиксели из вашего изображения. Вы должны сделать метод getData (прямой), который вычисляет, что rect соединяет 3 полосы из 3 изображений в один.
* 1 Как вы видите, он читает полосы с высотой 1 пиксель.
Если все так, как мне кажется, вам нужно всего лишь составить 3 изображения за раз. Не было бы необходимости в том, чтобы остальные 6 были в памяти.
Я знаю, что это не простое решение, но оно может помочь вам, если вы не найдете ничего проще.
Ответ 2
Вы также можете посмотреть эту библиотеку PNGJ (отказ от ответственности: я закодировал ее), она позволяет сохранить линию изображения PNG по строке.
Ответ 3
Можно ли использовать внешний инструмент? Я помню, используя ImageMagick для аналогичной цели, вам сначала нужно сохранить свои меньшие изображения.