/* * A Swing Icon that draws an SVG image. * * Cameron McCormack * * Permission is hereby granted to use, copy, modify and distribte this * code for any purpose, without fee. * * Initial version: April 21, 2005 */ package test.svg; import java.awt.*; import java.awt.geom.Dimension2D; import java.awt.image.BufferedImage; import java.util.*; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.Icon; import org.apache.batik.bridge.InterruptedBridgeException; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.swing.gvt.GVTTreeRendererEvent; import org.apache.batik.swing.gvt.GVTTreeRendererListener; import org.apache.batik.transcoder.*; import org.apache.batik.transcoder.image.ImageTranscoder; import org.apache.batik.util.EventDispatcher; import org.apache.batik.util.EventDispatcher.Dispatcher; /** * A Swing Icon that draws an SVG image. * * @author Cameron McCormack * @authot Kirill Grouchnikov */ public class SvgBatikIcon extends UserAgentAdapter implements Icon { /** * The current image generated from the SVG document. */ protected BufferedImage bufferedImage; /** * Contains all precomputed images. */ protected Map cachedImages = new HashMap(); /** * The width of the rendered image. */ protected int width; /** * The height of the rendered image. */ protected int height; /** * The original URI. May be null. */ protected String uri; /** * Whether a render was requested. */ protected boolean needRender; /** * The listener. */ protected Listener listener; /** * The listeners. */ protected List listeners = Collections.synchronizedList(new LinkedList()); /** * The currently running renderer. */ protected Renderer renderer; protected static ExecutorService executorService = Executors .newFixedThreadPool(10); /** * Create a new SVG icon. * * @param uri * The URI to read the SVG document from. */ public SvgBatikIcon(String uri) { this(uri, 0, 0); } /** * Create a new SVG icon. * * @param uri * The URI to read the SVG document from. * @param w * The width of the icon. * @param h * The height of the icon. */ public SvgBatikIcon(String uri, int w, int h) { this.uri = uri; this.width = w; this.height = h; this.addGVTTreeRendererListener(new Listener()); this.scheduleGVTRendering(); } /** * Renderer thread. * * @author Kirill Grouchnikov */ protected class Renderer implements Runnable { /** * Input for the transcoder. */ TranscoderInput in; /** * Requested width. */ int width; /** * Requested height. */ int height; /** * Creates renderer. * * @param in * Input for the transcoder. * @param width * Requested width. * @param height * Requested height. */ public Renderer(TranscoderInput in, int width, int height) { super(); this.in = in; this.width = width; this.height = height; } public void run() { GVTTreeRendererEvent ev = new GVTTreeRendererEvent(this, null); try { // if (isHalted()) { // fireEvent(cancelledDispatcher, ev); // return; // } // ev = new GVTTreeRendererEvent(this, null); fireEvent(startedDispatcher, ev); // if (isHalted()) { // fireEvent(cancelledDispatcher, ev); // return; // } // BufferedImageTranscoder t = new BufferedImageTranscoder(); if (width != 0 && height != 0) { t.setDimensions(width, height); } // if (isHalted()) { // fireEvent(cancelledDispatcher, ev); // return; // } // t.transcode(in, null); // if (isHalted()) { // fireEvent(cancelledDispatcher, ev); // return; // } bufferedImage = t.getBufferedImage(); width = bufferedImage.getWidth(); height = bufferedImage.getHeight(); synchronized (SvgBatikIcon.this) { cachedImages.put(width + ":" + height, bufferedImage); } // if (isHalted()) { // fireEvent(cancelledDispatcher, ev); // return; // } // ev = new GVTTreeRendererEvent(this, bufferedImage); fireEvent(completedDispatcher, ev); } catch (TranscoderException te) { fireEvent(failedDispatcher, ev); } catch (InterruptedBridgeException e) { // this sometimes happens with SVG Fonts since the glyphs are // not built till the rendering stage fireEvent(cancelledDispatcher, ev); } catch (ThreadDeath td) { fireEvent(failedDispatcher, ev); throw td; } catch (Throwable t) { fireEvent(failedDispatcher, ev); } } } /** * A transcoder that generates a BufferedImage. */ protected class BufferedImageTranscoder extends ImageTranscoder { /** * The BufferedImage generated from the SVG document. */ protected BufferedImage bufferedImage; /** * Creates a new ARGB image with the specified dimension. * * @param width * the image width in pixels * @param height * the image height in pixels */ public BufferedImage createImage(int width, int height) { return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); } /** * Writes the specified image to the specified output. * * @param img * the image to write * @param output * the output where to store the image * @param TranscoderException * if an error occured while storing the image */ public void writeImage(BufferedImage img, TranscoderOutput output) throws TranscoderException { bufferedImage = img; } /** * Returns the BufferedImage generated from the SVG document. */ public BufferedImage getBufferedImage() { return bufferedImage; } /** * Set the dimensions to be used for the image. */ public void setDimensions(int w, int h) { hints.put(KEY_WIDTH, new Float(w)); hints.put(KEY_HEIGHT, new Float(h)); } } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return width; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return height; } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { if (bufferedImage != null) g.drawImage(bufferedImage, x, y, null); } // UserAgent ///////////////////////////////////////////////////////////// /** * Returns the default size of this user agent. */ public Dimension2D getViewportSize() { return new Dimension(width, height); } /** * Sets the preferred size for this icon. The rendering is * scheduled automatically. * * @param dim * Preferred size. */ public void setPreferredSize(Dimension dim) { this.width = dim.width; this.height = dim.height; this.scheduleGVTRendering(); } /** * Fires event. * * @param dispatcher * Event dispatcher. * @param event * Event data. */ public void fireEvent(Dispatcher dispatcher, Object event) { EventDispatcher.fireEvent(dispatcher, listeners, event, true); } /** * Schedules a new GVT rendering. */ protected void scheduleGVTRendering() { if (renderer != null) { needRender = true; // renderer.halt(); } else { renderGVTTree(); } } /** * Renders the GVT tree. */ protected synchronized void renderGVTTree() { String key = this.width + ":" + this.height; if (this.cachedImages.containsKey(key)) { this.bufferedImage = this.cachedImages.get(key); return; } // if (renderer != null) // renderer.halt(); // executorService.execute(new Renderer(new TranscoderInput(this.uri), this.width, this.height)); // // renderer = new Renderer(new TranscoderInput(this.uri), this.width, // this.height); // renderer.setPriority(Thread.MIN_PRIORITY); // // renderer.start(); } /** * To hide the listener methods. */ protected class Listener implements GVTTreeRendererListener { /** * Creates a new Listener. */ protected Listener() { } /** * Called when a rendering is in its preparing phase. */ public void gvtRenderingPrepare(GVTTreeRendererEvent e) { } /** * Called when a rendering started. */ public void gvtRenderingStarted(GVTTreeRendererEvent e) { } /** * Called when a rendering was completed. */ public void gvtRenderingCompleted(GVTTreeRendererEvent e) { renderer = null; if (needRender) { renderGVTTree(); needRender = false; } } /** * Called when a rendering was cancelled. */ public void gvtRenderingCancelled(GVTTreeRendererEvent e) { renderingStopped(); } /** * Called when a rendering failed. */ public void gvtRenderingFailed(GVTTreeRendererEvent e) { renderingStopped(); } /** * The actual implementation of gvtRenderingCancelled() and * gvtRenderingFailed(). */ private void renderingStopped() { renderer = null; if (needRender) { renderGVTTree(); needRender = false; } } } /** * Adds a GVTTreeRendererListener to this GVTTreeRenderer. */ public void addGVTTreeRendererListener(GVTTreeRendererListener l) { listeners.add(l); } /** * Removes a GVTTreeRendererListener from this GVTTreeRenderer. */ public void removeGVTTreeRendererListener(GVTTreeRendererListener l) { listeners.remove(l); } static Dispatcher completedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingCompleted((GVTTreeRendererEvent) event); } }; static Dispatcher startedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingStarted((GVTTreeRendererEvent) event); } }; static Dispatcher failedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingFailed((GVTTreeRendererEvent) event); } }; static Dispatcher cancelledDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingCancelled((GVTTreeRendererEvent) event); } }; }