package org.argeo.app.swt.osgi; import static java.lang.System.Logger.Level.TRACE; import java.awt.Color; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.batik.transcoder.TranscoderException; import org.apache.batik.transcoder.TranscoderInput; import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.image.ImageTranscoder; import org.apache.batik.transcoder.image.PNGTranscoder; import org.argeo.cms.swt.osgi.BundleCmsSwtTheme; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.widgets.Display; import org.osgi.framework.BundleContext; /** Theme which can dynamically create icons from SVG data. */ public class BundleSvgTheme extends BundleCmsSwtTheme { private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName()); private Map> imageDataCache = Collections.synchronizedMap(new HashMap<>()); private Map transcoders = Collections.synchronizedMap(new HashMap<>()); private final static String IMAGE_CACHE_KEY = BundleSvgTheme.class.getName() + ".imageCache"; @Override public Image getIcon(String name, Integer preferredSize) { String path = "icons/types/svg/" + name + ".svg"; return createImageFromSvg(path, preferredSize); } @SuppressWarnings("unchecked") protected Image createImageFromSvg(String path, Integer preferredSize) { Display display = Display.getCurrent(); Objects.requireNonNull(display, "Not a user interface thread"); Map> imageCache = (Map>) display .getData(IMAGE_CACHE_KEY); if (imageCache == null) display.setData(IMAGE_CACHE_KEY, new HashMap>()); imageCache = (Map>) display.getData(IMAGE_CACHE_KEY); Image image = null; if (imageCache.containsKey(path)) { image = imageCache.get(path).get(preferredSize); } if (image != null) return image; ImageData imageData = loadFromSvg(path, preferredSize); image = new Image(display, imageData); if (!imageCache.containsKey(path)) imageCache.put(path, Collections.synchronizedMap(new HashMap<>())); imageCache.get(path).put(preferredSize, image); return image; } protected ImageData loadFromSvg(String path, int size) { ImageData imageData = null; if (imageDataCache.containsKey(path)) imageData = imageDataCache.get(path).get(size); if (imageData != null) return imageData; ImageTranscoder transcoder = null; synchronized (this) { transcoder = transcoders.get(size); if (transcoder == null) { transcoder = new PNGTranscoder(); transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) size); transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) size); transcoder.addTranscodingHint(PNGTranscoder.KEY_BACKGROUND_COLOR, new Color(255, 255, 255, 0)); transcoders.put(size, transcoder); } } try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) { if (in == null) throw new IllegalArgumentException(path + " not found"); TranscoderInput input = new TranscoderInput(in); TranscoderOutput output = new TranscoderOutput(out); transcoder.transcode(input, output); try (InputStream imageIn = new ByteArrayInputStream(out.toByteArray())) { imageData = new ImageData(imageIn); } logger.log(TRACE, () -> "Generated " + size + "x" + size + " PNG icon from " + path); } catch (IOException | TranscoderException e) { throw new RuntimeException("Cannot transcode SVG " + path, e); } // cache it if (!imageDataCache.containsKey(path)) imageDataCache.put(path, Collections.synchronizedMap(new HashMap<>())); imageDataCache.get(path).put(size, imageData); return imageData; } @Override public void init(BundleContext bundleContext, Map properties) { super.init(bundleContext, properties); // preload all icons // paths: for (String p : getImagesPaths()) { // if (!p.endsWith(".svg")) // continue paths; // createImageFromSvg(p, getDefaultIconSize()); // } } // @Override // public void destroy(BundleContext bundleContext, Map properties) { // Display display = Display.getDefault(); // if (display != null) // for (String path : imageCache.keySet()) { // for (Image image : imageCache.get(path).values()) { // display.syncExec(() -> image.dispose()); // } // } // super.destroy(bundleContext, properties); // } }