0

I have written a Java7-Application that creates a BufferedImage that gets drawn on by a background thread whenever the user changes something that has influence on the image. The problem is that when displaying the image, the correct image gets shown only for a few seconds, then an erratic imgage (in the right size) with rests of the frame buffer from the system is shown; see image below:

image

Here is what it should look like:

correct image

As soon as the drawing thread has finished, the BufferedImage will be drawn onto a JPanel component after being transformed with an AffineTransformation to reflect a certain zoom level.

The size of the BufferedImage is determined by a fixed number that is not dependent on the resolution of the MacBook or the JFrame (usually quite high, something like 4000x2000). The panel in which the BufferedImage gets drawn is inside a ScrollPane that adjusts to the size of the panel. It does not matter whether the BufferedImage is written over or created new each time a new version is drawn by the thread.

I have tested the tool on windows, a MacBook without retina display and three MacBooks with retina display, on all machines without retina display, the tool works perfectly. Any ideas?

Edit: This is how the program works: The class HexaVisExplorer is the JFrame GUI built with NetBeans of the project. It keeps a ScrollPane, and a VisualizationPanel that is the content of the ScrollPane, and whenever the user changes a property that has influence on the resulting image, the ActionListener for the component calls a method in the VisualizationPanel. There, a property is set and a Thread gets initialized and started that, based on the properties set in VisualizationPanel, will draw a new BufferedImage. When this is done, the new BufferedImage will be saved to the VisualizationPanel, which then will be repainted with an overridden paintComponent(), where the image will be transformed using a java.awt.geom.AffineTransform for efficient zooming. Finally, the viewport of the scroll pane gets modified to reflect the new image size. Here is the code:

Class HexavisExplorer.java

public class HexaVisExplorer extends javax.swing.JFrame {

    private VisualizationPanel visualizationPanel;
    private javax.swing.JScrollPane jScrollPane1;

    public HexaVisExplorer() {
        //...Example where a component listener calls a method in VisualizationPanel.java to set a property
        polygonBorderCheckbox.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                visualizationPanel.setPolygonBorders(polygonBorderCheckbox.isSelected());
            }
        });
        //...
    } 
}  

Class VisualizationPanel, method recalculate() creates a new VisualizationThread that generates the BufferedImage in question using the properties in VisualizationPanel. When recalculate is finisehd,

public class VisualizationPanel extends JPanel {

   private boolean polygonBorders;
   //here be more property class variables and getter/setter for them

    public void setPolygonBorders(boolean polygonBorders) {
        this.polygonBorders = polygonBorders;
        recalculate();
        this.revalidate();
        this.repaint();
    }

    public void recalculate(){
        vt = new VisualizationThread(this);
        vt.execute();
    }

    @Override
    protected void paintComponent(Graphics g) {


        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        dataTransformation.setToScale(transform_factor, transform_factor);
        g2d.transform(dataTransformation);
        g2d.drawImage(toDraw, 0, 0, null);
        this.setSize((int) Math.ceil(transform_factor * maxwidth), (int) Math.ceil(transform_factor * maxheight));
        this.setPreferredSize(new Dimension((int) Math.ceil(transform_factor * maxwidth), (int) Math.ceil(transform_factor * maxheight)));
        this.revalidate();
        hx.modifyVisPanelViewport(this);
    }
}

VisualizationThread takes a VisualizationPanel, reads its properties and calculates a new BufferedImage based on that. When done, done()is called, which then sets the newBufferedImageto draw to theVisualizationPanel` and repaints it.

public class VisualizationThread extends SwingWorker<Object, Object> {
    private VisualizationPanel vp;
    private BufferedImage toDraw;
    
    @Override
    protected Object doInBackground() throws Exception {
    // The bufferedImage  gets drawn on here.
    }

    @Override
    protected void done() {
        vp.setToDraw(toDraw);
        vp.setPreferredSize(new Dimension(toDraw.getWidth(), toDraw.getHeight()));
        vp.repaint();
        vp.revalidate();
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
jurib
  • 21
  • 1
  • 5

1 Answers1

2

It looks like the host peer component is expecting to render an image with one color model but encountering another. There are just too many ways for this to go wrong for a definitive answer, but several things bear closer scrutiny:

  • Verify that your BufferedImage has a compatible color model and space, for example.

  • Consider scaling with AffineTransformOp, outlined here, as it can create a "destination image with the correct size and number of bands."

  • While done() is executed on the event dispatch thread, verify that the rest of your program is correctly synchronized, for example.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045