Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 422139
Collapse All | Expand All

(-)a/org.eclipse.images.renderer/README.md (-9 / +9 lines)
Lines 1-25 Link Here
1
org.eclipse.ui.images.renderer
1
org.eclipse.images.renderer
2
==============================
2
==============================
3
3
4
org.eclipse.ui.images provides the a Maven generator of svg images located in the org.eclipse.ui.images plug-in. 
4
org.eclipse.images provides the a Maven generator of svg images located in the org.eclipse.images plug-in. 
5
5
6
org.eclipse.ui.images.renderer plug-in usage
6
org.eclipse.images.renderer plug-in usage
7
--------------------------------------------
7
--------------------------------------------
8
8
9
Install the org.eclipse.ui.images.renderer plug-in:
9
Install the org.eclipse.images.renderer plug-in:
10
10
11
cd org.eclipse.ui.images.renderer
11
cd org.eclipse.images.renderer
12
mvn clean install
12
mvn clean install
13
13
14
After the renderer plugin is installed, change into the root of the images project:
14
After the renderer plugin is installed, change into the root of the images project:
15
15
16
cd org.eclipse.ui.images
16
cd org.eclipse.images
17
17
18
Finally, execute the icon render mojo with:
18
Finally, execute the icon render mojo with:
19
19
20
mvn org.eclipse.ui:org.eclipse.ui.images.renderer:render-icons
20
mvn org.eclipse.images:org.eclipse.images.renderer:render-icons
21
21
22
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).
22
This renders all of the svg icons in "eclipse-svg" into the "eclipse-png" folder of the org.eclipse.images project, maintaining the directory structure (i.e. eclipse-svg/icondir will be rendered into org.eclipse.images/eclipse-png/icondir).
23
23
24
Supported runtime arguments (e.g mvn -Declipse.svg.scale=2 ...):
24
Supported runtime arguments (e.g mvn -Declipse.svg.scale=2 ...):
25
25
Lines 30-36 Link Here
30
30
31
Once the icon sets have been rendered, you can create galleries for evaluation and feedback with the gallery mojo:
31
Once the icon sets have been rendered, you can create galleries for evaluation and feedback with the gallery mojo:
32
32
33
mvn org.eclipse.ui:org.eclipse.ui.images.renderer:render-galleries
33
mvn org.eclipse.images:org.eclipse.images.renderer:render-galleries
34
34
35
This will create a set of galleries and gif comparisons comprised of the newly rendered icons, located in the target/ output directory.
35
This will create a set of galleries and gif comparisons comprised of the newly rendered icons, located in the target/ output directory.
36
36
(-)a/org.eclipse.images.renderer/pom.xml (-8 / +7 lines)
Lines 1-134 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
2
<!-- (c) Copyright 2013 l33t labs LLC and others. All rights reserved. This 
3
  (c) Copyright 2013 l33t labs LLC and others.
3
	program and the accompanying materials are made available under the terms 
4
  All rights reserved. This program and the accompanying materials
4
	of the Eclipse Distribution License v1.0 which accompanies this distribution, 
5
  are made available under the terms of the Eclipse Distribution License v1.0
5
	and is available at http://www.eclipse.org/org/documents/edl-v10.php Contributors: 
6
  which accompanies this distribution, and is available at
6
	Tony McCrary - initial implementation -->
7
  http://www.eclipse.org/org/documents/edl-v10.php
7
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
8
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
8
9
9
  Contributors:
10
     Tony McCrary - initial implementation
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/images/renderer/GalleryMojo.java (+412 lines)
Added Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.images.renderer;
13
14
import java.awt.Color;
15
import java.awt.Graphics2D;
16
import java.awt.RenderingHints;
17
import java.awt.image.BufferedImage;
18
import java.io.File;
19
import java.io.IOException;
20
import java.util.ArrayList;
21
import java.util.Collections;
22
import java.util.Date;
23
import java.util.HashMap;
24
import java.util.List;
25
import java.util.Map;
26
import java.util.Map.Entry;
27
28
import javax.imageio.ImageIO;
29
30
import org.apache.maven.plugin.AbstractMojo;
31
import org.apache.maven.plugin.MojoExecutionException;
32
import org.apache.maven.plugin.MojoFailureException;
33
import org.apache.maven.plugin.logging.Log;
34
35
import com.mortennobel.imagescaling.ResampleFilters;
36
import com.mortennobel.imagescaling.ResampleOp;
37
38
/**
39
 * <p>Mojo which renders galleries for comparing
40
 * and evaluating icons..</p>
41
 *
42
 * @goal render-galleries
43
 * @phase generate-resources
44
 */
45
public class GalleryMojo extends AbstractMojo {
46
47
    /** Maven logger */
48
    Log log;
49
50
    /** Used for finding gif files by extension. */
51
    public static final String GIF_EXT = ".gif";
52
    
53
    /** Used to specify the directory name where the SVGs are taken from. */
54
    public static final String PNG_DIR = "eclipse.svg.pngdirectory";
55
56
    /** Used to specify the directory name where the SVGs are taken from. */
57
    public static final String GIF_DIR = "eclipse.svg.gifdirectory"; 
58
59
    /**
60
     * <p>Mojo takes rendered images and generates various galleries for
61
     * testing and evaluation.</p>
62
     */
63
    public void execute() throws MojoExecutionException, MojoFailureException {
64
        log = getLog();
65
66
        // Defaults to "eclipse-png"
67
        String pngDir = "eclipse-png";
68
        String pngDirProp = System.getProperty(PNG_DIR);
69
        if (pngDirProp != null) {
70
            pngDir = pngDirProp;
71
        }
72
73
        // Defaults to "eclipse-gif"
74
        String gifDir = "eclipse-gif";
75
        String gifDirProp = System.getProperty(GIF_DIR);
76
        if (gifDirProp != null) {
77
            gifDir = gifDirProp;
78
        }
79
80
        File iconDirectoryRoot = new File(pngDir + "/");
81
        if (!iconDirectoryRoot.exists()){
82
            log.error("PNG directory' "+pngDir+"' does not exist.");
83
            return;
84
        }
85
86
        Map<String, List<IconEntry>> galleryIconSets = new HashMap<>();
87
88
        // Search each subdir in the root dir for svg icons
89
        for (File file : iconDirectoryRoot.listFiles()) {
90
            if(!file.isDirectory()) {
91
                continue;
92
            }
93
94
            List<IconEntry> icons = new ArrayList<>();
95
            IconGatherer.gatherIcons(icons, "png", file, file, iconDirectoryRoot, false);
96
97
            galleryIconSets.put(file.getName(), icons);
98
        }
99
100
        File mavenTargetDir = new File("target/");
101
        File galleryDir = new File(mavenTargetDir, "gallery/");
102
        File gifCompare = new File(galleryDir, "gifcompare/");
103
        File master = new File(galleryDir, "master/");
104
105
        if(galleryDir.exists()) {
106
            galleryDir.delete();
107
        }
108
109
        galleryDir.mkdirs();
110
        gifCompare.mkdirs();
111
        master.mkdirs();
112
113
        renderGalleries(galleryDir, gifCompare, master, galleryIconSets, 16, 800, pngDir, gifDir);
114
    }
115
116
    /**
117
     * <p>Renders each icon set into a gallery image for reviewing and showing off
118
     * icons, and then composes them into a master gallery image.</p>
119
     *
120
     * @param rasterizer
121
     * @param galleryDir
122
     * @param gifCompare
123
     * @param master
124
     * @param iconSize
125
     * @param width
126
     * @param pngDir
127
     * @param gifDir
128
     */
129
    public void renderGalleries(File galleryDir,  File gifCompare, File master, Map<String, List<IconEntry>> iconSets, int iconSize, int width, String pngDir, String gifDir) {
130
        // Render each icon set and a master list
131
        List<IconEntry> masterList = new ArrayList<>();
132
133
        for (Entry<String, List<IconEntry>> entry : iconSets.entrySet()) {
134
            String key = entry.getKey();
135
            List<IconEntry> value = entry.getValue();
136
137
            masterList.addAll(value);
138
139
            log.info("Creating gallery for: " + key);
140
            renderGallery(galleryDir, key, value, iconSize, width, 3);
141
            renderGifCompareGallery(gifCompare, key, value, iconSize, width, 6, pngDir, gifDir);
142
        }
143
144
        // Render the master image
145
        log.info("Rendering master icon gallery...");
146
        renderMasterGallery(galleryDir, master, "-gallery.png", iconSize, iconSize + width, true);
147
        renderMasterGallery(galleryDir, master, "-gallery.png", iconSize, iconSize + width, false);
148
149
        // Master gif compare
150
        //renderMasterGallery(outputDir, "-gifcompare.png", iconSize, iconSize + width, false);
151
    }
152
153
    /**
154
     * <p>Renders comparison images, the new png/svg icons vs old gifs.</p>
155
     *
156
     * @param outputDir
157
     * @param key
158
     * @param icons
159
     * @param iconSize
160
     * @param width
161
     * @param margin
162
     * @param pngDir
163
     * @param gifDir
164
     */
165
    private void renderGifCompareGallery(File outputDir, String key, List<IconEntry> icons, int iconSize, int width, int margin, String pngDir, String gifDir) {
166
        int leftColumnWidth = 300;
167
        int textHeaderHeight = 31;
168
        int outputSize = iconSize;
169
        int widthTotal = (outputSize * 4) + (margin * 6) + leftColumnWidth;
170
171
        int rowHeight = iconSize + (margin * 2);
172
173
        // Compute the height and add some room for the text header (31 px)
174
        int height = (icons.size() * rowHeight) + textHeaderHeight;
175
176
        BufferedImage bi = new BufferedImage(widthTotal + iconSize, height,
177
                BufferedImage.TYPE_INT_ARGB);
178
        Graphics2D g = bi.createGraphics();
179
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
180
                RenderingHints.VALUE_ANTIALIAS_ON);
181
182
        g.setColor(Color.GRAY);
183
        g.drawString("SVG Icon Set: " + key + " - Count: " + icons.size(), 8, 20);
184
185
        int x = leftColumnWidth;
186
        int y = textHeaderHeight;
187
188
        // Render
189
        ResampleOp resampleOp = new ResampleOp(outputSize, outputSize);
190
        resampleOp.setFilter(ResampleFilters.getLanczos3Filter());
191
        // resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Oversharpened);
192
        resampleOp.setNumberOfThreads(Runtime.getRuntime()
193
                .availableProcessors());
194
195
        int second = leftColumnWidth + margin + iconSize;
196
        
197
        g.setColor(Color.WHITE);
198
        g.fillRect(0, 0, widthTotal + 10, height);
199
        
200
        g.setColor(Color.DARK_GRAY);
201
        g.fillRect(second + (margin / 2) + iconSize + 20, 0, (margin * 2) + (iconSize * 2) + 10, height);
202
203
        g.drawString(key + " (GIF / PNG)", 15, 20);
204
        
205
        Collections.sort(icons);
206
        
207
        // Render each icon into the gallery grid
208
        for (IconEntry entry : icons) {
209
210
            if (entry.inputPath == null) {
211
                continue;
212
            }
213
214
            try {
215
                BufferedImage pngImage = ImageIO.read(entry.inputPath);
216
217
                // Munge the gif path
218
                File gifLocalPath = new File(entry.inputPath.getParentFile(), entry.nameBase + GIF_EXT);
219
                String absoluteLocalPath = gifLocalPath.getAbsolutePath();
220
                String gifAbsPath = absoluteLocalPath.replaceFirst(pngDir, gifDir);
221
                File gifPath = new File(gifAbsPath);
222
223
                log.debug("Search for GIF...");
224
                log.debug("Entry path: " + entry.inputPath.getAbsolutePath());
225
                log.debug("GIF path: " + gifPath.getAbsolutePath());
226
227
                BufferedImage gifImage = null;
228
229
                if(gifPath.exists()) {
230
                    gifImage = ImageIO.read(gifPath);
231
                } else {
232
                    log.debug("GIF not found: " + gifPath.getAbsolutePath());
233
                }
234
235
                g.drawString(entry.nameBase, 5, y + (margin * 3));
236
237
                g.drawLine(0, y, widthTotal, y);
238
239
                if(gifImage != null) {
240
                    g.drawImage(gifImage, leftColumnWidth, y + margin, null);
241
                }
242
243
                g.drawImage(pngImage, second, y + margin, null);
244
245
                if(gifImage != null) {
246
                    g.drawImage(gifImage, second + margin + iconSize + 30, y + margin, null);
247
                }
248
249
                g.drawImage(pngImage, second + (margin * 2) + (iconSize * 2) + 30, y + margin, null);
250
251
                y += iconSize + margin * 2;
252
            } catch (Exception e) {
253
                e.printStackTrace();
254
                log.error("Error rendering icon for gallery: " + entry.inputPath.getAbsolutePath());
255
                continue;
256
            }
257
        }
258
259
        try {
260
            // Write the gallery image to disk
261
            String outputName = key + "-" + iconSize + "-gifcompare.png";
262
            ImageIO.write(bi, "PNG", new File(outputDir, outputName));
263
        } catch (IOException e) {
264
            e.printStackTrace();
265
            log.error("Error writing gif comparison gallery: " + e.getMessage());
266
        }
267
    }
268
269
270
    /**
271
     * <p>Renders an icon set into a grid within an image.</p>
272
     *
273
     * @param outputRoot
274
     * @param key
275
     * @param icons
276
     */
277
    private void renderGallery(File outputRoot, String key, List<IconEntry> icons,
278
            int iconSize, int width, int margin) {
279
        int textHeaderHeight = 31;
280
        int outputSize = iconSize;
281
        int outputTotal = outputSize + (margin * 2);
282
        int div = width / outputTotal;
283
        int rowCount = icons.size() / div;
284
        
285
        if (width % outputTotal > 0) {
286
            rowCount++;
287
        }
288
289
        // Compute the height and add some room for the text header (31 px)
290
        int height = Math.max(outputTotal, rowCount * outputTotal)
291
                + textHeaderHeight;
292
293
        BufferedImage bi = new BufferedImage(width + iconSize, height,
294
                BufferedImage.TYPE_INT_ARGB);
295
        Graphics2D g = bi.createGraphics();
296
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
297
                RenderingHints.VALUE_ANTIALIAS_ON);
298
299
        g.setColor(Color.GRAY);
300
        g.drawString("SVG Icon Set: " + key + " - Count: " + icons.size(), 8, 20);
301
302
        int x = 1;
303
        int y = textHeaderHeight;
304
305
        // Render
306
        ResampleOp resampleOp = new ResampleOp(outputSize, outputSize);
307
        resampleOp.setFilter(ResampleFilters.getLanczos3Filter());
308
        // resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Oversharpened);
309
        resampleOp.setNumberOfThreads(Runtime.getRuntime().availableProcessors());
310
311
        // Render each icon into the gallery grid
312
        for (IconEntry def : icons) {
313
            try {
314
                if (def.inputPath == null) {
315
                    log.error("Undefined gallery image for : " + def.nameBase);
316
                    continue;
317
                }
318
319
                BufferedImage iconImage = ImageIO.read(def.inputPath);
320
                BufferedImage sizedImage = resampleOp.filter(iconImage, null);
321
322
                g.drawImage(sizedImage, x + margin, y + margin, null);
323
324
                x += outputTotal;
325
326
                if (x >= width) {
327
                    x = 1;
328
                    y += outputTotal;
329
                }
330
            } catch (Exception e) {
331
                log.error("Error rendering icon for gallery: " + def.inputPath.getAbsolutePath());
332
                e.printStackTrace();
333
                continue;
334
            }
335
        }
336
337
        try {
338
            // Write the gallery image to disk
339
            String outputName = key + "-" + iconSize + "-gallery.png";
340
            ImageIO.write(bi, "PNG", new File(outputRoot, outputName));
341
        } catch (IOException e) {
342
            log.error("Error writing icon: " + e.getMessage());
343
            e.printStackTrace();
344
        }
345
    }
346
347
    /**
348
     * <p>Renders a master gallery image that contains every icon set at the
349
     * current resolution.</p>
350
     * 
351
     * @param root
352
     * @param iconSize
353
     * @param width
354
     * @param dark
355
     */
356
    private void renderMasterGallery(File root, File output, String fileEnding, int iconSize, int width,
357
            boolean dark) {
358
        int headerHeight = 30;
359
        List<BufferedImage> images = new ArrayList<>();
360
        for (File file : root.listFiles()) {
361
            if (file.getName().endsWith(iconSize + fileEnding)) {
362
                BufferedImage set = null;
363
                try {
364
                    set = ImageIO.read(file);
365
                } catch (IOException e) {
366
                    log.error("Error reading icon: " + e.getMessage());
367
                    e.printStackTrace();
368
                    continue;
369
                }
370
                images.add(set);
371
                headerHeight += set.getHeight();
372
            }
373
        }
374
375
        BufferedImage bi = new BufferedImage(width, headerHeight,
376
                BufferedImage.TYPE_INT_ARGB);
377
        Graphics2D g = bi.createGraphics();
378
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
379
                RenderingHints.VALUE_ANTIALIAS_ON);
380
381
        if (dark) {
382
            g.setColor(Color.DARK_GRAY);
383
        } else {
384
            g.setColor(Color.WHITE);
385
        }
386
        g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
387
388
        g.setColor(Color.BLACK);
389
        g.drawString("SVG Icons for Eclipse - Count: "
390
                + iconSize + "x" + iconSize
391
                + " Rendered: " + new Date().toString(), 8, 20);
392
393
        int x = 0;
394
        int y = 31;
395
396
        // Draw each icon set image into the uber gallery
397
        for (BufferedImage image : images) {
398
            g.drawImage(image, x, y, null);
399
            y += image.getHeight();
400
        }
401
 
402
        try {
403
            // Write the uber gallery to disk
404
            String bgState = (dark) ? "dark" : "light";
405
            String outputName = "global-svg-" + iconSize + "-" + bgState + fileEnding + "-icons.png";
406
            ImageIO.write(bi, "PNG", new File(output, outputName));
407
        } catch (IOException e) {
408
            log.error("Error writing gallery: " + e.getMessage());
409
            e.printStackTrace();
410
        }
411
    }
412
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/images/renderer/IconEntry.java (+60 lines)
Added Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.images.renderer;
13
14
import java.io.File;
15
 
16
/**
17
 * <p>IconEntry is used to define an icon to rasterize,
18
 * where to put it and the dimensions to render it at.</p>
19
 */
20
class IconEntry implements Comparable<IconEntry> {
21
22
    /** The name of the icon minus extension */
23
    String nameBase;
24
25
    /** The input path of the source svg files. */
26
    File inputPath;
27
28
    /** The sizes this icon should be rendered at */
29
    int[] sizes;
30
31
    /**
32
     * The path rasterized versions of this icon should be written into.
33
     */
34
    File outputPath;
35
36
    /** The path to a disabled version of the icon (gets desaturated). */
37
    File disabledPath;
38
39
    /**
40
     * 
41
     * @param nameBase
42
     * @param inputPath
43
     * @param outputPath
44
     * @param disabledPath
45
     * @param sizes
46
     */
47
    public IconEntry(String nameBase, File inputPath, File outputPath,
48
            File disabledPath, int[] sizes) {
49
        this.nameBase = nameBase;
50
        this.sizes = sizes;
51
        this.inputPath = inputPath;
52
        this.outputPath = outputPath;
53
        this.disabledPath = disabledPath;
54
    }
55
56
	@Override
57
	public int compareTo(IconEntry o) {
58
		return nameBase.compareTo(o.nameBase);
59
	}
60
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/images/renderer/IconGatherer.java (+119 lines)
Added Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.images.renderer;
13
14
import java.io.File;
15
import java.net.URI;
16
import java.util.List;
17
18
/**
19
 * <p>Utility to find and organize icons for rendering.</p>
20
 * 
21
 * @author tmccrary@l33tlabs.com
22
 *
23
 */
24
public class IconGatherer {
25
26
    /**
27
     * <p>Searches the root resources directory for svg icons and adds them to a
28
     * collection for later rasterization.</p>	
29
     *
30
     * @param outputName
31
     * @param iconDir
32
     * @param outputBase
33
     * @param outputDir2
34
     */ 
35
    public static void gatherIcons(List<IconEntry> icons, String extension, File rootDir, File iconDir, File outputBase, boolean generateDisabledDirs) {
36
        File[] listFiles = iconDir.listFiles();
37
38
        for (File child : listFiles) {
39
            if (child.isDirectory()) {
40
            	if(child.getName().startsWith("d")) {
41
            		continue;
42
            	}
43
            	
44
                gatherIcons(icons, extension, rootDir, child, outputBase, generateDisabledDirs);
45
                continue;
46
            }
47
48
            if (!child.getName().endsWith(extension)) {
49
                continue;
50
            }
51
52
            // Compute a relative path for the output dir
53
            URI rootUri = rootDir.toURI();
54
            URI iconUri = iconDir.toURI();
55
56
            String relativePath = rootUri.relativize(iconUri).getPath();
57
            File outputDir = new File(outputBase, relativePath);
58
            File disabledOutputDir = null;
59
60
            File parentFile = child.getParentFile();
61
62
            /* Determine if/where to put a disabled version of the icon
63
               Eclipse traditionally uses a prefix of d for disabled, e for
64
               enabled in the folder name */
65
            if (generateDisabledDirs && parentFile != null) {
66
                String parentDirName = parentFile.getName();
67
                if (parentDirName.startsWith("e")) {
68
                    StringBuilder builder = new StringBuilder();
69
                    builder.append("d");
70
                    builder.append(parentDirName.substring(1, parentDirName.length()));
71
72
                    // Disabled variant folder name
73
                    String disabledVariant = builder.toString();
74
75
                    // The parent's parent, to create the disabled directory in
76
                    File setParent = parentFile.getParentFile();
77
78
                    // The source directory's disabled folder
79
                    File disabledSource = new File(setParent, disabledVariant);
80
81
                    // Compute a relative path, so we can create the output folder
82
                    String path = rootUri.relativize(
83
                              disabledSource.toURI()).getPath();
84
85
                    // Create the output folder, so a disabled icon is generated
86
                    disabledOutputDir = new File(outputBase, path);
87
                    if(!disabledOutputDir.exists()) {
88
                        disabledOutputDir.mkdirs();
89
                    }
90
                }
91
            }
92
93
            IconEntry icon = createIcon(child, outputDir, disabledOutputDir);
94
95
            icons.add(icon);
96
        }
97
    }
98
    
99
    /**
100
     * <p>Creates an IconEntry, which contains information about rendering an icon such
101
     * as the source file, where to render, what alternative types of output to
102
     * generate, etc.</p>
103
     *
104
     * @param input the source of the icon file (SVG document)
105
     * @param outputPath the path of the rasterized version to generate
106
     * @param disabledPath the path of the disabled (desaturated) icon, if one is required
107
     *
108
     * @return an IconEntry describing the rendering operation
109
     */
110
    public static IconEntry createIcon(File input, File outputPath, File disabledPath) {
111
        String name = input.getName();
112
        String[] split = name.split("\\.(?=[^\\.]+$)");
113
114
        IconEntry def = new IconEntry(split[0], input, outputPath, disabledPath, new int[0]);
115
116
        return def;
117
    }
118
119
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/images/renderer/RenderMojo.java (+647 lines)
Added Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.images.renderer;
13
14
import java.awt.RenderingHints;
15
import java.awt.image.BufferedImage;
16
import java.io.ByteArrayInputStream;
17
import java.io.ByteArrayOutputStream;
18
import java.io.File;
19
import java.io.FileInputStream;
20
import java.io.FileWriter;
21
import java.io.IOException;
22
import java.io.OutputStream;
23
import java.util.ArrayList;
24
import java.util.Collections;
25
import java.util.List;
26
import java.util.concurrent.Callable;
27
import java.util.concurrent.ExecutorService;
28
import java.util.concurrent.Executors;
29
import java.util.concurrent.atomic.AtomicInteger;
30
31
import javax.imageio.ImageIO;
32
33
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
34
import org.apache.batik.gvt.renderer.ImageRenderer;
35
import org.apache.batik.transcoder.ErrorHandler;
36
import org.apache.batik.transcoder.TranscoderException;
37
import org.apache.batik.transcoder.TranscoderInput;
38
import org.apache.batik.transcoder.TranscoderOutput;
39
import org.apache.batik.transcoder.image.PNGTranscoder;
40
import org.apache.batik.util.XMLResourceDescriptor;
41
import org.apache.maven.plugin.AbstractMojo;
42
import org.apache.maven.plugin.MojoExecutionException;
43
import org.apache.maven.plugin.MojoFailureException;
44
import org.apache.maven.plugin.logging.Log;
45
import org.w3c.dom.Element;
46
import org.w3c.dom.svg.SVGDocument;
47
48
import com.jhlabs.image.ContrastFilter;
49
import com.jhlabs.image.GrayscaleFilter;
50
import com.jhlabs.image.HSBAdjustFilter;
51
52
/**
53
 * <p>Mojo which renders SVG icons into PNG format.</p>
54
 *
55
 * @goal render-icons
56
 * @phase generate-resources
57
 */
58
public class RenderMojo extends AbstractMojo {
59
60
    /** Maven logger */
61
    Log log;
62
63
    /** Used for high res rendering support. */
64
    public static final String ECLIPSE_SVG_SCALE = "eclipse.svg.scale";
65
66
    /** Used to specify the number of render threads when rasterizing icons. */
67
    public static final String RENDERTHREADS = "eclipse.svg.renderthreads";
68
69
    /** Used to specify the directory name where the SVGs are taken from. */
70
    public static final String SOURCE_DIR = "eclipse.svg.sourcedirectory";
71
72
    /** Used to specify the directory name where the PNGs are saved to. */
73
    public static final String TARGET_DIR = "eclipse.svg.targetdirectory";
74
75
    /** A list of directories with svg sources to rasterize. */
76
    private List<IconEntry> icons;
77
78
    /** The pool used to render multiple icons concurrently. */
79
    private ExecutorService execPool;
80
81
    /** The number of threads to use when rendering icons. */
82
    private int threads;
83
84
    /**
85
     * A counter used to keep track of the number of rendered icons. Atomic is
86
     * used to make it easy to access between threads concurrently.
87
     */
88
    private AtomicInteger counter;
89
90
    /** List of icons that failed to render, made safe for parallel access */
91
    List<IconEntry> failedIcons = Collections
92
            .synchronizedList(new ArrayList<IconEntry>(5));
93
94
    /** The amount of scaling to apply to rasterized images. */
95
    private double outputScale;
96
97
    /**
98
     * @return the number of icons rendered at the time of the call
99
     */
100
    public int getIconsRendered() {
101
        return counter.get();
102
    }
103
104
    /**
105
     * @return the number of icons that failed during the rendering process
106
     */
107
    public int getFailedIcons() {
108
        return failedIcons.size();
109
    }
110
111
    /**
112
     * <p>Generates raster images from the input SVG vector image.</p>
113
     *
114
     * @param icon
115
     *            the icon to render
116
     */
117
    public void rasterize(IconEntry icon, GrayscaleFilter grayFilter, HSBAdjustFilter desaturator, ContrastFilter decontrast) {
118
        if (icon == null) {
119
            log.error("Null icon definition, skipping.");
120
            failedIcons.add(icon);
121
            return;
122
        }
123
124
        if (icon.inputPath == null) {
125
            log.error("Null icon input path, skipping: "
126
                    + icon.nameBase);
127
            failedIcons.add(icon);
128
            return;
129
        }
130
131
        if (!icon.inputPath.exists()) {
132
            log.error("Input path specified does not exist, skipping: "
133
                            + icon.nameBase);
134
            failedIcons.add(icon);
135
            return;
136
        }
137
138
        if (icon.outputPath != null && !icon.outputPath.exists()) {
139
            icon.outputPath.mkdirs();
140
        }
141
142
        if (icon.disabledPath != null && !icon.disabledPath.exists()) {
143
            icon.disabledPath.mkdirs();
144
        }
145
146
        // Create the document to rasterize
147
        SVGDocument svgDocument = generateSVGDocument(icon);
148
149
        if(svgDocument == null) {
150
            return;
151
        }
152
153
        // Determine the output sizes (native, double, quad)
154
        // We render at quad size and resample down for output
155
        Element svgDocumentNode = svgDocument.getDocumentElement();
156
        String nativeWidthStr = svgDocumentNode.getAttribute("width");
157
        String nativeHeightStr = svgDocumentNode.getAttribute("height");
158
        int nativeWidth = -1;
159
        int nativeHeight = -1;
160
161
        try{
162
            if (nativeWidthStr != "" && nativeHeightStr != ""){
163
                nativeWidth = Integer.parseInt(nativeWidthStr);
164
                nativeHeight = Integer.parseInt(nativeHeightStr);
165
            } else {
166
                // Vector graphics editing programs don't always output height and width attributes on SVG.
167
                // As fall back: parse the viewBox attribute (which is almost always set).
168
                String viewBoxStr = svgDocumentNode.getAttribute("viewBox");
169
                if (viewBoxStr == ""){
170
                    log.error("Icon defines neither width/height nor a viewBox, skipping: " + icon.nameBase);
171
                    failedIcons.add(icon);
172
                    return;
173
                }
174
                String[] splitted = viewBoxStr.split(" ");
175
                String xStr = splitted[0];
176
                String yStr = splitted[1];
177
                String widthStr = splitted[2];
178
                String heightStr = splitted[3];
179
                nativeWidth = Integer.parseInt(widthStr) - Integer.parseInt(xStr);
180
                nativeHeight = Integer.parseInt(heightStr) - Integer.parseInt(yStr); 
181
            }
182
        }catch (NumberFormatException e){
183
            log.error("Dimension could not be parsed ( "+e.getMessage()+ "), skipping: " + icon.nameBase);
184
            failedIcons.add(icon);
185
            return;
186
        }
187
188
        int outputWidth = (int) (nativeWidth * outputScale);
189
        int outputHeight = (int) (nativeHeight * outputScale);
190
191
        // Guesstimate the PNG size in memory, BAOS will enlarge if necessary.
192
        int outputInitSize = nativeWidth * nativeHeight * 4 + 1024;
193
        ByteArrayOutputStream iconOutput = new ByteArrayOutputStream(
194
                outputInitSize);
195
196
        // Render to SVG
197
        try {
198
            log.info(Thread.currentThread().getName() + " "
199
                    + " Rasterizing: " + icon.nameBase + ".png at " + outputWidth
200
                    + "x" + outputHeight);
201
202
            TranscoderInput svgInput = new TranscoderInput(svgDocument);
203
204
            boolean success = renderIcon(icon.nameBase, outputWidth, outputHeight, svgInput, iconOutput);
205
206
            if (!success) {
207
                log.error("Failed to render icon: " + icon.nameBase + ".png, skipping.");
208
                failedIcons.add(icon);
209
                return;
210
            }
211
        } catch (Exception e) {
212
            log.error("Failed to render icon: " + e.getMessage());
213
            failedIcons.add(icon);
214
            return;
215
        }
216
217
        // Generate a buffered image from Batik's png output
218
        byte[] imageBytes = iconOutput.toByteArray();
219
        ByteArrayInputStream imageInputStream = new ByteArrayInputStream(imageBytes);
220
221
        BufferedImage inputImage = null;
222
        try {
223
            inputImage = ImageIO.read(imageInputStream);
224
225
            if(inputImage == null) {
226
                log.error("Failed to generate BufferedImage from rendered icon, ImageIO returned null: " + icon.nameBase);
227
                failedIcons.add(icon);
228
                return;
229
            }
230
        } catch (IOException e2) {
231
            log.error("Failed to generate BufferedImage from rendered icon: "  + icon.nameBase + " - " + e2.getMessage());
232
            failedIcons.add(icon);
233
            return;
234
        }
235
236
        writeIcon(icon, outputWidth, outputHeight, inputImage);
237
238
        try {
239
            if (icon.disabledPath != null) {
240
                BufferedImage desaturated16 = desaturator.filter(
241
                    grayFilter.filter(inputImage, null), null);
242
243
                BufferedImage deconstrast = decontrast.filter(desaturated16, null);
244
245
                ImageIO.write(deconstrast, "PNG", new File(icon.disabledPath, icon.nameBase + ".png"));
246
            }
247
        } catch (Exception e1) {
248
            log.error("Failed to render disabled icon: "  +
249
                               icon.nameBase, e1);
250
            failedIcons.add(icon);
251
        }
252
    }
253
254
    /**
255
     * <p>Generates a Batik SVGDocument for the supplied IconEntry's input
256
     * file.</p>
257
     *
258
     * @param icon the icon entry to generate an SVG document for
259
     *
260
     * @return a batik SVGDocument instance or null if one could not be generated
261
     */
262
    private SVGDocument generateSVGDocument(IconEntry icon) {
263
        // Load the document and find out the native height/width
264
        // We reuse the document later for rasterization
265
        SVGDocument svgDocument = null;
266
        try {
267
            FileInputStream iconDocumentStream = new FileInputStream(icon.inputPath);
268
269
            String parser = XMLResourceDescriptor.getXMLParserClassName();
270
            SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
271
272
            // What kind of URI is batik expecting here??? the docs don't say
273
            svgDocument = f.createSVGDocument("file://" + icon.nameBase + ".svg", iconDocumentStream);
274
        } catch (Exception e3) {
275
            log.error("Error parsing SVG icon document: " + e3.getMessage());
276
            failedIcons.add(icon);
277
            return null;
278
        }
279
        return svgDocument;
280
    }
281
282
    /**
283
     * <p>Resizes the supplied inputImage to the specified width and height, using
284
     * lanczos resampling techniques.</p>
285
     *
286
     * @param icon the icon that's being resized
287
     * @param width the desired output width after rescaling operations
288
     * @param height the desired output height after rescaling operations
289
     * @param sourceImage the source image to resource
290
     */
291
    private void writeIcon(IconEntry icon, int width, int height, BufferedImage sourceImage) {
292
        try {
293
            String outputName = icon.nameBase;
294
            if (outputScale != 1) {
295
                String scaleId = outputScale == (double) (int) outputScale ? Integer.toString((int) outputScale): Double.toString(outputScale);
296
                outputName += "@" + scaleId + "x";
297
            }
298
            outputName += ".png";
299
            ImageIO.write(sourceImage, "PNG", new File(icon.outputPath, outputName));
300
        } catch (Exception e1) {
301
            log.error("Failed to resize rendered icon to output size: "  +
302
                               icon.nameBase, e1);
303
            failedIcons.add(icon);
304
        }
305
    }
306
307
    /**
308
     * <p>Handles concurrently rasterizing the icons to
309
     * reduce the duration on multicore systems.</p>
310
     */
311
    public void rasterizeAll() {
312
        // The number of icons that haven't been distributed to
313
        // callables
314
        int remainingIcons = icons.size();
315
316
        // The number of icons to distribute to a rendering callable
317
        final int threadExecSize = Math.max(1, icons.size() / this.threads);
318
319
        // The current offset to start a batch, as they're distributed
320
        // between rendering callables
321
        int batchOffset = 0;
322
323
        // A list of callables used to render icons on multiple threads
324
        // Each callable gets a set of icons to render
325
        List<Callable<Object>> tasks = new ArrayList<>(
326
                this.threads);
327
328
        // Distribute the rasterization operations between multiple threads
329
        while (remainingIcons > 0) {
330
            // The current start index for the current batch
331
            final int batchStart = batchOffset;
332
333
            // Increment the offset to reflect this batch (used for the next batch)
334
            batchOffset += threadExecSize;
335
336
            // Determine this batch size, used for batches that have less than
337
            // threadExecSize at the end of the distribution operation
338
            int batchSize = 0;
339
340
            // Determine if we can fit a full batch in this callable
341
            // or if we are at the end of gathered icons
342
            if (remainingIcons > threadExecSize) {
343
                batchSize = threadExecSize;
344
            } else {
345
                // We have less than a full batch worth of remaining icons
346
                // just add them all
347
                batchSize = remainingIcons;
348
            }
349
350
            // Deincrement the remaining Icons
351
            remainingIcons -= threadExecSize;
352
353
            // Used for access in the callable's scope
354
            final int execCount = batchSize;
355
356
            // Create the callable and add it to the task pool
357
            Callable<Object> runnable = new Callable<Object>() {
358
                @Override
359
                public Object call() throws Exception {
360
                    // The jhlabs filters are not thread safe, so provide one set per thread
361
                    GrayscaleFilter grayFilter = new GrayscaleFilter();
362
363
                    HSBAdjustFilter desaturator = new HSBAdjustFilter();
364
                         desaturator.setSFactor(0.0f);
365
366
                    ContrastFilter decontrast = new ContrastFilter();
367
                         decontrast.setBrightness(2.9f);
368
                         decontrast.setContrast(0.2f);
369
370
                    // Rasterize this batch
371
                    for (int count = 0; count < execCount; count++) {
372
                        rasterize(icons.get(batchStart + count), grayFilter, desaturator, decontrast);
373
                    }
374
375
                    // Update the render counter
376
                    counter.getAndAdd(execCount);
377
                    log.info("Finished rendering batch, index: " + batchStart);
378
379
                    return null;
380
                }
381
            };
382
383
            tasks.add(runnable);
384
        }
385
386
        // Execute the rasterization operations that
387
        // have been added to the pool
388
        try {
389
            execPool.invokeAll(tasks);
390
        } catch (InterruptedException e) {
391
            // TODO Auto-generated catch block
392
            e.printStackTrace();
393
        }
394
395
        // Print info about failed render operations, so they can be fixed
396
        log.info("Failed Icon Count: " + failedIcons.size());
397
        for (IconEntry icon : failedIcons) {
398
            log.info("Failed Icon: " + icon.nameBase);
399
        }
400
401
    }
402
403
    /**
404
     * Use batik to rasterize the input SVG into a raster image at the specified
405
     * image dimensions.
406
     *
407
     * @param width the width to render the icons at
408
     * @param height the height to render the icon at
409
     * @param input the SVG transcoder input
410
     * @param stream the stream to write the PNG data to
411
     */
412
    public boolean renderIcon(final String iconName, int width, int height,
413
            TranscoderInput tinput, OutputStream stream) {
414
        PNGTranscoder transcoder = new PNGTranscoder() {
415
            protected ImageRenderer createRenderer() {
416
                ImageRenderer renderer = super.createRenderer();
417
418
                RenderingHints renderHints = renderer.getRenderingHints();
419
420
                renderHints.add(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
421
                    RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
422
423
                renderHints.add(new RenderingHints(RenderingHints.KEY_RENDERING,
424
                    RenderingHints.VALUE_RENDER_QUALITY));
425
426
                renderHints.add(new RenderingHints(RenderingHints.KEY_DITHERING,
427
                    RenderingHints.VALUE_DITHER_DISABLE));
428
429
                renderHints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION,
430
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC));
431
432
                renderHints.add(new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION,
433
                    RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY));
434
435
                renderHints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
436
                    RenderingHints.VALUE_ANTIALIAS_ON));
437
438
                renderHints.add(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING,
439
                    RenderingHints.VALUE_COLOR_RENDER_QUALITY));
440
441
                renderHints.add(new RenderingHints(RenderingHints.KEY_STROKE_CONTROL,
442
                    RenderingHints.VALUE_STROKE_PURE));
443
444
                renderHints.add(new RenderingHints(RenderingHints.KEY_FRACTIONALMETRICS,
445
                    RenderingHints.VALUE_FRACTIONALMETRICS_ON));
446
447
                renderer.setRenderingHints(renderHints);
448
449
                return renderer;
450
            }
451
        };
452
453
        transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, new Float(width));
454
        transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, new Float(height));
455
456
        transcoder.setErrorHandler(new ErrorHandler() {
457
            public void warning(TranscoderException arg0)
458
                    throws TranscoderException {
459
                log.error("Icon: " + iconName + " - WARN: " + arg0.getMessage());
460
            }
461
462
            public void fatalError(TranscoderException arg0)
463
                    throws TranscoderException {
464
                log.error("Icon: " + iconName + " - FATAL: " + arg0.getMessage());
465
            }
466
467
            public void error(TranscoderException arg0)
468
                    throws TranscoderException {
469
                log.error("Icon: " + iconName + " - ERROR: " + arg0.getMessage());
470
            }
471
        });
472
473
        // Transcode the SVG document input to a PNG via the output stream
474
        TranscoderOutput output = new TranscoderOutput(stream);
475
476
        try {
477
            transcoder.transcode(tinput, output);
478
            return true;
479
        } catch (Exception e) {
480
            e.printStackTrace();
481
            return false;
482
        } finally {
483
            try {
484
                stream.close();
485
            } catch (IOException e) {
486
                // TODO Auto-generated catch block
487
                e.printStackTrace();
488
            }
489
        }
490
    }
491
492
    /**
493
     * <p>Initializes rasterizer defaults</p>
494
     *
495
     * @param threads the number of threads to render with
496
     * @param scale multiplier to use with icon output dimensions
497
     */
498
    private void init(int threads, double scale) {
499
        this.threads = threads;
500
        this.outputScale = Math.max(1, scale);
501
        icons = new ArrayList<>();
502
        execPool = Executors.newFixedThreadPool(threads);
503
        counter = new AtomicInteger();
504
    }
505
506
    /**
507
     * @see AbstractMojo#execute()
508
     */
509
    public void execute() throws MojoExecutionException, MojoFailureException {
510
        log = getLog();
511
512
        // Default to 2x the number of processor cores but allow override via jvm arg
513
        int threads = Math.max(1, Runtime.getRuntime().availableProcessors() * 2);
514
        String threadStr = System.getProperty(RENDERTHREADS);
515
        if (threadStr != null) {
516
            try {
517
                threads = Integer.parseInt(threadStr);
518
            } catch (Exception e) {
519
                e.printStackTrace();
520
                System.out
521
                        .println("Could not parse thread count, using default thread count");
522
            }
523
        }
524
525
        // if high res is enabled, the icons output size will be scaled by iconScale
526
        // Defaults to 1, meaning native size
527
        double iconScale = 1;
528
        String iconScaleStr = System.getProperty(ECLIPSE_SVG_SCALE);
529
        if (iconScaleStr != null) {
530
            iconScale = Double.parseDouble(iconScaleStr);
531
            if (iconScale != 1 && iconScale != 1.5 && iconScale != 2) {
532
                log.warn("Unusual scale factor: " + iconScaleStr + " (@" + iconScale + "x)");
533
            }
534
        }
535
536
        // Defaults to "eclipse-svg"
537
        String sourceDir = "eclipse-svg";
538
        String sourceDirProp = System.getProperty(SOURCE_DIR);
539
        if (sourceDirProp != null) {
540
            sourceDir = sourceDirProp;
541
        }
542
543
        // Defaults to "eclipse-png"
544
        String targetDir = "eclipse-png";
545
        String targetDirProp = System.getProperty(TARGET_DIR);
546
        if (targetDirProp != null) {
547
            targetDir = targetDirProp;
548
        }
549
550
        // Track the time it takes to render the entire set
551
        long totalStartTime = System.currentTimeMillis();
552
553
        // initialize defaults (the old renderer was instantiated via constructor)
554
        init(threads, iconScale);
555
556
        String workingDirectory = System.getProperty("user.dir");
557
558
        File outputDir = new File(workingDirectory + (iconScale == 1 ? "/" + targetDir + "/" : "/" + targetDir + "-highdpi/"));
559
        File iconDirectoryRoot = new File(sourceDir + "/");
560
        
561
        if (!iconDirectoryRoot.exists()){
562
            log.error("Source directory' "+sourceDir+"' does not exist.");
563
            return;
564
        }
565
566
        // Search each subdir in the root dir for svg icons
567
        for (File file : iconDirectoryRoot.listFiles()) {
568
            if(!file.isDirectory()) {
569
                continue;
570
            }
571
572
            String dirName = file.getName();
573
574
            // Where to place the rendered icon
575
            File outputBase = new File(outputDir, (iconScale == 1 ? dirName : dirName + ".highdpi"));
576
            if (iconScale != 1) {
577
                createFragmentFiles(outputBase, dirName);
578
            }
579
580
            IconGatherer.gatherIcons(icons, "svg", file, file, outputBase, true);
581
        }
582
583
        log.info("Working directory: " + outputDir.getAbsolutePath());
584
        log.info("SVG Icon Directory: " + iconDirectoryRoot.getAbsolutePath());
585
        log.info("Rendering icons with " + threads + " threads, scaling output to " + iconScale + "x");
586
        long startTime = System.currentTimeMillis();
587
588
        // Render the icons
589
        rasterizeAll();
590
591
        // Print summary of operations
592
        int iconRendered = getIconsRendered();
593
        int failedIcons = getFailedIcons();
594
        int fullIconCount = iconRendered - failedIcons;
595
596
        log.info(fullIconCount + " Icons Rendered");
597
        log.info(failedIcons + " Icons Failed");
598
        log.info("Took: "    + (System.currentTimeMillis() - startTime) + " ms.");
599
600
        log.info("Rasterization operations completed, Took: "
601
                + (System.currentTimeMillis() - totalStartTime) + " ms.");
602
    }
603
604
    private void createFragmentFiles(File outputBase, String dirName) {
605
        createFile(new File(outputBase, "build.properties"), "bin.includes = META-INF/,icons/,.\n");
606
        createFile(new File(outputBase, ".project"), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
607
                "<projectDescription>\n" + 
608
                "    <name>" + dirName + ".highdpi</name>\n" + 
609
                "    <comment></comment>\n" + 
610
                "    <projects>\n" + 
611
                "    </projects>\n" + 
612
                "    <buildSpec>\n" + 
613
                "        <buildCommand>\n" + 
614
                "            <name>org.eclipse.pde.ManifestBuilder</name>\n" + 
615
                "            <arguments>\n" + 
616
                "            </arguments>\n" + 
617
                "        </buildCommand>\n" + 
618
                "        <buildCommand>\n" + 
619
                "            <name>org.eclipse.pde.SchemaBuilder</name>\n" + 
620
                "            <arguments>\n" + 
621
                "            </arguments>\n" + 
622
                "        </buildCommand>\n" + 
623
                "    </buildSpec>\n" + 
624
                "    <natures>\n" + 
625
                "        <nature>org.eclipse.pde.PluginNature</nature>\n" + 
626
                "    </natures>\n" + 
627
                "</projectDescription>\n");
628
        createFile(new File(outputBase, "META-INF/MANIFEST.MF"), "Manifest-Version: 1.0\n" + 
629
                "Bundle-ManifestVersion: 2\n" + 
630
                "Bundle-Name: " + dirName + ".highdpi\n" + 
631
                "Bundle-SymbolicName: " + dirName + ".highdpi\n" + 
632
                "Bundle-Version: 0.1.0.qualifier\n" + 
633
                "Fragment-Host: " + dirName + "\n");
634
    }
635
636
    private void createFile(File file, String contents) {
637
        try {
638
            file.getParentFile().mkdirs();
639
            FileWriter writer = new FileWriter(file);
640
            writer.write(contents);
641
            writer.close();
642
        } catch (IOException e) {
643
            log.error(e);
644
        }
645
    }
646
647
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/ui/images/renderer/GalleryMojo.java (-408 lines)
Lines 1-408 Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.ui.images.renderer;
13
14
import java.awt.Color;
15
import java.awt.Graphics2D;
16
import java.awt.RenderingHints;
17
import java.awt.image.BufferedImage;
18
import java.io.File;
19
import java.io.IOException;
20
import java.util.ArrayList;
21
import java.util.Collections;
22
import java.util.Date;
23
import java.util.HashMap;
24
import java.util.List;
25
import java.util.Map;
26
import java.util.Map.Entry;
27
28
import javax.imageio.ImageIO;
29
30
import org.apache.maven.plugin.AbstractMojo;
31
import org.apache.maven.plugin.MojoExecutionException;
32
import org.apache.maven.plugin.MojoFailureException;
33
import org.apache.maven.plugin.logging.Log;
34
35
import com.mortennobel.imagescaling.ResampleFilters;
36
import com.mortennobel.imagescaling.ResampleOp;
37
38
/**
39
 * <p>Mojo which renders galleries for comparing
40
 * and evaluating icons..</p>
41
 *
42
 * @goal render-galleries
43
 * @phase generate-resources
44
 */
45
public class GalleryMojo extends AbstractMojo {
46
47
    /** Maven logger */
48
    Log log;
49
50
    /** Used for finding gif files by extension. */
51
    public static final String GIF_EXT = ".gif";
52
    
53
    /** Used to specify the directory name where the SVGs are taken from. */
54
    public static final String PNG_DIR = "eclipse.svg.pngdirectory";
55
56
    /** Used to specify the directory name where the SVGs are taken from. */
57
    public static final String GIF_DIR = "eclipse.svg.gifdirectory"; 
58
59
    /**
60
     * <p>Mojo takes rendered images and generates various galleries for
61
     * testing and evaluation.</p>
62
     */
63
    public void execute() throws MojoExecutionException, MojoFailureException {
64
        log = getLog();
65
66
        // Defaults to "eclipse-png"
67
        String pngDir = "eclipse-png";
68
        String pngDirProp = System.getProperty(PNG_DIR);
69
        if (pngDirProp != null) {
70
            pngDir = pngDirProp;
71
        }
72
73
        // Defaults to "eclipse-gif"
74
        String gifDir = "eclipse-gif";
75
        String gifDirProp = System.getProperty(GIF_DIR);
76
        if (gifDirProp != null) {
77
            gifDir = gifDirProp;
78
        }
79
80
        File iconDirectoryRoot = new File(pngDir + "/");
81
82
        Map<String, List<IconEntry>> galleryIconSets = new HashMap<>();
83
84
        // Search each subdir in the root dir for svg icons
85
        for (File file : iconDirectoryRoot.listFiles()) {
86
            if(!file.isDirectory()) {
87
                continue;
88
            }
89
90
            List<IconEntry> icons = new ArrayList<>();
91
            IconGatherer.gatherIcons(icons, "png", file, file, iconDirectoryRoot, false);
92
93
            galleryIconSets.put(file.getName(), icons);
94
        }
95
96
        File mavenTargetDir = new File("target/");
97
        File galleryDir = new File(mavenTargetDir, "gallery/");
98
        File gifCompare = new File(galleryDir, "gifcompare/");
99
        File master = new File(galleryDir, "master/");
100
101
        if(galleryDir.exists()) {
102
            galleryDir.delete();
103
        }
104
105
        galleryDir.mkdirs();
106
        gifCompare.mkdirs();
107
        master.mkdirs();
108
109
        renderGalleries(galleryDir, gifCompare, master, galleryIconSets, 16, 800, pngDir, gifDir);
110
    }
111
112
    /**
113
     * <p>Renders each icon set into a gallery image for reviewing and showing off
114
     * icons, and then composes them into a master gallery image.</p>
115
     *
116
     * @param rasterizer
117
     * @param galleryDir
118
     * @param gifCompare
119
     * @param master
120
     * @param iconSize
121
     * @param width
122
     * @param pngDir
123
     * @param gifDir
124
     */
125
    public void renderGalleries(File galleryDir,  File gifCompare, File master, Map<String, List<IconEntry>> iconSets, int iconSize, int width, String pngDir, String gifDir) {
126
        // Render each icon set and a master list
127
        List<IconEntry> masterList = new ArrayList<>();
128
129
        for (Entry<String, List<IconEntry>> entry : iconSets.entrySet()) {
130
            String key = entry.getKey();
131
            List<IconEntry> value = entry.getValue();
132
133
            masterList.addAll(value);
134
135
            log.info("Creating gallery for: " + key);
136
            renderGallery(galleryDir, key, value, iconSize, width, 3);
137
            renderGifCompareGallery(gifCompare, key, value, iconSize, width, 6, pngDir, gifDir);
138
        }
139
140
        // Render the master image
141
        log.info("Rendering master icon gallery...");
142
        renderMasterGallery(galleryDir, master, "-gallery.png", iconSize, iconSize + width, true);
143
        renderMasterGallery(galleryDir, master, "-gallery.png", iconSize, iconSize + width, false);
144
145
        // Master gif compare
146
        //renderMasterGallery(outputDir, "-gifcompare.png", iconSize, iconSize + width, false);
147
    }
148
149
    /**
150
     * <p>Renders comparison images, the new png/svg icons vs old gifs.</p>
151
     *
152
     * @param outputDir
153
     * @param key
154
     * @param icons
155
     * @param iconSize
156
     * @param width
157
     * @param margin
158
     * @param pngDir
159
     * @param gifDir
160
     */
161
    private void renderGifCompareGallery(File outputDir, String key, List<IconEntry> icons, int iconSize, int width, int margin, String pngDir, String gifDir) {
162
        int leftColumnWidth = 300;
163
        int textHeaderHeight = 31;
164
        int outputSize = iconSize;
165
        int widthTotal = (outputSize * 4) + (margin * 6) + leftColumnWidth;
166
167
        int rowHeight = iconSize + (margin * 2);
168
169
        // Compute the height and add some room for the text header (31 px)
170
        int height = (icons.size() * rowHeight) + textHeaderHeight;
171
172
        BufferedImage bi = new BufferedImage(widthTotal + iconSize, height,
173
                BufferedImage.TYPE_INT_ARGB);
174
        Graphics2D g = bi.createGraphics();
175
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
176
                RenderingHints.VALUE_ANTIALIAS_ON);
177
178
        g.setColor(Color.GRAY);
179
        g.drawString("SVG Icon Set: " + key + " - Count: " + icons.size(), 8, 20);
180
181
        int x = leftColumnWidth;
182
        int y = textHeaderHeight;
183
184
        // Render
185
        ResampleOp resampleOp = new ResampleOp(outputSize, outputSize);
186
        resampleOp.setFilter(ResampleFilters.getLanczos3Filter());
187
        // resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Oversharpened);
188
        resampleOp.setNumberOfThreads(Runtime.getRuntime()
189
                .availableProcessors());
190
191
        int second = leftColumnWidth + margin + iconSize;
192
        
193
        g.setColor(Color.WHITE);
194
        g.fillRect(0, 0, widthTotal + 10, height);
195
        
196
        g.setColor(Color.DARK_GRAY);
197
        g.fillRect(second + (margin / 2) + iconSize + 20, 0, (margin * 2) + (iconSize * 2) + 10, height);
198
199
        g.drawString(key + " (GIF / PNG)", 15, 20);
200
        
201
        Collections.sort(icons);
202
        
203
        // Render each icon into the gallery grid
204
        for (IconEntry entry : icons) {
205
206
            if (entry.inputPath == null) {
207
                continue;
208
            }
209
210
            try {
211
                BufferedImage pngImage = ImageIO.read(entry.inputPath);
212
213
                // Munge the gif path
214
                File gifLocalPath = new File(entry.inputPath.getParentFile(), entry.nameBase + GIF_EXT);
215
                String absoluteLocalPath = gifLocalPath.getAbsolutePath();
216
                String gifAbsPath = absoluteLocalPath.replaceFirst(pngDir, gifDir);
217
                File gifPath = new File(gifAbsPath);
218
219
                log.debug("Search for GIF...");
220
                log.debug("Entry path: " + entry.inputPath.getAbsolutePath());
221
                log.debug("GIF path: " + gifPath.getAbsolutePath());
222
223
                BufferedImage gifImage = null;
224
225
                if(gifPath.exists()) {
226
                    gifImage = ImageIO.read(gifPath);
227
                } else {
228
                    log.debug("GIF not found: " + gifPath.getAbsolutePath());
229
                }
230
231
                g.drawString(entry.nameBase, 5, y + (margin * 3));
232
233
                g.drawLine(0, y, widthTotal, y);
234
235
                if(gifImage != null) {
236
                    g.drawImage(gifImage, leftColumnWidth, y + margin, null);
237
                }
238
239
                g.drawImage(pngImage, second, y + margin, null);
240
241
                if(gifImage != null) {
242
                    g.drawImage(gifImage, second + margin + iconSize + 30, y + margin, null);
243
                }
244
245
                g.drawImage(pngImage, second + (margin * 2) + (iconSize * 2) + 30, y + margin, null);
246
247
                y += iconSize + margin * 2;
248
            } catch (Exception e) {
249
                e.printStackTrace();
250
                log.error("Error rendering icon for gallery: " + entry.inputPath.getAbsolutePath());
251
                continue;
252
            }
253
        }
254
255
        try {
256
            // Write the gallery image to disk
257
            String outputName = key + "-" + iconSize + "-gifcompare.png";
258
            ImageIO.write(bi, "PNG", new File(outputDir, outputName));
259
        } catch (IOException e) {
260
            e.printStackTrace();
261
            log.error("Error writing gif comparison gallery: " + e.getMessage());
262
        }
263
    }
264
265
266
    /**
267
     * <p>Renders an icon set into a grid within an image.</p>
268
     *
269
     * @param outputRoot
270
     * @param key
271
     * @param icons
272
     */
273
    private void renderGallery(File outputRoot, String key, List<IconEntry> icons,
274
            int iconSize, int width, int margin) {
275
        int textHeaderHeight = 31;
276
        int outputSize = iconSize;
277
        int outputTotal = outputSize + (margin * 2);
278
        int div = width / outputTotal;
279
        int rowCount = icons.size() / div;
280
        
281
        if (width % outputTotal > 0) {
282
            rowCount++;
283
        }
284
285
        // Compute the height and add some room for the text header (31 px)
286
        int height = Math.max(outputTotal, rowCount * outputTotal)
287
                + textHeaderHeight;
288
289
        BufferedImage bi = new BufferedImage(width + iconSize, height,
290
                BufferedImage.TYPE_INT_ARGB);
291
        Graphics2D g = bi.createGraphics();
292
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
293
                RenderingHints.VALUE_ANTIALIAS_ON);
294
295
        g.setColor(Color.GRAY);
296
        g.drawString("SVG Icon Set: " + key + " - Count: " + icons.size(), 8, 20);
297
298
        int x = 1;
299
        int y = textHeaderHeight;
300
301
        // Render
302
        ResampleOp resampleOp = new ResampleOp(outputSize, outputSize);
303
        resampleOp.setFilter(ResampleFilters.getLanczos3Filter());
304
        // resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Oversharpened);
305
        resampleOp.setNumberOfThreads(Runtime.getRuntime().availableProcessors());
306
307
        // Render each icon into the gallery grid
308
        for (IconEntry def : icons) {
309
            try {
310
                if (def.inputPath == null) {
311
                    log.error("Undefined gallery image for : " + def.nameBase);
312
                    continue;
313
                }
314
315
                BufferedImage iconImage = ImageIO.read(def.inputPath);
316
                BufferedImage sizedImage = resampleOp.filter(iconImage, null);
317
318
                g.drawImage(sizedImage, x + margin, y + margin, null);
319
320
                x += outputTotal;
321
322
                if (x >= width) {
323
                    x = 1;
324
                    y += outputTotal;
325
                }
326
            } catch (Exception e) {
327
                log.error("Error rendering icon for gallery: " + def.inputPath.getAbsolutePath());
328
                e.printStackTrace();
329
                continue;
330
            }
331
        }
332
333
        try {
334
            // Write the gallery image to disk
335
            String outputName = key + "-" + iconSize + "-gallery.png";
336
            ImageIO.write(bi, "PNG", new File(outputRoot, outputName));
337
        } catch (IOException e) {
338
            log.error("Error writing icon: " + e.getMessage());
339
            e.printStackTrace();
340
        }
341
    }
342
343
    /**
344
     * <p>Renders a master gallery image that contains every icon set at the
345
     * current resolution.</p>
346
     * 
347
     * @param root
348
     * @param iconSize
349
     * @param width
350
     * @param dark
351
     */
352
    private void renderMasterGallery(File root, File output, String fileEnding, int iconSize, int width,
353
            boolean dark) {
354
        int headerHeight = 30;
355
        List<BufferedImage> images = new ArrayList<>();
356
        for (File file : root.listFiles()) {
357
            if (file.getName().endsWith(iconSize + fileEnding)) {
358
                BufferedImage set = null;
359
                try {
360
                    set = ImageIO.read(file);
361
                } catch (IOException e) {
362
                    log.error("Error reading icon: " + e.getMessage());
363
                    e.printStackTrace();
364
                    continue;
365
                }
366
                images.add(set);
367
                headerHeight += set.getHeight();
368
            }
369
        }
370
371
        BufferedImage bi = new BufferedImage(width, headerHeight,
372
                BufferedImage.TYPE_INT_ARGB);
373
        Graphics2D g = bi.createGraphics();
374
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
375
                RenderingHints.VALUE_ANTIALIAS_ON);
376
377
        if (dark) {
378
            g.setColor(Color.DARK_GRAY);
379
        } else {
380
            g.setColor(Color.WHITE);
381
        }
382
        g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
383
384
        g.setColor(Color.BLACK);
385
        g.drawString("SVG Icons for Eclipse - Count: "
386
                + iconSize + "x" + iconSize
387
                + " Rendered: " + new Date().toString(), 8, 20);
388
389
        int x = 0;
390
        int y = 31;
391
392
        // Draw each icon set image into the uber gallery
393
        for (BufferedImage image : images) {
394
            g.drawImage(image, x, y, null);
395
            y += image.getHeight();
396
        }
397
 
398
        try {
399
            // Write the uber gallery to disk
400
            String bgState = (dark) ? "dark" : "light";
401
            String outputName = "global-svg-" + iconSize + "-" + bgState + fileEnding + "-icons.png";
402
            ImageIO.write(bi, "PNG", new File(output, outputName));
403
        } catch (IOException e) {
404
            log.error("Error writing gallery: " + e.getMessage());
405
            e.printStackTrace();
406
        }
407
    }
408
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/ui/images/renderer/IconEntry.java (-60 lines)
Lines 1-60 Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.ui.images.renderer;
13
14
import java.io.File;
15
 
16
/**
17
 * <p>IconEntry is used to define an icon to rasterize,
18
 * where to put it and the dimensions to render it at.</p>
19
 */
20
class IconEntry implements Comparable<IconEntry> {
21
22
    /** The name of the icon minus extension */
23
    String nameBase;
24
25
    /** The input path of the source svg files. */
26
    File inputPath;
27
28
    /** The sizes this icon should be rendered at */
29
    int[] sizes;
30
31
    /**
32
     * The path rasterized versions of this icon should be written into.
33
     */
34
    File outputPath;
35
36
    /** The path to a disabled version of the icon (gets desaturated). */
37
    File disabledPath;
38
39
    /**
40
     * 
41
     * @param nameBase
42
     * @param inputPath
43
     * @param outputPath
44
     * @param disabledPath
45
     * @param sizes
46
     */
47
    public IconEntry(String nameBase, File inputPath, File outputPath,
48
            File disabledPath, int[] sizes) {
49
        this.nameBase = nameBase;
50
        this.sizes = sizes;
51
        this.inputPath = inputPath;
52
        this.outputPath = outputPath;
53
        this.disabledPath = disabledPath;
54
    }
55
56
	@Override
57
	public int compareTo(IconEntry o) {
58
		return nameBase.compareTo(o.nameBase);
59
	}
60
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/ui/images/renderer/IconGatherer.java (-119 lines)
Lines 1-119 Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.ui.images.renderer;
13
14
import java.io.File;
15
import java.net.URI;
16
import java.util.List;
17
18
/**
19
 * <p>Utility to find and organize icons for rendering.</p>
20
 * 
21
 * @author tmccrary@l33tlabs.com
22
 *
23
 */
24
public class IconGatherer {
25
26
    /**
27
     * <p>Searches the root resources directory for svg icons and adds them to a
28
     * collection for later rasterization.</p>	
29
     *
30
     * @param outputName
31
     * @param iconDir
32
     * @param outputBase
33
     * @param outputDir2
34
     */ 
35
    public static void gatherIcons(List<IconEntry> icons, String extension, File rootDir, File iconDir, File outputBase, boolean generateDisabledDirs) {
36
        File[] listFiles = iconDir.listFiles();
37
38
        for (File child : listFiles) {
39
            if (child.isDirectory()) {
40
            	if(child.getName().startsWith("d")) {
41
            		continue;
42
            	}
43
            	
44
                gatherIcons(icons, extension, rootDir, child, outputBase, generateDisabledDirs);
45
                continue;
46
            }
47
48
            if (!child.getName().endsWith(extension)) {
49
                continue;
50
            }
51
52
            // Compute a relative path for the output dir
53
            URI rootUri = rootDir.toURI();
54
            URI iconUri = iconDir.toURI();
55
56
            String relativePath = rootUri.relativize(iconUri).getPath();
57
            File outputDir = new File(outputBase, relativePath);
58
            File disabledOutputDir = null;
59
60
            File parentFile = child.getParentFile();
61
62
            /* Determine if/where to put a disabled version of the icon
63
               Eclipse traditionally uses a prefix of d for disabled, e for
64
               enabled in the folder name */
65
            if (generateDisabledDirs && parentFile != null) {
66
                String parentDirName = parentFile.getName();
67
                if (parentDirName.startsWith("e")) {
68
                    StringBuilder builder = new StringBuilder();
69
                    builder.append("d");
70
                    builder.append(parentDirName.substring(1, parentDirName.length()));
71
72
                    // Disabled variant folder name
73
                    String disabledVariant = builder.toString();
74
75
                    // The parent's parent, to create the disabled directory in
76
                    File setParent = parentFile.getParentFile();
77
78
                    // The source directory's disabled folder
79
                    File disabledSource = new File(setParent, disabledVariant);
80
81
                    // Compute a relative path, so we can create the output folder
82
                    String path = rootUri.relativize(
83
                              disabledSource.toURI()).getPath();
84
85
                    // Create the output folder, so a disabled icon is generated
86
                    disabledOutputDir = new File(outputBase, path);
87
                    if(!disabledOutputDir.exists()) {
88
                        disabledOutputDir.mkdirs();
89
                    }
90
                }
91
            }
92
93
            IconEntry icon = createIcon(child, outputDir, disabledOutputDir);
94
95
            icons.add(icon);
96
        }
97
    }
98
    
99
    /**
100
     * <p>Creates an IconEntry, which contains information about rendering an icon such
101
     * as the source file, where to render, what alternative types of output to
102
     * generate, etc.</p>
103
     *
104
     * @param input the source of the icon file (SVG document)
105
     * @param outputPath the path of the rasterized version to generate
106
     * @param disabledPath the path of the disabled (desaturated) icon, if one is required
107
     *
108
     * @return an IconEntry describing the rendering operation
109
     */
110
    public static IconEntry createIcon(File input, File outputPath, File disabledPath) {
111
        String name = input.getName();
112
        String[] split = name.split("\\.(?=[^\\.]+$)");
113
114
        IconEntry def = new IconEntry(split[0], input, outputPath, disabledPath, new int[0]);
115
116
        return def;
117
    }
118
119
}
(-)a/org.eclipse.images.renderer/src/main/java/org/eclipse/ui/images/renderer/RenderMojo.java (-642 lines)
Lines 1-642 Link Here
1
/*******************************************************************************
2
 * (c) Copyright 2015 l33t labs LLC and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     l33t labs LLC and others - initial contribution
10
 *******************************************************************************/
11
12
package org.eclipse.ui.images.renderer;
13
14
import java.awt.RenderingHints;
15
import java.awt.image.BufferedImage;
16
import java.io.ByteArrayInputStream;
17
import java.io.ByteArrayOutputStream;
18
import java.io.File;
19
import java.io.FileInputStream;
20
import java.io.FileWriter;
21
import java.io.IOException;
22
import java.io.OutputStream;
23
import java.util.ArrayList;
24
import java.util.Collections;
25
import java.util.List;
26
import java.util.concurrent.Callable;
27
import java.util.concurrent.ExecutorService;
28
import java.util.concurrent.Executors;
29
import java.util.concurrent.atomic.AtomicInteger;
30
31
import javax.imageio.ImageIO;
32
33
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
34
import org.apache.batik.gvt.renderer.ImageRenderer;
35
import org.apache.batik.transcoder.ErrorHandler;
36
import org.apache.batik.transcoder.TranscoderException;
37
import org.apache.batik.transcoder.TranscoderInput;
38
import org.apache.batik.transcoder.TranscoderOutput;
39
import org.apache.batik.transcoder.image.PNGTranscoder;
40
import org.apache.batik.util.XMLResourceDescriptor;
41
import org.apache.maven.plugin.AbstractMojo;
42
import org.apache.maven.plugin.MojoExecutionException;
43
import org.apache.maven.plugin.MojoFailureException;
44
import org.apache.maven.plugin.logging.Log;
45
import org.w3c.dom.Element;
46
import org.w3c.dom.svg.SVGDocument;
47
48
import com.jhlabs.image.ContrastFilter;
49
import com.jhlabs.image.GrayscaleFilter;
50
import com.jhlabs.image.HSBAdjustFilter;
51
52
/**
53
 * <p>Mojo which renders SVG icons into PNG format.</p>
54
 *
55
 * @goal render-icons
56
 * @phase generate-resources
57
 */
58
public class RenderMojo extends AbstractMojo {
59
60
    /** Maven logger */
61
    Log log;
62
63
    /** Used for high res rendering support. */
64
    public static final String ECLIPSE_SVG_SCALE = "eclipse.svg.scale";
65
66
    /** Used to specify the number of render threads when rasterizing icons. */
67
    public static final String RENDERTHREADS = "eclipse.svg.renderthreads";
68
69
    /** Used to specify the directory name where the SVGs are taken from. */
70
    public static final String SOURCE_DIR = "eclipse.svg.sourcedirectory";
71
72
    /** Used to specify the directory name where the PNGs are saved to. */
73
    public static final String TARGET_DIR = "eclipse.svg.targetdirectory";
74
75
    /** A list of directories with svg sources to rasterize. */
76
    private List<IconEntry> icons;
77
78
    /** The pool used to render multiple icons concurrently. */
79
    private ExecutorService execPool;
80
81
    /** The number of threads to use when rendering icons. */
82
    private int threads;
83
84
    /**
85
     * A counter used to keep track of the number of rendered icons. Atomic is
86
     * used to make it easy to access between threads concurrently.
87
     */
88
    private AtomicInteger counter;
89
90
    /** List of icons that failed to render, made safe for parallel access */
91
    List<IconEntry> failedIcons = Collections
92
            .synchronizedList(new ArrayList<IconEntry>(5));
93
94
    /** The amount of scaling to apply to rasterized images. */
95
    private double outputScale;
96
97
    /**
98
     * @return the number of icons rendered at the time of the call
99
     */
100
    public int getIconsRendered() {
101
        return counter.get();
102
    }
103
104
    /**
105
     * @return the number of icons that failed during the rendering process
106
     */
107
    public int getFailedIcons() {
108
        return failedIcons.size();
109
    }
110
111
    /**
112
     * <p>Generates raster images from the input SVG vector image.</p>
113
     *
114
     * @param icon
115
     *            the icon to render
116
     */
117
    public void rasterize(IconEntry icon, GrayscaleFilter grayFilter, HSBAdjustFilter desaturator, ContrastFilter decontrast) {
118
        if (icon == null) {
119
            log.error("Null icon definition, skipping.");
120
            failedIcons.add(icon);
121
            return;
122
        }
123
124
        if (icon.inputPath == null) {
125
            log.error("Null icon input path, skipping: "
126
                    + icon.nameBase);
127
            failedIcons.add(icon);
128
            return;
129
        }
130
131
        if (!icon.inputPath.exists()) {
132
            log.error("Input path specified does not exist, skipping: "
133
                            + icon.nameBase);
134
            failedIcons.add(icon);
135
            return;
136
        }
137
138
        if (icon.outputPath != null && !icon.outputPath.exists()) {
139
            icon.outputPath.mkdirs();
140
        }
141
142
        if (icon.disabledPath != null && !icon.disabledPath.exists()) {
143
            icon.disabledPath.mkdirs();
144
        }
145
146
        // Create the document to rasterize
147
        SVGDocument svgDocument = generateSVGDocument(icon);
148
149
        if(svgDocument == null) {
150
            return;
151
        }
152
153
        // Determine the output sizes (native, double, quad)
154
        // We render at quad size and resample down for output
155
        Element svgDocumentNode = svgDocument.getDocumentElement();
156
        String nativeWidthStr = svgDocumentNode.getAttribute("width");
157
        String nativeHeightStr = svgDocumentNode.getAttribute("height");
158
        int nativeWidth = -1;
159
        int nativeHeight = -1;
160
161
        try{
162
            if (nativeWidthStr != "" && nativeHeightStr != ""){
163
                nativeWidth = Integer.parseInt(nativeWidthStr);
164
                nativeHeight = Integer.parseInt(nativeHeightStr);
165
            } else {
166
                // Vector graphics editing programs don't always output height and width attributes on SVG.
167
                // As fall back: parse the viewBox attribute (which is almost always set).
168
                String viewBoxStr = svgDocumentNode.getAttribute("viewBox");
169
                if (viewBoxStr == ""){
170
                    log.error("Icon defines neither width/height nor a viewBox, skipping: " + icon.nameBase);
171
                    failedIcons.add(icon);
172
                    return;
173
                }
174
                String[] splitted = viewBoxStr.split(" ");
175
                String xStr = splitted[0];
176
                String yStr = splitted[1];
177
                String widthStr = splitted[2];
178
                String heightStr = splitted[3];
179
                nativeWidth = Integer.parseInt(widthStr) - Integer.parseInt(xStr);
180
                nativeHeight = Integer.parseInt(heightStr) - Integer.parseInt(yStr); 
181
            }
182
        }catch (NumberFormatException e){
183
            log.error("Dimension could not be parsed ( "+e.getMessage()+ "), skipping: " + icon.nameBase);
184
            failedIcons.add(icon);
185
            return;
186
        }
187
188
        int outputWidth = (int) (nativeWidth * outputScale);
189
        int outputHeight = (int) (nativeHeight * outputScale);
190
191
        // Guesstimate the PNG size in memory, BAOS will enlarge if necessary.
192
        int outputInitSize = nativeWidth * nativeHeight * 4 + 1024;
193
        ByteArrayOutputStream iconOutput = new ByteArrayOutputStream(
194
                outputInitSize);
195
196
        // Render to SVG
197
        try {
198
            log.info(Thread.currentThread().getName() + " "
199
                    + " Rasterizing: " + icon.nameBase + ".png at " + outputWidth
200
                    + "x" + outputHeight);
201
202
            TranscoderInput svgInput = new TranscoderInput(svgDocument);
203
204
            boolean success = renderIcon(icon.nameBase, outputWidth, outputHeight, svgInput, iconOutput);
205
206
            if (!success) {
207
                log.error("Failed to render icon: " + icon.nameBase + ".png, skipping.");
208
                failedIcons.add(icon);
209
                return;
210
            }
211
        } catch (Exception e) {
212
            log.error("Failed to render icon: " + e.getMessage());
213
            failedIcons.add(icon);
214
            return;
215
        }
216
217
        // Generate a buffered image from Batik's png output
218
        byte[] imageBytes = iconOutput.toByteArray();
219
        ByteArrayInputStream imageInputStream = new ByteArrayInputStream(imageBytes);
220
221
        BufferedImage inputImage = null;
222
        try {
223
            inputImage = ImageIO.read(imageInputStream);
224
225
            if(inputImage == null) {
226
                log.error("Failed to generate BufferedImage from rendered icon, ImageIO returned null: " + icon.nameBase);
227
                failedIcons.add(icon);
228
                return;
229
            }
230
        } catch (IOException e2) {
231
            log.error("Failed to generate BufferedImage from rendered icon: "  + icon.nameBase + " - " + e2.getMessage());
232
            failedIcons.add(icon);
233
            return;
234
        }
235
236
        writeIcon(icon, outputWidth, outputHeight, inputImage);
237
238
        try {
239
            if (icon.disabledPath != null) {
240
                BufferedImage desaturated16 = desaturator.filter(
241
                    grayFilter.filter(inputImage, null), null);
242
243
                BufferedImage deconstrast = decontrast.filter(desaturated16, null);
244
245
                ImageIO.write(deconstrast, "PNG", new File(icon.disabledPath, icon.nameBase + ".png"));
246
            }
247
        } catch (Exception e1) {
248
            log.error("Failed to render disabled icon: "  +
249
                               icon.nameBase, e1);
250
            failedIcons.add(icon);
251
        }
252
    }
253
254
    /**
255
     * <p>Generates a Batik SVGDocument for the supplied IconEntry's input
256
     * file.</p>
257
     *
258
     * @param icon the icon entry to generate an SVG document for
259
     *
260
     * @return a batik SVGDocument instance or null if one could not be generated
261
     */
262
    private SVGDocument generateSVGDocument(IconEntry icon) {
263
        // Load the document and find out the native height/width
264
        // We reuse the document later for rasterization
265
        SVGDocument svgDocument = null;
266
        try {
267
            FileInputStream iconDocumentStream = new FileInputStream(icon.inputPath);
268
269
            String parser = XMLResourceDescriptor.getXMLParserClassName();
270
            SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
271
272
            // What kind of URI is batik expecting here??? the docs don't say
273
            svgDocument = f.createSVGDocument("file://" + icon.nameBase + ".svg", iconDocumentStream);
274
        } catch (Exception e3) {
275
            log.error("Error parsing SVG icon document: " + e3.getMessage());
276
            failedIcons.add(icon);
277
            return null;
278
        }
279
        return svgDocument;
280
    }
281
282
    /**
283
     * <p>Resizes the supplied inputImage to the specified width and height, using
284
     * lanczos resampling techniques.</p>
285
     *
286
     * @param icon the icon that's being resized
287
     * @param width the desired output width after rescaling operations
288
     * @param height the desired output height after rescaling operations
289
     * @param sourceImage the source image to resource
290
     */
291
    private void writeIcon(IconEntry icon, int width, int height, BufferedImage sourceImage) {
292
        try {
293
            String outputName = icon.nameBase;
294
            if (outputScale != 1) {
295
                String scaleId = outputScale == (double) (int) outputScale ? Integer.toString((int) outputScale): Double.toString(outputScale);
296
                outputName += "@" + scaleId + "x";
297
            }
298
            outputName += ".png";
299
            ImageIO.write(sourceImage, "PNG", new File(icon.outputPath, outputName));
300
        } catch (Exception e1) {
301
            log.error("Failed to resize rendered icon to output size: "  +
302
                               icon.nameBase, e1);
303
            failedIcons.add(icon);
304
        }
305
    }
306
307
    /**
308
     * <p>Handles concurrently rasterizing the icons to
309
     * reduce the duration on multicore systems.</p>
310
     */
311
    public void rasterizeAll() {
312
        // The number of icons that haven't been distributed to
313
        // callables
314
        int remainingIcons = icons.size();
315
316
        // The number of icons to distribute to a rendering callable
317
        final int threadExecSize = Math.max(1, icons.size() / this.threads);
318
319
        // The current offset to start a batch, as they're distributed
320
        // between rendering callables
321
        int batchOffset = 0;
322
323
        // A list of callables used to render icons on multiple threads
324
        // Each callable gets a set of icons to render
325
        List<Callable<Object>> tasks = new ArrayList<>(
326
                this.threads);
327
328
        // Distribute the rasterization operations between multiple threads
329
        while (remainingIcons > 0) {
330
            // The current start index for the current batch
331
            final int batchStart = batchOffset;
332
333
            // Increment the offset to reflect this batch (used for the next batch)
334
            batchOffset += threadExecSize;
335
336
            // Determine this batch size, used for batches that have less than
337
            // threadExecSize at the end of the distribution operation
338
            int batchSize = 0;
339
340
            // Determine if we can fit a full batch in this callable
341
            // or if we are at the end of gathered icons
342
            if (remainingIcons > threadExecSize) {
343
                batchSize = threadExecSize;
344
            } else {
345
                // We have less than a full batch worth of remaining icons
346
                // just add them all
347
                batchSize = remainingIcons;
348
            }
349
350
            // Deincrement the remaining Icons
351
            remainingIcons -= threadExecSize;
352
353
            // Used for access in the callable's scope
354
            final int execCount = batchSize;
355
356
            // Create the callable and add it to the task pool
357
            Callable<Object> runnable = new Callable<Object>() {
358
                @Override
359
                public Object call() throws Exception {
360
                    // The jhlabs filters are not thread safe, so provide one set per thread
361
                    GrayscaleFilter grayFilter = new GrayscaleFilter();
362
363
                    HSBAdjustFilter desaturator = new HSBAdjustFilter();
364
                         desaturator.setSFactor(0.0f);
365
366
                    ContrastFilter decontrast = new ContrastFilter();
367
                         decontrast.setBrightness(2.9f);
368
                         decontrast.setContrast(0.2f);
369
370
                    // Rasterize this batch
371
                    for (int count = 0; count < execCount; count++) {
372
                        rasterize(icons.get(batchStart + count), grayFilter, desaturator, decontrast);
373
                    }
374
375
                    // Update the render counter
376
                    counter.getAndAdd(execCount);
377
                    log.info("Finished rendering batch, index: " + batchStart);
378
379
                    return null;
380
                }
381
            };
382
383
            tasks.add(runnable);
384
        }
385
386
        // Execute the rasterization operations that
387
        // have been added to the pool
388
        try {
389
            execPool.invokeAll(tasks);
390
        } catch (InterruptedException e) {
391
            // TODO Auto-generated catch block
392
            e.printStackTrace();
393
        }
394
395
        // Print info about failed render operations, so they can be fixed
396
        log.info("Failed Icon Count: " + failedIcons.size());
397
        for (IconEntry icon : failedIcons) {
398
            log.info("Failed Icon: " + icon.nameBase);
399
        }
400
401
    }
402
403
    /**
404
     * Use batik to rasterize the input SVG into a raster image at the specified
405
     * image dimensions.
406
     *
407
     * @param width the width to render the icons at
408
     * @param height the height to render the icon at
409
     * @param input the SVG transcoder input
410
     * @param stream the stream to write the PNG data to
411
     */
412
    public boolean renderIcon(final String iconName, int width, int height,
413
            TranscoderInput tinput, OutputStream stream) {
414
        PNGTranscoder transcoder = new PNGTranscoder() {
415
            protected ImageRenderer createRenderer() {
416
                ImageRenderer renderer = super.createRenderer();
417
418
                RenderingHints renderHints = renderer.getRenderingHints();
419
420
                renderHints.add(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
421
                    RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
422
423
                renderHints.add(new RenderingHints(RenderingHints.KEY_RENDERING,
424
                    RenderingHints.VALUE_RENDER_QUALITY));
425
426
                renderHints.add(new RenderingHints(RenderingHints.KEY_DITHERING,
427
                    RenderingHints.VALUE_DITHER_DISABLE));
428
429
                renderHints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION,
430
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC));
431
432
                renderHints.add(new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION,
433
                    RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY));
434
435
                renderHints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
436
                    RenderingHints.VALUE_ANTIALIAS_ON));
437
438
                renderHints.add(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING,
439
                    RenderingHints.VALUE_COLOR_RENDER_QUALITY));
440
441
                renderHints.add(new RenderingHints(RenderingHints.KEY_STROKE_CONTROL,
442
                    RenderingHints.VALUE_STROKE_PURE));
443
444
                renderHints.add(new RenderingHints(RenderingHints.KEY_FRACTIONALMETRICS,
445
                    RenderingHints.VALUE_FRACTIONALMETRICS_ON));
446
447
                renderer.setRenderingHints(renderHints);
448
449
                return renderer;
450
            }
451
        };
452
453
        transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, new Float(width));
454
        transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, new Float(height));
455
456
        transcoder.setErrorHandler(new ErrorHandler() {
457
            public void warning(TranscoderException arg0)
458
                    throws TranscoderException {
459
                log.error("Icon: " + iconName + " - WARN: " + arg0.getMessage());
460
            }
461
462
            public void fatalError(TranscoderException arg0)
463
                    throws TranscoderException {
464
                log.error("Icon: " + iconName + " - FATAL: " + arg0.getMessage());
465
            }
466
467
            public void error(TranscoderException arg0)
468
                    throws TranscoderException {
469
                log.error("Icon: " + iconName + " - ERROR: " + arg0.getMessage());
470
            }
471
        });
472
473
        // Transcode the SVG document input to a PNG via the output stream
474
        TranscoderOutput output = new TranscoderOutput(stream);
475
476
        try {
477
            transcoder.transcode(tinput, output);
478
            return true;
479
        } catch (Exception e) {
480
            e.printStackTrace();
481
            return false;
482
        } finally {
483
            try {
484
                stream.close();
485
            } catch (IOException e) {
486
                // TODO Auto-generated catch block
487
                e.printStackTrace();
488
            }
489
        }
490
    }
491
492
    /**
493
     * <p>Initializes rasterizer defaults</p>
494
     *
495
     * @param threads the number of threads to render with
496
     * @param scale multiplier to use with icon output dimensions
497
     */
498
    private void init(int threads, double scale) {
499
        this.threads = threads;
500
        this.outputScale = Math.max(1, scale);
501
        icons = new ArrayList<>();
502
        execPool = Executors.newFixedThreadPool(threads);
503
        counter = new AtomicInteger();
504
    }
505
506
    /**
507
     * @see AbstractMojo#execute()
508
     */
509
    public void execute() throws MojoExecutionException, MojoFailureException {
510
        log = getLog();
511
512
        // Default to 2x the number of processor cores but allow override via jvm arg
513
        int threads = Math.max(1, Runtime.getRuntime().availableProcessors() * 2);
514
        String threadStr = System.getProperty(RENDERTHREADS);
515
        if (threadStr != null) {
516
            try {
517
                threads = Integer.parseInt(threadStr);
518
            } catch (Exception e) {
519
                e.printStackTrace();
520
                System.out
521
                        .println("Could not parse thread count, using default thread count");
522
            }
523
        }
524
525
        // if high res is enabled, the icons output size will be scaled by iconScale
526
        // Defaults to 1, meaning native size
527
        double iconScale = 1;
528
        String iconScaleStr = System.getProperty(ECLIPSE_SVG_SCALE);
529
        if (iconScaleStr != null) {
530
            iconScale = Double.parseDouble(iconScaleStr);
531
            if (iconScale != 1 && iconScale != 1.5 && iconScale != 2) {
532
                log.warn("Unusual scale factor: " + iconScaleStr + " (@" + iconScale + "x)");
533
            }
534
        }
535
536
        // Defaults to "eclipse-svg"
537
        String sourceDir = "eclipse-svg";
538
        String sourceDirProp = System.getProperty(SOURCE_DIR);
539
        if (sourceDirProp != null) {
540
            sourceDir = sourceDirProp;
541
        }
542
543
        // Defaults to "eclipse-png"
544
        String targetDir = "eclipse-png";
545
        String targetDirProp = System.getProperty(TARGET_DIR);
546
        if (targetDirProp != null) {
547
            targetDir = targetDirProp;
548
        }
549
550
        // Track the time it takes to render the entire set
551
        long totalStartTime = System.currentTimeMillis();
552
553
        // initialize defaults (the old renderer was instantiated via constructor)
554
        init(threads, iconScale);
555
556
        String workingDirectory = System.getProperty("user.dir");
557
558
        File outputDir = new File(workingDirectory + (iconScale == 1 ? "/" + targetDir + "/" : "/" + targetDir + "-highdpi/"));
559
        File iconDirectoryRoot = new File(sourceDir + "/");
560
561
        // Search each subdir in the root dir for svg icons
562
        for (File file : iconDirectoryRoot.listFiles()) {
563
            if(!file.isDirectory()) {
564
                continue;
565
            }
566
567
            String dirName = file.getName();
568
569
            // Where to place the rendered icon
570
            File outputBase = new File(outputDir, (iconScale == 1 ? dirName : dirName + ".highdpi"));
571
            if (iconScale != 1) {
572
                createFragmentFiles(outputBase, dirName);
573
            }
574
575
            IconGatherer.gatherIcons(icons, "svg", file, file, outputBase, true);
576
        }
577
578
        log.info("Working directory: " + outputDir.getAbsolutePath());
579
        log.info("SVG Icon Directory: " + iconDirectoryRoot.getAbsolutePath());
580
        log.info("Rendering icons with " + threads + " threads, scaling output to " + iconScale + "x");
581
        long startTime = System.currentTimeMillis();
582
583
        // Render the icons
584
        rasterizeAll();
585
586
        // Print summary of operations
587
        int iconRendered = getIconsRendered();
588
        int failedIcons = getFailedIcons();
589
        int fullIconCount = iconRendered - failedIcons;
590
591
        log.info(fullIconCount + " Icons Rendered");
592
        log.info(failedIcons + " Icons Failed");
593
        log.info("Took: "    + (System.currentTimeMillis() - startTime) + " ms.");
594
595
        log.info("Rasterization operations completed, Took: "
596
                + (System.currentTimeMillis() - totalStartTime) + " ms.");
597
    }
598
599
    private void createFragmentFiles(File outputBase, String dirName) {
600
        createFile(new File(outputBase, "build.properties"), "bin.includes = META-INF/,icons/,.\n");
601
        createFile(new File(outputBase, ".project"), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
602
                "<projectDescription>\n" + 
603
                "    <name>" + dirName + ".highdpi</name>\n" + 
604
                "    <comment></comment>\n" + 
605
                "    <projects>\n" + 
606
                "    </projects>\n" + 
607
                "    <buildSpec>\n" + 
608
                "        <buildCommand>\n" + 
609
                "            <name>org.eclipse.pde.ManifestBuilder</name>\n" + 
610
                "            <arguments>\n" + 
611
                "            </arguments>\n" + 
612
                "        </buildCommand>\n" + 
613
                "        <buildCommand>\n" + 
614
                "            <name>org.eclipse.pde.SchemaBuilder</name>\n" + 
615
                "            <arguments>\n" + 
616
                "            </arguments>\n" + 
617
                "        </buildCommand>\n" + 
618
                "    </buildSpec>\n" + 
619
                "    <natures>\n" + 
620
                "        <nature>org.eclipse.pde.PluginNature</nature>\n" + 
621
                "    </natures>\n" + 
622
                "</projectDescription>\n");
623
        createFile(new File(outputBase, "META-INF/MANIFEST.MF"), "Manifest-Version: 1.0\n" + 
624
                "Bundle-ManifestVersion: 2\n" + 
625
                "Bundle-Name: " + dirName + ".highdpi\n" + 
626
                "Bundle-SymbolicName: " + dirName + ".highdpi\n" + 
627
                "Bundle-Version: 0.1.0.qualifier\n" + 
628
                "Fragment-Host: " + dirName + "\n");
629
    }
630
631
    private void createFile(File file, String contents) {
632
        try {
633
            file.getParentFile().mkdirs();
634
            FileWriter writer = new FileWriter(file);
635
            writer.write(contents);
636
            writer.close();
637
        } catch (IOException e) {
638
            log.error(e);
639
        }
640
    }
641
642
}
(-)a/org.eclipse.images/.gitignore (+2 lines)
Added Link Here
1
/bin/
2
target/
(-)a/org.eclipse.images/.project (+12 lines)
Lines 6-17 Link Here
6
	</projects>
6
	</projects>
7
	<buildSpec>
7
	<buildSpec>
8
		<buildCommand>
8
		<buildCommand>
9
			<name>org.eclipse.jdt.core.javabuilder</name>
10
			<arguments>
11
			</arguments>
12
		</buildCommand>
13
		<buildCommand>
9
			<name>org.eclipse.pde.ManifestBuilder</name>
14
			<name>org.eclipse.pde.ManifestBuilder</name>
15
			<arguments>
16
			</arguments>
17
		</buildCommand>
18
		<buildCommand>
19
			<name>org.eclipse.m2e.core.maven2Builder</name>
10
			<arguments>
20
			<arguments>
11
			</arguments>
21
			</arguments>
12
		</buildCommand>
22
		</buildCommand>
13
	</buildSpec>
23
	</buildSpec>
14
	<natures>
24
	<natures>
25
		<nature>org.eclipse.jdt.core.javanature</nature>
26
		<nature>org.eclipse.m2e.core.maven2Nature</nature>
15
		<nature>org.eclipse.pde.PluginNature</nature>
27
		<nature>org.eclipse.pde.PluginNature</nature>
16
	</natures>
28
	</natures>
17
</projectDescription>
29
</projectDescription>
(-)a/org.eclipse.images/META-INF/MANIFEST.MF (-1 / +1 lines)
Lines 1-7 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
Bundle-ManifestVersion: 2
2
Bundle-ManifestVersion: 2
3
Bundle-Name: %Plugin.name
3
Bundle-Name: %Plugin.name
4
Bundle-SymbolicName: org.eclipse.ui.images
4
Bundle-SymbolicName: org.eclipse.images
5
Bundle-Version: 1.0.0.qualifier
5
Bundle-Version: 1.0.0.qualifier
6
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
6
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
7
Bundle-Vendor: %Plugin.providerName
7
Bundle-Vendor: %Plugin.providerName
(-)a/org.eclipse.images/README.md (-3 / +3 lines)
Lines 1-11 Link Here
1
org.eclipse.ui.images
1
org.eclipse.images
2
=====================
2
=====================
3
3
4
org.eclipse.ui.images provides svg versions of the Eclipse SDK images. The "eclipse-svg" folder contains svg version of the icons, while the "eclipse-png" folder contain generated png files. These icons can be used in custom Eclipse plug-ins or in arbitrary rich client applications. 
4
org.eclipse.images provides svg versions of the Eclipse SDK images. The "eclipse-svg" folder contains svg version of the icons, while the "eclipse-png" folder contain generated png files. These icons can be used in custom Eclipse plug-ins or in arbitrary rich client applications. 
5
Generate png files
5
Generate png files
6
------------------
6
------------------
7
7
8
To generate the png files based on the svg files, see the README.md file in the org.eclipse.ui.images.renderer plug-in.
8
To generate the png files based on the svg files, see the README.md file in the org.eclipse.images.renderer plug-in.
9
9
10
License
10
License
11
-------
11
-------
(-)a/org.eclipse.images/plugin.properties (-1 / +1 lines)
Lines 8-13 Link Here
8
# Contributors:
8
# Contributors:
9
#     IBM Corporation - initial API and implementation
9
#     IBM Corporation - initial API and implementation
10
###############################################################################
10
###############################################################################
11
Plugin.name = Eclipse UI Image Resources
11
Plugin.name = Eclipse Image Resources
12
Plugin.providerName = Eclipse.org
12
Plugin.providerName = Eclipse.org
13
13
(-)a/org.eclipse.images/pom.xml (-9 lines)
Lines 1-24 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
  Copyright (c) 2013 - vogella GmbH
4
  All rights reserved. This program and the accompanying materials
5
  are made available under the terms of the Eclipse Distribution License v1.0
6
  which accompanies this distribution, and is available at
7
  http://www.eclipse.org/org/documents/edl-v10.php
8
9
  Contributors:
10
     Lars Vogel - initial implementation
(-)a/pom.xml (+87 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
  Copyright (c) 2015 l33t labs LLC.
4
  All rights reserved. This program and the accompanying materials
5
  are made available under the terms of the Eclipse Distribution License v1.0
6
  which accompanies this distribution, and is available at
7
  http://www.eclipse.org/org/documents/edl-v10.php
8
 
9
  Contributors:
10
     Tony McCrary - initial implementation
11
-->
12
<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">
13
  <modelVersion>4.0.0</modelVersion>
14
15
  <parent>
16
    <groupId>org.eclipse</groupId>
17
    <artifactId>eclipse-platform-parent</artifactId>
18
    <version>4.6.0-SNAPSHOT</version>
19
    <relativePath>../eclipse-platform-parent</relativePath>
20
  </parent>
21
22
  <groupId>eclipse.images</groupId>
23
  <artifactId>org.eclipse.images.parent</artifactId>
24
  <packaging>pom</packaging>
25
  <name>Eclipse Images</name>
26
27
  <properties>
28
    <tycho.scmUrl>scm:git:git://git.eclipse.org/gitroot/platform/eclipse.platform.images.git</tycho.scmUrl>
29
  </properties>
30
31
  <repositories>
32
    <repository>
33
      <releases>
34
        <enabled>true</enabled>
35
      </releases>
36
      <snapshots>
37
        <enabled>true</enabled>
38
      </snapshots>
39
      <id>eclipse-hosted</id>
40
      <url>https://repo.eclipse.org/content/repositories/eclipse/</url>
41
    </repository>
42
  </repositories>
43
44
  <modules>
45
    <module>org.eclipse.images</module>
46
    <module>org.eclipse.images.renderer</module>
47
  </modules>
48
  <build>
49
  	<pluginManagement>
50
  		<plugins>
51
  			<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
52
  			<plugin>
53
  				<groupId>org.eclipse.m2e</groupId>
54
  				<artifactId>lifecycle-mapping</artifactId>
55
  				<version>1.0.0</version>
56
  				<configuration>
57
  					<lifecycleMappingMetadata>
58
  						<pluginExecutions>
59
  							<pluginExecution>
60
  								<pluginExecutionFilter>
61
  									<groupId>
62
  										org.eclipse.cbi.maven.plugins
63
  									</groupId>
64
  									<artifactId>
65
  										eclipse-cbi-plugin
66
  									</artifactId>
67
  									<versionRange>
68
  										[1.0.5,)
69
  									</versionRange>
70
  									<goals>
71
  										<goal>
72
  											generate-api-build-xml
73
  										</goal>
74
  									</goals>
75
  								</pluginExecutionFilter>
76
  								<action>
77
  									<ignore></ignore>
78
  								</action>
79
  							</pluginExecution>
80
  						</pluginExecutions>
81
  					</lifecycleMappingMetadata>
82
  				</configuration>
83
  			</plugin>
84
  		</plugins>
85
  	</pluginManagement>
86
  </build>
87
</project>

Return to bug 422139