【问题标题】:Mirroring animated gif on load in Java - ImageIcon在 Java 中加载时镜像动画 gif - ImageIcon
【发布时间】:2013-08-09 15:35:51
【问题描述】:

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

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

我可以用这个来画它:

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

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

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

【问题讨论】:

    标签: java swing animation awt imageicon


    【解决方案1】:

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

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

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

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

    更新了“某种”工作示例

    这是thisthis 答案的组合,使用this GIF 作家。

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

    然后它会将原始文件和镜像文件加载回ImageIcons,主要是因为我真的不打算重新发明轮子来显示动画 gif。是的,你可以做到,你需要的一切都在里面提供..

    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。应该可以改一下,但是我没时间。

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

    播放原始 gif 后更新

    我正在玩的 GIF 进行了优化。也就是说,每一帧都“添加”到动画中,而不是一个全新的帧。你的情况正好相反。每一帧都是一个完整的图像。

    现在,您可能有很多方法可以检查这个,但现在,我无暇顾及......

    相反...在mirror(File, File) 方法中,我对其进行了更改,以便不再使用单个“主”图像,而是每帧创建一个新的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 以将元数据设置为更接近原始数据...

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

    【讨论】:

    • 是的,显然我会跟踪两个实例。我要问的是如何在加载时镜像 gif,这样我就不必在每次绘制时都镜像它。
    • 它在标题和帖子中说它是动画的,是的
    • 好的,你有问题
    • 有时你需要绕着灌木丛寻找复活节彩蛋;)
    • 感谢您的详细回复。您提供的代码正确地生成了 gif 的各个帧,但是 GifWriter 似乎有问题,因为它生成的 gif 将动画中的后期图像简单地绘制在早期的图像上而不是在它们自己的帧中,它会导致在 gif 结束时,所有帧都相互重叠。
    【解决方案2】:

    ..每次转换的开销..

    该开销大约为 0。但如果您不想使用 AffineTransform,只需在循环中更改 x,y。

    有关更多提示,另请参阅Show an animated BG in Swing

    注意

    这个:

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

    应该是:

    g.drawImage(image, x, y, this); // containers are typically an ImageObserver!
    

    【讨论】:

      【解决方案3】:

      对于非动画图像,您可以创建镜像。

      // Width and height
      int w = image.getWidth(null);
      int h = image.getHeight(null);
      
      // Create a new BufferedImage
      BufferedImage mirror = new BufferedImage(w, h);
      
      // Draw the image flipping it horizontally
      Graphics2D g = mirror.createGraphics();
      g.drawImage(image, 0, 0, w, h, w, 0, 0, h, null);
      
      // Dispose the graphics.
      g.dispose();
      

      然后您可以使用已经水平镜像的mirror

      【讨论】:

      • 问题是它是一个动画 gif,所以如果我这样做,mirror 将包含镜像动画的一帧,就是这样
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-22
      • 2016-05-24
      • 1970-01-01
      • 1970-01-01
      • 2013-08-04
      • 2011-08-15
      相关资源
      最近更新 更多