From 9c81a473fe8a8d086cd9efd81c9fca5edcdff3ed Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Sat, 9 Jun 2018 20:53:05 +0200 Subject: [PATCH] Improve image positioning (correctly centered and scaled for all rendering targets) and remove processing dependency --- gephi-plugin-image-preview | 1 - .../pom.xml | 65 ++++++------- .../gephi/imagepreview/ImageItem.java | 84 +++++++---------- .../gephi/imagepreview/ImageNodes.java | 91 +++++++++++-------- .../imagepreview/NodeImageItemBuilder.java | 68 ++++++++------ .../src/main/nbm/manifest.mf | 0 .../gephi/imagepreview/Bundle.properties | 0 pom.xml | 2 +- 8 files changed, 155 insertions(+), 156 deletions(-) delete mode 160000 gephi-plugin-image-preview rename modules/{gephi-plugin-image-preview => image-preview}/pom.xml (56%) rename modules/{gephi-plugin-image-preview => image-preview}/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java (51%) rename modules/{gephi-plugin-image-preview => image-preview}/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java (75%) rename modules/{gephi-plugin-image-preview => image-preview}/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java (51%) rename modules/{gephi-plugin-image-preview => image-preview}/src/main/nbm/manifest.mf (100%) rename modules/{gephi-plugin-image-preview => image-preview}/src/main/resources/org/yale/cs/graphics/gephi/imagepreview/Bundle.properties (100%) diff --git a/gephi-plugin-image-preview b/gephi-plugin-image-preview deleted file mode 160000 index e355bc57e5..0000000000 --- a/gephi-plugin-image-preview +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e355bc57e5553e20134d0638b6b975e9720c953d diff --git a/modules/gephi-plugin-image-preview/pom.xml b/modules/image-preview/pom.xml similarity index 56% rename from modules/gephi-plugin-image-preview/pom.xml rename to modules/image-preview/pom.xml index bc3bc931e9..c41c64874e 100644 --- a/modules/gephi-plugin-image-preview/pom.xml +++ b/modules/image-preview/pom.xml @@ -16,57 +16,45 @@ - - org.gephi - preview-plugin - - - org.gephi - core-library-wrapper - - - org.netbeans.api - org-openide-util-lookup - ${netbeans.version} - - - org.gephi - graph-api - - - org.gephi - preview-api - - - org.netbeans.api - org-openide-util - ${netbeans.version} - - - - org.processing - core - 3.3.7 - + + org.gephi + preview-plugin + + + org.gephi + core-library-wrapper + + + org.netbeans.api + org-openide-util-lookup + + + org.gephi + graph-api + + + org.gephi + preview-api + + + org.netbeans.api + org-openide-util + org.netbeans.api org-openide-windows - ${netbeans.version} org.netbeans.api org-openide-util-ui - ${netbeans.version} org.netbeans.api org-openide-awt - ${netbeans.version} org.netbeans.api org-netbeans-modules-settings - ${netbeans.version} @@ -77,11 +65,10 @@ nbm-maven-plugin Yitzchak Lockerman (Yale Computer Graphics Group) - $sourcecode_url + https://github.com/totetmatt/gephi-plugins/tree/image-preview Apache 2.0 - - org.yale.cs.graphics.gephi.imagepreview + org.yale.cs.graphics.gephi.imagepreview diff --git a/modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java b/modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java similarity index 51% rename from modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java rename to modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java index 36de70868c..589054fcaa 100644 --- a/modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java +++ b/modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageItem.java @@ -25,7 +25,6 @@ package org.yale.cs.graphics.gephi.imagepreview; import com.itextpdf.text.BadElementException; -import com.itextpdf.text.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -33,20 +32,16 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; -import org.gephi.preview.api.G2DTarget; import org.gephi.preview.plugin.items.AbstractItem; -import processing.core.PConstants; -import processing.core.PImage; +import org.gephi.preview.plugin.items.NodeItem; /** * A Item that represents a image within the file system. *

- * Each instance of ImageItem represents a single image file. When - * the file is loaded (using one of the methods) the image data is cached in - * memory, allowing for faster rendering in the future. + * Each instance of ImageItem represents a single image file. When the file is loaded (using one of the methods) the image data is cached in memory, allowing for faster rendering in the + * future. *

- * In addition to the cached images, the item contains all the properties of - * {@link NodeItem}. + * In addition to the cached images, the item contains all the properties of {@link NodeItem}. * * @author Yitzchak Lockerman */ @@ -58,16 +53,16 @@ public class ImageItem extends AbstractItem { public static final String IMAGE = "Image"; /** - * The data key for accessing the Processing image file within the cache. + * The data key for accessing the image file within the cache. */ - public static final String PROCESSING_DATA = "Processing_Data"; + public static final String IMAGE_DATA = "Image_Data"; /** * The data key for accessing the iText PDF image file within the cache. */ public static final String PDF_DATA = "PDF_Data"; - private static final Logger logger = Logger.getLogger(ImageItem.class.getName()); + private static final Logger LOG = Logger.getLogger(ImageItem.class.getName()); /** * @@ -78,85 +73,70 @@ public ImageItem(String source) { } /** - * Prepares this ImageItem to be rendered by the Processing target. Loading - * from the cache, if available. If the method is forced to load a image - * from the hard drive, it store it in the cache. + * Prepares this ImageItem to be rendered by the grapchis target. Loading from the cache, if available. If the method is forced to load a image from the hard drive, it store it in the cache. * - * @param location_name The name of the folder to load images from. - * @param target The properties of the current rendering. - * @return A Processing image corresponding to this item. + * @param directory The name of the folder to load images from. + * @return A image corresponding to this item. */ - public PImage renderProcessing(File location_name, G2DTarget target) { - PImage image = (PImage) data.get(PROCESSING_DATA); + public BufferedImage loadImage(File directory) { + BufferedImage image = (BufferedImage) data.get(IMAGE_DATA); if (image == null) { if (source instanceof String) { - File full_file = new File(location_name, (String) source); - try { - //if(target.getApplet() != null) - // image = target.getApplet().loadImage(full_file.getCanonicalPath()); - // else - //based on http://processing.org/discourse/beta/num_1234546778.html - //http://forum.processing.org/topic/converting-bufferedimage-to-pimage - BufferedImage im_plane; - - im_plane = ImageIO.read(full_file); - image = new PImage(im_plane.getWidth(), im_plane.getHeight(), PConstants.ARGB); - im_plane.getRGB(0, 0, image.width, image.height, image.pixels, 0, image.width); - image.updatePixels(); + final File file = new File(directory, (String) source); + try { + image = ImageIO.read(file); } catch (java.io.IOException e) { - logger.log(Level.SEVERE, "Unable to load image: " + full_file, e); + LOG.log(Level.SEVERE, "Unable to load image: " + file, e); } } //If we can't render the image if (image == null) { - logger.log(Level.WARNING, "Unable to load image: {0}", source); + LOG.log(Level.WARNING, "Unable to load image: {0}", source); return null; } - data.put(PROCESSING_DATA, image); + data.put(IMAGE_DATA, image); } + return image; } /** - * @param location_name The name of the folder to load images from. - * @return The attribute to be added to the SVG file to represent this - * image. + * @param directory The name of the folder to load images from. + * @return The attribute to be added to the SVG file to represent this image. */ - public String renderSVG(File location_name) { - return new File(location_name, (String) source).getAbsolutePath(); + public String renderSVG(File directory) { + return new File(directory, (String) source).getAbsolutePath(); } /** - * Prepares this ImageItem to be rendered by the PDF target. Loading from - * the cache, if available. If the method is forced to load a image from the - * hard drive, it stores it in the cache. + * Prepares this ImageItem to be rendered by the PDF target. Loading from the cache, if available. If the method is forced to load a image from the hard drive, it stores it in the cache. * - * @param location_name The name of the folder to load images from. + * @param directory The name of the folder to load images from. * @return A iText PDF Image corresponding to this item. */ - public com.itextpdf.text.Image renderPDF(File location_name) { + public com.itextpdf.text.Image renderPDF(File directory) { com.itextpdf.text.Image image = (com.itextpdf.text.Image) data.get(PDF_DATA); if (image == null) { if (source instanceof String) { - File full_file = new File(location_name,(String) source); + final File file = new File(directory, (String) source); try { - image = Image.getInstance(full_file.getCanonicalPath()); + image = com.itextpdf.text.Image.getInstance(file.getCanonicalPath()); } catch (BadElementException ex) { - logger.log(Level.SEVERE, "Unable to load image: " + full_file, ex); + LOG.log(Level.SEVERE, "Unable to load image: " + file, ex); } catch (MalformedURLException ex) { - logger.log(Level.SEVERE, "Unable to load image: " + full_file, ex); + LOG.log(Level.SEVERE, "Unable to load image: " + file, ex); } catch (IOException ex) { - logger.log(Level.SEVERE, "Unable to load image: " + full_file, ex); + LOG.log(Level.SEVERE, "Unable to load image: " + file, ex); } } //If we can't render the image, fallback to the defult render if (image == null) { - logger.log(Level.WARNING, "Unable to load image: {0}", source); + LOG.log(Level.WARNING, "Unable to load image: {0}", source); return null; } diff --git a/modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java b/modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java similarity index 75% rename from modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java rename to modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java index 37a7eeac1d..2342b074fe 100644 --- a/modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java +++ b/modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/ImageNodes.java @@ -25,10 +25,13 @@ package org.yale.cs.graphics.gephi.imagepreview; import com.itextpdf.text.DocumentException; -import com.itextpdf.text.Image; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfGState; +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.awt.Graphics2D; import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,33 +45,28 @@ import org.openide.util.NbPreferences; import org.openide.util.lookup.ServiceProvider; import org.w3c.dom.Element; -import processing.core.PImage; /** * A service that renders Nodes as images. *

- * This class works in conjunction with {@link ImageItem} and - * {@link NodeImageItemBuilder}. + * This class works in conjunction with {@link ImageItem} and {@link NodeImageItemBuilder}. *

- * This class provides the properties and last step rendering code for the - * images themselves. It can easily be overloaded to support other images. All - * that would be needed would be the creation of a new {@link ItemBuilder} that - * creates proper images. (This class may need to be overidden to modify - * needsItemBuilder. + * This class provides the properties and last step rendering code for the images themselves. It can easily be overloaded to support other images. All that would be needed would be the creation of a + * new {@link ItemBuilder} that creates proper images. (This class may need to be overidden to modify needsItemBuilder. *

* * @author Yirzchak Lockerman (Yale Computer Graphics Group) */ -@ServiceProvider(service = Renderer.class, position = 200) +@ServiceProvider(service = Renderer.class, position = 350) public class ImageNodes implements Renderer { final static String IMAGE_DESCRIPTION = "ImageNodes.property.imageDescription"; final static String IMAGE_DIRECTORY = "ImageNodes.property.path"; final static String IMAGE_OPACITY = "ImageNodes.property.opacity"; - final static String IMAGE_SCALE ="ImageNodes.property.scale"; + final static String IMAGE_SCALE = "ImageNodes.property.scale"; final static String CATEGORY_NODE_IMAGE = "Node Images"; - private static final Logger logger = Logger.getLogger(ImageNodes.class.getName()); + private static final Logger LOG = Logger.getLogger(ImageNodes.class.getName()); @Override public String getDisplayName() { @@ -82,13 +80,14 @@ public void render(Item item, RenderTarget target, PreviewProperties properties) } String imagesPath = properties.getValue(IMAGE_DIRECTORY); - if (imagesPath == null || imagesPath.isEmpty()) { + if (imagesPath == null || imagesPath.trim().isEmpty()) { return; } + File imagesDir = new File(imagesPath); if (showNodes(properties)) { if (target instanceof G2DTarget) { - renderImageProcessing((ImageItem) item, (G2DTarget) target, properties, imagesDir); + renderImageGraphics2d((ImageItem) item, (G2DTarget) target, properties, imagesDir); } else if (target instanceof SVGTarget) { renderImageSVG((ImageItem) item, (SVGTarget) target, properties, imagesDir); } else if (target instanceof PDFTarget) { @@ -98,40 +97,59 @@ public void render(Item item, RenderTarget target, PreviewProperties properties) } - public void renderImageProcessing(ImageItem item, G2DTarget target, PreviewProperties properties, File directory) { - PImage image = item.renderProcessing(directory, target); + public void renderImageGraphics2d(ImageItem item, G2DTarget target, PreviewProperties properties, File directory) { + BufferedImage image = item.loadImage(directory); if (image == null) { - logger.log(Level.WARNING, "Unable to load image: {0}", item.getSource()); + LOG.log(Level.WARNING, "Unable to load image: {0}", item.getSource()); return; } - Float x = item.getData(NodeItem.X); - Float y = item.getData(NodeItem.Y); - Float size = item.getData(NodeItem.SIZE); + float x = item.getData(NodeItem.X); + float y = item.getData(NodeItem.Y); + float size = item.getData(NodeItem.SIZE); - int alpha = (int) ((properties.getFloatValue(IMAGE_OPACITY) / 100f) * 255f); - if (alpha > 255) { - alpha = 255; + float alpha = (properties.getFloatValue(IMAGE_OPACITY) / 100f); + if (alpha <= 0) { + return; + } + + if (alpha > 1) { + alpha = 1f; } + /* Taking height of an image as "base" dimention 1 - Need to preserve the aspect on the width 2 - Apply a "Image Scale" to adjust the image - */ - int aspectRatio = size.intValue() * image.width / image.height; - int newHeight =(int)(aspectRatio * properties.getFloatValue(IMAGE_SCALE)); - int newWidth = (int)(size.intValue()* properties.getFloatValue(IMAGE_SCALE)); - image.resize(newHeight,newWidth); // There is a AffineTransformation Scale , didn't manage how to properly use it. + */ + float aspectRatio = (float) image.getWidth() / (float) image.getHeight(); + int width = image.getWidth(); + + //Draw: + Graphics2D g2 = target.getGraphics(); + + Composite originalComposite = g2.getComposite(); + if (alpha < 1) { + Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); + + g2.setComposite(comp); + } - // The AffineTransform will position the image to the center of the "node" - target.getGraphics().drawImage(image.getImage(), AffineTransform.getTranslateInstance(x - (image.width / 2), y - (image.height / 2)), null); + AffineTransform transform = new AffineTransform(); + transform.translate(x - size / 2, y - size / 2 / aspectRatio); + float scaleRatio = size / width; + transform.scale(scaleRatio, scaleRatio); + g2.drawImage(image, transform, null); + if (alpha < 1f) { + g2.setComposite(originalComposite); + } } public void renderImagePDF(ImageItem item, PDFTarget target, PreviewProperties properties, File directory) { - Image image = item.renderPDF(directory); + com.itextpdf.text.Image image = item.renderPDF(directory); if (image == null) { - logger.log(Level.WARNING, "Unable to load image: {0}", item.getSource()); + LOG.log(Level.WARNING, "Unable to load image: {0}", item.getSource()); return; } @@ -151,12 +169,13 @@ public void renderImagePDF(ImageItem item, PDFTarget target, PreviewProperties p cb.setGState(gState); } - image.setAbsolutePosition(x - size / 2, -y - size / 2); + float aspectRatio = (float) image.getWidth() / (float) image.getHeight(); + image.setAbsolutePosition(x - size / 2, -y - size / 2 / aspectRatio); image.scaleToFit(size, size); try { cb.addImage(image); } catch (DocumentException ex) { - logger.log(Level.SEVERE, "Unable to add image to document: " + item.getSource(), ex); + LOG.log(Level.SEVERE, "Unable to add image to document: " + item.getSource(), ex); } if (alpha < 1f) { @@ -191,9 +210,9 @@ public void renderImageSVG(ImageItem item, SVGTarget target, PreviewProperties p @Override public void preProcess(PreviewModel previewModel) { NbPreferences.forModule(NodeImageItemBuilder.class) - .put("ImageNodes.imageDirectory", (String)previewModel.getProperties().getValue(IMAGE_DIRECTORY)); + .put("ImageNodes.imageDirectory", (String) previewModel.getProperties().getValue(IMAGE_DIRECTORY)); } - + private String getImageDirectory() { return NbPreferences.forModule(NodeImageItemBuilder.class) .get("ImageNodes.imageDirectory", FileSystemView.getFileSystemView().getDefaultDirectory().getAbsolutePath()); diff --git a/modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java b/modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java similarity index 51% rename from modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java rename to modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java index 526183b735..4213084e06 100644 --- a/modules/gephi-plugin-image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java +++ b/modules/image-preview/src/main/java/org/yale/cs/graphics/gephi/imagepreview/NodeImageItemBuilder.java @@ -21,16 +21,19 @@ Contributor(s): Totetmatt (0.9.X Transition) This file is based on, and meant to be used with, Gephi. (http://gephi.org/) -*/ - + */ package org.yale.cs.graphics.gephi.imagepreview; import java.awt.Color; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import org.gephi.graph.api.Column; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; import org.gephi.preview.api.Item; import org.gephi.preview.plugin.items.NodeItem; import org.gephi.preview.spi.ItemBuilder; @@ -38,42 +41,53 @@ /** * This is a simple class that creates an {@link ImageItem} for each node. - * + * * @author Yitzchak Lockerman */ -@ServiceProvider(service=ItemBuilder.class) -public class NodeImageItemBuilder implements ItemBuilder{ - private static final Logger logger = Logger.getLogger(NodeImageItemBuilder.class.getName()); +@ServiceProvider(service = ItemBuilder.class) +public class NodeImageItemBuilder implements ItemBuilder { + + private static final Logger LOG = Logger.getLogger(NodeImageItemBuilder.class.getName()); + @Override public Item[] getItems(Graph graph) { + final List items = new ArrayList(); + final Column imageColumn = graph.getModel().getNodeTable().getColumn("image"); + + if (imageColumn == null) { + return new Item[0]; + } - Item[] items = new ImageItem[graph.getNodeCount()]; - - int i = 0; + final NodeIterable nodesIterable = graph.getNodes(); try { - for (Node n : graph.getNodes()) { - ImageItem imageItem = new ImageItem((String)n.getAttribute("image")); - imageItem.setData(NodeItem.X, n.x()); - imageItem.setData(NodeItem.Y, -n.y()); - imageItem.setData(NodeItem.Z, n.z()); - imageItem.setData(NodeItem.SIZE, n.size() * 2f); - imageItem.setData(NodeItem.COLOR, new Color((int) (n.r() * 255), - (int) (n.g() * 255), - (int) (n.b() * 255), - (int) (n.alpha() * 255))); - items[i++] = imageItem; + for (Node node : nodesIterable) { + final String imageName = Objects.toString(node.getAttribute(imageColumn), null); + + if (imageName != null && !imageName.trim().isEmpty()) { + final ImageItem imageItem = new ImageItem(imageName); + imageItem.setData(NodeItem.X, node.x()); + imageItem.setData(NodeItem.Y, -node.y()); + imageItem.setData(NodeItem.Z, node.z()); + imageItem.setData(NodeItem.SIZE, node.size() * 2f); + imageItem.setData(NodeItem.COLOR, new Color((int) (node.r() * 255), + (int) (node.g() * 255), + (int) (node.b() * 255), + (int) (node.alpha() * 255))); + + items.add(imageItem); + } } - } catch (Exception e){ - items = new ImageItem[0]; - logger.log(Level.SEVERE,null,e); - } - return items; + } catch (Exception e) { + nodesIterable.doBreak(); + LOG.log(Level.SEVERE, null, e); + } + + return items.toArray(new Item[0]); } @Override public String getType() { return ImageItem.IMAGE; } - - + } diff --git a/modules/gephi-plugin-image-preview/src/main/nbm/manifest.mf b/modules/image-preview/src/main/nbm/manifest.mf similarity index 100% rename from modules/gephi-plugin-image-preview/src/main/nbm/manifest.mf rename to modules/image-preview/src/main/nbm/manifest.mf diff --git a/modules/gephi-plugin-image-preview/src/main/resources/org/yale/cs/graphics/gephi/imagepreview/Bundle.properties b/modules/image-preview/src/main/resources/org/yale/cs/graphics/gephi/imagepreview/Bundle.properties similarity index 100% rename from modules/gephi-plugin-image-preview/src/main/resources/org/yale/cs/graphics/gephi/imagepreview/Bundle.properties rename to modules/image-preview/src/main/resources/org/yale/cs/graphics/gephi/imagepreview/Bundle.properties diff --git a/pom.xml b/pom.xml index 9a7972100a..ad483f3502 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ - modules/gephi-plugin-image-preview + modules/image-preview