JDK-8032667 : [macosx] Components cannot be rendered in HiDPI to BufferedImage
  • Type: Enhancement
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 8,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • Submitted: 2014-01-24
  • Updated: 2014-07-29
  • Resolved: 2014-03-28
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u20Fixed 9 b12Fixed
Related Reports
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
When painting a component via component.paint(Graphics), it is impossible to paint the HiDPI version of the component (e.g. a JCheckBox) to a BufferedImage.

Even with a double-sized BufferedImage and a 2x scale factor, components are still rendered in their low/regular resolution variant.

JUSTIFICATION :
Sometimes it is necessary to paint a component to a BufferedImage, e.g. to use in an animation. One would expect to be able to achieve the same quality when painting to a buffer as it is possible when painting to the screen. Currently that's not possible with a BufferedImage.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When painting a component to a BufferedImage with a scale factor of 2x, I would expect the HiDPI version of the component to be rendered.
ACTUAL -
Even with a scale factor of 2x set, components are always painted in low/regular resolution, thus look pixely.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

// Simple demo class. I'd expect both checkboxes to render in the same quality, but they don't using a BufferedImage.
public class RenderHiDPI {

    public static void main(String[] args) {

        final JFrame frame = new JFrame();
        final JLabel label = new JLabel();
        frame.getContentPane().setLayout(new FlowLayout());
        final JCheckBox hiDPICheckBox = new JCheckBox("Hi DPI");
        hiDPICheckBox.setFocusable(false);
        frame.getContentPane().add(hiDPICheckBox);
        frame.getContentPane().add(label);
        final JCheckBox checkBox = new JCheckBox("Lo DPI");
        checkBox.setSize(new Dimension(100, 100));
        final BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
        final Graphics g = image.getGraphics();
        ((Graphics2D)g).scale(2f, 2f);
        checkBox.paint(g);
        g.dispose();
        label.setIcon(new Icon(){
            @Override
            public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
                final Graphics2D graphics = (Graphics2D)g.create();
                graphics.scale(0.5f, 0.5f);
                graphics.drawImage(image, x, y, c);
            }

            @Override
            public int getIconWidth() {
                return 100;
            }

            @Override
            public int getIconHeight() {
                return 100;
            }
        });

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                frame.setBounds(100, 100, 100, 100);
                frame.setVisible(true);
            }
        });
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use VolatileImage (scaling not necessary)