Search code examples
javaimageswinganimationscreenshot

Animated GIF, displayed on JPanel, not screenshoting in file with paint method, but JPG and PNG are ok


Can't use 100% real code, sorry. A lot of side-functions in other classes, some of them isn't mine.

I'm making a visual novel - just remark about my project, for better undestanding.

I have a JFrame with card layout of JPanels. gameMainPanel are one of them. My target - make a shot of non-visible at shoting moment panel. On one panel is my game drawing, on another I'm making game-savefile with screenshot of moment in game - so game panel isn't visible at that moment. It's working OK, but when I use an animated GIF on my game panel - it's not screenshoting, but every other GUI components do.

I'm loading and resizing an animated GIF with code like this:

ImageIcon imageIcon = new ImageIcon(Engine.getPathString("/resource/character/" + name));
imageIcon.setImage(imageIcon.getImage().getScaledInstance(resizeX, resizeY, Image.SCALE_DEFAULT));
GameWindow.gameCharacterCenter.setIcon(imageIcon); //gameCharacterCenter is a simple JLabel on game panel

Engine.getPathString is a little wrapper, just returns a string with full path to file. Engine.getPathString("/example/dummy.txt") will return something like "C:/Java projects/Folder with that project/example/dummy.txt", regarding full correct path of course.

In one moment I need to make a shot of my game panel, I'm making it like this:

screenshot = new File(String.valueOf(Engine.getPathFile("/saves/" + name + ".jpeg")));
BufferedImage bufImage = new BufferedImage(GameWindow.gameMainPanel.getSize().width, GameWindow.gameMainPanel.getSize().height, BufferedImage.TYPE_INT_RGB);
GameWindow.gameMainPanel.paint(bufImage.createGraphics());
try {
    screenshot.createNewFile();
    ImageIO.write(bufImage, "jpeg", screenshot);
} catch (Exception ex) {
}

And that code is correctly screenshoting my gameMainPanel, with all it's childs - game menu, characters, labels with text... But not the characters if they are animated GIFs. When I'm loading with exact same code a simple *.jpg or *.png - it's capping OK.

Why are JLabel with animated GIF in it isn't visible when I'm painting it parent, but when it's JPG in it - it's paint to BufferedImage as normal?

Can't provide full code of project, sorry. English not my native, so - also - sorry for that too.

Made with PrintScreen button:

https://i.sstatic.net/n6EcE.jpg

Made with ingame methods:

https://i.sstatic.net/zKlgn.jpg


Solution

  • So, I put together a quick test, basically, I took a PNG image a animated gif image, I used ImageIO to load both and attempted to save the resulting panel and then I used ImageIcon to load the image and save it

    My inputs...

    PNG Image

    PNG Image

    GIF Image

    Gif Image

    The "code"

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        try {
                            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                            ex.printStackTrace();
                        }
    
                        BufferedImage png = ImageIO.read(...));
                        BufferedImage gif = ImageIO.read(...);
                        ImageIcon gifImg = new ImageIcon(...);
    
                        TestPane pngPane = new TestPane(png);
                        TestPane gifPane = new TestPane(gif);
                        TestPane imgPane = new TestPane(gifImg.getImage());
    
                        save(pngPane, "PNGTest.png");
                        save(gifPane, "gifTest.png");
                        save(imgPane, "imgGifTest.png");
    
                    } catch (IOException ex) {
                        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            });
        }
    
        public void save(JPanel pane, String to) throws IOException {
            pane.setSize(pane.getPreferredSize());
            BufferedImage img = new BufferedImage(pane.getWidth(), pane.getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = img.createGraphics();
            pane.printAll(g2d);
            g2d.dispose();
    
            ImageIO.write(img, "png", new File(to));
        }
    
        public class TestPane extends JPanel {
    
            private Image img;
    
            public TestPane(Image img) {
                this.img = img;
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(img.getWidth(this), img.getHeight(this));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.drawImage(img, 0, 0, this);
                g2d.dispose();
            }
    
        }
    
    }
    

    The Results...

    PNG Image via ImageIO

    PNG Image via <code>ImageIO</code>

    GIF Image via ImageIO

    GIF Image via <code>ImageIO</code>

    GIF Image via ImageIcon

    GIF Image via <code>ImageIcon</code>

    I don't want to get into the hows and whys of it all, needless to say, ImageIcon does use a background thread to load the image and relies on the ImageObserver support of the component to get it repainted as it's state changes, this is how it gets the animated GIF to update on the screen, so, one might surmise that we're just missing that update notification.

    I've not tested it, but you could use a MediaTracker to monitor the ImageIcon's state and wait till it's loaded, but given the simplicity of using ImageIO I'd question whether it's worth it or not.

    Cavert

    You will need to use ImageIcon if you want the animated gif to play on a live component! So you can only use this "cheat" when generating the preview!