【问题标题】:Java GIF animation not repainting correctlyJava GIF 动画未正确重绘
【发布时间】:2014-12-07 11:03:33
【问题描述】:

我正在尝试为 GIF 图像制作动画。动画效果很好,但画得不好。

显示如下(非动画截图):

在图片中,尾巴像这样摇摆:

如您所见,图像重绘效果不佳。我不想使用 JLabels 但它不能正常工作,所以我关注了this question when my image did not animate

我的代码是这样的:

public void draw(JPanel canvas, Graphics2D g2d, int x, int y) {
    getFrontImage().paintIcon(canvas, g2d, x, y);
}

图像的检索和保存位置如下:

ImageIcon gif = new ImageIcon(getClass().getResource(filename));

在 JPanel 画布中,我创建了一个绘制方法和一个每 10 毫秒重新绘制一次的计时器线程。这适用于除 GIF 之外的所有内容。谁能帮我解决这个问题?

--- 编辑

对于造成的误解,我深表歉意,我已将图像更新为我实际使用的图像。我希望得到正确答案不会太麻烦......

【问题讨论】:

  • 如果没有更多的conetxt,我会说你已经破坏了油漆链并且你得到了没有被清除的“旧”帧。考虑提供一个runnable example 来证明您的问题。这将导致更少的混乱和更好的响应
  • 其实玩了一下,好像dipsoseMethod没有设置成clear
  • “非动画截图”看起来所有动画帧都相互堆叠(看看移动最多的耳朵和尾巴是如何变得更大的)。鼠兔!
  • 我无法正确描述发生的事情,感谢 user1803551 击中了它的头:-)

标签: java swing animation gif imageicon


【解决方案1】:

好的,经过一番折腾,我终于可以将框架的处理方法更改为restoreToBackgroundColor。基本上,这意味着动画不是增量变化,而是完整的帧替换……

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class AnimatedGifTest1 {

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

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

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

    public class PaintPane extends JPanel {

        private ImageIcon image;

        public PaintPane() {
            image = new ImageIcon(getClass().getResource("/ertcM02.gif"));
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            });
            timer.start();
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); // This is very important!
            int x = (getWidth() - image.getIconWidth()) / 2;
            int y = (getHeight() - image.getIconHeight()) / 2;
            image.paintIcon(this, g, x, y);
        }

    }



}

更新...

所以,我终于可以看一下你正在使用的gif的处理方法,设置为restoreToPrevious,根据GRAPHICS INTERCHANGE FORMAT Version 89a的意思是:

恢复到以前。解码器需要将被图形覆盖的区域恢复为渲染图形之前的区域。

我上面提供的图片使用restoreToBackgroundColor,根据GRAPHICS INTERCHANGE FORMAT Version 89a的意思是:

恢复为背景颜色。图形使用的区域必须恢复为背景色

您可以使用以下代码自行检查...

public static class AnimatedGif {

    public enum DisposalMethod {

        RESTORE_TO_BACKGROUND,
        RESTORE_TO_PREVIOUS,
        DO_NOT_DISPOSE,
        UNSPECIFIED;

        public static DisposalMethod find(String text) {

            DisposalMethod dm = UNSPECIFIED;
            System.out.println(text);

            switch (text) {
                case "restoreToBackgroundColor":
                    dm = RESTORE_TO_BACKGROUND;
                    break;
                case "restoreToPrevious":
                    dm = RESTORE_TO_PREVIOUS;
                    break;
            }

            return dm;

        }
    }

    private List<ImageFrame> frames;
    private int frame;

    public AnimatedGif(JComponent player, URL url) throws IOException {
        frames = new ArrayList<>(25);
        try (InputStream is = url.openStream(); ImageInputStream stream = ImageIO.createImageInputStream(is)) {
            Iterator readers = ImageIO.getImageReaders(stream);
            if (!readers.hasNext()) {
                throw new RuntimeException("no image reader found");
            }
            ImageReader reader = (ImageReader) readers.next();
            reader.setInput(stream);            // don't omit this line!
            int n = reader.getNumImages(true);  // don't use false!
            System.out.println("numImages = " + n);
            for (int i = 0; i < n; i++) {
                BufferedImage image = reader.read(i);
                ImageFrame imageFrame = new ImageFrame(image);

                IIOMetadata imd = reader.getImageMetadata(i);
                Node tree = imd.getAsTree("javax_imageio_gif_image_1.0");
                NodeList children = tree.getChildNodes();

                for (int j = 0; j < children.getLength(); j++) {
                    Node nodeItem = children.item(j);
                    NamedNodeMap attr = nodeItem.getAttributes();
                    switch (nodeItem.getNodeName()) {
                        case "ImageDescriptor":
                            ImageDescriptor id = new ImageDescriptor(
                                            getIntValue(attr.getNamedItem("imageLeftPosition")),
                                            getIntValue(attr.getNamedItem("imageTopPosition")),
                                            getIntValue(attr.getNamedItem("imageWidth")),
                                            getIntValue(attr.getNamedItem("imageHeight")),
                                            getBooleanValue(attr.getNamedItem("interlaceFlag")));
                            imageFrame.setImageDescriptor(id);
                            break;
                        case "GraphicControlExtension":
                            GraphicControlExtension gc = new GraphicControlExtension(
                                            DisposalMethod.find(getNodeValue(attr.getNamedItem("disposalMethod"))),
                                            getBooleanValue(attr.getNamedItem("userInputFlag")),
                                            getBooleanValue(attr.getNamedItem("transparentColorFlag")),
                                            getIntValue(attr.getNamedItem("delayTime")) * 10,
                                            getIntValue(attr.getNamedItem("transparentColorIndex")));
                            imageFrame.setGraphicControlExtension(gc);
                            break;
                    }
                }
                frames.add(imageFrame);
            }
        } finally {
        }
    }

    protected String getNodeValue(Node node) {
        return node == null ? null : node.getNodeValue();
    }

    protected int getIntValue(Node node) {
        return node == null ? 0 : getIntValue(node.getNodeValue());
    }

    protected boolean getBooleanValue(Node node) {
        return node == null ? false : getBooleanValue(node.getNodeValue());
    }

    protected int getIntValue(String value) {
        return value == null ? 0 : Integer.parseInt(value);
    }

    protected boolean getBooleanValue(String value) {
        return value == null ? false : Boolean.parseBoolean(value);
    }

    public class ImageFrame {

        private BufferedImage image;
        private ImageDescriptor imageDescriptor;
        private GraphicControlExtension graphicControlExtension;

        public ImageFrame(BufferedImage image) {
            this.image = image;
        }

        protected void setImageDescriptor(ImageDescriptor imageDescriptor) {
            this.imageDescriptor = imageDescriptor;
        }

        protected void setGraphicControlExtension(GraphicControlExtension graphicControlExtension) {
            this.graphicControlExtension = graphicControlExtension;
            System.out.println(graphicControlExtension.getDisposalMethod());
        }

        public GraphicControlExtension getGraphicControlExtension() {
            return graphicControlExtension;
        }

        public BufferedImage getImage() {
            return image;
        }

        public ImageDescriptor getImageDescriptor() {
            return imageDescriptor;
        }

    }

    public class GraphicControlExtension {

        private DisposalMethod disposalMethod;
        private boolean userInputFlag;
        private boolean transparentColorFlag;
        private int delayTime;
        private int transparentColorIndex;

        public GraphicControlExtension(DisposalMethod disposalMethod, boolean userInputFlag, boolean transparentColorFlag, int delayTime, int transparentColorIndex) {
            this.disposalMethod = disposalMethod;
            this.userInputFlag = userInputFlag;
            this.transparentColorFlag = transparentColorFlag;
            this.delayTime = delayTime;
            this.transparentColorIndex = transparentColorIndex;
        }

        public int getDelayTime() {
            return delayTime;
        }

        public DisposalMethod getDisposalMethod() {
            return disposalMethod;
        }

        public int getTransparentColorIndex() {
            return transparentColorIndex;
        }

        public boolean isTransparentColorFlag() {
            return transparentColorFlag;
        }

        public boolean isUserInputFlag() {
            return userInputFlag;
        }

    }

    public class ImageDescriptor {

        private int imageLeftPosition;
        private int imageTopPosition;
        private int imageHeight;
        private int imageWeight;
        private boolean interlaced;

        public ImageDescriptor(int imageLeftPosition, int imageTopPosition, int imageHeight, int imageWeight, boolean interlaced) {
            this.imageLeftPosition = imageLeftPosition;
            this.imageTopPosition = imageTopPosition;
            this.imageHeight = imageHeight;
            this.imageWeight = imageWeight;
            this.interlaced = interlaced;
        }

        public int getImageHeight() {
            return imageHeight;
        }

        public int getImageLeftPosition() {
            return imageLeftPosition;
        }

        public int getImageTopPosition() {
            return imageTopPosition;
        }

        public int getImageWeight() {
            return imageWeight;
        }

        public boolean isInterlaced() {
            return interlaced;
        }

    }

}

此代码来自.gif image doesn't moves on adding it to the JTabbed pane

【讨论】:

  • 只有当 GIF 包含任何透明度时才需要这样做吗? (我猜“是”,但它可能取决于整个渲染管道——例如,双缓冲也会破坏非透明动画。)
  • 我会说它总是很重要。恢复背景基本会清除当前帧并绘制下一帧,否则它将逐步组合每个帧,这就是您所看到的。如果每一帧都是完整的、独立的图像,那么你需要使用“dispose”,如果下一帧“添加”到最后一帧,那么你就不要...
  • 我对“将框架的处理方法更改为restoreToBackgroundColor”感到困惑。不就是打电话super.paint吗?
  • 不,这是 GIF 格式的基本要求,你有两种基本形式的 GIF,你有一种“渐进式”风格,每一帧都连接到最后一帧来构建动画,这是高度优化,因为只包括帧之间的变化,或者在你的情况下,每个帧都是一个独立的单元格/帧(很像电影中的帧)......
  • 在摆弄了你的代码之后,我发现我已经做得很好了。我只是不知道有不同种类的 GIF。我给出的问题图像不是我使用的图像。我从互联网上得到了一个,以为它们会是一样的。对不起,我浪费了您的时间,但我添加了我使用的图像。
猜你喜欢
  • 1970-01-01
  • 2017-07-05
  • 2020-04-17
  • 2014-04-10
  • 2012-04-13
  • 1970-01-01
  • 2019-10-31
  • 2012-05-30
相关资源
最近更新 更多