I am thinking about the implementation of the zoom operations (zoom in/out, panning) in Swing. The main problem refers to the amount of data represented by 1e+06 points/lines that need to be drawn.
To make the zoom operations as fast as possible, the following approach has been adopted.
1] I am using an off-screen buffer represented by the buffered image img.
2] During the zoom operations the data remains unchained. Instead of drawn all points, the current image is replaced with the off-screen buffer.
g2d.drawImage(img, 0, 0, this);
There is a simple example illustrating basic operations zoom in/out over the randomly generated points:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ZoomOperations extends JPanel{
private boolean repaint;
private double zoom;
private List<Point2D.Double> points;
private AffineTransform at;
private BufferedImage img;
public ZoomOperations() {
repaint = true;
zoom = 0.1;
points = new ArrayList<>();
at = new AffineTransform();
img = null;
Random r = new Random();
for (int i = 0; i < 100; i++)
points.add(new Point2D.Double(5000 * r.nextDouble(), 5000 * r.nextDouble()));
addMouseWheelListener(new MouseAdapter() {
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getPreciseWheelRotation() < 0)
zoom = Math.min(zoom+= 0.02,2);
else
zoom = Math.max(zoom-= 0.02,0.01);
repaint();
}
});
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (img == null)
img = (BufferedImage)createImage(5000, 5000);
at = g2d.getTransform();
at.translate(0, 0);
at.scale(zoom, zoom);
g2d.setTransform(at);
if (repaint){
Graphics2D g2c = img.createGraphics();
for (Point2D.Double p:points)
g2c.fillOval((int)p.x-50, (int)p.y-50, 50, 50);
g2c.dispose();
repaint = false; //Only illustrative example
}
g2d.drawImage(img, 0, 0, this);
}
public static void main(String[] args) {
JFrame jf = new JFrame();
jf.setSize(800, 600);
jf.add(new ZoomOperations());
jf.setVisible(true);
}
}
The decision whether points will be drawn or not is made using the flag
private boolean repaint;
The zoom operations are relatively fast but I am not sure how to set the initial size of the buffer
img = (BufferedImage) createImage(5000, 5000); // How to set the size ?
to avoid getting outside the buffer when new points are added. There is no a priori information about the point coordinates X,Y, the zoom ratio is [1/100.0, 100.0]. Keep in mind that affine transformation shrinks / enlarges the buffer width and height (zoom < 1 / zoom > 1).
Unfortunately, the vector data drawn from the off-screen buffer are not smooth, but rasterized (pixels are visible under the magnification)...
Is there any more suitable approach?
Thanks for your help...