3

My question is simple, the solution surely not. I am looking for a way to shape a JFrame the same as an Image it will be displaying. By shape I mean the shape of the pixels that have an alpha != 0. I've already found a working example using a GeneralPath object, but it created ~110000 "nodes" for an Image of about 500*400, so starting the JFrame took more than 2 minutes, which is definitely not the desired effect, the startup should be in under 2 seconds.

Thanks for your time.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Jan Weber
  • 137
  • 13
  • 4
    This might help: [How to Create Translucent and Shaped Windows](http://docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html). But of course the main problem is you still need to retrieve image shape, this might help with that: [Image/Graphic into a Shape in Java?](http://stackoverflow.com/a/7059497/1133011) – David Kroukamp Nov 27 '12 at 17:11

3 Answers3

9

I personally would ditch the window shape in favor of a transparent window, it's just simpler for what you are trying to do...

enter image description here

And with the close button (look to the bottom left)

enter image description here

The red border around the image is deliberate, as it shows the "window" bounds.

This relies on either Java 1.7 or Java 1.6_10+, there are checks in the code.

public class TransparentFrame {

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

    public TransparentFrame() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setUndecorated(true);
                frame.setContentPane(new ContentPane());

                String version = System.getProperty("java.version");
                System.out.println(version);
                if (version.startsWith("1.7")) {
                    frame.setBackground(new Color(0, 0, 0, 0));
                } else if (version.startsWith("1.6")) {
                    if (supportsPerAlphaPixel()) {
                        setOpaque(frame, false);
                    } else {
                        System.out.println("Per Pixel Alphering is not support with Java " + version);
                        System.exit(1);
                    }
                } else {
                    System.out.println("Per Pixel Alphering is not support with Java " + version);
                    System.exit(1);
                }
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new ImagePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static boolean supportsPerAlphaPixel() {

        boolean support = false;

        try {

            Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
            support = true;

        } catch (Exception exp) {
        }

        return support;

    }

    public static void setOpaque(Window window, boolean opaque) {

        try {

            Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
            if (awtUtilsClass != null) {

                Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
                method.invoke(null, window, opaque);

            }

        } catch (Exception exp) {
        }

    }

    public class ContentPane extends JPanel {

        public ContentPane() {
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.RED);
            g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
        }
    }

    public class ImagePane extends JPanel {

        private BufferedImage background;
        private BufferedImage offImage;
        private Ellipse2D offButton;
        private boolean mouseIn;

        public ImagePane() {
            setOpaque(false);
            try {
                background = ImageIO.read(new File("tamagotchi400.png"));
                offImage = ImageIO.read(new File("powerSmall.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            offButton = new Ellipse2D.Float(212, 330, 25, 25);
            MouseAdapter handler = new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
                        if (offButton.contains(e.getPoint())) {
                            Window window = SwingUtilities.getWindowAncestor(ImagePane.this);
                            if (window != null) {
                                window.dispose();
                            }
                        }
                    }
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    Cursor cursor = Cursor.getDefaultCursor();
                    if (offButton.contains(e.getPoint())) {
                        cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
                    }
                    setCursor(cursor);
                }

                @Override
                public void mouseEntered(MouseEvent e) {
                    mouseIn = true;
                    repaint();
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    mouseIn = false;
                    repaint();
                }
            };
            addMouseListener(handler);
            addMouseMotionListener(handler);
        }

        @Override
        public Dimension getPreferredSize() {
            return background == null ? new Dimension(400, 400) : new Dimension(background.getWidth(), background.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (background != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                int x = (getWidth() - background.getWidth()) / 2;
                int y = (getHeight() - background.getHeight()) / 2;
                g2d.drawImage(background, x, y, this);
                if (mouseIn && offImage != null) {
                    g2d.drawImage(offImage, (int) offButton.getX(), (int) offButton.getY(), this);
                }
                g2d.dispose();
            }
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 3
    +1 very nice example but i must ask why do you check for per pixel translucency via checking java version and classes etc, would `isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT)` be better see [here](http://docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html#capability) – David Kroukamp Nov 28 '12 at 08:05
  • @DavidKroukamp `isWindowTranslucencySupported` is only supported in Java 7. Under Java 6 I need to follow a different API path, but, it's only available after Java 1.6 update 10, hence the use of reflection. Under Java 7 I should actually use `isWindowTranslucencySupported` as well and I will endeavour to add that into my library code, cheers :D – MadProgrammer Nov 28 '12 at 08:20
3

I could not understand your first question. My English is not so good and this could be the reason.

About the second question, have you tried something about TrayIcon? Please, take e look: http://docs.oracle.com/javase/tutorial/uiswing/misc/systemtray.html and http://docs.oracle.com/javase/6/docs/api/java/awt/TrayIcon.html

matheuslf
  • 309
  • 1
  • 9
  • Thanks, but I already found those links you posted. About the first question: Let's say I'm making something like a PC-Tamagotchi. For that, I have a background image shaped like the "oldschool" Tamagotchi controllers. This image is of a non-rectangular shape, and I don't want the JFrame outlines visible around the image. This is why I need the JFrame to adapt to the image. – Jan Weber Nov 27 '12 at 16:56
  • 1
    A shaped frame won't display the border or title bar (as you've clipped the frame), personally, I think you're better off using a transparent frame – MadProgrammer Nov 27 '12 at 19:11
  • @MadProgrammer thiught if that, why OP doesn't just create transparent frame thus making the interface of tamagotchi visible while background of Jframe invisible . +1 good suggestion – David Kroukamp Nov 27 '12 at 19:13
  • @MadProgrammer thought about it some more if you have a look at an installation menu like starcraft 2 the window is completely shaped and they manually add icons for exit minimize etc so I guess thats what the OP is trying to achieve maybe? – David Kroukamp Nov 27 '12 at 20:22
  • 1
    @DavidKroukamp not in front of my PC at the mo, will look at a demo when I get there ;) – MadProgrammer Nov 27 '12 at 20:24
0

Simply Add New Label Image In JFrame then Set Jframe Background Transparents

frame.setBackground(new Color(0, 0, 0, 0));