summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorTony McCrary2013-11-19 21:55:25 (EST)
committer Lars Vogel2013-11-20 07:29:18 (EST)
commite610525823664ea89eed74eb2bfb97667172c3c9 (patch)
tree0dfe2a0b0f6621783d4662049b8bed4cb679f294
parent2a663db0ec8b5526b9ed1c61e6a793b495110234 (diff)
downloadeclipse.platform.ui-e610525823664ea89eed74eb2bfb97667172c3c9.zip
eclipse.platform.ui-e610525823664ea89eed74eb2bfb97667172c3c9.tar.gz
eclipse.platform.ui-e610525823664ea89eed74eb2bfb97667172c3c9.tar.bz2
Added SVG rendering plugin for generating PNG images from o.e.u.imagesrefs/changes/93/18593/3
SVG icons, along with instructions (README.md). Change-Id: Ia873adbc99b71f3a07b2a6a57197a105f13d82c6 Signed-off-by: Tony McCrary <tmccrary@gmail.com>
-rw-r--r--bundles/org.eclipse.ui.images.renderer/.classpath26
-rw-r--r--bundles/org.eclipse.ui.images.renderer/.gitignore2
-rw-r--r--bundles/org.eclipse.ui.images.renderer/.project23
-rw-r--r--bundles/org.eclipse.ui.images.renderer/README.md31
-rw-r--r--bundles/org.eclipse.ui.images.renderer/pom.xml97
-rw-r--r--bundles/org.eclipse.ui.images.renderer/src/main/java/org/eclipse/ui/images/renderer/RenderMojo.java651
6 files changed, 830 insertions, 0 deletions
diff --git a/bundles/org.eclipse.ui.images.renderer/.classpath b/bundles/org.eclipse.ui.images.renderer/.classpath
new file mode 100644
index 0000000..c832ecd
--- /dev/null
+++ b/bundles/org.eclipse.ui.images.renderer/.classpath
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="target/classes" path="src/main/java">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/bundles/org.eclipse.ui.images.renderer/.gitignore b/bundles/org.eclipse.ui.images.renderer/.gitignore
new file mode 100644
index 0000000..1fa817f
--- /dev/null
+++ b/bundles/org.eclipse.ui.images.renderer/.gitignore
@@ -0,0 +1,2 @@
+target/
+.settings/
diff --git a/bundles/org.eclipse.ui.images.renderer/.project b/bundles/org.eclipse.ui.images.renderer/.project
new file mode 100644
index 0000000..da3a507
--- /dev/null
+++ b/bundles/org.eclipse.ui.images.renderer/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.ui.images.renderer</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.ui.images.renderer/README.md b/bundles/org.eclipse.ui.images.renderer/README.md
new file mode 100644
index 0000000..9d9ead4
--- /dev/null
+++ b/bundles/org.eclipse.ui.images.renderer/README.md
@@ -0,0 +1,31 @@
+org.eclipse.ui.images.renderer
+==============================
+
+org.eclipse.ui.images provides the a Maven generator of svg images located in the org.eclipse.ui.images plug-in.
+
+org.eclipse.ui.images.renderer plug-in usage
+--------------------------------------------
+
+Install the org.eclipse.ui.images.renderer plug-in:
+
+cd org.eclipse.ui.images.renderer
+mvn clean install
+
+After the renderer plugin is installed, change into the root of the images project:
+
+cd org.eclipse.ui.images
+
+Finally, execute the icon render mojo with:
+
+mvn org.eclipse.ui:org.eclipse.ui.images.renderer:render-icons
+
+This renders all of the svg icons in "eclipse-svg" into the "eclipse-png" folder of the org.eclipse.ui.images project, maintaining the directory structure (i.e. eclipse-svg/icondir will be rendered into org.eclipse.ui.images/eclipse-png/icondir).
+
+
+License
+-------
+
+[Eclipse Public License (EPL) v1.0][2]
+
+[1]: http://wiki.eclipse.org/Platform_UI
+[2]: http://wiki.eclipse.org/EPL \ No newline at end of file
diff --git a/bundles/org.eclipse.ui.images.renderer/pom.xml b/bundles/org.eclipse.ui.images.renderer/pom.xml
new file mode 100644
index 0000000..3734cef
--- /dev/null
+++ b/bundles/org.eclipse.ui.images.renderer/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ (c) Copyright 2013 l33t labs LLC and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Eclipse Distribution License v1.0
+ which accompanies this distribution, and is available at
+ http://www.eclipse.org/org/documents/edl-v10.php
+
+ Contributors:
+ Tony McCrary - initial implementation
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.ui.images.renderer</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>Eclipse Platform SVG Renderer Plugin</name>
+
+ <packaging>maven-plugin</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ <version>3.0-alpha-2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.twdata.maven</groupId>
+ <artifactId>mojo-executor</artifactId>
+ <version>2.0</version>
+ </dependency>
+ <!--
+ Image Filtering Library
+ License: Apache License, Version 2.0
+ http://www.jhlabs.com/ip/filters/
+ -->
+ <dependency>
+ <groupId>com.jhlabs</groupId>
+ <artifactId>filters</artifactId>
+ <version>2.0.235-1</version>
+ </dependency>
+
+ <!--
+ Advanced Image Scaling Library
+ License: New BSD License
+ https://code.google.com/p/java-image-scaling/
+ -->
+ <dependency>
+ <groupId>com.mortennobel</groupId>
+ <artifactId>java-image-scaling</artifactId>
+ <version>0.8.4</version>
+ </dependency>
+
+ <!--
+ Batik SVG Rendering Library
+ License: Apache License, Version 2.0
+ http://xmlgraphics.apache.org/batik/
+ -->
+ <dependency>
+ <groupId>batik</groupId>
+ <artifactId>batik-rasterizer</artifactId>
+ <version>1.6-1</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!--
+ Setup Java 1.5 output
+ -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-plugin-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <goalPrefix>eclipse-images-renderer</goalPrefix>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/bundles/org.eclipse.ui.images.renderer/src/main/java/org/eclipse/ui/images/renderer/RenderMojo.java b/bundles/org.eclipse.ui.images.renderer/src/main/java/org/eclipse/ui/images/renderer/RenderMojo.java
new file mode 100644
index 0000000..fc145b9
--- /dev/null
+++ b/bundles/org.eclipse.ui.images.renderer/src/main/java/org/eclipse/ui/images/renderer/RenderMojo.java
@@ -0,0 +1,651 @@
+/*******************************************************************************
+ * (c) Copyright 2013 l33t labs LLC and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * l33t labs LLC and others - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.ui.images.renderer;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.imageio.ImageIO;
+
+import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
+import org.apache.batik.transcoder.ErrorHandler;
+import org.apache.batik.transcoder.TranscoderException;
+import org.apache.batik.transcoder.TranscoderInput;
+import org.apache.batik.transcoder.TranscoderOutput;
+import org.apache.batik.transcoder.image.PNGTranscoder;
+import org.apache.batik.util.XMLResourceDescriptor;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
+import org.w3c.dom.Element;
+import org.w3c.dom.svg.SVGDocument;
+
+import com.jhlabs.image.GrayscaleFilter;
+import com.jhlabs.image.HSBAdjustFilter;
+import com.mortennobel.imagescaling.ResampleFilters;
+import com.mortennobel.imagescaling.ResampleOp;
+
+/**
+ * <p>Mojo which renders SVG icons into PNG format./p>
+ *
+ * @goal render-icons
+ * @phase generate-resources
+ */
+public class RenderMojo extends AbstractMojo {
+
+ /** Maven logger */
+ Log log;
+
+ /** Used for high res rendering support. */
+ public static final String ECLIPSE_SVG_HIGHRES = "eclipse.svg.highres";
+
+ /** Used to specify the number of render threads when rasterizing icons. */
+ public static final String RENDERTHREADS = "eclipse.svg.renderthreads";
+
+ /**
+ * <p>IconEntry is used to define an icon to rasterize,
+ * where to put it and the dimensions to render it at.</p>
+ */
+ class IconEntry {
+
+ /** The name of the icon minus extension */
+ String nameBase;
+
+ /** The input path of the source svg files. */
+ File inputPath;
+
+ /**
+ * The path rasterized versions of this icon should be written into.
+ */
+ File outputPath;
+
+ /** The path to a disabled version of the icon (gets desaturated). */
+ private File disabledPath;
+
+ /**
+ * Creates an IconEntry used for record keeping when
+ * rendering a set of SVG icons.
+ *
+ * @param nameBase the name of the icon file, minus any extension
+ * @param inputPath the SVG file that is rendered
+ * @param outputPath the path to the rendered icon data
+ * @param disabledPath the part to the disabled version of the output icon
+ */
+ public IconEntry(String nameBase, File inputPath, File outputPath,
+ File disabledPath) {
+ this.nameBase = nameBase;
+ this.inputPath = inputPath;
+ this.outputPath = outputPath;
+ this.disabledPath = disabledPath;
+ }
+ }
+
+ /** A list of directories with svg sources to rasterize. */
+ private List<IconEntry> icons;
+
+ /** The pool used to render multiple icons concurrently. */
+ private ExecutorService execPool;
+
+ /** The number of threads to use when rendering icons. */
+ private int threads;
+
+ /**
+ * A counter used to keep track of the number of rendered icons. Atomic is
+ * used to make it easy to access between threads concurrently.
+ */
+ private AtomicInteger counter;
+
+ /**
+ * A collection of lists for each Eclipse icon sets (o.e.workbench.ui,
+ * o.e.jd.ui, etc).
+ */
+ Map<String, List<IconEntry>> galleryIconSets;
+
+ /** List of icons that failed to render, made safe for parallel access */
+ List<IconEntry> failedIcons = Collections
+ .synchronizedList(new ArrayList<IconEntry>(5));
+
+ /** Whether to render the icons at 2x for high dpi displays. */
+ private boolean highres;
+
+ /** Used for creating desaturated icons */
+ private GrayscaleFilter grayFilter;
+
+ /** Used for creating desaturated icons */
+ private HSBAdjustFilter desaturator;
+
+ /**
+ * @return the number of icons rendered at the time of the call
+ */
+ public int getIconsRendered() {
+ return counter.get();
+ }
+
+ /**
+ * @return the number of icons that failed during the rendering process
+ */
+ public int getFailedIcons() {
+ return failedIcons.size();
+ }
+
+ /**
+ * <p>Creates an IconEntry during the icon gather operation.</p>
+ *
+ * @param input the source of the icon file (SVG document)
+ * @param outputPath the path of the rasterized version to generate
+ * @param disabledPath the path of the disabled (desaturated) icon, if one is required
+ *
+ * @return an IconEntry describing the rendering operation
+ */
+ public IconEntry createIcon(File input, File outputPath, File disabledPath) {
+ String name = input.getName();
+ String[] split = name.split("\\.(?=[^\\.]+$)");
+
+ IconEntry def = new IconEntry(split[0], input, outputPath, disabledPath);
+
+ return def;
+ }
+
+ /**
+ * <p>Generates raster images from the input SVG vector image.</p>
+ *
+ * @param icon
+ * the icon to render
+ */
+ public void rasterize(IconEntry icon) {
+ if (icon == null) {
+ log.error("Null icon definition, skipping.");
+ failedIcons.add(icon);
+ return;
+ }
+
+ if (icon.inputPath == null) {
+ log.error("Null icon input path, skipping: "
+ + icon.nameBase);
+ failedIcons.add(icon);
+ return;
+ }
+
+ if (!icon.inputPath.exists()) {
+ log.error("Input path specified does not exist, skipping: "
+ + icon.nameBase);
+ failedIcons.add(icon);
+ return;
+ }
+
+ if (icon.outputPath != null && !icon.outputPath.exists()) {
+ icon.outputPath.mkdirs();
+ }
+
+ if (icon.disabledPath != null && !icon.disabledPath.exists()) {
+ icon.disabledPath.mkdirs();
+ }
+
+ // Create the document to rasterize
+ SVGDocument svgDocument = generateSVGDocument(icon);
+
+ if(svgDocument == null) {
+ return;
+ }
+
+ // Determine the output sizes (native, double, quad)
+ // We render at quad size and resample down for output
+ Element svgDocumentNode = svgDocument.getDocumentElement();
+ String nativeWidthStr = svgDocumentNode.getAttribute("width");
+ String nativeHeightStr = svgDocumentNode.getAttribute("height");
+
+ int nativeWidth = Integer.parseInt(nativeWidthStr);
+ int nativeHeight = Integer.parseInt(nativeHeightStr);
+
+ int doubleWidth = nativeWidth * 2;
+ int doubleHeight = nativeHeight * 2;
+
+ int quadWidth = nativeWidth * 4;
+ int quadHeight = nativeHeight * 4;
+
+ // Guesstimate the PNG size in memory, BAOS will enlarge if necessary.
+ int outputInitSize = quadWidth * quadHeight * 4 + 1024;
+ ByteArrayOutputStream iconOutput = new ByteArrayOutputStream(
+ outputInitSize);
+
+ // Render to SVG
+ try {
+ log.info(Thread.currentThread().getName() + " "
+ + " Rasterizing: " + icon.nameBase + ".png at " + quadWidth
+ + "x" + quadHeight);
+
+ TranscoderInput svgInput = new TranscoderInput(svgDocument);
+
+ boolean success = renderIcon(icon.nameBase, quadWidth, quadHeight, svgInput, iconOutput);
+
+ if (!success) {
+ log.error("Failed to render icon: " + icon.nameBase + ".png, skipping.");
+ failedIcons.add(icon);
+ return;
+ }
+ } catch (Exception e) {
+ log.error("Failed to render icon: " + e.getMessage());
+ failedIcons.add(icon);
+ return;
+ }
+
+ // Generate a buffered image from Batik's png output
+ byte[] imageBytes = iconOutput.toByteArray();
+ ByteArrayInputStream imageInputStream = new ByteArrayInputStream(imageBytes);
+
+ BufferedImage inputImage = null;
+ try {
+ inputImage = ImageIO.read(imageInputStream);
+
+ if(inputImage == null) {
+ log.error("Failed to generate BufferedImage from rendered icon, ImageIO returned null: " + icon.nameBase);
+ failedIcons.add(icon);
+ return;
+ }
+ } catch (IOException e2) {
+ log.error("Failed to generate BufferedImage from rendered icon: " + icon.nameBase + " - " + e2.getMessage());
+ failedIcons.add(icon);
+ return;
+ }
+
+ // Icons lose definition and accuracy when rendered directly
+ // to <128px res with Batik
+ // Here we resize a 16x,32x,48x,64x images down, which gives better
+ // results
+
+ // Default to the native svg size
+ int targetWidth = nativeWidth;
+ int targetHeight = nativeHeight;
+
+ if(!highres) {
+ log.info(Thread.currentThread().getName() + " "
+ + " Rasterizing (Scaling Native): " + icon.nameBase
+ + ".png at " + nativeWidth + "x" + nativeHeight);
+ } else {
+ log.info(Thread.currentThread().getName() + " "
+ + " Rasterizing (Scaling Half): " + icon.nameBase
+ + ".png at " + doubleWidth + "x" + doubleWidth);
+
+ targetWidth = doubleWidth;
+ targetHeight = doubleHeight;
+ }
+
+ resizeIcon(icon, targetWidth, targetHeight, inputImage);
+ }
+
+ /**
+ * <p>Generates a Batik SVGDocument for the supplied IconEntry's input
+ * file.</p>
+ *
+ * @param icon the icon entry to generate an SVG document for
+ *
+ * @return a batik SVGDocument instance or null if one could not be generated
+ */
+ private SVGDocument generateSVGDocument(IconEntry icon) {
+ // Load the document and find out the native height/width
+ // We reuse the document later for rasterization
+ SVGDocument svgDocument = null;
+ try {
+ FileInputStream iconDocumentStream = new FileInputStream(icon.inputPath);
+
+ String parser = XMLResourceDescriptor.getXMLParserClassName();
+ SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
+
+ // What kind of URI is batik expecting here??? the docs don't say
+ svgDocument = f.createSVGDocument("file://" + icon.nameBase + ".svg", iconDocumentStream);
+ } catch (Exception e3) {
+ log.error("Error parsing SVG icon document: " + e3.getMessage());
+ failedIcons.add(icon);
+ return null;
+ }
+ return svgDocument;
+ }
+
+ /**
+ * <p>Resizes the supplied inputImage to the specified width and height, using
+ * lanczos resampling techniques.</p>
+ *
+ * @param icon the icon that's being resized
+ * @param width the desired output width after rescaling operations
+ * @param height the desired output height after rescaling operations
+ * @param sourceImage the source image to resource
+ */
+ private void resizeIcon(IconEntry icon, int width, int height, BufferedImage sourceImage) {
+ ResampleOp resampleOpNative = new ResampleOp(width, height);
+ resampleOpNative.setFilter(ResampleFilters.getLanczos3Filter());
+ // resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Oversharpened);
+ resampleOpNative.setNumberOfThreads(2);
+
+ try {
+ // Resize and render the 16x16 icon
+ BufferedImage rescaled = resampleOpNative.filter(sourceImage, null);
+
+ ImageIO.write(rescaled, "PNG", new File(icon.outputPath, icon.nameBase + ".png"));
+
+ if (icon.disabledPath != null) {
+ BufferedImage desaturated16 = desaturator.filter(
+ grayFilter.filter(rescaled, null), null);
+
+ ImageIO.write(desaturated16, "PNG", new File(icon.disabledPath, icon.nameBase + ".png"));
+ }
+ } catch (Exception e1) {
+ log.error("Failed to resize rendered icon to output size: " +
+ icon.nameBase + " - " + e1.getMessage());
+ failedIcons.add(icon);
+ }
+ }
+
+ /**
+ * <p>Handles concurrently rasterizing the icons to
+ * reduce the duration on multicore systems.</p>
+ */
+ public void rasterizeAll() {
+ // The number of icons that haven't been distributed to
+ // callables
+ int remainingIcons = icons.size();
+
+ // The number of icons to distribute to a rendering callable
+ final int threadExecSize = icons.size() / this.threads;
+
+ // The current offset to start a batch, as they're distributed
+ // between rendering callables
+ int batchOffset = 0;
+
+ // A list of callables used to render icons on multiple threads
+ // Each callable gets a set of icons to render
+ List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(
+ this.threads);
+
+ // Distribute the rasterization operations between multiple threads
+ while (remainingIcons > 0) {
+ // The current start index for the current batch
+ final int batchStart = batchOffset;
+
+ // Increment the offset to reflect this batch (used for the next batch)
+ batchOffset += threadExecSize;
+
+ // Determine this batch size, used for batches that have less than
+ // threadExecSize at the end of the distribution operation
+ int batchSize = 0;
+
+ // Determine if we can fit a full batch in this callable
+ // or if we are at the end of gathered icons
+ if (remainingIcons > threadExecSize) {
+ batchSize = threadExecSize;
+ } else {
+ // We have less than a full batch worth of remaining icons
+ // just add them all
+ batchSize = remainingIcons;
+ }
+
+ // Deincrement the remaining Icons
+ remainingIcons -= threadExecSize;
+
+ // Used for access in the callable's scope
+ final int execCount = batchSize;
+
+ // Create the callable and add it to the task pool
+ Callable<Object> runnable = new Callable<Object>() {
+ public Object call() throws Exception {
+ // Rasterize this batch
+ for (int count = 0; count < execCount; count++) {
+ rasterize(icons.get(batchStart + count));
+ }
+
+ // Update the render counter
+ counter.getAndAdd(execCount);
+ log.info("Finished rendering batch, index: " + batchStart);
+
+ return null;
+ }
+ };
+
+ tasks.add(runnable);
+ }
+
+ // Execute the rasterization operations that
+ // have been added to the pool
+ try {
+ execPool.invokeAll(tasks);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ // Print info about failed render operations, so they can be fixed
+ log.info("Failed Icon Count: " + failedIcons.size());
+ for (IconEntry icon : failedIcons) {
+ log.info("Failed Icon: " + icon.nameBase);
+ }
+
+ }
+
+ /**
+ * Use batik to rasterize the input SVG into a raster image at the specified
+ * image dimensions.
+ *
+ * @param width the width to render the icons at
+ * @param height the height to render the icon at
+ * @param input the SVG transcoder input
+ * @param stream the stream to write the PNG data to
+ */
+ public boolean renderIcon(final String iconName, int width, int height,
+ TranscoderInput tinput, OutputStream stream) {
+ PNGTranscoder t = new PNGTranscoder();
+ t.addTranscodingHint(PNGTranscoder.KEY_WIDTH, new Float(width));
+ t.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, new Float(height));
+
+ t.setErrorHandler(new ErrorHandler() {
+ public void warning(TranscoderException arg0)
+ throws TranscoderException {
+ log.error("Icon: " + iconName + " - WARN: " + arg0.getMessage());
+ }
+
+ public void fatalError(TranscoderException arg0)
+ throws TranscoderException {
+ log.error("Icon: " + iconName + " - FATAL: " + arg0.getMessage());
+ }
+
+ public void error(TranscoderException arg0)
+ throws TranscoderException {
+ log.error("Icon: " + iconName + " - ERROR: " + arg0.getMessage());
+ }
+ });
+
+ // Transcode the SVG document input to a PNG via the output stream
+ TranscoderOutput output = new TranscoderOutput(stream);
+
+ try {
+ t.transcode(tinput, output);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * <p>Search the root resources directory for svg icons and add them to our
+ * collection for rasterization later.</p>
+ *
+ * @param outputName
+ * @param iconDir
+ * @param outputBase
+ * @param outputDir2
+ */
+ public void gatherIcons(String outputName, File rootDir, File iconDir,
+ File outputBase) {
+
+ File[] listFiles = iconDir.listFiles();
+
+ for (File child : listFiles) {
+ if (child.isDirectory()) {
+ gatherIcons(outputName, rootDir, child, outputBase);
+ continue;
+ }
+
+ if (!child.getName().endsWith("svg")) {
+ return;
+ }
+
+ // Compute a relative path for the output dir
+ URI rootUri = rootDir.toURI();
+ URI iconUri = iconDir.toURI();
+
+ String relativePath = rootUri.relativize(iconUri).getPath();
+ File outputDir = new File(outputBase, relativePath);
+ File disabledOutputDir = null;
+
+ File parentFile = child.getParentFile();
+
+ /* Determine if/where to put a disabled version of the icon
+ Eclipse traditionally uses a prefix of d for disabled, e for
+ enabled in the folder name */
+ if (parentFile != null) {
+ String parentDirName = parentFile.getName();
+ if (parentDirName.startsWith("e")) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("d");
+ builder.append(parentDirName.substring(1,
+ parentDirName.length()));
+ String disabledVariant = builder.toString();
+
+ File setParent = parentFile.getParentFile();
+ for (File disabledFolder : setParent.listFiles()) {
+ if (disabledFolder.getName()
+ .equals(disabledVariant)) {
+ String path = rootUri.relativize(
+ disabledFolder.toURI()).getPath();
+ disabledOutputDir = new File(outputBase, path);
+ }
+ }
+
+ }
+ }
+
+ IconEntry icon = createIcon(child, outputDir, disabledOutputDir);
+
+ icons.add(icon);
+ }
+ }
+
+ /**
+ * <p>Initializes rasterizer defaults</p>
+ *
+ * @param threads the number of threads to render with
+ * @param highres whether to render high res output
+ */
+ private void init(int threads, boolean highres) {
+ this.threads = threads;
+ this.highres = highres;
+ icons = new ArrayList<IconEntry>();
+ execPool = Executors.newFixedThreadPool(threads);
+ counter = new AtomicInteger();
+
+ grayFilter = new GrayscaleFilter();
+
+ desaturator = new HSBAdjustFilter();
+ desaturator.setSFactor(0.0f);
+ }
+
+ /**
+ * @see AbstractMojo#execute()
+ */
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ log = getLog();
+
+ // Default to 2x the number of processor cores but allow override via jvm arg
+ int threads = Math.max(1, Runtime.getRuntime().availableProcessors() * 2);
+ String threadStr = System.getProperty(RENDERTHREADS);
+ if (threadStr != null) {
+ try {
+ threads = Integer.parseInt(threadStr);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out
+ .println("Could not parse thread count, using default thread count");
+ }
+ }
+
+ // if high res is enabled, the icons will be rendered at 2x their native svg size
+ String highresStr = System.getProperty(ECLIPSE_SVG_HIGHRES);
+ boolean highres = Boolean.parseBoolean(highresStr);
+
+ // Track the time it takes to render the entire set
+ long totalStartTime = System.currentTimeMillis();
+
+ // initialize defaults (the old renderer was instantiated via constructor)
+ init(threads, highres);
+
+ String workingDirectory = System.getProperty("user.dir");
+
+ File outputDir = new File(workingDirectory+"/eclipse-png/");
+ File iconDirectoryRoot = new File("eclipse-svg/");
+
+ // Search each subdir in the root dir for svg icons
+ for (File file : iconDirectoryRoot.listFiles()) {
+ if(!file.isDirectory()) {
+ continue;
+ }
+
+ String dirName = file.getName();
+
+ // Where to place the rendered icon
+ File outputBase = new File(outputDir, dirName);
+
+ gatherIcons(dirName, file, file, outputBase);
+ }
+
+ log.info("Working directory: " + outputDir.getAbsolutePath());
+ log.info("SVG Icon Directory: " + iconDirectoryRoot.getAbsolutePath());
+ log.info("Rendering icons with " + threads + " threads, high res output enabled: " + highres);
+ long startTime = System.currentTimeMillis();
+
+ // Render the icons
+ rasterizeAll();
+
+ // Print summary of operations
+ int iconRendered = getIconsRendered();
+ int failedIcons = getFailedIcons();
+ int fullIconCount = iconRendered - failedIcons;
+
+ log.info(fullIconCount + " Icons Rendered");
+ log.info(failedIcons + " Icons Failed");
+ log.info("Took: " + (System.currentTimeMillis() - startTime) + " ms.");
+
+ log.info("Rasterization operations completed, Took: "
+ + (System.currentTimeMillis() - totalStartTime) + " ms.");
+ }
+
+}