/* * Copyright (c) 2005, the JUNG Project and the Regents of the University of * California All rights reserved. * * This software is open-source under the BSD license; see either "license.txt" * or http://jung.sourceforge.net/license.txt for a description. * * Created on Jun 17, 2005 */ package edu.uci.ics.jung.visualization; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; /** * Provides factory methods that, given a BufferedImage, an Image, * or the fileName of an image, will return a java.awt.Shape that * is the contiguous traced outline of the opaque part of the image. * This could be used to define an image for use in a Vertex, where * the shape used for picking and edge-arrow placement follows the * opaque part of an image that has a transparent background. * The methods try to detect lines in order to minimize points * in the path * * @author Tom Nelson * * */ public class PivotingImageShaper { /** * the number of pixels to skip while sampling the * images edges */ static int sample = 1; /** * the first x coordinate of the shape. Used to discern * when we are done */ static int firstx = 0; public static Shape getShape(String fileName) { return getShape(fileName, Integer.MAX_VALUE); } public static Shape getShape(String fileName, int max) { BufferedImage image = null; try { image = ImageIO.read(FourPassImageShaper.class.getResource(fileName)); } catch(IOException ex) { ex.printStackTrace(); } return getShape(image, max); } /** * Given an image, possibly with a transparent background, return * the Shape of the opaque part of the image * @param image * @return the Shape */ public static Shape getShape(Image image) { return getShape(image, Integer.MAX_VALUE); } public static Shape getShape(Image image, int max) { BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics g = bi.createGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); return getShape(bi, max); } /** * Given an image, possibly with a transparent background, return * the Shape of the opaque part of the image * @param image * @return the Shape */ public static Shape getShape(BufferedImage image, int max) { float width = image.getWidth(); float height = image.getHeight(); if(width > max || height > max) { BufferedImage smaller = new BufferedImage(max, max, BufferedImage.TYPE_INT_ARGB); Graphics g = smaller.createGraphics(); AffineTransform at = AffineTransform.getScaleInstance(max/width,max/height); AffineTransform back = AffineTransform.getScaleInstance(width/max,height/max); Graphics2D g2 = (Graphics2D)g; g2.drawImage(image, at, null); g2.dispose(); return back.createTransformedShape(getShape(smaller)); } else { return getShape(image); } } /** * Given an image, possibly with a transparent background, return * the Shape of the opaque part of the image * @param image * @return the Shape */ public static Shape getShape(BufferedImage image) { firstx = 0; return leftEdge(image, new GeneralPath()); } private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, Line2D line, GeneralPath path) { if(p2 == null) { p2 = p; line.setLine(p1,p2); } // check for line else if(line.ptLineDistSq(p) < 1) { // its on the line // make it p2 p2.setLocation(p); } else { // its not on the current line p1.setLocation(p2); p2.setLocation(p); line.setLine(p1,p2); path.lineTo((float)p1.getX(), (float)p1.getY()); } return p2; } /** * trace the left side of the image * @param image * @param path * @return */ private static Shape leftEdge(BufferedImage image, GeneralPath path) { int lastj = 0; Point2D p1 = null; Point2D p2 = null; Line2D line = new Line2D.Float(); for(int i=0; i<image.getHeight(); i+=sample) { boolean aPointExistsOnThisLine = false; // go until we reach an opaque point, then stop for(int j=0; j<image.getWidth(); j+=sample) { if((image.getRGB(j,i) & 0xff000000) != 0) { // this is a point I want Point2D p = new Point2D.Float(j,i); aPointExistsOnThisLine = true; if(path.getCurrentPoint() != null) { // this is a continuation of a path p2 = detectLine(p1,p2,p,line,path); } else { // this is the first point in the path path.moveTo(j,i); firstx = j; p1 = p; } lastj = j; break; } } if(aPointExistsOnThisLine == false) { break; } } return bottomEdge(image, path, lastj); } /** * trace the bottom of the image * @param image * @param path * @param start * @return */ private static Shape bottomEdge(BufferedImage image, GeneralPath path, int start) { int lastj = 0; Point2D p1 = path.getCurrentPoint(); Point2D p2 = null; Line2D line = new Line2D.Float(); for(int i=start; i<image.getWidth(); i+=sample) { boolean aPointExistsOnThisLine = false; for(int j=image.getHeight()-1; j>=0; j-=sample) { if((image.getRGB(i,j) & 0xff000000) != 0) { // this is a point I want Point2D p = new Point2D.Float(i,j); aPointExistsOnThisLine = true; p2 = detectLine(p1,p2,p,line,path); lastj = j; break; } } if(aPointExistsOnThisLine == false) { break; } } return rightEdge(image, path, lastj); } /** * trace the right side of the image * @param image * @param path * @param start * @return */ private static Shape rightEdge(BufferedImage image, GeneralPath path, int start) { int lastj = 0; Point2D p1 = path.getCurrentPoint(); Point2D p2 = null; Line2D line = new Line2D.Float(); for(int i=start; i>=0; i-=sample) { boolean aPointExistsOnThisLine = false; for(int j=image.getWidth()-1; j>=0; j-=sample) { if((image.getRGB(j,i) & 0xff000000) != 0) { // this is a point I want Point2D p = new Point2D.Float(j,i); aPointExistsOnThisLine = true; p2 = detectLine(p1,p2,p,line,path); lastj=j; break; } } if(aPointExistsOnThisLine == false) { break; } } return topEdge(image, path, lastj); } /** * trace the top of the image * @param image * @param path * @param start * @return */ private static Shape topEdge(BufferedImage image, GeneralPath path, int start) { Point2D p1 = path.getCurrentPoint(); Point2D p2 = null; Line2D line = new Line2D.Float(); for(int i=start; i>=firstx; i-=sample) { boolean aPointExistsOnThisLine = false; for(int j=0; j<image.getHeight(); j+=sample) { if((image.getRGB(i,j) & 0xff000000) != 0) { // this is a point I want Point2D p = new Point2D.Float(i,j); aPointExistsOnThisLine = true; p2 = detectLine(p1,p2,p,line,path); break; } } if(aPointExistsOnThisLine == false) { break; } } path.closePath(); return path; } }