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.GeneralPath;
19  import java.awt.geom.Line2D;
20  import java.awt.geom.Point2D;
21  import java.awt.image.BufferedImage;
22  import java.io.IOException;
23  
24  import javax.imageio.ImageIO;
25  
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 PivotingImageShaper {
42      
43      /**
44       * the number of pixels to skip while sampling the
45       * images edges
46       */
47      static int sample = 1;
48      /**
49       * the first x coordinate of the shape. Used to discern
50       * when we are done 
51       */
52      static int firstx = 0;
53      
54      public static Shape getShape(String fileName) {
55          return getShape(fileName, Integer.MAX_VALUE);
56      }
57      public static Shape getShape(String fileName, int max) {
58          BufferedImage image = null;
59          try {
60              image = ImageIO.read(FourPassImageShaper.class.getResource(fileName));
61          } catch(IOException ex) {
62              ex.printStackTrace();
63          }
64          return getShape(image, max);
65      }
66      
67      /**
68       * Given an image, possibly with a transparent background, return
69       * the Shape of the opaque part of the image
70       * @param image
71       * @return the Shape
72       */
73      public static Shape getShape(Image image) {
74          return getShape(image, Integer.MAX_VALUE);
75      }
76      public static Shape getShape(Image image, int max) {
77          BufferedImage bi = 
78              new BufferedImage(image.getWidth(null), image.getHeight(null), 
79                      BufferedImage.TYPE_INT_ARGB);
80          Graphics g = bi.createGraphics();
81          g.drawImage(image, 0, 0, null);
82          g.dispose();
83          return getShape(bi, max);
84      }
85      
86      /**
87       * Given an image, possibly with a transparent background, return
88       * the Shape of the opaque part of the image
89       * @param image
90       * @return the Shape
91       */
92      public static Shape getShape(BufferedImage image, int max) {
93          
94          float width = image.getWidth();
95          float height = image.getHeight();
96          if(width > max || height > max) {
97              BufferedImage smaller = 
98                  new BufferedImage(max, max, BufferedImage.TYPE_INT_ARGB);
99              Graphics g = smaller.createGraphics();
100             AffineTransform at = AffineTransform.getScaleInstance(max/width,max/height);
101             AffineTransform back = AffineTransform.getScaleInstance(width/max,height/max);
102             Graphics2D g2 = (Graphics2D)g;
103             g2.drawImage(image, at, null);
104             g2.dispose();
105             return back.createTransformedShape(getShape(smaller));
106         } else {
107             return getShape(image);
108         }
109     }
110     
111     /**
112      * Given an image, possibly with a transparent background, return
113      * the Shape of the opaque part of the image
114      * @param image
115      * @return the Shape
116      */
117     public static Shape getShape(BufferedImage image) {
118         firstx = 0;
119         return leftEdge(image, new GeneralPath());
120     }
121     
122     private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, 
123             Line2D line, GeneralPath path) {
124         if(p2 == null) {
125             p2 = p;
126             line.setLine(p1,p2);
127         }
128         // check for line
129         else if(line.ptLineDistSq(p) < 1) { // its on the line
130             // make it p2
131             p2.setLocation(p);
132         } else { // its not on the current line
133             p1.setLocation(p2);
134             p2.setLocation(p);
135             line.setLine(p1,p2);
136             path.lineTo((float)p1.getX(), (float)p1.getY());
137         }
138         return p2;
139     }
140     /**
141      * trace the left side of the image
142      * @param image
143      * @param path
144      * @return
145      */
146     private static Shape leftEdge(BufferedImage image, GeneralPath path) {
147         int lastj = 0;
148         Point2D p1 = null;
149         Point2D p2 = null;
150         Line2D line = new Line2D.Float();
151         for(int i=0; i<image.getHeight(); i+=sample) {
152             boolean aPointExistsOnThisLine = false;
153             // go until we reach an opaque point, then stop
154             for(int j=0; j<image.getWidth(); j+=sample) {
155                 if((image.getRGB(j,i) & 0xff000000) != 0) {
156                     // this is a point I want
157                     Point2D p = new Point2D.Float(j,i);
158                     aPointExistsOnThisLine = true;
159                     if(path.getCurrentPoint() != null) {
160                         // this is a continuation of a path
161                         p2 = detectLine(p1,p2,p,line,path);
162                     } else {
163                         // this is the first point in the path
164                         path.moveTo(j,i);
165                         firstx = j;
166                         p1 = p;
167                     }
168                     lastj = j;
169                     break;
170                 }
171             }
172             if(aPointExistsOnThisLine == false) {
173                 break;
174             }
175         }
176         return bottomEdge(image, path, lastj);
177     }
178     
179     /**
180      * trace the bottom of the image
181      * @param image
182      * @param path
183      * @param start
184      * @return
185      */
186     private static Shape bottomEdge(BufferedImage image, GeneralPath path, int start) {
187         int lastj = 0;
188         Point2D p1 = path.getCurrentPoint();
189         Point2D p2 = null;
190         Line2D line = new Line2D.Float();
191         for(int i=start; i<image.getWidth(); i+=sample) {
192             boolean aPointExistsOnThisLine = false;
193             for(int j=image.getHeight()-1; j>=0; j-=sample) {
194                 if((image.getRGB(i,j) & 0xff000000) != 0) {
195                     // this is a point I want
196                     Point2D p = new Point2D.Float(i,j);
197                     aPointExistsOnThisLine = true;
198                     p2 = detectLine(p1,p2,p,line,path);
199                     lastj = j;
200                     break;
201                 }
202             }
203             if(aPointExistsOnThisLine == false) {
204                 break;
205             }
206         }
207         return rightEdge(image, path, lastj);
208     }
209     
210     /**
211      * trace the right side of the image
212      * @param image
213      * @param path
214      * @param start
215      * @return
216      */
217     private static Shape rightEdge(BufferedImage image, GeneralPath path, int start) {
218         int lastj = 0;
219         Point2D p1 = path.getCurrentPoint();
220         Point2D p2 = null;
221         Line2D line = new Line2D.Float();
222         for(int i=start; i>=0; i-=sample) {
223             boolean aPointExistsOnThisLine = false;
224 
225             for(int j=image.getWidth()-1; j>=0; j-=sample) {
226                 if((image.getRGB(j,i) & 0xff000000) != 0) {
227                     // this is a point I want
228                     Point2D p = new Point2D.Float(j,i);
229                     aPointExistsOnThisLine = true;
230                     p2 = detectLine(p1,p2,p,line,path);
231                     lastj=j;
232                     break;
233                 }
234             }
235             if(aPointExistsOnThisLine == false) {
236                 break;
237             }
238         }
239         return topEdge(image, path, lastj);
240     }
241     
242     /**
243      * trace the top of the image
244      * @param image
245      * @param path
246      * @param start
247      * @return
248      */
249     private static Shape topEdge(BufferedImage image, GeneralPath path, int start) {
250         Point2D p1 = path.getCurrentPoint();
251         Point2D p2 = null;
252         Line2D line = new Line2D.Float();
253         for(int i=start; i>=firstx; i-=sample) {
254             boolean aPointExistsOnThisLine = false;
255             for(int j=0; j<image.getHeight(); j+=sample) {
256                 if((image.getRGB(i,j) & 0xff000000) != 0) {
257                     // this is a point I want
258                     Point2D p = new Point2D.Float(i,j);
259                     aPointExistsOnThisLine = true;
260                     p2 = detectLine(p1,p2,p,line,path);
261                     break;
262                 }
263             }
264             if(aPointExistsOnThisLine == false) {
265                 break;
266             }
267         }
268         path.closePath();
269         return path;
270     }
271 }