Загрузка анимированного изображения в массив BufferedImage

Я пытаюсь легко внедрить анимированные текстуры в игру OpenGL. Я создал общий класс ImageDecoder для перевода любого BufferedImage в ByteBuffer. На данный момент он работает отлично, хотя и не загружает анимированные изображения.

Я не пытаюсь загрузить анимированное изображение как ImageIcon. Мне нужно BufferedImage, чтобы получить ByteBuffer, совместимый с OpenGL.

Как я могу загрузить все кадры в виде массива BufferedImage в анимированном изображении? Аналогично, как я могу получить скорость/период анимации?

Обрабатывает ли Java APNG?


person Klems    schedule 13.01.2012    source источник


Ответы (2)


Я не думаю, что Java поддерживает APNG по умолчанию, но вы можете использовать стороннюю библиотеку для его анализа:

http://code.google.com/p/javapng/source/browse/trunk/javapng2/src/apng/com/sixlegs/png/AnimatedPngImage.java?r=300

Это может быть ваш самый простой метод. Что касается получения кадров из анимированного gif, вам необходимо зарегистрировать ImageObserver:

new ImageIcon( url ).setImageObserver( new ImageObserver() {
    public void imageUpdate( Image img, int infoFlags, int x, int y, int width, int height ) {
        if( infoFlags & ImageObserver.FRAMEBITS == ImageObserver.FRAMEBITS ) {
            // another frame was loaded do something with it.
        }
    }
});

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

http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/image/ImageObserver.html

person chubbsondubs    schedule 13.01.2012
comment
Я использовал библиотеку. Мне пришлось сделать свою собственную банку для поддержки анимированного PNG, но теперь все работает как задумано. - person Klems; 15.01.2012

Следующий код является адаптацией моей собственной реализации для размещения части «в массив».

Проблема с GIF-файлами заключается в следующем: существуют разные методы удаления, которые необходимо учитывать, если вы хотите, чтобы это работало со всеми ними. Код ниже пытается компенсировать это. Например, существует специальная реализация для режима «doNotDispose», который берет все кадры от начала до N и рисует их друг поверх друга в BufferedImage.

Преимущество этого метода по сравнению с тем, который опубликован chubbsondubs, заключается в том, что ему не нужно ждать задержки анимации gif, а можно сделать практически мгновенно.

BufferedImage[] array = null;
ImageInputStream imageInputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(data)); // or any other source stream
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
while (imageReaders.hasNext())
{
    ImageReader reader = (ImageReader) imageReaders.next();
    try
    {
        reader.setInput(imageInputStream);
        frames = reader.getNumImages(true);
        array = new BufferedImage[frames];
        for (int frameId : frames)
        {
            int w = reader.getWidth(0);
            int h = reader.getHeight(0);
            int fw = reader.getWidth(frameId);
            int fh = reader.getHeight(frameId);
            if (h != fh || w != fw)
            {
                GifMeta gm = getGifMeta(reader.getImageMetadata(frameId));
                // disposalMethodNames: "none", "doNotDispose","restoreToBackgroundColor","restoreToPrevious",
                if ("doNotDispose".equals(gm.disposalMethod))
                {
                    image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
                    Graphics2D g = (Graphics2D) image.getGraphics();
                    for (int f = 0; f <= frameId; f++)
                    {
                        gm = getGifMeta(reader.getImageMetadata(f));

                        if ("doNotDispose".equals(gm.disposalMethod))
                        {
                            g.drawImage(reader.read(f), null, gm.imageLeftPosition, gm.imageTopPosition);
                        }
                        else
                        {
                            // XXX "Unimplemented disposalMethod (" + getName() + "): " + gm.disposalMethod);
                        }
                    }
                    g.dispose();
                }
                else
                {
                    image = reader.read(frameId);
                    // XXX "Unimplemented disposalMethod (" + getName() + "): " + gm.disposalMethod;
                }
            }
            else
            {
                image = reader.read(frameId);
            }
            if (image == null)
            {
                throw new NullPointerException();
            }
            array[frame] = image;
        }
    }
    finally
    {
        reader.dispose();
    }
}
return array;

private final static class GifMeta
{

    String disposalMethod = "none";
    int imageLeftPosition = 0;
    int imageTopPosition = 0;
    int delayTime = 0;
}

private GifMeta getGifMeta(IIOMetadata meta)
{
    GifMeta gm = new GifMeta();
    final IIOMetadataNode gifMeta = (IIOMetadataNode) meta.getAsTree("javax_imageio_gif_image_1.0");
    NodeList childNodes = gifMeta.getChildNodes();
    for (int i = 0; i < childNodes.getLength(); ++i)
    {
        IIOMetadataNode subnode = (IIOMetadataNode) childNodes.item(i);
        if (subnode.getNodeName().equals("GraphicControlExtension"))
        {
            gm.disposalMethod = subnode.getAttribute("disposalMethod");
            gm.delayTime = Integer.parseInt(subnode.getAttribute("delayTime"));
        }
        else if (subnode.getNodeName().equals("ImageDescriptor"))
        {
            gm.imageLeftPosition = Integer.parseInt(subnode.getAttribute("imageLeftPosition"));
            gm.imageTopPosition = Integer.parseInt(subnode.getAttribute("imageTopPosition"));
        }
    }
    return gm;
}
person Frederic Leitenberger    schedule 16.07.2014