commit 6b04f8fb548874b9139abc4bb08ac675b6a66829
Author: Simon Legner <Simon.Legner@gmail.com>
Date: 2020-03-22 14:35:14 +0100
see #16733 - Use radiance-photon for transcoding SVG icons to Java classes
diff --git a/build.xml b/build.xml
index 465f7df38..e75e4b6b8 100644
|
a
|
b
|
Build-Date: ${build.tstamp}
|
| 1187 | 1187 | <ivy:retrieve pattern="${lib.dir}/compile/[artifact]-[type].[ext]" conf="compile"/> |
| 1188 | 1188 | <ivy:retrieve pattern="${lib.dir}/runtime/[artifact]-[type].[ext]" conf="runtime"/> |
| 1189 | 1189 | <ivy:retrieve pattern="${lib.dir}/sources/[artifact]-[type].[ext]" conf="sources"/> |
| | 1190 | <ivy:retrieve file="${tools.ivy}" pattern="${lib.dir}/radiance-photon/[artifact]-[type].[ext]" conf="radiance-photon"/> |
| 1190 | 1191 | <ivy:retrieve pattern="${lib.dir}/tools/[artifact]-[type].[ext]" conf="javacc,checkstyle" file="${tools.ivy}"/> |
| 1191 | 1192 | </target> |
| | 1193 | <target name="transform-svg" depends="resolve"> |
| | 1194 | <ivy:cachepath file="${tools.ivy}" pathid="radiance-photon.classpath" conf="radiance-photon"/> |
| | 1195 | <mkdir dir="src/org/openstreetmap/josm/images"/> |
| | 1196 | <java classname="org.pushingpixels.photon.api.transcoder.SvgBatchConverter" failonerror="true"> |
| | 1197 | <classpath refid="radiance-photon.classpath"/> |
| | 1198 | <arg value="sourceFolder=resources/images/"/> |
| | 1199 | <arg value="maxDepth=10"/> |
| | 1200 | <arg value="outputFolder=src/org/openstreetmap/josm/images"/> |
| | 1201 | <arg value="outputPackageName=org.openstreetmap.josm.images"/> |
| | 1202 | <arg value="outputClassNamePrefix=img_"/> |
| | 1203 | <arg value="outputLanguage=java"/> |
| | 1204 | <arg value="templateFile=/org/pushingpixels/photon/api/transcoder/java/SvgTranscoderTemplateResizable.templ"/> |
| | 1205 | </java> |
| | 1206 | </target> |
| 1192 | 1207 | </project> |
diff --git a/ivy.xml b/ivy.xml
index 8e76fba07..f3ad56601 100644
|
a
|
b
|
|
| 24 | 24 | <dependency org="org.tukaani" name="xz" rev="1.8" conf="api->default"/> |
| 25 | 25 | <dependency org="com.drewnoakes" name="metadata-extractor" rev="2.13.0" conf="api->default"/> |
| 26 | 26 | <dependency org="ch.poole" name="OpeningHoursParser" rev="0.21.1" conf="api->default"/> |
| | 27 | <dependency org="org.pushing-pixels" name="radiance-neon" rev="3.0-SNAPSHOT" conf="api->default"/> |
| 27 | 28 | <!-- sources->sources --> |
| 28 | 29 | <dependency org="javax.json" name="javax.json-api" rev="1.1.4" conf="sources->sources"/> |
| 29 | 30 | <dependency org="org.glassfish" name="javax.json" rev="1.1.4" conf="sources->sources"/> |
diff --git a/ivysettings.xml b/ivysettings.xml
index 3cd393631..e919942b1 100644
|
a
|
b
|
|
| 2 | 2 | <!-- License: GPL. For details, see LICENSE file. --> |
| 3 | 3 | <ivysettings> |
| 4 | 4 | <settings defaultResolver="chain"/> |
| | 5 | <property name="m2-pattern" value="${user.home}/.m2/repository/[organisation]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]" override="false" /> |
| 5 | 6 | <resolvers> |
| 6 | 7 | <chain name="chain"> |
| | 8 | <filesystem name="local-maven2" m2compatible="true" > |
| | 9 | <artifact pattern="${m2-pattern}"/> |
| | 10 | <ivy pattern="${m2-pattern}"/> |
| | 11 | </filesystem> |
| 7 | 12 | <ibiblio name="josm-nexus" m2compatible="true" root="https://josm.openstreetmap.de/nexus/content/repositories/public/" /> |
| 8 | 13 | <ibiblio name="jcenter" m2compatible="true" root="https://jcenter.bintray.com/" /> |
| 9 | 14 | </chain> |
diff --git a/src/org/openstreetmap/josm/tools/ImageProvider.java b/src/org/openstreetmap/josm/tools/ImageProvider.java
index 89de2b570..15b1f2f44 100644
|
a
|
b
|
|
| 15 | 15 | import java.awt.RenderingHints; |
| 16 | 16 | import java.awt.Toolkit; |
| 17 | 17 | import java.awt.Transparency; |
| | 18 | import java.awt.geom.Dimension2D; |
| 18 | 19 | import java.awt.image.BufferedImage; |
| 19 | 20 | import java.awt.image.ColorModel; |
| 20 | 21 | import java.awt.image.FilteredImageSource; |
| … |
… |
|
| 81 | 82 | import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; |
| 82 | 83 | import org.openstreetmap.josm.io.CachedFile; |
| 83 | 84 | import org.openstreetmap.josm.spi.preferences.Config; |
| | 85 | import org.pushingpixels.neon.api.icon.ResizableIcon; |
| 84 | 86 | import org.w3c.dom.Element; |
| 85 | 87 | import org.w3c.dom.Node; |
| 86 | 88 | import org.w3c.dom.NodeList; |
| … |
… |
private ImageResource getIfAvailableImpl() {
|
| 853 | 855 | // for example in loops in map entries (ie freeze when such entry is retrieved) |
| 854 | 856 | |
| 855 | 857 | String prefix = isDisabled ? "dis:" : ""; |
| | 858 | |
| | 859 | if (Utils.hasExtension(name, "svg")) { |
| | 860 | final ImageResource ir = getResizableIcon(); |
| | 861 | if (ir != null) { |
| | 862 | cache.put(prefix + name, ir); |
| | 863 | return ir; |
| | 864 | } |
| | 865 | } |
| | 866 | |
| 856 | 867 | if (name.startsWith("data:")) { |
| 857 | 868 | String url = name; |
| 858 | 869 | ImageResource ir = cache.get(prefix + url); |
| … |
… |
private ImageResource getIfAvailableImpl() {
|
| 955 | 966 | return null; |
| 956 | 967 | } |
| 957 | 968 | |
| | 969 | private ImageResource getResizableIcon() { |
| | 970 | try { |
| | 971 | // rewrite foo/bar/baz.svg to org.openstreetmap.josm.images.foo.bar.img_baz (class is prefixed with img_) |
| | 972 | final String[] nameParts = name.replaceAll("[ -]", "_").split("/"); |
| | 973 | nameParts[nameParts.length - 1] = "img_" + nameParts[nameParts.length - 1].replaceFirst(".svg$", ""); |
| | 974 | final String className = "org.openstreetmap.josm.images." + String.join(".", nameParts); |
| | 975 | final Class<?> imageClass = Class.forName(className); |
| | 976 | final ResizableIcon.Factory factory = (ResizableIcon.Factory) imageClass.getDeclaredMethod("factory").invoke(null); |
| | 977 | |
| | 978 | Logging.info("ImageProvider: For image {0}, using {1}", name, imageClass); |
| | 979 | final ResizableIcon resizableIcon = factory.createNewIcon(); |
| | 980 | return new ImageResource(resizableIcon); |
| | 981 | |
| | 982 | } catch (Exception ex) { |
| | 983 | Logging.warn(ex); |
| | 984 | } |
| | 985 | return null; |
| | 986 | } |
| | 987 | |
| 958 | 988 | /** |
| 959 | 989 | * Internal implementation of the image request for URL's. |
| 960 | 990 | * |
| … |
… |
public static BufferedImage createImageFromSvg(SVGDiagram svg, Dimension dim) {
|
| 1640 | 1670 | } |
| 1641 | 1671 | final float sourceWidth = svg.getWidth(); |
| 1642 | 1672 | final float sourceHeight = svg.getHeight(); |
| 1643 | | final float realWidth; |
| 1644 | | final float realHeight; |
| 1645 | | if (dim.width >= 0) { |
| 1646 | | realWidth = dim.width; |
| 1647 | | if (dim.height >= 0) { |
| 1648 | | realHeight = dim.height; |
| 1649 | | } else { |
| 1650 | | realHeight = sourceHeight * realWidth / sourceWidth; |
| 1651 | | } |
| 1652 | | } else if (dim.height >= 0) { |
| 1653 | | realHeight = dim.height; |
| 1654 | | realWidth = sourceWidth * realHeight / sourceHeight; |
| 1655 | | } else { |
| 1656 | | realWidth = GuiSizesHelper.getSizeDpiAdjusted(sourceWidth); |
| 1657 | | realHeight = GuiSizesHelper.getSizeDpiAdjusted(sourceHeight); |
| 1658 | | } |
| 1659 | 1673 | |
| 1660 | | int roundedWidth = Math.round(realWidth); |
| 1661 | | int roundedHeight = Math.round(realHeight); |
| | 1674 | final Dimension2D realDimension = new ComputedDimension(dim, sourceWidth, sourceHeight); |
| | 1675 | int roundedWidth = (int) Math.round(realDimension.getWidth()); |
| | 1676 | int roundedHeight = (int) Math.round(realDimension.getHeight()); |
| 1662 | 1677 | if (roundedWidth <= 0 || roundedHeight <= 0 || roundedWidth >= Integer.MAX_VALUE || roundedHeight >= Integer.MAX_VALUE) { |
| 1663 | | Logging.error("createImageFromSvg: {0} {1} realWidth={2} realHeight={3}", |
| 1664 | | svg.getXMLBase(), dim, Float.toString(realWidth), Float.toString(realHeight)); |
| | 1678 | Logging.error("createImageFromSvg: {0} {1} realDimension={2}", svg.getXMLBase(), dim, realDimension); |
| 1665 | 1679 | return null; |
| 1666 | 1680 | } |
| 1667 | 1681 | BufferedImage img = new BufferedImage(roundedWidth, roundedHeight, BufferedImage.TYPE_INT_ARGB); |
| 1668 | 1682 | Graphics2D g = img.createGraphics(); |
| 1669 | 1683 | g.setClip(0, 0, img.getWidth(), img.getHeight()); |
| 1670 | | g.scale(realWidth / sourceWidth, realHeight / sourceHeight); |
| | 1684 | g.scale((float) realDimension.getWidth() / sourceWidth, (float) realDimension.getHeight() / sourceHeight); |
| 1671 | 1685 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| 1672 | 1686 | try { |
| 1673 | 1687 | synchronized (getSvgUniverse()) { |
| … |
… |
public String toString() {
|
| 2123 | 2137 | + (archive != null ? "archive=" + archive + ", " : "") |
| 2124 | 2138 | + (inArchiveDir != null && !inArchiveDir.isEmpty() ? "inArchiveDir=" + inArchiveDir : "") + ']').replaceAll(", \\]", "]"); |
| 2125 | 2139 | } |
| | 2140 | |
| | 2141 | static class ComputedDimension extends Dimension2D { |
| | 2142 | private final float realWidth; |
| | 2143 | private final float realHeight; |
| | 2144 | |
| | 2145 | ComputedDimension(Dimension dim, float sourceWidth, float sourceHeight) { |
| | 2146 | dim = GuiSizesHelper.getDimensionDpiAdjusted(dim); |
| | 2147 | if (dim.width >= 0) { |
| | 2148 | realWidth = dim.width; |
| | 2149 | if (dim.height >= 0) { |
| | 2150 | realHeight = dim.height; |
| | 2151 | } else { |
| | 2152 | realHeight = sourceHeight * realWidth / sourceWidth; |
| | 2153 | } |
| | 2154 | } else if (dim.height >= 0) { |
| | 2155 | realHeight = dim.height; |
| | 2156 | realWidth = sourceWidth * realHeight / sourceHeight; |
| | 2157 | } else { |
| | 2158 | realWidth = GuiSizesHelper.getSizeDpiAdjusted(sourceWidth); |
| | 2159 | realHeight = GuiSizesHelper.getSizeDpiAdjusted(sourceHeight); |
| | 2160 | } |
| | 2161 | } |
| | 2162 | |
| | 2163 | @Override |
| | 2164 | public double getWidth() { |
| | 2165 | return realWidth; |
| | 2166 | } |
| | 2167 | |
| | 2168 | @Override |
| | 2169 | public double getHeight() { |
| | 2170 | return realHeight; |
| | 2171 | } |
| | 2172 | |
| | 2173 | @Override |
| | 2174 | public void setSize(double width, double height) { |
| | 2175 | throw new UnsupportedOperationException(); |
| | 2176 | } |
| | 2177 | |
| | 2178 | @Override |
| | 2179 | public String toString() { |
| | 2180 | return "ComputedDimension{realWidth=" + realWidth + ", realHeight=" + realHeight + '}'; |
| | 2181 | } |
| | 2182 | } |
| 2126 | 2183 | } |
diff --git a/src/org/openstreetmap/josm/tools/ImageResource.java b/src/org/openstreetmap/josm/tools/ImageResource.java
index 56f5d4f87..62301ddfb 100644
|
a
|
b
|
|
| 3 | 3 | |
| 4 | 4 | import java.awt.Dimension; |
| 5 | 5 | import java.awt.Image; |
| | 6 | import java.awt.geom.Dimension2D; |
| 6 | 7 | import java.awt.image.BufferedImage; |
| 7 | 8 | import java.util.List; |
| 8 | 9 | import java.util.Map; |
| … |
… |
|
| 16 | 17 | import javax.swing.UIManager; |
| 17 | 18 | |
| 18 | 19 | import com.kitfox.svg.SVGDiagram; |
| | 20 | import org.pushingpixels.neon.api.icon.ResizableIcon; |
| 19 | 21 | |
| 20 | 22 | /** |
| 21 | 23 | * Holds data for one particular image. |
| … |
… |
|
| 35 | 37 | * SVG diagram information in case of SVG vector image. |
| 36 | 38 | */ |
| 37 | 39 | private SVGDiagram svg; |
| | 40 | |
| | 41 | private ResizableIcon resizableIcon; |
| | 42 | private short resizableIconWidth; |
| | 43 | private short resizableIconHeight; |
| 38 | 44 | /** |
| 39 | 45 | * Use this dimension to request original file dimension. |
| 40 | 46 | */ |
| … |
… |
public ImageResource(SVGDiagram svg) {
|
| 70 | 76 | this.svg = svg; |
| 71 | 77 | } |
| 72 | 78 | |
| | 79 | public ImageResource(ResizableIcon resizableIcon) { |
| | 80 | CheckParameterUtil.ensureParameterNotNull(resizableIcon); |
| | 81 | this.resizableIcon = resizableIcon; |
| | 82 | this.resizableIconWidth = (short) resizableIcon.getIconWidth(); |
| | 83 | this.resizableIconHeight = (short) resizableIcon.getIconHeight(); |
| | 84 | } |
| | 85 | |
| 73 | 86 | /** |
| 74 | 87 | * Constructs a new {@code ImageResource} from another one and sets overlays. |
| 75 | 88 | * @param res the existing resource |
| … |
… |
public ImageResource(SVGDiagram svg) {
|
| 78 | 91 | */ |
| 79 | 92 | public ImageResource(ImageResource res, List<ImageOverlay> overlayInfo) { |
| 80 | 93 | this.svg = res.svg; |
| | 94 | this.resizableIcon = res.resizableIcon; |
| | 95 | this.resizableIconWidth = res.resizableIconWidth; |
| | 96 | this.resizableIconHeight = res.resizableIconHeight; |
| 81 | 97 | this.baseImage = res.baseImage; |
| 82 | 98 | this.overlayInfo = overlayInfo; |
| 83 | 99 | } |
| … |
… |
public ImageIcon getImageIcon(Dimension dim, boolean multiResolution) {
|
| 158 | 174 | () -> dim + " is invalid"); |
| 159 | 175 | BufferedImage img = imgCache.get(dim); |
| 160 | 176 | if (img == null) { |
| 161 | | if (svg != null) { |
| 162 | | Dimension realDim = GuiSizesHelper.getDimensionDpiAdjusted(dim); |
| 163 | | img = ImageProvider.createImageFromSvg(svg, realDim); |
| | 177 | if (resizableIcon != null) { |
| | 178 | final Dimension2D dimension = new ImageProvider.ComputedDimension(dim, resizableIconWidth, resizableIconHeight); |
| | 179 | resizableIcon.setDimension(new Dimension((int) dimension.getWidth(), (int) dimension.getHeight())); |
| | 180 | img = resizableIcon.toImage(); |
| | 181 | } else if (svg != null) { |
| | 182 | img = ImageProvider.createImageFromSvg(svg, dim); |
| 164 | 183 | if (img == null) { |
| 165 | 184 | return null; |
| 166 | 185 | } |
| … |
… |
public ImageIcon getImageIconBounded(Dimension maxSize, boolean multiResolution)
|
| 248 | 267 | float sourceHeight; |
| 249 | 268 | int maxWidth = maxSize.width; |
| 250 | 269 | int maxHeight = maxSize.height; |
| 251 | | if (svg != null) { |
| | 270 | if (resizableIcon != null) { |
| | 271 | sourceHeight = resizableIconHeight; |
| | 272 | sourceWidth = resizableIconWidth; |
| | 273 | } else if (svg != null) { |
| 252 | 274 | sourceWidth = svg.getWidth(); |
| 253 | 275 | sourceHeight = svg.getHeight(); |
| 254 | 276 | } else { |
diff --git a/tools/ivy.xml b/tools/ivy.xml
index dd4fc3b9b..f3c990041 100644
|
a
|
b
|
|
| 8 | 8 | <conf name="proguard" description="Everything needed for running ProGuard"/> |
| 9 | 9 | <conf name="pmd" description="Everything needed for running PMD"/> |
| 10 | 10 | <conf name="spotbugs" description="Everything needed for running SpotBugs"/> |
| | 11 | <conf name="radiance-photon" description="Everything needed for running radiance-photon"/> |
| 11 | 12 | </configurations> |
| 12 | 13 | <dependencies> |
| 13 | 14 | <!-- javacc->default --> |
| … |
… |
|
| 26 | 27 | <!-- spotbugs->default --> |
| 27 | 28 | <dependency org="com.github.spotbugs" name="spotbugs" rev="3.1.12" conf="spotbugs->default"/> |
| 28 | 29 | <dependency org="com.github.spotbugs" name="spotbugs-ant" rev="3.1.12" conf="spotbugs->default"/> |
| | 30 | <!-- radiance-photon->default --> |
| | 31 | <dependency org="org.pushing-pixels" name="radiance-photon" rev="3.0-SNAPSHOT" conf="radiance-photon->default"/> |
| | 32 | <dependency org="org.apache.xmlgraphics" name="batik-all" rev="1.12" conf="radiance-photon->default"> |
| | 33 | <artifact name="batik-all" type="pom" ext="pom"/> |
| | 34 | </dependency> |
| 29 | 35 | </dependencies> |
| 30 | 36 | </ivy-module> |