Zoom box for area around mouse location on screen [closed]

I'm sure there are a number of different ways that this could be achieved.

This basically uses a separate component, which acts as the "zoom box". You supply it a component that you want to "zoom" on. It adds a mouse listener so it can monitor mouse motion events and enter and exit events.

These are used to determine when the "popup" window should be shown, where the popup window should be shown and the area that should be "painted".

This uses the "component to be zoomed" paint method to paint a region to of it to a backing buffer, which is then scaled and painted to the "zoom box"...simple

I've not played around with the zoom factor, so there may still be some quirks, but you should get the basic idea...

While I've presented a image to act as the background, this should work on any component

enter image description here

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.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
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.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ZoomBoxWindow {

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

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

        TestPane pane = new TestPane();
        ZoomPane zoomPane = new ZoomPane(pane);

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
      }
    });
  }

  public static class ZoomPane extends JPanel {

    protected static final int ZOOM_AREA = 40;

    private JComponent parent;
    private JWindow popup;

    private BufferedImage buffer;

    private float zoomLevel = 2f;

    public ZoomPane(JComponent parent) {
      this.parent = parent;
      popup = new JWindow();
      popup.setLayout(new BorderLayout());
      popup.add(this);
      popup.pack();
      MouseAdapter ma = new MouseAdapter() {
        @Override
        public void mouseMoved(MouseEvent e) {
          Point p = e.getPoint();
          Point pos = e.getLocationOnScreen();
          updateBuffer(p);
          popup.setLocation(pos.x + 16, pos.y + 16);
          repaint();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
          popup.setVisible(true);
        }

        @Override
        public void mouseExited(MouseEvent e) {
          popup.setVisible(false);
        }

      };

      parent.addMouseListener(ma);
      parent.addMouseMotionListener(ma);
    }

    protected void updateBuffer(Point p) {
      int width = Math.round(ZOOM_AREA);
      int height = Math.round(ZOOM_AREA);
      buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2d = buffer.createGraphics();
      AffineTransform at = new AffineTransform();

      int xPos = (ZOOM_AREA / 2) - p.x;
      int yPos = (ZOOM_AREA / 2) - p.y;

      if (xPos > 0) {
        xPos = 0;
      }
      if (yPos > 0) {
        yPos = 0;
      }

      if ((xPos * -1) + ZOOM_AREA > parent.getWidth()) {
        xPos = (parent.getWidth() - ZOOM_AREA) * -1;
      }
      if ((yPos * -1) + ZOOM_AREA > parent.getHeight()) {
        yPos = (parent.getHeight()- ZOOM_AREA) * -1;
      }

      at.translate(xPos, yPos);
      g2d.setTransform(at);
      parent.paint(g2d);
      g2d.dispose();
    }

    @Override
    public Dimension getPreferredSize() {
      return new Dimension(Math.round(ZOOM_AREA * zoomLevel), Math.round(ZOOM_AREA * zoomLevel));
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g.create();
      if (buffer != null) {
        AffineTransform at = g2d.getTransform();
        g2d.setTransform(AffineTransform.getScaleInstance(zoomLevel, zoomLevel));
        g2d.drawImage(buffer, 0, 0, this);
        g2d.setTransform(at);
      }
      g2d.setColor(Color.RED);
      g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2d.dispose();
    }

  }

  public class TestPane extends JPanel {

    private BufferedImage img;

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

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

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (img != null) {
        Graphics2D g2d = (Graphics2D) g.create();
        int x = (getWidth() - img.getWidth()) / 2;
        int y = (getHeight() - img.getHeight()) / 2;
        g2d.drawImage(img, x, y, this);
        g2d.dispose();
      }
    }

  }

}

Updated with "screen" version

This version will allow you to display a "zoom window" any where on the screen.

This has a minor issue in the fact that you need to hide the zoom window before you capture the screen, then re-show it.

I might be tempted to change the process so that when the updateBuffer method detected that the mouse position hadn't changed, it updated the buffer and showed the zoom window. When the mouse position changes, it would hide the window again...but that's me ;)

enter image description here

import java.awt.AWTException;
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.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.Action;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import static zoomboxwindow.ZoomBoxWindow.ZoomPane.ZOOM_AREA;

public class GlobalZoomBox {

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

  public GlobalZoomBox() {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
          ex.printStackTrace();
        }
        Zoomer zoomer = new Zoomer();
        zoomer.setZoomWinodwVisible(true);
      }
    });
  }

  public class Zoomer extends JPanel {

    protected static final int ZOOM_AREA = 40;

    private JWindow popup;

    private BufferedImage buffer;
    private Robot bot;

    private float zoomLevel = 2f;
    private Point lastPoint;
    private final Timer timer;

    public Zoomer() {
      popup = new JWindow();
      popup.setLayout(new BorderLayout());
      popup.add(this);
      popup.pack();
      try {
        bot = new Robot();
      } catch (AWTException ex) {
        ex.printStackTrace();
      }
      timer = new Timer(125, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          updateBuffer();
        }
      });
      timer.setCoalesce(true);
      timer.setInitialDelay(0);
    }

    public void setZoomWinodwVisible(boolean value) {

      if (value && !popup.isVisible()) {

        timer.start();
        popup.setVisible(true);

      } else {

        timer.stop();
        popup.setVisible(false);

      }

    }

    @Override
    public Dimension getPreferredSize() {
      return new Dimension(Math.round(ZOOM_AREA * zoomLevel), Math.round(ZOOM_AREA * zoomLevel));
    }

    protected void updateBuffer() {
      if (bot != null) {
        PointerInfo info = MouseInfo.getPointerInfo();
        Point p = info.getLocation();
        if (lastPoint == null || !lastPoint.equals(p)) {
          int x = p.x - (ZOOM_AREA / 2);
          int y = p.y - (ZOOM_AREA / 2);
          popup.setLocation(p.x + 16, p.y + 16);
          popup.setVisible(false);
          buffer = bot.createScreenCapture(new Rectangle(x, y, ZOOM_AREA, ZOOM_AREA));
          popup.setVisible(true);
          lastPoint = p;
          repaint();
        }
      }
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g.create();
      if (buffer != null) {
        AffineTransform at = g2d.getTransform();
        g2d.setTransform(AffineTransform.getScaleInstance(zoomLevel, zoomLevel));
        g2d.drawImage(buffer, 0, 0, this);
        g2d.setTransform(at);
      }
      g2d.setColor(Color.RED);
      g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2d.dispose();
    }

  }

}

Updated with "tooltip" style popup

The main problems with the second example is the fact that you need to hide the popup in order to grab a screen shoot. This is done to prevent the popup from begin captured as well. This makes the popup "flash" every time the mouse is moved.

You "could" get around this ensuring the popup is positioned out side the range of the capture, but as you increase the capture area, the popup will move further away from the cursor.

This would, of course, be a great solution for fixed position display (ie, you had a panel fixed on a JFrame instead of a floating box)

This is an additional update that uses a second timer to display the zoom box after the user has stopped moving the mouse.

enter image description here

import java.awt.AWTException;
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.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.Action;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import static zoomboxwindow.ZoomBoxWindow.ZoomPane.ZOOM_AREA;

public class GlobalZoomBox {

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

  public GlobalZoomBox() {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
          ex.printStackTrace();
        }
        Zoomer zoomer = new Zoomer();
        zoomer.setZoomWinodwVisible(true);
      }
    });
  }

  public class Zoomer extends JPanel {

    protected static final int ZOOM_AREA = 80;

    private JWindow popup;

    private BufferedImage buffer;
    private Robot bot;

    private float zoomLevel = 2f;
    private Point lastPoint;
    private final Timer timer;
    private final Timer popupTimer;

    public Zoomer() {
      popup = new JWindow();
      popup.setLayout(new BorderLayout());
      popup.add(this);
      popup.pack();
      try {
        bot = new Robot();
      } catch (AWTException ex) {
        ex.printStackTrace();
      }
      timer = new Timer(125, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          updateBuffer();
        }
      });
      timer.setCoalesce(true);
      timer.setInitialDelay(0);

      popupTimer = new Timer(250, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          if (lastPoint != null) {
            System.out.println("lastPoint = " + lastPoint);
            popup.setVisible(false);
            Point p = lastPoint;
            int x = p.x - (ZOOM_AREA / 2);
            int y = p.y - (ZOOM_AREA / 2);
            popup.setLocation(p.x + 16, p.y + 16);
            buffer = bot.createScreenCapture(new Rectangle(x, y, ZOOM_AREA, ZOOM_AREA));
            repaint();
            popup.setVisible(true);
          }
        }
      });
      popupTimer.setRepeats(false);
    }

    public void setZoomWinodwVisible(boolean value) {

      if (value && !popup.isVisible()) {

        timer.start();
        popup.setVisible(true);

      } else {

        timer.stop();
        popup.setVisible(false);

      }

    }

    @Override
    public Dimension getPreferredSize() {
      return new Dimension(Math.round(ZOOM_AREA * zoomLevel), Math.round(ZOOM_AREA * zoomLevel));
    }

    protected void updateBuffer() {
      if (bot != null) {
        PointerInfo info = MouseInfo.getPointerInfo();
        Point p = info.getLocation();
        if (lastPoint == null || !lastPoint.equals(p)) {
          lastPoint = p;
          popupTimer.stop();
          popup.setVisible(false);
        } else {
          if (!popup.isVisible()) {
            popupTimer.start();
          }
        }
      }
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g.create();
      if (buffer != null) {
        AffineTransform at = g2d.getTransform();
        g2d.setTransform(AffineTransform.getScaleInstance(zoomLevel, zoomLevel));
        g2d.drawImage(buffer, 0, 0, this);
        g2d.setTransform(at);
      }
      g2d.setColor(Color.RED);
      g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2d.dispose();
    }

  }

}

Zoom On Mouse

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;

class ZoomOnMouse {

    public static void main(String[] args) throws AWTException {
        final Robot robot = new Robot();
        Runnable r = new Runnable() {

            @Override
            public void run() {
                final int size = 256;
                final BufferedImage bi = new BufferedImage(
                        size, size, BufferedImage.TYPE_INT_RGB);
                final JLabel gui = new JLabel(new ImageIcon(bi));
                ActionListener zoomListener = new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        PointerInfo pi = MouseInfo.getPointerInfo();
                        Point p = pi.getLocation();
                        BufferedImage temp = robot.createScreenCapture(
                                new Rectangle(p.x-(size/4), p.y-(size/4), 
                                (size/2), (size/2)));
                        Graphics g = bi.getGraphics();
                        g.drawImage(temp, 0, 0, size, size, null);
                        g.dispose();
                        gui.repaint();
                    }
                };
                Timer t = new Timer(40, zoomListener);
                t.start();

                JOptionPane.showMessageDialog(null, gui);

                t.stop();
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}