/* * JAME 6.2.1 * http://jame.sourceforge.net * * Copyright 2001, 2016 Andrea Medeghini * * This file is part of JAME. * * JAME is an application for creating fractals and other graphics artifacts. * * JAME 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, either version 3 of the License, or * (at your option) any later version. * * JAME 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 JAME. If not, see <http://www.gnu.org/licenses/>. * */ package net.sf.jame.twister.swing; import java.awt.AlphaComposite; import java.awt.Canvas; import java.awt.Color; import java.awt.Composite; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.RenderingHints; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.dnd.InvalidDnDOperationException; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.TooManyListenersException; import java.util.concurrent.Semaphore; import javax.swing.SingleSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.xml.parsers.ParserConfigurationException; import net.sf.jame.core.extension.Extension; import net.sf.jame.core.extension.ExtensionException; import net.sf.jame.core.swing.util.GUIUtil; import net.sf.jame.core.tree.NodeAction; import net.sf.jame.core.util.IntegerVector2D; import net.sf.jame.core.util.RenderContext; import net.sf.jame.core.util.RenderContextListener; import net.sf.jame.core.util.Surface; import net.sf.jame.core.util.Tile; import net.sf.jame.core.xml.XML; import net.sf.jame.core.xml.XMLImportException; import net.sf.jame.twister.ControllerListener; import net.sf.jame.twister.TwisterBookmark; import net.sf.jame.twister.TwisterClip; import net.sf.jame.twister.TwisterClipController; import net.sf.jame.twister.TwisterClipXMLImporter; import net.sf.jame.twister.TwisterConfig; import net.sf.jame.twister.TwisterRegistry; import net.sf.jame.twister.TwisterRuntime; import net.sf.jame.twister.TwisterSequence; import net.sf.jame.twister.converter.extension.ConverterExtensionRuntime; import net.sf.jame.twister.effect.extension.EffectExtensionRuntime; import net.sf.jame.twister.renderer.DefaultTwisterRenderer; import net.sf.jame.twister.renderer.OverlayTwisterRenderer; import net.sf.jame.twister.renderer.TwisterRenderer; import net.sf.jame.twister.renderer.TwisterRenderingHints; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * @author Andrea Medeghini */ @SuppressWarnings("unchecked") public class TwisterCanvas extends Canvas implements RenderContext { // private static final Logger logger = Logger.getLogger(TwisterCanvas.class); private static final int MIN_HEIGHT = 20; private static final int MIN_WITH = 20; private static final long frameRate = 25; private static final long serialVersionUID = 1L; private static final int SCALE = 5; public static final int SYMBOL_NONE = 0; public static final int SYMBOL_PLAY = 1; public static final int SYMBOL_PAUSE = 2; public static final int SYMBOL_RECORD = 3; public static final int STATE_INIT = 0; public static final int STATE_EDIT = 1; public static final int STATE_PLAY = 2; public static final int STATE_SCRIPT = 3; private Color messageColor = new Color(80, 80, 80, 180); private final SingleSelectionModel model; private final List<BookmarkIcon> bookmarkIcons; private TwisterRenderer[] renderers; private TwisterRuntime[] runtimes; private OverlayTwisterRenderer overlayRenderer; private TwisterRuntime overlayRuntime; private TwisterConfig config; private TwisterClip clip; private InputAdapter adapter; private boolean showBookmarkIcons; private RefreshTask refreshCanvas; private int selectedIndex = -1; private final int hcells; private final int vcells; private final Font messageFont = Font.decode("arial-normal-12"); private final ChangeEvent event = new ChangeEvent(this); private final List<ChangeListener> changeListeners = new LinkedList<ChangeListener>(); private final List<RenderContextListener> contextListeners = new LinkedList<RenderContextListener>(); private IntegerVector2D size = new IntegerVector2D(200, 200); private int symbol = TwisterCanvas.SYMBOL_NONE; private int state = TwisterCanvas.STATE_INIT; private BufferedImage bufferedImage; private long time; private Surface surface; private RenderListener listener; boolean paintBorder = false; boolean isTarget = true; private final DropTarget target; private final DragSource source; private boolean dragEnabled; private boolean dropEnabled = true; private TwisterClipController clipController; private final Semaphore semaphore = new Semaphore(1, true); private final List<Runnable>[] commands = new LinkedList[2]; private TwisterMessage twisterMessage; private volatile Thread resizeThread; /** * @param hcells * @param vcells * @param model * @throws ExtensionException */ public TwisterCanvas(final int hcells, final int vcells, final SingleSelectionModel model) throws ExtensionException { this.hcells = hcells; this.vcells = vcells; this.model = model; commands[0] = new LinkedList<Runnable>(); commands[1] = new LinkedList<Runnable>(); source = new DragSource(); target = new DropTarget(); this.setSize(new Dimension(size.getX(), size.getY())); bookmarkIcons = new ArrayList<BookmarkIcon>(); final CanvasListener listener = new CanvasListener(); addComponentListener(listener); addFocusListener(listener); addKeyListener(listener); addMouseListener(listener); addMouseMotionListener(listener); setFocusable(true); setBackground(new Color(0x2f2f2f)); if (source.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, listener) != null) { setDropTarget(target); } try { target.addDropTargetListener(listener); } catch (final TooManyListenersException e) { e.printStackTrace(); } } /** * @param listener */ public void addChangeListener(final ChangeListener listener) { changeListeners.add(listener); } /** * @param listener */ public void removeChangeListener(final ChangeListener listener) { changeListeners.remove(listener); } /** * */ protected void fireChangeEvent() { for (final ChangeListener listener : changeListeners) { listener.stateChanged(event); } } /** * @param message * @param size * @param x * @param y * @param time * @param hasBackground */ public void showMessage(String message, float size, float x, float y, long time, boolean hasBackground) { try { acquire(); twisterMessage = new TwisterMessage(message, size, x, y, time, hasBackground); release(); refresh(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * @param command */ public void submitCommand(final Runnable command) { synchronized (commands[0]) { commands[0].add(command); } // workaround to refresh layer preview config.getContext().updateTimestamp(); refresh(); } /** * @see net.sf.jame.core.util.RenderContext#acquire() */ public void acquire() throws InterruptedException { semaphore.acquire(); } /** * @see net.sf.jame.core.util.RenderContext#release() */ public void release() { semaphore.release(); } /** * @see net.sf.jame.core.util.RenderContext#addRenderContextListener(net.sf.jame.core.util.RenderContextListener) */ public void addRenderContextListener(RenderContextListener listener) { synchronized (contextListeners) { contextListeners.add(listener); } } /** * @see net.sf.jame.core.util.RenderContext#removeRenderContextListener(net.sf.jame.core.util.RenderContextListener) */ public void removeRenderContextListener(RenderContextListener listener) { synchronized (contextListeners) { contextListeners.remove(listener); } } /** * */ public void refresh() { if (refreshCanvas != null) { refreshCanvas.refresh(); } synchronized (contextListeners) { for (RenderContextListener listener : contextListeners) { listener.refresh(); } } } /** * @return */ public TwisterConfig getConfig() { return config; } /** * @return */ public TwisterClip getClip() { return clip; } /** * @return the dragEnabled */ public boolean isDragEnabled() { return dragEnabled; } /** * @param dragEnabled the dragEnabled to set */ public void setDragEnabled(final boolean dragEnabled) { this.dragEnabled = dragEnabled; } /** * @return the dropEnabled */ public boolean isDropEnabled() { return dropEnabled; } /** * @param dropEnabled the dropEnabled to set */ public void setDropEnabled(final boolean dropEnabled) { this.dropEnabled = dropEnabled; } /** * @see java.awt.Canvas#update(java.awt.Graphics) */ @Override public void update(final Graphics g) { paint(g); } /** * @return */ public boolean isStarted() { return (refreshCanvas != null) && refreshCanvas.isStarted(); } /** * */ public void start(TwisterClip clip) { clipController = new TwisterClipController(clip); clipController.setRenderContext(TwisterCanvas.this); clipController.addControllerListener(new ControllerListener() { public void actionRedone(final NodeAction action) { } public void actionUndone(final NodeAction action) { } public void configChanged() { } }); clipController.init(); final TwisterConfig config = clipController.getConfig(); if (clipController.getDuration() > 0) { this.clip = clip; setListener(new RenderListener() { public void frameRendered() { if (clipController != null) { if (!clipController.redoAction(1000 / getFrameRate(), true)) { clipController.init(); } } } }); state = TwisterCanvas.STATE_PLAY; symbol = TwisterCanvas.SYMBOL_PLAY; } else { clip = null; clipController = null; setListener(null); state = TwisterCanvas.STATE_EDIT; symbol = TwisterCanvas.SYMBOL_NONE; } final IntegerVector2D size = new IntegerVector2D(Math.max(getWidth(), MIN_WITH), Math.max(getHeight(), MIN_HEIGHT)); init(config, size, hcells, vcells); if (refreshCanvas == null) { refreshCanvas = new RefreshTask(); refreshCanvas.start(); } } /** * */ public void start(final TwisterConfig config) { final IntegerVector2D size = new IntegerVector2D(Math.max(getWidth(), MIN_WITH), Math.max(getHeight(), MIN_HEIGHT)); init(config, size, hcells, vcells); clip = null; clipController = null; setListener(null); state = TwisterCanvas.STATE_EDIT; symbol = TwisterCanvas.SYMBOL_NONE; if (refreshCanvas == null) { refreshCanvas = new RefreshTask(); refreshCanvas.start(); } } /** * */ public void start() { if (clip != null) { start(clip); } else { if (config != null) { start(config); } } } /** * */ public void stop() { if (refreshCanvas != null) { refreshCanvas.stop(); refreshCanvas = null; } dispose(); symbol = TwisterCanvas.SYMBOL_NONE; // refreshTask = null; clipController = null; setListener(null); } /** * */ public void startScript() { if (state == STATE_EDIT) { state = STATE_SCRIPT; } } /** * */ public void stopScript() { if (state == STATE_SCRIPT) { state = STATE_EDIT; } } /** * @return */ public boolean isSuspended() { return (clipController != null) && (listener == null); } /** * @throws ExtensionException */ public void resume() { setListener(new RenderListener() { public void frameRendered() { if (clipController != null) { if (!clipController.redoAction(1000 / getFrameRate(), true)) { clipController.init(); } } } }); } /** * */ public void suspend() { setListener(null); } /** * @see net.sf.jame.core.util.RenderContext#getImageSize() */ public IntegerVector2D getImageSize() { return size; } /** * @return */ public long getFrameRate() { return TwisterCanvas.frameRate; } private void init(final TwisterConfig config, final IntegerVector2D size, final int nx, final int ny) { final int dx = size.getX() / nx; final int dy = size.getY() / ny; final HashMap<Object, Object> hints = new HashMap<Object, Object>(); hints.put(TwisterRenderingHints.KEY_QUALITY, TwisterRenderingHints.QUALITY_REALTIME); if (Boolean.getBoolean("jame.lowMemory")) { hints.put(TwisterRenderingHints.KEY_MEMORY, TwisterRenderingHints.MEMORY_LOW); } if (runtimes != null) { for (int i = 0; i < runtimes.length; i++) { if (runtimes[i] != null) { runtimes[i].dispose(); } } } if (overlayRuntime != null) { overlayRuntime.dispose(); } renderers = new TwisterRenderer[nx * ny]; runtimes = new TwisterRuntime[nx * ny]; for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { final int k = j * nx + i; runtimes[k] = new TwisterRuntime(config); renderers[k] = new DefaultTwisterRenderer(runtimes[k]); renderers[k].setRenderingHints(hints); renderers[k].setTile(new Tile(new IntegerVector2D(size.getX(), size.getY()), new IntegerVector2D(dx, dy), new IntegerVector2D(dx * i, dy * j), new IntegerVector2D(24, 24))); } } final HashMap<Object, Object> overlayHints = new HashMap<Object, Object>(); overlayHints.put(TwisterRenderingHints.KEY_QUALITY, TwisterRenderingHints.QUALITY_REALTIME); if (Boolean.getBoolean("jame.lowMemory")) { overlayHints.put(TwisterRenderingHints.KEY_MEMORY, TwisterRenderingHints.MEMORY_LOW); } overlayHints.put(TwisterRenderingHints.KEY_TYPE, TwisterRenderingHints.TYPE_OVERLAY); overlayRuntime = new TwisterRuntime(config); overlayRenderer = new OverlayTwisterRenderer(overlayRuntime); overlayRenderer.setRenderingHints(overlayHints); overlayRenderer.setTile(new Tile(new IntegerVector2D(size.getX(), size.getY()), new IntegerVector2D(size.getX(), size.getY()), new IntegerVector2D(0, 0), new IntegerVector2D(0, 0))); adapter = new DefaultInputAdapter(new DefaultRenderContext(), config); this.size = size; this.config = config; } private void dispose() { if (renderers != null) { for (final TwisterRenderer element : renderers) { element.dispose(); } } if (overlayRenderer != null) { overlayRenderer.dispose(); } renderers = null; overlayRenderer = null; if (runtimes != null) { for (final TwisterRuntime element : runtimes) { element.dispose(); } } if (overlayRuntime != null) { overlayRuntime.dispose(); } runtimes = null; overlayRuntime = null; } private void resize(final IntegerVector2D size, final int nx, final int ny) throws ExtensionException { try { if (!this.size.equals(size)) { final int dx = size.getX() / nx; final int dy = size.getY() / ny; acquire(); stopRenderers(); if (renderers != null) { for (int i = 0; i < nx; i++) { for (int j = 0; j < ny; j++) { final int k = j * nx + i; renderers[k].setTile(new Tile(new IntegerVector2D(size.getX(), size.getY()), new IntegerVector2D(dx, dy), new IntegerVector2D(dx * i, dy * j), new IntegerVector2D(32, 32))); } } } if (overlayRenderer != null) { overlayRenderer.setTile(new Tile(new IntegerVector2D(size.getX(), size.getY()), new IntegerVector2D(size.getX(), size.getY()), new IntegerVector2D(0, 0), new IntegerVector2D(0, 0))); } startRenderers(); final int width = getMaxWidth(); final int height = getMaxHeight(); for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); bookmarkIcon.getBookmark().getRenderer().abortRenderer(); } for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); bookmarkIcon.getBookmark().getRenderer().joinRenderer(); bookmarkIcon.getBookmark().getRenderer().setTile(new Tile(new IntegerVector2D(width, height), new IntegerVector2D(width, height), new IntegerVector2D(0, 0), new IntegerVector2D(0, 0))); bookmarkIcon.getBookmark().getRenderer().startRenderer(); } this.size = size; if (surface != null) { surface.dispose(); surface = null; } if (bufferedImage != null) { bufferedImage.flush(); bufferedImage = null; } release(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * @see java.awt.Canvas#paint(java.awt.Graphics) */ @Override public void paint(Graphics g) { if (bufferedImage == null) { bufferedImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); refresh(); } g.drawImage(bufferedImage, 0, 0, this); } private void draw() { if (bufferedImage != null) { Graphics2D g = bufferedImage.createGraphics(); configureGraphics(g); paintImage(g); g.dispose(); } } private void clear() { if (bufferedImage != null) { Graphics2D g = bufferedImage.createGraphics(); configureGraphics(g); clearImage(g); g.dispose(); } } private void configureGraphics(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); } private void clearImage(final Graphics g) { g.setColor(getBackground()); ((Graphics2D) g).setComposite(AlphaComposite.SrcOver); g.fillRect(0, 0, getWidth(), getHeight()); } private void paintImage(final Graphics2D g) { synchronized (contextListeners) { if (state == TwisterCanvas.STATE_EDIT) { if ((renderers != null) && (overlayRenderer != null)) { for (final TwisterRenderer renderer : renderers) { renderer.prepareImage(true); } overlayRenderer.prepareImage(true); for (RenderContextListener listener : contextListeners) { listener.prepareImage(true); } final EffectExtensionRuntime<?> effectRuntime = overlayRenderer.getRuntime().getEffectElement().getEffectRuntime(); if ((effectRuntime != null) && overlayRenderer.getRuntime().getEffectElement().isEnabled()) { if (surface == null) { surface = new Surface(size.getX(), size.getY()); } for (final TwisterRenderer renderer : renderers) { renderer.drawImage(surface.getGraphics2D()); } effectRuntime.setSize(size); effectRuntime.prepareEffect(); effectRuntime.renderImage(surface); g.setColor(new Color(config.getBackground().getARGB(), true)); g.setComposite(AlphaComposite.SrcOver); g.fillRect(0, 0, surface.getWidth(), surface.getHeight()); g.drawImage(surface.getImage(), 0, 0, null); } else { if (surface != null) { surface.dispose(); surface = null; } for (final TwisterRenderer renderer : renderers) { renderer.drawImage(g); } } overlayRenderer.drawImage(g); for (RenderContextListener listener : contextListeners) { listener.drawImage(); } } g.setComposite(AlphaComposite.SrcOver); if (showBookmarkIcons) { final Composite composite = g.getComposite(); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); g.setColor(Color.BLACK); for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); drawBookmarkIcon(g, bookmarkIcon); } // if (model.getSelectedIndex() != -1 && model.getSelectedIndex() != selectedIndex) // { // g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.9f)); // // g.setColor(Color.BLUE); // // BookmarkIcon bookmarkIcon = bookmarkIcons.get(model.getSelectedIndex()); // // drawBookmarkIcon(g, bookmarkIcon); // } // if (selectedIndex != -1) { g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.9f)); g.setColor(Color.RED); final BookmarkIcon bookmarkIcon = bookmarkIcons.get(selectedIndex); drawBookmarkIcon(g, bookmarkIcon); } g.setComposite(composite); } } else if ((state == TwisterCanvas.STATE_PLAY) || (state == TwisterCanvas.STATE_SCRIPT)) { if ((renderers != null) && (overlayRenderer != null)) { for (final TwisterRenderer renderer : renderers) { renderer.prepareImage(true); } overlayRenderer.prepareImage(true); final EffectExtensionRuntime<?> effectRuntime = overlayRenderer.getRuntime().getEffectElement().getEffectRuntime(); if ((effectRuntime != null) && overlayRenderer.getRuntime().getEffectElement().isEnabled()) { if (surface == null) { surface = new Surface(size.getX(), size.getY()); } for (final TwisterRenderer renderer : renderers) { renderer.drawImage(surface.getGraphics2D()); } effectRuntime.setSize(size); effectRuntime.prepareEffect(); effectRuntime.renderImage(surface); g.setColor(new Color(config.getBackground().getARGB(), true)); g.setComposite(AlphaComposite.SrcOver); g.fillRect(0, 0, surface.getWidth(), surface.getHeight()); g.drawImage(surface.getImage(), 0, 0, null); } else { if (surface != null) { surface.dispose(); surface = null; } for (final TwisterRenderer renderer : renderers) { renderer.drawImage(g); } } overlayRenderer.drawImage(g); } } g.setComposite(AlphaComposite.SrcOver); if (twisterMessage != null) { if (System.currentTimeMillis() < twisterMessage.getExpireTime()) { float x = (twisterMessage.getX() * getWidth()) / 100f; float y = (twisterMessage.getY() * getHeight()) / 100f; final Font font = twisterMessage.getFont(getHeight()); g.setFont(font); if (twisterMessage.hasBackground()) { g.setColor(messageColor); Rectangle2D bounds = font.getStringBounds(twisterMessage.getMessage(), g.getFontRenderContext()); g.fillRect(0, (int) Math.rint(y - font.getSize2D() - 5), getWidth(), (int) bounds.getHeight() + 10); } g.setColor(Color.YELLOW); g.drawString(twisterMessage.getMessage(), x, y); } else { twisterMessage = null; } } if (hasFocus()) { g.setClip(null); g.setColor(Color.BLUE); g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); } switch (symbol) { case SYMBOL_NONE: { break; } case SYMBOL_PLAY: { g.setColor(Color.GREEN); final Polygon p = new Polygon(); p.addPoint(10, 10); p.addPoint(25, 17); p.addPoint(10, 24); g.fill(p); break; } case SYMBOL_PAUSE: { g.setColor(Color.YELLOW); g.fillRect(10, 10, 6, 15); g.fillRect(19, 10, 6, 15); break; } case SYMBOL_RECORD: { if (((System.currentTimeMillis() - time) / 1000) % 2 == 0) { g.setColor(Color.RED); g.fillOval(10, 10, 15, 15); } break; } } if (paintBorder) { g.setColor(Color.BLACK); g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); } } } private void drawBookmarkIcon(final Graphics2D g, final BookmarkIcon bookmarkIcon) { final int x = (int) Math.rint(bookmarkIcon.x); final int y = (int) Math.rint(bookmarkIcon.y); final int w = (int) Math.rint(bookmarkIcon.w); final int h = (int) Math.rint(bookmarkIcon.h); bookmarkIcon.getBookmark().getRenderer().drawImage(g, x, y - 2, w, h, 2, 2); g.setClip(null); g.drawRect(x + 2, y, w - 5, h - 5); } private class RefreshTask implements Runnable { private final Object lock = new Object(); private Thread refreshThread; private boolean running; private volatile boolean refresh; /** * */ public void refresh() { synchronized (lock) { refresh = true; lock.notify(); } } /** * */ public void start() { if (refreshThread == null) { running = true; refreshThread = new Thread(this); refreshThread.setName("TwisterCanvas RefreshTask"); refreshThread.setPriority(Thread.MAX_PRIORITY); refreshThread.setDaemon(true); refreshThread.start(); } } /** * */ public void stop() { if (refreshThread != null) { running = false; refreshThread.interrupt(); try { refreshThread.join(); } catch (final InterruptedException e) { } refreshThread = null; } } /** * @return */ public boolean isStarted() { return refreshThread != null; } /** * @see java.lang.Runnable#run() */ public void run() { try { long startTime = 0; long totalTime = 0; final long pauseTime = 1000 / TwisterCanvas.frameRate; long sleepTime = pauseTime; long idleTime = 0; while (running) { startTime = System.currentTimeMillis(); synchronized (commands) { LinkedList<Runnable> tmpCommands = (LinkedList<Runnable>) commands[1]; commands[0] = commands[1]; commands[1] = tmpCommands; } acquire(); draw(); adapter.refresh(); refresh |= needsRefresh(); if (listener != null) { listener.frameRendered(); } while (commands[1].size() > 0) { commands[1].remove(0).run(); } release(); repaint(); synchronized (lock) { if (refresh || (twisterMessage != null) || (symbol == TwisterCanvas.SYMBOL_RECORD) || (symbol == TwisterCanvas.SYMBOL_PLAY)) { idleTime = System.currentTimeMillis(); } if (System.currentTimeMillis() - idleTime > 500) { if (!refresh) { lock.wait(); } idleTime = System.currentTimeMillis(); } refresh = false; } totalTime = System.currentTimeMillis() - startTime; sleepTime = pauseTime - totalTime; if (!running) { break; } if (sleepTime > 10) { Thread.sleep(sleepTime); } else { Thread.sleep(10); } } } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } catch (final Exception e) { e.printStackTrace(); } clear(); } private boolean needsRefresh() { boolean needsRefresh = false; if (runtimes != null) { for (final TwisterRuntime runtime : runtimes) { needsRefresh |= runtime.isChanged(); } } if (overlayRuntime != null) { needsRefresh |= overlayRuntime.isChanged(); } return needsRefresh; } } /** * @see net.sf.jame.core.util.RenderContext#startRenderers() */ public void startRenderers() { synchronized (contextListeners) { if (renderers != null) { for (final TwisterRenderer renderer : renderers) { renderer.startRenderer(); } } if (overlayRenderer != null) { overlayRenderer.startRenderer(); } for (RenderContextListener listener : contextListeners) { listener.startRenderer(); } } } /** * @see net.sf.jame.core.util.RenderContext#stopRenderers() */ public void stopRenderers() { synchronized (contextListeners) { if (renderers != null) { for (final TwisterRenderer renderer : renderers) { renderer.abortRenderer(); } } if (overlayRenderer != null) { overlayRenderer.abortRenderer(); } for (RenderContextListener listener : contextListeners) { listener.abortRenderer(); } if (renderers != null) { for (final TwisterRenderer renderer : renderers) { renderer.joinRenderer(); } } if (overlayRenderer != null) { overlayRenderer.joinRenderer(); } for (RenderContextListener listener : contextListeners) { listener.joinRenderer(); } } } private void resizeIcons(final double maxWidth, final double maxHeight) { double minWidth = maxWidth / 2.0; double minHeight = maxHeight / 2.0; if (minWidth * bookmarkIcons.size() > getWidth() - maxWidth) { minWidth = (getWidth() - maxWidth) / bookmarkIcons.size(); minHeight = (minWidth * getHeight()) / getWidth(); for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); bookmarkIcon.w = minWidth; bookmarkIcon.h = minHeight; bookmarkIcon.tw = minWidth; bookmarkIcon.th = minHeight; } } else { for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); bookmarkIcon.w = minWidth; bookmarkIcon.h = minHeight; bookmarkIcon.tw = minWidth; bookmarkIcon.th = minHeight; } } double w = 0; for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); w += bookmarkIcon.w; } double x = (getWidth() - w) / 2.0; for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); bookmarkIcon.y = getHeight() - bookmarkIcon.h; bookmarkIcon.x = x; bookmarkIcon.tx = bookmarkIcon.x; bookmarkIcon.ty = bookmarkIcon.y; x += bookmarkIcon.w; } selectedIndex = -1; } private void zoomIcons(final double maxWidth, final double maxHeight, final double px, final double py) { int k = -1; for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); if ((bookmarkIcon.tx <= px) && (bookmarkIcon.tx + bookmarkIcon.tw >= px)) { if ((bookmarkIcon.y <= py) && (bookmarkIcon.y + bookmarkIcon.h >= py)) { k = i; break; } } } if (k != -1) { BookmarkIcon bookmarkIcon = bookmarkIcons.get(k); for (int i = 0; i < bookmarkIcons.size(); i++) { bookmarkIcon = bookmarkIcons.get(i); final double d = bookmarkIcon.tw; final double D = maxWidth; final double t = px - bookmarkIcon.tx; bookmarkIcon.x = bookmarkIcon.tx - getPos(t, D, d); bookmarkIcon.w = bookmarkIcon.tx + d - getPos(t - d, D, d) - bookmarkIcon.x; bookmarkIcon.h = (bookmarkIcon.th / bookmarkIcon.tw) * bookmarkIcon.w; bookmarkIcon.y = getHeight() - bookmarkIcon.h; } selectedIndex = k; } else { this.zoomIcons(); } } private void zoomIcons() { for (int i = 0; i < bookmarkIcons.size(); i++) { final BookmarkIcon bookmarkIcon = bookmarkIcons.get(i); bookmarkIcon.x = bookmarkIcon.tx; bookmarkIcon.y = bookmarkIcon.ty; bookmarkIcon.w = bookmarkIcon.tw; bookmarkIcon.h = bookmarkIcon.th; } selectedIndex = -1; } private double getPos(final double x, final double D, final double d) { final double z = Math.abs(x / d * 2.0); final double s = (x >= 0) ? +1 : -1; if ((z >= 0) && (z <= 7)) { return s * (D * ((getA(1, 0.25, 7, 1) * z + getB(1, 0.25, 7, 1)) * z) + (D / 4.0 - d / 2.0)); } else { return s * (D + (D / 4.0 - d / 2.0)); } } private double getA(final double x0, final double y0, final double x1, final double y1) { return (y1 * x0 - y0 * x1) / (x0 * x1 * (x1 - x0)); } private double getB(final double x0, final double y0, final double x1, final double y1) { return (y0 - (y1 * x0 - y0 * x1) / (x0 * x1 * (x1 - x0)) * x0) / x0; } private int getMaxHeight() { return getHeight() / TwisterCanvas.SCALE; } private int getMaxWidth() { return getWidth() / TwisterCanvas.SCALE; } /** * @param bookmark */ public void addBookmark(final TwisterBookmark bookmark) { GUIUtil.executeTask(new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { final int width = TwisterCanvas.this.getMaxWidth(); final int height = TwisterCanvas.this.getMaxHeight(); bookmark.getRenderer().setTile(new Tile(new IntegerVector2D(width, height), new IntegerVector2D(width, height), new IntegerVector2D(0, 0), new IntegerVector2D(0, 0))); final BookmarkIcon bookmarkIcon = new BookmarkIcon(bookmark); bookmarkIcons.add(bookmarkIcon); TwisterCanvas.this.resizeIcons(width, height); bookmark.getRenderer().startRenderer(); refresh(); } }, true); } /** * @param bookmark */ public void removeBookmark() { if (selectedIndex != -1) { GUIUtil.executeTask(new Runnable() { /** * @see java.lang.Runnable#run() */ public void run() { bookmarkIcons.remove(selectedIndex); TwisterCanvas.this.resizeIcons(TwisterCanvas.this.getMaxWidth(), TwisterCanvas.this.getMaxHeight()); model.setSelectedIndex(-1); refresh(); } }, true); } } /** * @return */ public TwisterBookmark getSelectedBookmark() { if (model.getSelectedIndex() != -1) { return bookmarkIcons.get(model.getSelectedIndex()).getBookmark(); } return null; } /** * @return the showBookmarks */ public boolean isShowBookmarkIcons() { return showBookmarkIcons; } /** * @param showBookmarks the showBookmarks to set */ public void setShowBookmarkIcons(final boolean showBookmarks) { showBookmarkIcons = showBookmarks; } /** * @return the state */ public int getState() { return state; } /** * @return the symbol */ public int getSymbol() { return symbol; } /** * @param symbol the symbol to set */ public void setSymbol(final int symbol) { time = System.currentTimeMillis(); this.symbol = symbol; if ((symbol == TwisterCanvas.SYMBOL_RECORD) || (symbol == TwisterCanvas.SYMBOL_PLAY)) { if (overlayRuntime != null) { if (overlayRuntime.getEffectElement().getEffectRuntime() != null) { overlayRuntime.getEffectElement().getEffectRuntime().reset(); } } } refresh(); } /** * @return the listener */ public RenderListener getListener() { return listener; } /** * @param listener the listener to set */ public void setListener(final RenderListener listener) { this.listener = listener; } private void resize() { if (state != TwisterCanvas.STATE_INIT) { try { final IntegerVector2D size = new IntegerVector2D(Math.max(getWidth(), MIN_WITH), Math.max(getHeight(), MIN_HEIGHT)); TwisterCanvas.this.resize(size, hcells, vcells); final int width = getWidth() / TwisterCanvas.SCALE; final int height = getHeight() / TwisterCanvas.SCALE; resizeIcons(width, height); refresh(); } catch (final ExtensionException x) { x.printStackTrace(); } catch (final Exception x) { x.printStackTrace(); } catch (final Error x) { x.printStackTrace(); } } } private class CanvasListener implements ComponentListener, MouseListener, MouseMotionListener, KeyListener, FocusListener, DropTargetListener, DragGestureListener, DragSourceListener { /** * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent) */ public void componentHidden(final ComponentEvent e) { } /** * @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent) */ public void componentMoved(final ComponentEvent e) { } /** * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent) */ public void componentResized(final ComponentEvent e) { if ((e.getComponent().getWidth() > 0) && (e.getComponent().getHeight() > 0)) { if (resizeThread == null) { resizeThread = new Thread(new Runnable() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { } GUIUtil.executeTask(new Runnable() { public void run() { resize(); } }, false); resizeThread = null; } }, "Resize Thread"); resizeThread.setDaemon(true); resizeThread.start(); } } } /** * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent) */ public void componentShown(final ComponentEvent e) { } /** * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) */ public void mouseClicked(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { if (e.isShiftDown()) { if (showBookmarkIcons && (selectedIndex != -1)) { removeBookmark(); } else { try { acquire(); adapter.mouseClicked(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } else { if (showBookmarkIcons && (selectedIndex != -1)) { model.setSelectedIndex(selectedIndex); } else { try { acquire(); adapter.mouseClicked(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } } else if (state == TwisterCanvas.STATE_SCRIPT) { fireChangeEvent(); } } /** * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent) */ public void mouseEntered(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { final int maxWidth = getMaxWidth(); final int maxHeight = getMaxHeight(); TwisterCanvas.this.zoomIcons(maxWidth, maxHeight, e.getX(), e.getY()); try { acquire(); adapter.mouseEntered(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent) */ public void mouseExited(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { TwisterCanvas.this.zoomIcons(); try { acquire(); adapter.mouseExited(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent) */ public void mousePressed(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { if (!showBookmarkIcons || (selectedIndex == -1)) { try { acquire(); adapter.mousePressed(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } } /** * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) */ public void mouseReleased(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { try { acquire(); adapter.mouseReleased(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent) */ public void mouseDragged(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { try { acquire(); adapter.mouseDragged(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent) */ public void mouseMoved(final MouseEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { final int maxWidth = getMaxWidth(); final int maxHeight = getMaxHeight(); TwisterCanvas.this.zoomIcons(maxWidth, maxHeight, e.getX(), e.getY()); try { acquire(); adapter.mouseMoved(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } if (selectedIndex == -1) { model.setSelectedIndex(-1); } } } /** * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent) */ public void keyTyped(final KeyEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { try { acquire(); adapter.keyTyped(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) */ public void keyPressed(final KeyEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { try { acquire(); adapter.keyPressed(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent) */ public void keyReleased(final KeyEvent e) { refresh(); if (state == TwisterCanvas.STATE_EDIT) { if (e.getKeyCode() == KeyEvent.VK_B) { showBookmarkIcons = !showBookmarkIcons; } try { acquire(); adapter.keyReleased(e); release(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } /** * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) */ public void focusGained(final FocusEvent e) { refresh(); } /** * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent) */ public void focusLost(final FocusEvent e) { refresh(); } /** * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent) */ public void dragEnter(final DropTargetDragEvent e) { if (!isDropEnabled()) { e.rejectDrag(); } else if (isTarget) { final DataFlavor[] flavors = e.getCurrentDataFlavors(); boolean accept = false; if (state == TwisterCanvas.STATE_EDIT) { for (final DataFlavor element : flavors) { if (element.equals(TransferableTwisterConfig.TWISTER_CONFIG_FLAVOR)) { accept = true; break; } else if (element.equals(TransferableTwisterClip.TWISTER_CLIP_FLAVOR)) { accept = true; break; } else if (element.equals(DataFlavor.javaFileListFlavor)) { accept = true; break; } } } if (accept) { e.acceptDrag(DnDConstants.ACTION_MOVE); paintBorder = true; refresh(); } else { e.rejectDrag(); } } } /** * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent) */ public void dragOver(final DropTargetDragEvent e) { } /** * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent) */ public void dragExit(final DropTargetEvent e) { if (isTarget) { paintBorder = false; refresh(); } } /** * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent) */ public void dropActionChanged(final DropTargetDragEvent e) { } /** * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent) */ public void drop(final DropTargetDropEvent e) { if (isTarget) { if (state == TwisterCanvas.STATE_EDIT) { final DataFlavor[] flavors = e.getCurrentDataFlavors(); boolean accept = false; for (final DataFlavor element : flavors) { if (element.equals(TransferableTwisterConfig.TWISTER_CONFIG_FLAVOR)) { try { e.acceptDrop(DnDConstants.ACTION_COPY); final TwisterConfig config = (TwisterConfig) e.getTransferable().getTransferData(TransferableTwisterConfig.TWISTER_CONFIG_FLAVOR); acquire(); stopRenderers(); stop(); TwisterCanvas.this.config.getContext().updateTimestamp(); TwisterCanvas.this.config.setFrameConfigElement(config.getFrameConfigElement().clone()); TwisterCanvas.this.config.setEffectConfigElement(config.getEffectConfigElement().clone()); TwisterCanvas.this.config.setBackground(config.getBackground()); start(TwisterCanvas.this.config); startRenderers(); release(); accept = true; } catch (final UnsupportedFlavorException x) { x.printStackTrace(); } catch (final IOException x) { x.printStackTrace(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } break; } else if (element.equals(TransferableTwisterClip.TWISTER_CLIP_FLAVOR)) { try { e.acceptDrop(DnDConstants.ACTION_COPY); final TwisterClip clip = (TwisterClip) e.getTransferable().getTransferData(TransferableTwisterClip.TWISTER_CLIP_FLAVOR); acquire(); stopRenderers(); stop(); start(clip); startRenderers(); release(); accept = true; } catch (final UnsupportedFlavorException x) { x.printStackTrace(); } catch (final IOException x) { x.printStackTrace(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } break; } else if (element.equals(DataFlavor.javaFileListFlavor)) { try { e.acceptDrop(DnDConstants.ACTION_COPY); final List<File> files = (List<File>) e.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); if (files.size() > 0) { final TwisterClipXMLImporter importer = new TwisterClipXMLImporter(); final InputStream is = new FileInputStream(files.get(0)); TwisterClip clip = null; try { Document doc = XML.loadDocument(is, "twister-clip.xml"); clip = importer.importFromElement(doc.getDocumentElement()); is.close(); } catch (Exception x) { List<Extension<ConverterExtensionRuntime>> extensions = TwisterRegistry.getInstance().getConverterRegistry().getExtensionList(); for (Extension<ConverterExtensionRuntime> extension : extensions) { try { ConverterExtensionRuntime runtime = extension.createExtensionRuntime(); TwisterConfig config = runtime.createConverter().convert(files.get(0)); if (config != null) { clip = new TwisterClip(); clip.addSequence(new TwisterSequence()); clip.getSequence(0).setDuration(0); clip.getSequence(0).setInitialConfig(config); } } catch (Exception q) { q.printStackTrace(); } } if (clip == null) { throw x; } } acquire(); stopRenderers(); stop(); if (clip.getSequenceCount() == 1) { if (clip.getSequence(0).getDuration() == 0) { if (clip.getSequence(0).getInitialConfig() != null) { config.getContext().updateTimestamp(); config.setFrameConfigElement(clip.getSequence(0).getInitialConfig().getFrameConfigElement().clone()); config.setEffectConfigElement(clip.getSequence(0).getInitialConfig().getEffectConfigElement().clone()); config.setBackground(clip.getSequence(0).getInitialConfig().getBackground()); start(config); } else if (clip.getSequence(0).getFinalConfig() != null) { config.getContext().updateTimestamp(); config.setFrameConfigElement(clip.getSequence(0).getFinalConfig().getFrameConfigElement().clone()); config.setEffectConfigElement(clip.getSequence(0).getFinalConfig().getEffectConfigElement().clone()); config.setBackground(clip.getSequence(0).getFinalConfig().getBackground()); start(config); } } else { start(clip); } } else { start(clip); } startRenderers(); release(); accept = true; } } catch (InterruptedException x) { Thread.currentThread().interrupt(); } catch (final UnsupportedFlavorException x) { x.printStackTrace(); } catch (final IOException x) { x.printStackTrace(); } catch (SAXException x) { x.printStackTrace(); } catch (ParserConfigurationException x) { x.printStackTrace(); } catch (XMLImportException x) { x.printStackTrace(); } catch (Exception x) { x.printStackTrace(); } break; } } if (accept) { e.dropComplete(true); paintBorder = false; fireChangeEvent(); refresh(); } else { e.rejectDrop(); } } } } /** * @see java.awt.dnd.DragGestureListener#dragGestureRecognized(java.awt.dnd.DragGestureEvent) */ public void dragGestureRecognized(final DragGestureEvent e) { if (isDragEnabled()) { try { source.startDrag(e, DragSource.DefaultCopyDrop, new TransferableTwisterConfig(config), this); paintBorder = true; isTarget = false; refresh(); } catch (final InvalidDnDOperationException x) { x.printStackTrace(); } } } /** * @see java.awt.dnd.DragSourceListener#dragEnter(java.awt.dnd.DragSourceDragEvent) */ public void dragEnter(final DragSourceDragEvent e) { } /** * @see java.awt.dnd.DragSourceListener#dragOver(java.awt.dnd.DragSourceDragEvent) */ public void dragOver(final DragSourceDragEvent e) { } /** * @see java.awt.dnd.DragSourceListener#dragExit(java.awt.dnd.DragSourceEvent) */ public void dragExit(final DragSourceEvent e) { } /** * @see java.awt.dnd.DragSourceListener#dropActionChanged(java.awt.dnd.DragSourceDragEvent) */ public void dropActionChanged(final DragSourceDragEvent e) { } /** * @see java.awt.dnd.DragSourceListener#dragDropEnd(java.awt.dnd.DragSourceDropEvent) */ public void dragDropEnd(final DragSourceDropEvent e) { paintBorder = false; isTarget = true; refresh(); } } private class BookmarkIcon { private final TwisterBookmark bookmark; public double x; public double y; public double w; public double h; public double tx; public double ty; public double tw; public double th; /** * @param bookmark */ public BookmarkIcon(final TwisterBookmark bookmark) { this.bookmark = bookmark; } /** * @return the bookmark */ public TwisterBookmark getBookmark() { return bookmark; } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object obj) { return bookmark.equals(((BookmarkIcon) obj).bookmark); } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return bookmark.hashCode(); } } private class DefaultRenderContext implements RenderContext { /** * @see net.sf.jame.core.util.RenderContext#startRenderers() */ public void startRenderers() { TwisterCanvas.this.startRenderers(); } /** * @see net.sf.jame.core.util.RenderContext#stopRenderers() */ public void stopRenderers() { TwisterCanvas.this.stopRenderers(); } /** * @see net.sf.jame.core.util.RenderContext#getImageSize() */ public IntegerVector2D getImageSize() { return size; } /** * @see net.sf.jame.core.util.RenderContext#refresh() */ public void refresh() { TwisterCanvas.this.refresh(); } /** * @see net.sf.jame.core.util.RenderContext#acquire() */ public void acquire() throws InterruptedException { TwisterCanvas.this.acquire(); } /** * @see net.sf.jame.core.util.RenderContext#release() */ public void release() { TwisterCanvas.this.release(); } /** * @see net.sf.jame.core.util.RenderContext#addRenderContextListener(net.sf.jame.core.util.RenderContextListener) */ public void addRenderContextListener(RenderContextListener listener) { TwisterCanvas.this.addRenderContextListener(listener); } /** * @see net.sf.jame.core.util.RenderContext#removeRenderContextListener(net.sf.jame.core.util.RenderContextListener) */ public void removeRenderContextListener(RenderContextListener listener) { TwisterCanvas.this.removeRenderContextListener(listener); } } private class TwisterMessage { private String message; private float x; private float y; private float size; private long time; private long expireTime; private Font font = messageFont; private int height; private boolean hasBackground; /** * @return */ public Font getFont(int height) { if (this.height != height) { float fontSize = ((size * height) / 100f); if (fontSize > 0) { this.height = height; font = messageFont.deriveFont((fontSize * 96f) / 72f); } } return font; } /** * @return */ public long getExpireTime() { return expireTime; } /** * @return the background */ public boolean hasBackground() { return hasBackground; } /** * @return the time */ public long getTime() { return time; } /** * @return the message */ public String getMessage() { return message; } /** * @return the x */ public float getX() { return x; } /** * @return the y */ public float getY() { return y; } /** * @return the size */ public float getSize() { return size; } /** * @param message * @param size * @param x * @param y * @param time * @param hasBackground */ public TwisterMessage(String message, float size, float x, float y, long time, boolean hasBackground) { this.message = message; this.size = size; this.x = x; this.y = y; this.time = time; this.hasBackground = hasBackground; expireTime = System.currentTimeMillis() + time; } } }