在 Java 中加载时镜像动画 gif - ImageIcon [英] Mirroring animated gif on load in Java - ImageIcon

查看:34
本文介绍了在 Java 中加载时镜像动画 gif - ImageIcon的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个动画 gif 可以像这样加载到 ImageIcon 中:

So I have an animated gif that I load into an ImageIcon like this:

Image image = new ImageIcon("image.gif").getImage();

我可以用这个来画它:

g.drawImage(image, x, y, null);

我知道我可以使用 AffineTransform 即时镜像它,但我需要能够在加载后水平镜像它,以便我可以在需要时绘制镜像而不需要每次转换它的开销重绘.有没有办法使用swing/awt来做到这一点?

I know that I can mirror it on the fly using AffineTransform, but I need to be able to mirror it horizontally after loading, so that I can draw the mirrored one instead if needed without the overhead of transforming it every time it gets redrawn. Is there a way to do this using swing/awt?

一个可以做到这一点的图书馆也将是一个巨大的帮助.

A library that could do this would also be a huge help.

推荐答案

正如您所指出的,问题在于 gif 是动画的.

The problem is, as you have pointed out, is the fact the gif's are animated.

除非您非常想接手必须自己渲染每一帧的工作,否则您唯一的选择就是在paint方法中使用AffineTransform.

Unless you desperately want to take over the job of having to render each frame yourself, the only choice you have is to use an AffineTransform with in the paint method.

一般来说,您不应看到显着差异(在渲染方面).

Generally speaking, you shouldn't see a significant difference (in rendering).

如果你真的很绝望,你可以简单地在外部预先渲染 gif 并提供一个镜像版本

If you are really desperate, you could simply pre-render the gif externally and provide a mirrored version

更新了某种"工作示例

这是thisthis 答案,使用 这个 GIF 作家.

This is a combination of this and this answers, using this GIF writer.

这个例子的基本作用是读取原始 gif 图像,逐帧镜像,然后写回镜像文件.

Basically what this example does is it reads an original gif image, mirrors it frame by frame, and writes back out to a mirrored file.

然后它将原始文件和镜像文件作为 ImageIcon 加载回,主要是因为我不太愿意重新发明轮子来显示动画 gif.是的,你可以做到,你需要的一切都在..

It then loads both the original and mirrored files back in as ImageIcons, mostly because I'm not really up to re-inventing the wheel for display animated gifs. Yes, you could do it and everything you would need is provided within..

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class MirrorImage {

    public static void main(String[] args) {
        new MirrorImage();
    }

    public MirrorImage() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private ImageIcon orig;
        private ImageIcon mirror;

        public TestPane() {
            mirror(new File("java_animated.gif"), new File("Mirror.gif"));
            orig = new ImageIcon("java_animated.gif");
            mirror = new ImageIcon("Mirror.gif");
        }

        @Override
        public Dimension getPreferredSize() {
            return mirror == null ? new Dimension(200, 200) : new Dimension(orig.getIconWidth(), orig.getIconHeight() * 2);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (orig != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                int x = (getWidth() - orig.getIconWidth()) / 2;
                int y = (getHeight() - (orig.getIconHeight() * 2)) / 2;
                g2d.drawImage(orig.getImage(), x, y, this);

//                AffineTransform at = new AffineTransform();
//                at.setToScale(1, -1);
//                at.translate(0, -mirror.getIconHeight());
//                g2d.setTransform(at);
                g2d.drawImage(mirror.getImage(), x, y + mirror.getIconHeight(), this);
                g2d.dispose();
            }
        }
    }

    public static void mirror(File source, File dest) {

        List<BufferedImage> images = new ArrayList<>(25);
        List<Integer> delays = new ArrayList<>(25);
        int delay = 0;

        ImageOutputStream output = null;
        GifSequenceWriter writer = null;

        try {

            String[] imageatt = new String[]{
                "imageLeftPosition",
                "imageTopPosition",
                "imageWidth",
                "imageHeight"
            };

            ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next();
            ImageInputStream ciis = ImageIO.createImageInputStream(source);
            reader.setInput(ciis, false);
            int noi = reader.getNumImages(true);
            BufferedImage master = null;

            for (int i = 0; i < noi; i++) {

                BufferedImage image = reader.read(i);
                IIOMetadata metadata = reader.getImageMetadata(i);

                Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
                NodeList children = tree.getChildNodes();
                for (int j = 0; j < children.getLength(); j++) {
                    Node nodeItem = children.item(j);
                    System.out.println(nodeItem.getNodeName());
                    if (nodeItem.getNodeName().equals("ImageDescriptor")) {
                        Map<String, Integer> imageAttr = new HashMap<String, Integer>();
                        NamedNodeMap attr = nodeItem.getAttributes();
//                        for (int index = 0; index < attr.getLength(); index++) {
//                            Node node = attr.item(index);
//                            System.out.println("----> " + node.getNodeName() + "=" + node.getNodeValue());
//                        }
                        for (int k = 0; k < imageatt.length; k++) {
                            Node attnode = attr.getNamedItem(imageatt[k]);
                            imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
                        }

                        if (master == null) {
                            master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
                        }

                        Graphics2D g2d = master.createGraphics();
                        g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
                        g2d.dispose();

                        BufferedImage frame = mirror(copyImage(master));
                        ImageIO.write(frame, "png", new File("img" + i + ".png"));
                        images.add(frame);

                    } else if (nodeItem.getNodeName().equals("GraphicControlExtension")) {
                        NamedNodeMap attr = nodeItem.getAttributes();
                        Node delayNode = attr.getNamedItem("delayTime");
                        if (delayNode != null) {
                            delay = Math.max(delay, Integer.valueOf(delayNode.getNodeValue()));
                            delays.add(delay);
                        }
                    }
                }

            }

            output = new FileImageOutputStream(dest);
            writer = new GifSequenceWriter(output, images.get(0).getType(), delay * 10, true);

            for (int i = 0; i < images.size(); i++) {
                BufferedImage nextImage = images.get(i);
                writer.writeToSequence(nextImage);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (Exception e) {
            }
            try {
                output.close();
            } catch (Exception e) {
            }
        }
    }

    public static BufferedImage mirror(BufferedImage img) {

        BufferedImage mirror = createCompatibleImage(img);
        Graphics2D g2d = mirror.createGraphics();
        AffineTransform at = new AffineTransform();
        at.setToScale(1, -1);
        at.translate(0, -img.getHeight());
        g2d.setTransform(at);
        g2d.drawImage(img, 0, 0, null);
        g2d.dispose();

        return mirror;

    }

    public static BufferedImage copyImage(BufferedImage img) {
        int width = img.getWidth();
        int height = img.getHeight();

        BufferedImage newImage = createCompatibleImage(img);
        Graphics graphics = newImage.createGraphics();

        int x = (width - img.getWidth()) / 2;
        int y = (height - img.getHeight()) / 2;

        graphics.drawImage(img, x, y, img.getWidth(), img.getHeight(), null);
        graphics.dispose();

        return newImage;
    }

    public static BufferedImage createCompatibleImage(BufferedImage image) {
        return getGraphicsConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
    }

    public static GraphicsConfiguration getGraphicsConfiguration() {
        return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    }

    public static class GifSequenceWriter {

        protected ImageWriter gifWriter;
        protected ImageWriteParam imageWriteParam;
        protected IIOMetadata imageMetaData;

        /**
         * Creates a new GifSequenceWriter
         *
         * @param outputStream the ImageOutputStream to be written to
         * @param imageType one of the imageTypes specified in BufferedImage
         * @param timeBetweenFramesMS the time between frames in miliseconds
         * @param loopContinuously wether the gif should loop repeatedly
         * @throws IIOException if no gif ImageWriters are found
         *
         * @author Elliot Kroo (elliot[at]kroo[dot]net)
         */
        public GifSequenceWriter(
                ImageOutputStream outputStream,
                int imageType,
                int timeBetweenFramesMS,
                boolean loopContinuously) throws IIOException, IOException {
            // my method to create a writer
            gifWriter = getWriter();
            imageWriteParam = gifWriter.getDefaultWriteParam();
            ImageTypeSpecifier imageTypeSpecifier
                    = ImageTypeSpecifier.createFromBufferedImageType(imageType);

            imageMetaData
                    = gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
                    imageWriteParam);

            String metaFormatName = imageMetaData.getNativeMetadataFormatName();

            IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);

            IIOMetadataNode graphicsControlExtensionNode = getNode(
                    root,
                    "GraphicControlExtension");

            graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
            graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
            graphicsControlExtensionNode.setAttribute(
                    "transparentColorFlag",
                    "FALSE");
            graphicsControlExtensionNode.setAttribute(
                    "delayTime",
                    Integer.toString(timeBetweenFramesMS / 10));
            graphicsControlExtensionNode.setAttribute(
                    "transparentColorIndex",
                    "0");

            IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
            commentsNode.setAttribute("CommentExtension", "Created by MAH");

            IIOMetadataNode appEntensionsNode = getNode(
                    root,
                    "ApplicationExtensions");

            IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");

            child.setAttribute("applicationID", "NETSCAPE");
            child.setAttribute("authenticationCode", "2.0");

            int loop = loopContinuously ? 0 : 1;

            child.setUserObject(new byte[]{0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)});
            appEntensionsNode.appendChild(child);

            imageMetaData.setFromTree(metaFormatName, root);

            gifWriter.setOutput(outputStream);

            gifWriter.prepareWriteSequence(null);
        }

        public void writeToSequence(RenderedImage img) throws IOException {
            gifWriter.writeToSequence(
                    new IIOImage(
                    img,
                    null,
                    imageMetaData),
                    imageWriteParam);
        }

        /**
         * Close this GifSequenceWriter object. This does not close the underlying
         * stream, just finishes off the GIF.
         */
        public void close() throws IOException {
            gifWriter.endWriteSequence();
        }

        /**
         * Returns the first available GIF ImageWriter using
         * ImageIO.getImageWritersBySuffix("gif").
         *
         * @return a GIF ImageWriter object
         * @throws IIOException if no GIF image writers are returned
         */
        private static ImageWriter getWriter() throws IIOException {
            Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
            if (!iter.hasNext()) {
                throw new IIOException("No GIF Image Writers Exist");
            } else {
                return iter.next();
            }
        }

        /**
         * Returns an existing child node, or creates and returns a new child node
         * (if the requested node does not exist).
         *
         * @param rootNode the <tt>IIOMetadataNode</tt> to search for the child
         * node.
         * @param nodeName the name of the child node.
         *
         * @return the child node, if found or a new node created with the given
         * name.
         */
        private static IIOMetadataNode getNode(
                IIOMetadataNode rootNode,
                String nodeName) {
            int nNodes = rootNode.getLength();
            for (int i = 0; i < nNodes; i++) {
                if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)
                        == 0) {
                    return ((IIOMetadataNode) rootNode.item(i));
                }
            }
            IIOMetadataNode node = new IIOMetadataNode(nodeName);
            rootNode.appendChild(node);
            return (node);
        }
    }
}

注意事项

Gif 编写器目前仅适用于固定速率的 gif.应该可以改变这一点,但我没有时间.

The Gif writer currently only works with fixed rate gifs. It should be possible to change this, but I didn't have the time.

基本上,据我所知,您需要将帧"延迟传递给 writeToSquence 方法.在此方法中,您需要构造一个适当的 IIOMetadata,其中包含所有必需的属性,以及您的帧延迟...

Basically, as I understand it, you would need to pass a "frame" delay to the writeToSquence method. Within this method you would need to construct an appropriate IIOMetadata with all the required properties, plus your frame delay...

播放原始gif后更新

我正在播放的 GIF 已优化.也就是说,每一帧都添加"到动画中,而不是成为一个全新的帧.你的情况正好相反.每一帧都是一个完整的图像.

The GIF I was playing with was optimised. That is, each frame "added" to the animation, rather then being a brand new frame. Yours is the other way round. Each frame is an entire image.

现在,您可能有很多方法可以检查这一点,但现在,我不能打扰...

Now, there are probably lots of ways you could check for this, but right now, I can't be bothered...

相反......在 mirror(File, File) 方法中,我改变了它,而不是使用单个主"图像,每一帧创建一个新的 BufferedImage

Instead...in the mirror(File, File) method, I changed it so that rather then using a single "master" image, each frame creates a new BufferedImage

BufferedImage frame = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);

Graphics2D g2d = frame.createGraphics();
g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
g2d.dispose();

frame = mirror(frame);
ImageIO.write(frame, "png", new File("img" + i + ".png"));
images.add(frame);

我还更新了 GifSequenceWriter 以将元数据设置为更接近原始数据...

I also updated the GifSequenceWriter to set the meta data to more closely match the original as well...

graphicsControlExtensionNode.setAttribute("disposalMethod", "restoreToBackgroundColor");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
        "transparentColorFlag",
        "TRUE");

这篇关于在 Java 中加载时镜像动画 gif - ImageIcon的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆