Drawing a Component to BufferedImage causes display corruption

Summary: The original JScrollNavigator uses the Swing opacity property to render a convenient green NavBox over a scaled thumbnail of the component in an adjacent JScrollPane. Because it extends JPanel, the (shared) UI delegate’s use of opacity conflicts with that of the scrollable component. The images seen in edit 5 above typify the associated rendering artifact, also shown here. The solution is to let NavBox, JScrollNavigator and the scrollable component extend JComponent, as suggested in the second addendum below. Each component can then manage it’s own properties individually.

enter image description here

I see no unusual rendering artifact with your code as posted on my platform, Mac OS X, Java 1.6. Sorry, I don’t see any glaring portability violations.

image one

A few probably irrelevant, but perhaps useful, observations.

  • Even if you use setSize(), appropriately in this case, you should still pack() the enclosing Window.

    f.pack();
    f.setSize(300, 200);
    
  • For convenience, add() forwards the component to the content pane.

    f.add(nav, BorderLayout.WEST);
    
  • Prefer StringBuilder to StringBuffer.

  • Consider ComponentAdapter in place of ComponentListener.

Addendum: As suggested here, I got somewhat more flexible results using RenderingHints instead of getScaledInstance() as shown below. Adding a few icons makes it easier to see the disparate effect on images and text.

image two

editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon"));
editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
...
@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Component view = jScrollPane.getViewport().getView();
    BufferedImage img = new BufferedImage(view.getWidth(),
        view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D off = img.createGraphics();
    off.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    off.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    view.paint(off);
    Graphics2D on = (Graphics2D)g;
    on.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    on.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    on.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}

Addendum secundum: It looks like the JPanel UI delegate is not cooperating. One workaround is to extend JComponent so that you can control opacity. It’s only slightly more work to manage the backgroundColor. NavBox and JScrollNavigator are also candidates for a similar treatment.

enter image description here

jsp.setViewportView(new JComponent() {

    {
        setBackground(Color.red);
        setBorder(BorderFactory.createLineBorder(Color.BLACK, 16));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 300);
    }
});

Leave a Comment