在Java中镜像动画gif加载 – ImageIcon

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

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

我可以用它绘制它:

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

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

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

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

除非你拼命想要接管自己渲染每一帧的工作,否则你唯一的选择就是在paint方法中使用AffineTransform

一般来说,你不应该看到显着的差异(在渲染中)。

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

更新了“一种”工作示例

这是这个和这个答案的组合,使用这个 GIF编写器。

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

然后它将原始文件和镜像文件作为ImageIcon加载回来,主要是因为我不是真的要重新发明用于显示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 images = new ArrayList<>(25); List 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 imageAttr = new HashMap(); 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 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 IIOMetadataNode 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"); 

在此处输入图像描述在此处输入图像描述

..每次改造它的头脑。

这个开销只是大约0.但是如果你不想使用AffineTransform只需在循环中更改x,y。

有关更多提示,另请参阅在Swing中显示动画BG 。

注意

这个:

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

应该:

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

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

 // 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(); 

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