skip to Main Content

I need to get a mirror of JLabel or JTextArea.

http://www.subirimagenes.net/i/150305101716451074.jpg

If I use a JTextArea, I’ll need the letters are complety mirrored.
If I use a JLabel, I’ll need format and the mirrored letters.

The example was created on Photoshop.

My idea is using graphics(), but I don’t have idea how to do it.

2

Answers


  1. Here’s one way to create a mirror image.

    Basically, you print the contents of the JTextArea on a BufferedImage. Then you reverse the pixels of the BufferedImage on the X axis.

    package com.ggl.testing;
    
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.image.BufferedImage;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.SwingUtilities;
    import javax.swing.event.DocumentEvent;
    import javax.swing.event.DocumentListener;
    
    public class MirrorImage implements Runnable {
    
        private JFrame frame;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new MirrorImage());
        }
    
        @Override
        public void run() {
            frame = new JFrame("Mirror Image Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            JPanel mainPanel = new JPanel();
            mainPanel.setLayout(new FlowLayout());
    
            JPanel textPanel = new JPanel();
    
            JTextArea textArea = new JTextArea(15, 30);
            textPanel.add(textArea);
    
            mainPanel.add(textPanel);
    
            MirrorPanel mirrorPanel = new MirrorPanel();
            mirrorPanel.setPreferredSize(textPanel.getPreferredSize());
    
            mainPanel.add(mirrorPanel);
    
            TextListener listener = new TextListener(textArea, mirrorPanel);
            textArea.getDocument().addDocumentListener(listener);
    
            frame.add(mainPanel);
    
            frame.pack();
            frame.setLocationByPlatform(true);
    
            listener.createImage(textArea);
    
            frame.setVisible(true);
        }
    
        private class MirrorPanel extends JPanel {
    
            private static final long serialVersionUID = 2496058019297247364L;
    
            private Image image;
    
            public void setImage(Image image) {
                this.image = image;
                repaint();
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.drawImage(image, (getWidth() - image.getWidth(this)) / 2,
                        (getHeight() - image.getHeight(this)) / 2, this);
            }
        }
    
        private class TextListener implements DocumentListener {
    
            private JTextArea textArea;
    
            private MirrorPanel mirrorPanel;
    
            public TextListener(JTextArea textArea, MirrorPanel mirrorPanel) {
                this.textArea = textArea;
                this.mirrorPanel = mirrorPanel;
            }
    
            @Override
            public void insertUpdate(DocumentEvent event) {
                createImage(textArea);
            }
    
            @Override
            public void removeUpdate(DocumentEvent event) {
                createImage(textArea);
            }
    
            @Override
            public void changedUpdate(DocumentEvent event) {
                createImage(textArea);
            }
    
            public void createImage(JTextArea textArea) {
                BufferedImage img = new BufferedImage(textArea.getWidth(),
                        textArea.getHeight(), BufferedImage.TYPE_INT_RGB);
    
                Graphics2D g2d = img.createGraphics();
                textArea.printAll(g2d);
                g2d.dispose();
    
                createMirrorImage(img);
                mirrorPanel.setImage(img);
            }
    
            private void createMirrorImage(BufferedImage img) {
                int width = img.getWidth();
                int height = img.getHeight();
    
                int[][] pixels = new int[width][height];
    
                for (int i = width - 1; i >= 0; i--) {
                    int j = width - i - 1;
                    for (int k = 0; k < height; k++) {
                        pixels[j][k] = img.getRGB(i, k);
                    }
                }
    
                for (int i = 0; i < width; i++) {
                    for (int j = 0; j < height; j++) {
                        img.setRGB(i, j, pixels[i][j]);
                    }
                }
            }
    
        }
    
    }
    
    Login or Signup to reply.
  2. Here’s the bad news: It’s not as straight-forward as we might wish, there’s a limitation. In Swing, graphics transformations are applied only on the paint operation, not the general layout and event process. Therefore, in Swing the mirrored component is basically “unusable”, it cannot be used for anything else than displaying the mirror image of the primary component. Coordinates of mouse clicks etc. will be wrong.

    Therefore, this is all tricky stuff, a bit hackish.

    There are multiple ways how you can do that.
    One possibility is to use two views on one model and tell the Graphics of one of the views to flip horizontally.

    Here’s an example how to do so which demonstrates a flipped JEditorPane:

    import java.awt.*;
    import java.awt.geom.*;
    import javax.swing.*;
    
    public class MirrorText {
        public static void main(final String... args) {
            SwingUtilities.invokeLater(MirrorText::setupUI);
        }
        public static void setupUI() {
            final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
            final JEditorPane mirroredEditor = new JEditorPane("text/html", "") {
                protected Graphics getComponentGraphics(final Graphics g) {
                    return horizontalFlip(super.getComponentGraphics(g), getWidth());
                }
            };
            mirroredEditor.setDocument(editor.getDocument());
    
            final JFrame frame = new JFrame("mirrored label");
            final JPanel mirrorPanel = new JPanel(new GridLayout(1, 2));
            mirrorPanel.add(new JScrollPane(editor));
            mirrorPanel.add(new JScrollPane(mirroredEditor));
            frame.add(mirrorPanel);
            frame.pack();
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.setVisible(true);
        }
        public static Graphics horizontalFlip(final Graphics g, final int width) {
            final Graphics2D g2d = (Graphics2D) g;
            final AffineTransform tx = g2d.getTransform();
            tx.scale(-1.0, 1.0);
            tx.translate(-width, 0);
            g2d.setTransform(tx);
            return g2d;
        }
    }
    

    The advantage of this solution is that the mirror entirely the original component’s MVC and observers because it is the same type of component (View/Controller) on the very same model.
    The disadvantage of this solution is that you have create the mirror component in a way that is very specific to the component that is mirrored.

    Another possibility is to create a decorator JComponent Mirror which can mirror an arbitrary other JComponent. This is a bit tricky, as in Java, decorators cannot override methods of the decorated object, and the Mirror needs to be updated (repainted) as well whenever the original component is updated (repainted).

    Here’s an incomplete example using a Mirror which hooks into the corresponding events. Incomplete because it only hooks into DocumentEvent but should also hook onto other events as well, like CaretEvent. It would be nice if Swing would have something like a PaintEvent. As far as I am aware of, it hasn’t. (Well, in fact it has, but there’s no corresponding PaintListener and addPaintListener().)
    Also incomplete because the Mirror doesn’t observe the original component’s attributes like size. It only works because the GridLayout on the MirrorPanel keeps the mirror size in sync with the original component.

    import java.awt.*;
    import java.awt.geom.*;
    import javax.swing.*;
    import javax.swing.event.*;
    
    public class MirrorText {
        public static void main(final String... args) {
            SwingUtilities.invokeLater(MirrorText::setupUI);
        }
        public static void setupUI() {
            final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
            final MirrorPanel mirrorPanel = new MirrorPanel(new JScrollPane(editor));
    
            editor.getDocument().addDocumentListener(new DocumentListener() {
                public void changedUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
                public void insertUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
                public void removeUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
            });
    
            final JFrame frame = new JFrame("mirrored label");
            frame.add(mirrorPanel);
            frame.pack();
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.setVisible(true);
        }
    }
    
    class MirrorPanel extends JPanel {
        public MirrorPanel(final JComponent c) {
            super(new GridLayout(1, 2));
            add(c);
            add(new Mirror(c));
        }
        public void updateMirror() {
            repaint();
        }
    }
    
    class Mirror extends JComponent {
        private final JComponent mirroredComponent;
        public Mirror(final JComponent mirroredComponent) {
            this.mirroredComponent = mirroredComponent;
        }
        public static Graphics horizontalFlip(final Graphics g, final int width) {
            final Graphics2D g2d = (Graphics2D) g;
            final AffineTransform tx = g2d.getTransform();
            tx.scale(-1.0, 1.0);
            tx.translate(-width, 0);
            g2d.setTransform(tx);
            return g2d;
        }
        public void paint(final Graphics g) {
            mirroredComponent.paint(horizontalFlip(g, mirroredComponent.getWidth()));
        }
    }
    

    There probably are more possibilities as well. For example, one could override the mirrored component’s paint() method to update the mirror component as well. That would get rid of getting notified, but it would lead to unnecessary paint() calls in case painting isn’t done due to content change but due to buffer destruction (i.e. other window moved away).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search