javaswinggraphicsbufferedimage

Is there a way to display a scaled BufferedImage that looks identical to a full-resolution Robot screencapture?


I am trying to display a screen capture as an ImageIcon on a JLabel. To generate the BufferedImage object I used a Robot object along with a MultiResultionScreenCapture. As noted in this Stackoverflow question, the resultion of the capture is altered depending on display scale, and my display has a 175% scale setting. When I display the scaled down capture it's the proper size but isn't the same resolution as the display. When I display the full size capture, its larger than the captured area, but the correct resolution.

Scaled Down:

no_scaling

Full Scale:

no_scaling_large

Scaled Down with bilinear interpolation:

bilinear_scaling

img.getScaledInstance Method:

img_scaling_method

public static void updateCurrentSelection(Rectangle rect) {
    Robot robot;
    try {
        robot = new Robot();
    } catch (AWTException e) {
        e.printStackTrace();
        return;
    }
    MultiResolutionImage multiResImg = robot.createMultiResolutionScreenCapture(rect);
    java.util.List<Image> imgs = multiResImg.getResolutionVariants();

    Image img = imgs.getLast(); // Gets largest image
    mainMenu.setImage(img, imgs.getFirst().getWidth(mainMenu), imgs.getFirst().getHeight(mainMenu)); Passing smaller size to be resized to
}

public void setImage(Image img, int width, int height) {
    BufferedImage buf = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = buf.createGraphics();
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g.drawImage(img, 0, 0, width, height, null);
    g.dispose();

    selectionLabel.setIcon(new ImageIcon(buf));
}

I have tried manually scaling the full size image using Graphics2D and tweaking the anti-aliasing and interpolation settings with some improvement but not perfect/what I'm looking for.

Is there some way I can achieve the same quality image in my program's window as my display? The desired outcome is to have the image displayed on the window to be the same resolution AND size as the native screen.


Solution

  • The problem is that you are using Swings ImageIcon which has no support for a logical size different from the image’s physical size. You just have to use your own Icon implementation to call drawImage with the original screen capture and the logical size you already know even before doing the screen capture.

    public static void updateCurrentSelection(Rectangle rect) {
        Robot robot;
        try {
            robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
            return;
        }
        Image img = robot.createMultiResolutionScreenCapture(rect)
              .getResolutionVariants()
              .getLast(); // Gets native image
        mainMenu.setImage(img, rect.width, rect.height);
    }
    
    public void setImage(Image img, int width, int height) {
        record IconWithSize(Image image, int getIconWidth, int getIconHeight)
                                                                     implements Icon {
            @Override public void paintIcon(Component c, Graphics g, int x, int y) {
                g.drawImage(image, x, y, getIconWidth, getIconHeight, c);
            }
        }
        selectionLabel.setIcon(new IconWithSize(img, width, height));
    }