Java:动画 GIF 会自动部分透明
Posted
技术标签:
【中文标题】Java:动画 GIF 会自动部分透明【英文标题】:Java: Animated GIFs go automatically partially transparent 【发布时间】:2016-08-09 11:48:16 【问题描述】:我想使用 Java Swing 显示一个带有动画 GIF 的等待对话框,但 GIF 显示不正确。
代码示例:
final JDialog progressDialog = new JDialog(new Frame(), "...", true);
URL url = getClass().getResource("wait.gif");
Icon icon = new ImageIcon(url);
JLabel label = new JLabel(icon);
progressDialog.getContentPane().setBackground(new Color (0, 200, 200));
progressDialog.getContentPane().add(label);
progressDialog.pack();
progressDialog.setVisible(true);
原始 GIF 在这里:
但 Java 输出如下所示:
有什么想法可能是错的吗?
【问题讨论】:
也发布了here,可能是同样的问题。我无法重现它,但由于我另一台计算机的硬件问题,我现在被困在 Java 6 上。我对另一张海报也说过同样的话,MCVE 非常有帮助。 ***.com/help/mcve 另见例如***.com/a/4694343/2891664。再次,MCVE,非常有帮助。 @Radiodef 感谢您的帖子,我没有注意到它(同样的问题,今天......难以置信)。 @Radiodef ***.com/a/4694343/2891664 是一个有点不同的问题(透明区域是黑色的,它是静态 GIF)。这里的黑色区域是透明的(不仅黑色会受到影响,如果 GIF 是红色的,那么“纯红色”也是透明的)。标准静态 GIF 以及动画 GIF 的第一帧都能正确显示。 是的,这很公平;但是,链接的部分要点是说明 MCVE 中的值。我无法重现这个问题,但我必须编写自己的程序来测试它,也许你和其他发帖人(从未发布过 MCVE)正在做一些不同的事情。如果我仍然无法重现它,MCVE 还可以为任何其他看到您的问题的 Swing 人员节省时间。 【参考方案1】:当您遇到这些类型的问题时,您想开始使用帧的disposalMethod
。
我通过一些检查代码运行了您的 gif,发现 disposalMethod
设置为 RESTORE_TO_BACKGROUND
所以,基本上,我拿了你的 gif 并通过以下代码运行它,它使用 disposalMethod
的 none
创建了一个新的 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("Qzlxj.gif"), new File("Test.gif"));
orig = new ImageIcon("Qzlxj.gif");
mirror = new ImageIcon("Test.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));
BufferedImage frame = 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");
//restoreToBackgroundColor
//restoreToPrevious
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
以上内容来自Mirroring animated gif on load in Java - ImageIcon的调查
【讨论】:
以上是关于Java:动画 GIF 会自动部分透明的主要内容,如果未能解决你的问题,请参考以下文章