Могу ли я сохранить огромный PNG без памяти? - PullRequest
15 голосов
/ 21 августа 2011

Я сохраняю очень большой 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: Чтобы нарисовать изображения, я делаю это в цикле:

<code>BufferedImage tile = ImageIO.read(new File("file.png"));
g2d.drawImage(tile, x, y, w, h);

Это повторяется много раз (обычно это примерно 25x25, но иногда больше), поэтому, если здесь есть даже небольшая утечка памяти, это может вызвать проблему.

Ответы [ 3 ]

5 голосов
/ 02 сентября 2011

Вы также можете взглянуть на эту библиотеку PNGJ (отказ от ответственности: я ее кодировал), она позволяет сохранять изображение PNG построчно.

4 голосов
/ 21 августа 2011

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, чтобы быть в памяти.

Я знаю, что это не простое решение, но оно может помочь вам, если вы не найдете ничего проще.

1 голос
/ 22 августа 2011

Будет ли возможным использование внешнего инструмента? Я помню, что используя ImageMagick для аналогичных целей, вам сначала нужно сохранить изображения меньшего размера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...