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:
Here is what it should look like:
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 new
BufferedImageto draw to the
VisualizationPanel` 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();
}
}