001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.renderer; 031 032import java.awt.Color; 033import java.awt.Dimension; 034import java.awt.Graphics; 035import java.awt.geom.Area; 036import java.awt.geom.Path2D; 037import java.awt.image.BufferedImage; 038import java.awt.image.ImageObserver; 039import java.io.Writer; 040import java.util.ArrayList; 041import java.util.List; 042 043import org.apache.batik.dom.GenericDOMImplementation; 044import org.apache.batik.svggen.SVGGeneratorContext; 045import org.apache.batik.svggen.SVGGraphics2D; 046import org.apache.batik.svggen.SVGGraphics2DIOException; 047import org.openimaj.image.FImage; 048import org.openimaj.image.Image; 049import org.openimaj.image.ImageUtilities; 050import org.openimaj.image.SVGImage; 051import org.openimaj.image.colour.RGBColour; 052import org.openimaj.math.geometry.point.Point2d; 053import org.openimaj.math.geometry.point.Point2dImpl; 054import org.openimaj.math.geometry.shape.Polygon; 055import org.openimaj.math.geometry.shape.Shape; 056import org.w3c.dom.DOMImplementation; 057import org.w3c.dom.Document; 058import org.w3c.dom.Element; 059 060/** 061 * {@link ImageRenderer} for {@link FImage} images. Supports both anti-aliased 062 * and fast rendering. 063 * 064 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 065 * 066 */ 067public class SVGRenderer extends ImageRenderer<Float[], SVGImage> { 068 069 private SVGGraphics2D svgGen; 070 071 /** 072 * Construct with given target image. 073 * 074 * @param targetImage 075 * the target image. 076 */ 077 public SVGRenderer(final SVGImage targetImage) { 078 super(targetImage); 079 prepareSVG(); 080 } 081 082 /** 083 * Construct with given target image and rendering hints. 084 * 085 * @param targetImage 086 * the target image. 087 * @param hints 088 * the render hints 089 */ 090 public SVGRenderer(final SVGImage targetImage, final SVGRenderHints hints) { 091 super(targetImage, hints); 092 prepareSVG(); 093 } 094 095 /** 096 * Construct with given target image and rendering hints. 097 * 098 * @param hints 099 * the render hints 100 */ 101 public SVGRenderer(final SVGRenderHints hints) { 102 super(null, hints); 103 prepareSVG(); 104 } 105 106 /** 107 * @param img 108 * @param create 109 */ 110 public SVGRenderer(SVGImage img, Graphics create) { 111 super(img); 112 this.svgGen = (SVGGraphics2D) create; 113 } 114 115 public SVGRenderer(SVGImage img, RenderHints renderHints, Graphics create) { 116 super(img, renderHints); 117 this.svgGen = (SVGGraphics2D) create; 118 } 119 120 private void prepareSVG() { 121 final DOMImplementation impl = GenericDOMImplementation.getDOMImplementation(); 122 final String svgNS = "http://www.w3.org/2000/svg"; 123 final Document doc = impl.createDocument(svgNS, "svg", null); 124 // Create an instance of the SVG Generator 125 final SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(doc); 126 127 // Reuse our embedded base64-encoded image data. 128 // GenericImageHandler ihandler = new CachedImageHandlerBase64Encoder(); 129 // ctx.setGenericImageHandler(ihandler); 130 this.svgGen = new SVGGraphics2D(ctx, false); 131 132 if (this.targetImage != null) { 133 final int w = targetImage.getWidth(); 134 final int h = targetImage.getHeight(); 135 this.svgGen.setSVGCanvasSize(new Dimension(w, h)); 136 } else if (this.hints != null && hints instanceof SVGRenderHints) { 137 final int w = ((SVGRenderHints) hints).width; 138 final int h = ((SVGRenderHints) hints).height; 139 this.svgGen.setSVGCanvasSize(new Dimension(w, h)); 140 } 141 142 } 143 144 @Override 145 public void drawLine(int x1, int y1, double theta, int length, int thickness, Float[] col) { 146 final int x2 = x1 + (int) Math.round(Math.cos(theta) * length); 147 final int y2 = y1 + (int) Math.round(Math.sin(theta) * length); 148 149 this.drawLine(x1, y1, x2, y2, thickness, col); 150 } 151 152 @Override 153 public void drawLine(int x0, int y0, int x1, int y1, int thickness, Float[] col) { 154 if (thickness <= 1) { 155 this.svgGen.setColor(colorFromFloats(col)); 156 this.svgGen.drawLine(x0, y0, x1, y1); 157 } else { 158 final double theta = Math.atan2(y1 - y0, x1 - x0); 159 final double t = thickness / 2; 160 final double sin = t * Math.sin(theta); 161 final double cos = t * Math.cos(theta); 162 163 final Polygon p = new Polygon(); 164 p.addVertex(new Point2dImpl((float) (x0 - sin), (float) (y0 + cos))); 165 p.addVertex(new Point2dImpl((float) (x0 + sin), (float) (y0 - cos))); 166 p.addVertex(new Point2dImpl((float) (x1 + sin), (float) (y1 - cos))); 167 p.addVertex(new Point2dImpl((float) (x1 - sin), (float) (y1 + cos))); 168 169 this.drawPolygonFilled(p, col); 170 } 171 } 172 173 private Color colorFromFloats(Float[] col) { 174 final Color ret = new Color(col[0], col[1], col[2]); 175 return ret; 176 } 177 178 @Override 179 public void drawLine(float x0, float y0, float x1, float y1, int thickness, Float[] col) { 180 this.drawLine((int) x0, (int) y0, (int) x1, (int) y1, thickness, col); 181 } 182 183 @Override 184 public void drawPoint(Point2d p, Float[] col, int size) { 185 this.svgGen.setColor(colorFromFloats(col)); 186 final Path2D.Double path = new Path2D.Double(); 187 path.moveTo(p.getX(), p.getY()); 188 path.lineTo(p.getX(), p.getY()); 189 this.svgGen.draw(path); 190 } 191 192 @Override 193 public void drawPolygon(Polygon p, int thickness, Float[] col) { 194 this.svgGen.setColor(colorFromFloats(col)); 195 final List<Area> a = jPolyFromPolygon(p); 196 for (final Area polygon : a) { 197 this.svgGen.draw(polygon); 198 } 199 200 } 201 202 @Override 203 public void drawPolygonFilled(Polygon p, Float[] col) { 204 this.svgGen.setColor(colorFromFloats(col)); 205 final List<Area> a = jPolyFromPolygon(p); 206 for (final Area polygon : a) { 207 this.svgGen.fill(polygon); 208 } 209 } 210 211 @Override 212 public void drawShapeFilled(Shape s, Float[] col) { 213 super.drawPolygonFilled(s.asPolygon(), col); 214 } 215 216 private List<java.awt.geom.Area> jPolyFromPolygon(Polygon p) { 217 final List<java.awt.geom.Area> ret = new ArrayList<java.awt.geom.Area>(); 218 final Path2D path = new Path2D.Double(); 219 boolean start = true; 220 for (final Point2d p2d : p.getVertices()) { 221 if (start) { 222 path.moveTo(p2d.getX(), p2d.getY()); 223 start = false; 224 } 225 else { 226 path.lineTo(p2d.getX(), p2d.getY()); 227 } 228 229 } 230 // final java.awt.geom.Area pp = new java.awt.geom.Area(); 231 ret.add(new java.awt.geom.Area(path)); 232 return ret; 233 } 234 235 @Override 236 protected void drawHorizLine(int x1, int x2, int y, Float[] col) { 237 this.drawLine(x1, y, x2, y, 1, col); 238 } 239 240 @Override 241 public Float[] defaultForegroundColour() { 242 return RGBColour.BLACK; 243 } 244 245 @Override 246 public Float[] defaultBackgroundColour() { 247 return RGBColour.WHITE; 248 } 249 250 @Override 251 protected Float[] sanitise(Float[] colour) { 252 return colour; 253 } 254 255 /** 256 * @param out 257 * @throws SVGGraphics2DIOException 258 */ 259 public void write(Writer out) throws SVGGraphics2DIOException { 260 this.svgGen.stream(out, true); 261 } 262 263 public void drawOIImage(Image<?, ?> im) { 264 final BufferedImage createBufferedImage = ImageUtilities.createBufferedImage(im); 265 this.svgGen.drawImage(createBufferedImage, 0, 0, this.colorFromFloats(this.defaultBackgroundColour()), 266 new ImageObserver() { 267 268 @Override 269 public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height) { 270 return true; 271 } 272 }); 273 } 274 275 @Override 276 public SVGRenderer clone() { 277 final SVGRenderer ret = new SVGRenderer((SVGRenderHints) this.getRenderHints()); 278 return ret; 279 } 280 281 @Override 282 public void drawImage(SVGImage image, int x, int y) { 283 // TODO: fix this... 284 // throw new UnsupportedOperationException(); 285 // Element root = this.svgGen.getRoot(); 286 // System.out.println(root); 287 // Node node = 288 // this.svgGen.getDOMFactory().importNode(image.createRenderer().svgGen.getRoot(), 289 // true); 290 // image.createRenderer().svgGen.getRoot((Element) node); 291 // root.appendChild(node); 292 } 293 294 public SVGGraphics2D getGraphics2D() { 295 return this.svgGen; 296 } 297 298 public Document getDocument() { 299 final Element root = svgGen.getRoot(); 300 // 301 // Enforce that the default and xlink namespace 302 // declarations appear on the root element 303 // 304 305 // final Document doc = null; 306 try { 307 // 308 // Enforce that the default and xlink namespace 309 // declarations appear on the root element 310 // 311 root.setAttributeNS(SVGGraphics2D.XMLNS_NAMESPACE_URI, 312 SVGGraphics2D.XMLNS_PREFIX, 313 SVGGraphics2D.SVG_NAMESPACE_URI); 314 315 root.setAttributeNS(SVGGraphics2D.XMLNS_NAMESPACE_URI, 316 SVGGraphics2D.XMLNS_PREFIX + ":" + SVGGraphics2D.XLINK_PREFIX, 317 SVGGraphics2D.XLINK_NAMESPACE_URI); 318 // DOMImplementation impl = 319 // GenericDOMImplementation.getDOMImplementation(); 320 // String svgNS = "http://www.w3.org/2000/svg"; 321 // doc = impl.createDocument(svgNS, "svg", null); 322 // doc.appendChild(root); 323 } catch (final Exception e) { 324 325 } 326 return root.getOwnerDocument(); 327 328 } 329}