On Sun, 23 Dec 2007 12:23:43 +0000, rossum <rossum48@coldmail.com>
wrote:
On Fri, 21 Dec 2007 18:22:31 +0000, rossum <rossum48@coldmail.com>
wrote:
A happy new year and thank you all again for your ongoing help.
The problem I found with my previous version was that I was passing a
reference to my working image, so if I resized the window while the
image was being drawn then the same image was being updated and read
at the same time - not a good thing (though it never actually fell
over). Hence I looked at ways to pass back a deep clone of the
current state of the image. BufferedImage does not clone easily, so I
dropped down to the Raster level for the intermediate stages of the
process. The final image is no longer updated so it was OK to leave
that as a direct reference to the BufferedImage.
The code now publishes intermediate results as a Raster which the
process() function copies into the off screen image before calling
repaint(). This breaks the direct link between the working image and
the image used by repaint(), which is what I was looking for.
rossum
// --- Partial Code Starts ---
final class GraphPanel extends JPanel {
final int biXpos = 30;
final int biYpos = 60;
final int biHeight = 100;
final int biWidth = 150;
volatile BufferedImage mOffScreenImage;
public GraphPanel() {
// Set up off screen image
mOffScreenImage = new BufferedImage(biWidth,
biHeight, BufferedImage.TYPE_INT_BGR);
// Start pixel calculation thread
PixelCalc pc = new PixelCalc();
/** @todo - move thread start out of constructor. */
pc.execute();
} // end constructor
/** Calculates colours for pixels */
class PixelCalc extends SwingWorker<BufferedImage, Raster> {
/** The working copy of the image */
BufferedImage mBim = new BufferedImage(biWidth,
biHeight, BufferedImage.TYPE_INT_BGR);
@Override
public BufferedImage doInBackground() {
Graphics2D g2d = mBim.createGraphics();
final int xLimit = mBim.getWidth();
final int yLimit = mBim.getHeight();
for (int x = 0; x < xLimit; ++x) {
for (int y = 0; y < yLimit; ++y) {
g2d.setColor(pickColour(x, y));
g2d.drawLine(x, y, x + 1, y + 1);
} // end for
// Check for termination request
if (isCancelled()) { return null; }
Thread.yield(); // Play nice
// Publish Raster of partial progress
if (x % 50 == 49) {
publish(mBim.getRaster());
} // end if
// Artificial Delay
try {
Thread.sleep(25);
} catch (InterruptedException iex) {
return null;
} // end try/catch
} // end for
g2d.dispose();
// Return completed image
return mBim;
} // end doInBackground()
@Override
protected void process(List<Raster> images) {
if (images == null || images.size() == 0) { return; }
// Copy latest Raster data into image
final int latest = images.size() - 1;
mOffScreenImage.setData(images.get(latest));
repaint();
} // end process()
@Override
protected void done() {
try {
mOffScreenImage = get();
} catch (InterruptedException ignore) {
// Do nothing
} catch (ExecutionException eex) {
throw new RuntimeException("PixelCalc.done: " +
"Unable to calculate image.", eex);
} // end try/catch
repaint();
} // end done()
private Color pickColour(int x, int y) {
switch ((x + y) % 3) {
case 0: return Color.CYAN;
case 1: return Color.GREEN;
case 2: return Color.MAGENTA;
default: return Color.BLACK;
} // end switch
} // end pickColor()
} // end class PixelCalc
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Resize
Container parent = this.getParent();
this.setSize(parent.getWidth(), parent.getHeight());
// Show image
g.drawImage(mOffScreenImage, biXpos, biYpos, null);
} // end paintComponent()
} // end class GraphPanel
// --- Partial Code Finishes ---
I'm not sure why this has to be so complicated. One image should be
plenty. Also, I don't think I would change the size of my component in
the paintComponent().