1

I'm making a small game. It's not an action one, but a puzzle one, so performance is not quite as important. Now, I have the main game area, a background image. In certain occasions, I want to draw other images over parts of the background image. My problem is that both the background image as well as the superimposed one(s) can be animated gifs, with an unknown number of frames (basically, user selected).

Now, what I want to do is basically: Draw the background image and have it animated (if it's a gif), then draw 0-n smaller animated gifs on top of it, position relative to background image, given in pixel coordinates. Additionally, it should be resizeable so that the images zoom/move accordingly.

How end result could look like: https://i.stack.imgur.com/PxdDt.png (just imagine it animated)

I have found a solution to it which works by setting the layout manager to null and using absolutely positioned JLabels with Icons to have them animated, using one as background and the other one added to it as foreground:

background.setSize(backgroundIcon.getIconWidth(), backgroundIcon.getIconHeight());
foreground.setSize(foregroundIcon.getIconWidth(), foregroundIcon.getIconHeight());
background.setLocation(0, 0);
foreground.setLocation(30, 30);
background.setLayout(null);
background.add(foreground);
frame.setLayout(null);
frame.add(background);

(full code below)

Setting the layout manager to null is, as I heard, problematic (esp. since I'd want to add some text to the image later on, though I'd use another Label inside the background one, not the background label itself or something), and resizing seems not to be possible this way as I can't resize the JLabels image icon (found some solutions which splitted the gif into one bufferedImage per layer and resized them all before putting them back together again, through which it loses specific delays between frames and so on). Also, since the position should be pixel perfect, resizing even if possible could be problematic due to rounding errors.

Now, is there a better way to do this? Can I somehow load animated gifs and merge them manually (even tough they may have a different number of layers) so I had to paint only one image? Is there a layout manager where I can set the components positions manually but which automatically scales it if you resize the window/panel? Is there maybe a third-party graphics project that could do what I want off the bat?

Ideally would be something like what I would do if they weren't animations, like building a merged Image and then resizing and displaying that one.

Full code:

import java.awt.Dimension;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public final class Tester  extends JFrame {
    public static void main(String[] args) throws MalformedURLException {
        new Tester();
    }

    private Tester() throws MalformedURLException {
        setTitle("Tester");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Icon backgroundIcon = new ImageIcon(new URL("http://s5.favim.com/orig/51/animated-gif-gif-hands-sign-language-Favim.com-542945.gif"));
        Icon foregroundIcon = new ImageIcon(new URL("http://i.imgur.com/89HANHg.gif"));

        JLabel background = new JLabel(backgroundIcon);
        JLabel foreground = new JLabel(foregroundIcon);

        // ugly
        background.setSize(backgroundIcon.getIconWidth(), backgroundIcon.getIconHeight());
        foreground.setSize(foregroundIcon.getIconWidth(), foregroundIcon.getIconHeight());
        background.setLocation(0, 0);
        foreground.setLocation(30, 30);
        background.setLayout(null);
        background.add(foreground);
        setLayout(null);
        add(background);

        // set size of frame to size of content
        setResizable(false);
        getContentPane().setPreferredSize(new Dimension(backgroundIcon.getIconWidth(), backgroundIcon.getIconHeight()));
        pack();

        setLocationRelativeTo(null);
        setVisible(true);
    }
}
Kadser
  • 243
  • 2
  • 9
  • In your [sscce](http://sscce.org/), access posted images via `URL`, as shown [here](http://stackoverflow.com/a/10862262/230513) or [below](http://stackoverflow.com/a/18434145/230513); use synthetic images as shown [here](http://stackoverflow.com/a/15982915/230513); or use `UIManager` icons, as shown [here](http://stackoverflow.com/a/12228640/230513). – trashgod Aug 25 '13 at 22:39

1 Answers1

3

This works for me. The smaller orbital image is centered inside the larger moon phases image.

import java.awt.*;
import javax.swing.*;
import java.net.*;

public final class Tester  extends JFrame {
    public static void main(String[] args) throws MalformedURLException {
        new Tester();
    }

    private Tester() throws MalformedURLException {
        setTitle("Tester");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Icon backgroundIcon = new ImageIcon(
                new URL("http://1point1c.org/gif/moon/moonphases.gif"));
        Icon foregroundIcon = new ImageIcon(
                new URL("http://1point1c.org/gif/thum/plnttm.gif"));

        JLabel background = new JLabel(backgroundIcon);
        JLabel foreground = new JLabel(foregroundIcon);

        background.setLayout(new GridBagLayout());
        background.add(foreground);
        add(background);

        pack();

        setLocationByPlatform(true);
        setVisible(true);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • This (or other layout managers) would be fine if the foreground image was supposed to be centered, but it has to be at a very specific location. Imagine, for example, the background image showing a wall with an empty painting frame in which, on mouseover or so, the animated gif should be shown (overlayed). it has to be exactly inside the frame, therefore all Layout managers I can think of would not work. – Kadser Aug 25 '13 at 22:03
  • What, so the 'empty painting frame' (whatever that is) is not centered? Does it move, or is it in a static location? – Andrew Thompson Aug 25 '13 at 22:06
  • *" it has to be exactly inside the frame,"* Exactly **where?** *"therefore all Layout managers I can think of would not work."* If there is a logic to positioning the 2nd image, that logic can be included in a layout manager. – Andrew Thompson Aug 25 '13 at 22:08
  • I mean an empty frame not in a programming sense but in a "you hang it on a wall" sense, like [this](http://1.bp.blogspot.com/-sWJPwZJEjzI/TeWZG8Zq3gI/AAAAAAAAA4g/DSZtJtcfPJo/s1600/IMG_0832.jpg). You know where the X/Y position of the frame is, but it isn't centered. Imagine this picture and then through some user interaction, an animated gif would be put "inside" one of the frames. (the shape of the frame does not matter as this is just an example with which I hope to explain what I need) – Kadser Aug 25 '13 at 22:28
  • 1
    +1 for [sscce](http://sscce.org/); Klaue: [`OverlayLayout`](http://stackoverflow.com/a/13437388/230513) may be worth a look – trashgod Aug 25 '13 at 22:38
  • Looked into it a bit, but with overlayLayout, there seems to be no way to spezify the position of the overlays. In the linked question is also JLayeredPane which looked promising, but the position is given by "setBounds" on the component, which does not really seem to be any improvement over my method above (it supports more layers, which is good, so I'll probably use it anyway, but it doesn't fix the main problem). I think I just have to accept that there's no better way to do it – Kadser Aug 25 '13 at 23:01
  • 1
    Tip: Add @trashgod (or whoever, the `@` is important) to *notify* the person of a new comment. I think this is perhaps a case of 'forget layout managers and paint all the images to a single `BufferedImage` displayed in a `JLabel`'. I liked the suggestion of trashgod though.. – Andrew Thompson Aug 25 '13 at 23:54
  • 1
    @Klaue: Using `setSize()` makes _you_ responsible for the correct size; beware of this [pitfall](http://stackoverflow.com/a/12532237/230513). – trashgod Aug 26 '13 at 00:56
  • @Andrew-Thompson thanks, I'm still new here. I hope it was correct with the dash there. The single BufferedImage was the first thing I went for, but a BufferedImage has only one frame and supports no animation – Kadser Aug 26 '13 at 04:08
  • @thrashgod I think that should be no problem in my case, as the size I need is the size of the background image. Still, thanks – Kadser Aug 26 '13 at 04:10
  • @Andrew Thompson hmmm +1 JLabel.setLayout(new GridBagLayout()); – mKorbel Aug 26 '13 at 07:20