View Javadoc

1   /*
2    * Copyright (c) 2005, the JUNG Project and the Regents of the University of
3    * California All rights reserved.
4    *
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or http://jung.sourceforge.net/license.txt for a description.
7    *
8    * Created on Jun 17, 2005
9    */
10  
11  package edu.uci.ics.jung.visualization;
12  
13  import java.awt.Graphics;
14  import java.awt.Graphics2D;
15  import java.awt.Image;
16  import java.awt.Shape;
17  import java.awt.geom.AffineTransform;
18  import java.awt.geom.Area;
19  import java.awt.geom.GeneralPath;
20  import java.awt.geom.Line2D;
21  import java.awt.geom.Point2D;
22  import java.awt.image.BufferedImage;
23  import java.io.IOException;
24  
25  import javax.imageio.ImageIO;
26  
27  /**
28   * Provides factory methods that, given a BufferedImage, an Image,
29   * or the fileName of an image, will return a java.awt.Shape that
30   * is the contiguous traced outline of the opaque part of the image.
31   * This could be used to define an image for use in a Vertex, where
32   * the shape used for picking and edge-arrow placement follows the
33   * opaque part of an image that has a transparent background.
34   * The methods try to detect lines in order to minimize points
35   * in the path
36   * 
37   * @author Tom Nelson
38   *
39   * 
40   */
41  public class FourPassImageShaper {
42      
43      /**
44       * given the fileName of an image, possibly with a transparent
45       * background, return the Shape of the opaque part of the image
46       * @param fileName name of the image, loaded from the classpath
47       * @return the Shape
48       */
49      public static Shape getShape(String fileName) {
50          return getShape(fileName, Integer.MAX_VALUE);
51      }
52      public static Shape getShape(String fileName, int max) {
53          BufferedImage image = null;
54          try {
55              image = ImageIO.read(FourPassImageShaper.class.getResource(fileName));
56          } catch(IOException ex) {
57              ex.printStackTrace();
58          }
59          return getShape(image, max);
60      }
61      
62      /**
63       * Given an image, possibly with a transparent background, return
64       * the Shape of the opaque part of the image
65       * @param image
66       * @return the Shape
67       */
68      public static Shape getShape(Image image) {
69          return getShape(image, Integer.MAX_VALUE);
70      }
71      public static Shape getShape(Image image, int max) {
72          BufferedImage bi = 
73              new BufferedImage(image.getWidth(null), image.getHeight(null), 
74                      BufferedImage.TYPE_INT_ARGB);
75          Graphics g = bi.createGraphics();
76          g.drawImage(image, 0, 0, null);
77          g.dispose();
78          return getShape(bi, max);
79      }
80      
81      /**
82       * Given an image, possibly with a transparent background, return
83       * the Shape of the opaque part of the image
84       * 
85       * If the image is larger than max in either direction, scale the
86       * image down to max-by-max, do the trace (on fewer points) then
87       * scale the resulting shape back up to the size of the original
88       * image.
89       * 
90       * @param image the image to trace
91       * @param max used to restrict number of points in the resulting shape
92       * @return the Shape
93       */
94      public static Shape getShape(BufferedImage image, int max) {
95          
96          float width = image.getWidth();
97          float height = image.getHeight();
98          if(width > max || height > max) {
99              BufferedImage smaller = 
100                 new BufferedImage(max, max, BufferedImage.TYPE_INT_ARGB);
101             Graphics g = smaller.createGraphics();
102             AffineTransform at = AffineTransform.getScaleInstance(max/width,max/height);
103             AffineTransform back = AffineTransform.getScaleInstance(width/max,height/max);
104             Graphics2D g2 = (Graphics2D)g;
105             g2.drawImage(image, at, null);
106             g2.dispose();
107             return back.createTransformedShape(getShape(smaller));
108         } else {
109             return getShape(image);
110         }
111     }
112     
113     public static Shape getShape(BufferedImage image) {
114         Area area = new Area(leftEdge(image));
115         area.intersect(new Area(bottomEdge(image)));
116         area.intersect(new Area(rightEdge(image)));
117         area.intersect(new Area(topEdge(image)));
118         return area;
119     }
120     /**
121      * Checks to see if point p is on a line that passes thru
122      * points p1 and p2. If p is on the line, extend the line
123      * segment so that it is from p1 to the location of p.
124      * If the point p is not on the line, update my shape
125      * with a line extending to the old p2 location, make
126      * the old p2 the new p1, and make p2 the old p
127      * @param p1
128      * @param p2
129      * @param p
130      * @param line
131      * @param path
132      * @return
133      */
134     private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, 
135             Line2D line, GeneralPath path) {
136         if(p2 == null) {
137             p2 = p;
138             line.setLine(p1,p2);
139         }
140         // check for line
141         else if(line.ptLineDistSq(p) < 1) { // its on the line
142             // make it p2
143             p2.setLocation(p);
144         } else { // its not on the current line
145             p1.setLocation(p2);
146             p2.setLocation(p);
147             line.setLine(p1,p2);
148             path.lineTo((float)p1.getX(), (float)p1.getY());
149         }
150         return p2;
151     }
152     /**
153      * trace the left side of the image
154      * @param image
155      * @param path
156      * @return
157      */
158     private static Shape leftEdge(BufferedImage image) {
159         GeneralPath path = new GeneralPath();
160         Point2D p1 = new Point2D.Float(image.getWidth()-1, 0);
161         Point2D p2 = null;
162         Line2D line = new Line2D.Float();
163         Point2D p = new Point2D.Float();
164         path.moveTo(image.getWidth()-1, 0);
165         
166         for(int i=0; i<image.getHeight(); i++) {
167             p.setLocation(image.getWidth()-1, i);
168             // go until we reach an opaque point, then stop
169             for(int j=0; j<image.getWidth(); j++) {
170                 if((image.getRGB(j,i) & 0xff000000) != 0) {
171                     // this is a point I want
172                     p.setLocation(j,i);
173                     break;
174                 }
175             }
176             p2 = detectLine(p1, p2, p, line, path);
177         }
178         p.setLocation(image.getWidth()-1, image.getHeight()-1);
179         detectLine(p1, p2, p, line, path);
180         path.closePath();
181         return path;
182     }
183     
184     /**
185      * trace the bottom of the image
186      * @param image
187      * @param path
188      * @param start
189      * @return
190      */
191     private static Shape bottomEdge(BufferedImage image) {
192         GeneralPath path = new GeneralPath();
193         Point2D p1 = new Point2D.Float(0, 0);
194         Point2D p2 = null;
195         Line2D line = new Line2D.Float();
196         Point2D p = new Point2D.Float();
197         path.moveTo(0, 0);
198         for(int i=0; i<image.getWidth(); i++) {
199             p.setLocation(i, 0);
200             for(int j=image.getHeight()-1; j>=0; j--) {
201                 if((image.getRGB(i,j) & 0xff000000) != 0) {
202                     // this is a point I want
203                     p.setLocation(i,j);
204                     break;
205                 }
206             }
207             p2 = detectLine(p1, p2, p, line, path);
208         }
209         p.setLocation(image.getWidth()-1, 0);
210         detectLine(p1, p2, p, line, path);
211         path.closePath();
212         return path;
213     }
214     
215     /**
216      * trace the right side of the image
217      * @param image
218      * @param path
219      * @param start
220      * @return
221      */
222     private static Shape rightEdge(BufferedImage image) {
223         GeneralPath path = new GeneralPath();
224         Point2D p1 = new Point2D.Float(0, image.getHeight()-1);
225         Point2D p2 = null;
226         Line2D line = new Line2D.Float();
227         Point2D p = new Point2D.Float();
228         path.moveTo(0, image.getHeight()-1);
229         
230         for(int i=image.getHeight()-1; i>=0; i--) {
231             p.setLocation(0, i);
232             for(int j=image.getWidth()-1; j>=0; j--) {
233                 if((image.getRGB(j,i) & 0xff000000) != 0) {
234                     // this is a point I want
235                     p.setLocation(j,i);
236                     break;
237                 }
238             }
239             p2 = detectLine(p1, p2, p, line, path);
240         }
241         p.setLocation(0, 0);
242         detectLine(p1, p2, p,line, path);
243         path.closePath();
244         return path;
245     }
246     
247     /**
248      * trace the top of the image
249      * @param image
250      * @param path
251      * @param start
252      * @return
253      */
254     private static Shape topEdge(BufferedImage image) {
255         GeneralPath path = new GeneralPath();
256         Point2D p1 = new Point2D.Float(image.getWidth()-1, image.getHeight()-1);
257         Point2D p2 = null;
258         Line2D line = new Line2D.Float();
259         Point2D p = new Point2D.Float();
260         path.moveTo(image.getWidth()-1, image.getHeight()-1);
261         
262         for(int i=image.getWidth()-1; i>=0; i--) {
263             p.setLocation(i, image.getHeight()-1);
264             for(int j=0; j<image.getHeight(); j++) {
265                 if((image.getRGB(i,j) & 0xff000000) != 0) {
266                     // this is a point I want
267                     p.setLocation(i,j);
268                     break;
269                 }
270             }
271             p2 = detectLine(p1, p2, p, line, path);
272         }
273         p.setLocation(0, image.getHeight()-1);
274         detectLine(p1, p2, p, line, path);
275         path.closePath();
276         return path;
277     }
278 }