0

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...

justik
  • 4,145
  • 6
  • 32
  • 53
  • 1
    Admittedly, I did not thoroughly read your code, but the question *might* be a duplicate of another one - regardless of whether it is, the answer that I gave there may be interesting for you, also regarding the "rasterized" appearance that you mentioned: http://stackoverflow.com/a/27444894/3182664 – Marco13 Dec 08 '16 at 12:27
  • @ Marco: thanks for your comment and excellent thread. Last week, I tried 2 approaches you recommended. However, for the raster data (image, 10MB) and some vector data, I found scaling the graphic objects as slower: http://stackoverflow.com/questions/40957504/java-drawing-scaled-objects-buffered-image-and-vector-graphics. Currently I would like implement zoom operation for large vector data... I could try to combine the off-screen buffer with the object scaling... – justik Dec 08 '16 at 12:49
  • The performance of the different approaches is something that I did not mention in the linked answer. It's a *very* complex topic, and depends on *many* factors: How many objects are drawn? What kind of objects? How often do they change? Can they be "cached" in any way?. I think finding or chosing the "most efficient" approach can only be done when you exactly know what you are going to draw. – Marco13 Dec 08 '16 at 15:18
  • @ Marco: Thanks for your comments. During the zoom operations approximately 1e5 objects are processed. Objects are represented by the lines, points, ellipses, sampled curves (integer arithmetic). During the zoom operations their shape and relations remain unchained (a similarity transformation is sufficient, Java AffineTrransformation is utilized). They can not be cached, but some off-screen buffer can be used. – justik Dec 08 '16 at 16:32

0 Answers0