将动画图像加载到 BufferedImage 数组

发布于 2024-12-27 01:18:06 字数 276 浏览 7 评论 0原文

我正在尝试将动画纹理无缝地实现到 OpenGL 游戏中。我创建了一个通用的 ImageDecoder 类来将任何 BufferedImage 转换为 ByteBuffer。尽管它不加载动画图像,但目前效果很好。

我不想将动画图像加载为 ImageIcon。我需要 BufferedImage 来获取兼容 OpenGL 的 ByteBuffer。

如何将每个帧作为 BufferedImage 数组加载到动画图像中? 同样,我怎样才能获得动画速率/周期?

Java 可以处理 APNG 吗?

I'm trying to implement animated textures into an OpenGL game seamlessly. I made a generic ImageDecoder class to translate any BufferedImage into a ByteBuffer. It works perfectly for now, though it doesn't load animated images.

I'm not trying to load an animated image as an ImageIcon. I need the BufferedImage to get an OpenGL-compliant ByteBuffer.

How can I load every frames as a BufferedImage array in an animated image ?
On a similar note, how can I get the animation rate / period ?

Does Java handle APNG ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笨死的猪 2025-01-03 01:18:06

以下代码是我自己的实现的改编,以适应“进入数组”部分。

gif 的问题是:如果您希望它适用于所有处理方法,则必须考虑不同的处理方法。下面的代码试图弥补这一点。例如,“doNotDispose”模式有一个特殊的实现,它获取从 start 到 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;
}

The following code is an adaption from my own implementation to accommodate the "into array" part.

The problem with gifs is: There are different disposal methods which have to be considered, if you want this to work with all of them. The code below tries to compensate for that. For example there is a special implementation for "doNotDispose" mode, which takes all frames from start to N and paints them on top of each other into a BufferedImage.

The advantage of this method over the one posted by chubbsondubs is that it does not have to wait for the gif animation delays, but can be done basically instantly.

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;
}
分分钟 2025-01-03 01:18:06

我认为 Java 默认不支持 APNG,但您可以使用第 3 方库来解析它:

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

I don't think Java supports APNG by default, but you can use an 3rd party library to parse it:

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

That might be your easiest method. As for getting the frames from an animated gif you have to register an 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.
        }
    }
});

This loads asynchronously on another thread so imageUpdate() won't be called immediately. But it will be called for each frame as it parses it.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文