3

I draw in my JComponent some curves, etc .. with Graphics G ( not 2D ).

Now I want to use the scroll wheel of my mouse to zoom in and out.

Any tracks ?

I heard talk about a BuferredImage ?

Choubidou
  • 351
  • 2
  • 4
  • 17
  • There are a number of considerations. Even if you render the base content to a BufferedImage, you will still need to modify the preferred size of the components so it can laid out correctly. Check out AffineTransform – MadProgrammer Oct 28 '13 at 19:58

3 Answers3

12

There are a few considerations you need to take into account...

The end result will depend on what you want to achieve. If you are drawing curves using the Graphics2D API, it might be simpler to simply scale the coordinates each time the component is rendered. You will need to make sure that any changes in the scale are reflected in the preferred size of the component itself.

You could also render the "default" output to a BufferedImage and simply use an AffineTransform to change the scaling the is used to render the result, for example.

This simple uses a BufferedImage and loads a picture from disk, but the basic concept is the same.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ZoomPane {

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

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

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

    public class TestPane extends JPanel {

        private BufferedImage img;
        private float scale = 1;

        public TestPane() {
            try {
                img = ImageIO.read(new File("/path/to/image"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            addMouseWheelListener(new MouseAdapter() {

                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    double delta = 0.05f * e.getPreciseWheelRotation();
                    scale += delta;
                    revalidate();
                    repaint();
                }

            });
        }

        @Override
        public Dimension getPreferredSize() {            
            Dimension size = new Dimension(200, 200);
            if (img != null) {            
                size.width = Math.round(img.getWidth() * scale);
                size.height = Math.round(img.getHeight() * scale);                
            }        
            return size;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (img != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                AffineTransform at = new AffineTransform();
                at.scale(scale, scale);
                g2d.drawImage(img, at, this);
                g2d.dispose();
            }
        }
    }

}

You could also scale the Graphics context passed to your paintComponent method directly.

The important thing here is to remember to reset the AffineTransform after you have completed, otherwise it will be passed to other components when they are rendered, which won't generate the expected output...

This example basically creates a copy of the Graphics context which we can manipulate and dispose of without effecting the original, making it simpler to mess with

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ZoomPane {

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

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

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

    public class TestPane extends JPanel {

        private float scale = 1;

        public TestPane() {
            addMouseWheelListener(new MouseAdapter() {

                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    double delta = 0.05f * e.getPreciseWheelRotation();
                    scale += delta;
                    revalidate();
                    repaint();
                }

            });
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension size = new Dimension(200, 200);
            size.width = Math.round(size.width * scale);
            size.height = Math.round(size.height * scale);
            return size;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            AffineTransform at = new AffineTransform();
            at.scale(scale, scale);
            g2d.setTransform(at);

            g2d.setColor(Color.RED);

            // This is for demonstration purposes only
            // I prefer to use getWidth and getHeight
            int width = 200;
            int height = 200;

            Path2D.Float path = new Path2D.Float();
            int seg = width / 3;
            path.moveTo(0, height / 2);
            path.curveTo(0, 0, seg, 0, seg, height / 2);
            path.curveTo(
                    seg, height, 
                    seg * 2, height, 
                    seg * 2, height / 2);
            path.curveTo(
                    seg * 2, 0, 
                    seg * 3, 0, 
                    seg * 3, height / 2);

            g2d.draw(path);


            g2d.dispose();
        }
    }
}

Take a look at Transforming Shapes, Text and Images for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • @Sage How? You still need to do all the work, so why complicate it further. I can also apply `RenderingHints` to the output if I wanted to - this is just me though ;) – MadProgrammer Oct 29 '13 at 00:42
  • @MadProgrammer, probably off topic but still with the mention of `RenderingHints`: while working a 1-2 months back with scaling, i found that setting `RenderingHints`: `BICUBIC` or `BILINEAR` for down scaling image, the quality is poor in comparison to other native image processing application, at the very least if the image contains `Text`. I have tried with `ANTIALIASING` hints but a little improvement. – Sage Oct 29 '13 at 00:56
  • @Sage Text scaling is a pain. A lot will also depending on if you are doing a direct scale or stepped scale algorithm, [for example](http://stackoverflow.com/questions/14115950/quality-of-image-after-resize-very-low-java/14116752#14116752) – MadProgrammer Oct 29 '13 at 00:59
3

Try JFreeChart; the setMouseWheelEnabled() method, used to control zooming in ChartPanel, is illustrated in examples cited here.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
0

I put this simple code to show you how to use mouse wheel mouving by adding a MouseWheelListener to a JPanel:

myJpanel.addMouseWheelListener(new MouseWheelListener() {               
   @Override
   public void mouseWheelMoved(MouseWheelEvent mwe) {
      jPanelMouseWheelMoved(mwe);
   }
});

To implement the mouse wheel listener:

private void jPaneMouseWheelMoved(MouseWheelEvent mwe) {
    if(Event.ALT_MASK != 0) {
        if(mwe.getWheelRotation() > 0) {
            //here you put your code to scrool douwn or to minimize. 
            System.out.println(" minimize by "+(-1*mwe.getWheelRotation()));             
        }
        else if(mwe.getWheelRotation() < 0) {
            //here you put your code to scrool up or to maximize.
            System.out.println(" maximaze by "+(-1*mwe.getWheelRotation()));
        }           
    }        
}

You can adapt this exemple to zoom or to scrool what you want.