/**
TrakEM2 plugin for ImageJ(C).
Copyright (C) 2007-2009 Albert Cardona and Rodney Douglas.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation (http://www.gnu.org/licenses/gpl.txt )
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
You may contact Albert Cardona at acardona at ini.phys.ethz.ch
Institute of Neuroinformatics, University of Zurich / ETH, Switzerland.
**/
package ini.trakem2.display;
import ini.trakem2.utils.CachingThread;
import java.awt.Component;
import java.awt.Rectangle;
import java.util.LinkedList;
public abstract class AbstractRepaintThread extends CachingThread {
final protected AbstractOffscreenThread off;
private final java.util.List<PaintEvent> events = new LinkedList<PaintEvent>();
private final Component target;
public AbstractRepaintThread(final Component target, final String name, final AbstractOffscreenThread off) {
super(name);
this.target = target;
this.off = off;
setPriority(Thread.NORM_PRIORITY + 1);
try { setDaemon(true); } catch (Exception e) { e.printStackTrace(); }
start();
}
private class PaintEvent {
final Rectangle clipRect;
final boolean update_graphics;
PaintEvent(final Rectangle clipRect, final boolean update_graphics) {
this.clipRect = clipRect;
this.update_graphics = update_graphics; // java is sooo verbose... this class is just a tuple!
}
}
/** Queue a new request for painting, updating offscreen graphics. */
public final void paint(final Rectangle clipRect) {
paint(clipRect, true);
}
/** Queue a new request for painting. */
public void paint(final Rectangle clipRect, final boolean update_graphics) {
//Utils.log2("update_graphics: " + update_graphics);
//Utils.printCaller(this, 5);
// queue the event and signal a repaint request
synchronized (events) {
events.add(new PaintEvent(clipRect, update_graphics));
events.notifyAll();
}
}
/** Will gracefully kill this thread by breaking its infinite wait-for-event loop, and also call cancel on all registered offscreen threads. */
public void quit() {
interrupt();
// notify and finish
synchronized (events) {
events.notifyAll();
}
//
off.quit();
}
public void run() {
while (!isInterrupted()) {
try {
// wait until anyone issues a repaint event
synchronized (events) {
while (0 == events.size()) {
if (isInterrupted()) return;
try { events.wait(); } catch (InterruptedException ie) {}
}
}
if (isInterrupted()) {
return;
}
// wait a bit to catch fast subsequent events
// 10 miliseconds
try { Thread.sleep(10); } catch (InterruptedException ie) {}
// obtain all events up to now and clear the event queue
final PaintEvent[] pe;
synchronized (events) {
pe = new PaintEvent[events.size()];
events.toArray(pe);
events.clear();
}
if (0 == pe.length) {
continue;
}
// obtain repaint parameters from merged events
Rectangle clipRect = pe[0].clipRect;
boolean update_graphics = pe[0].update_graphics;
for (int i=1; i<pe.length; i++) {
if (null != clipRect) {
if (null == pe[i].clipRect) clipRect = null; // all
else clipRect.add(pe[i].clipRect);
} // else 'null' clipRect means repaint the entire canvas
if (!update_graphics) update_graphics = pe[i].update_graphics;
else if (null == clipRect) break;
}
// issue an offscreen thread if necessary
if (update_graphics) {
handleUpdateGraphics(target, clipRect);
}
// repaint
/*
if (null == clipRect) target.repaint(0, 0, 0, target.getWidth(), target.getHeight()); // using super.repaint() causes infinite thread loops in the IBM-1.4.2-ppc
else target.repaint(0, clipRect.x, clipRect.y, clipRect.width, clipRect.height);
*/
// Crazy idea: paint NOW
final java.awt.Graphics g = target.getGraphics();
if (null != g) {
// Ensure full clip rect
g.setClip(0, 0, target.getWidth(), target.getHeight());
target.paint(g);
g.dispose();
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
/** Child classes need to extend this method for handling the need of recreating offscreen images. */
abstract protected void handleUpdateGraphics(Component target, Rectangle clipRect);
/** Waits until the offscreen thread is finished with the current cycle. */
public void waitForOffs() {
off.waitOnRepaintCycle();
}
}