org.polymap.rhei.batik.app.SvgImageRegistryHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.rhei.batik.app.SvgImageRegistryHelper.java

Source

/* 
 * polymap.org
 * Copyright (C) 2015, Falko Brutigam. All rights reserved.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3.0 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */
package org.polymap.rhei.batik.app;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;

import org.apache.batik.transcoder.TranscoderException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.common.base.Joiner;

import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IDecoration;

import org.eclipse.ui.plugin.AbstractUIPlugin;

import org.polymap.core.runtime.Timer;
import org.polymap.core.runtime.config.Config2;
import org.polymap.core.runtime.config.ConfigurationFactory;
import org.polymap.core.runtime.config.DefaultString;
import org.polymap.core.runtime.config.Immutable;
import org.polymap.core.ui.ImageRegistryHelper;

import org.polymap.rhei.batik.engine.svg.ImageConfiguration;
import org.polymap.rhei.batik.engine.svg.ImageConfiguration.ReplaceConfiguration;
import org.polymap.rhei.batik.engine.svg.Scale;
import org.polymap.rhei.batik.engine.svg.Svg2Png;
import org.polymap.rhei.batik.engine.svg.Svg2Png.COLOR_TYPE;

/**
 * Provides auto generated PNG icons from SVG source.
 *
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 */
public class SvgImageRegistryHelper extends ImageRegistryHelper {

    private static final Log log = LogFactory.getLog(SvgImageRegistryHelper.class);

    /** Image configuration used to create {@link #svgImage(String, String)}. */
    public final static String NORMAL48 = "normal48";

    /** Image configuration used to create {@link #svgImage(String, String)}. */
    public final static String ACTION48 = "normal48-link";

    /** Normal image configuration used to create {@link #svgImage(String, String)}. */
    public final static String NORMAL12 = "normal12";

    /** Action image configuration used to create {@link #svgImage(String, String)}. */
    public final static String ACTION12 = "normal12-link";

    /** Normal image configuration used to create {@link #svgImage(String, String)}. */
    public final static String NORMAL24 = "normal24";

    /** White/background image configuration used to create {@link #svgImage(String, String)}. */
    public final static String WHITE24 = "white24";

    /** Action image configuration used to create {@link #svgImage(String, String)}. */
    public final static String ACTION24 = "normal24-link";

    /** Action image configuration used to create {@link #svgImage(String, String)}. */
    public final static String OK24 = "normal24-ok";

    /** Action image configuration used to create {@link #svgImage(String, String)}. */
    public final static String ALERT24 = "normal24-alert";

    /** Action image configuration used to create {@link #svgImage(String, String)}. */
    public final static String ERROR24 = "normal24-error";

    /** Image configuration for normale, disabled icons created by {@link #svgImage(String, String)}. */
    public final static String DISABLED24 = "normal24-disabled";

    /** Image configuration for normale, disabled icons created by {@link #svgImage(String, String)}. */
    public final static String DISABLED12 = "normal12-disabled";

    /** Image configuration for normale, hovered icons created by {@link #svgImage(String, String)}. */
    public final static String HOVER24 = "normal24-hovered";

    /** Normal image configuration used to create {@link #svgImage(String, String)}. */
    public final static String OVR12_ACTION = "ovr12-action";

    public static final RGB COLOR_WHITE = new RGB(0xFF, 0xFF, 0xFF);
    public static final RGB COLOR_ACTION = new RGB(103, 134, 190); // #374A5E check _standard.scss!
    public static final RGB COLOR_NORMAL = new RGB(0x84, 0x9f, 0xbd); // #849FBD
    public static final RGB COLOR_DISABLED = new RGB(0xb0, 0xb0, 0xb0);
    public static final RGB COLOR_DANGER = new RGB(0xdf, 0x3e, 0x3e); //0xFF, 0x61, 0x39 );
    public static final RGB COLOR_OK = new RGB(0x81, 0xCC, 0x39);

    /**
     * The quadrant of an overlay created by {@link SvgImageRegistryHelper#svgOverlayedImage(String, String, String, String, Quadrant)}.
     */
    public enum Quadrant {
        TopLeft(IDecoration.TOP_LEFT), TopRight(IDecoration.TOP_RIGHT), BottmLeft(
                IDecoration.BOTTOM_LEFT), BottomRight(IDecoration.BOTTOM_RIGHT);

        public int iDecorationConstant;

        private Quadrant(int iDecorationConstant) {
            this.iDecorationConstant = iDecorationConstant;
        }
    }

    // instance *******************************************

    @Immutable
    @DefaultString("resources/icons/")
    public Config2<SvgImageRegistryHelper, String> svgBasePath;

    private File tempFolder;

    private Map<String, SvgConfiguration> svgConfigs = new HashMap();

    public SvgImageRegistryHelper(AbstractUIPlugin plugin) {
        super(plugin);
        ConfigurationFactory.inject(this);

        // temp folder
        try {
            tempFolder = new File(plugin.getStateLocation().toFile(), "svg-icons");
            tempFolder.mkdirs();
            FileUtils.cleanDirectory(tempFolder);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // default configs
        putConfig(NORMAL12, new ReplaceBlackSvgConfiguration(COLOR_NORMAL, 16));
        putConfig(NORMAL24, new ReplaceBlackSvgConfiguration(COLOR_NORMAL, 24));
        putConfig(NORMAL48, new ReplaceBlackSvgConfiguration(COLOR_NORMAL, 48));
        putConfig(ACTION48, new ReplaceBlackSvgConfiguration(COLOR_ACTION, 48));
        putConfig(ACTION24, new ReplaceBlackSvgConfiguration(COLOR_ACTION, 24));
        putConfig(ACTION12, new ReplaceBlackSvgConfiguration(COLOR_ACTION, 16));
        putConfig(OK24, new ReplaceBlackSvgConfiguration(COLOR_OK, 24));
        putConfig(ALERT24, new ReplaceBlackSvgConfiguration(new RGB(0xFF, 0xD2, 0x2C), 24));
        putConfig(ERROR24, new ReplaceBlackSvgConfiguration(COLOR_DANGER, 24));
        putConfig(WHITE24, new ReplaceBlackSvgConfiguration(COLOR_WHITE, 24));
        putConfig(OVR12_ACTION, new ReplaceBlackSvgConfiguration(new RGB(140, 240, 100), 16));
        putConfig(DISABLED24, new ReplaceBlackSvgConfiguration(COLOR_DISABLED, 24));
        putConfig(DISABLED12, new ReplaceBlackSvgConfiguration(COLOR_DISABLED, 16));
    }

    public void putConfig(String name, SvgConfiguration config) {
        config.baseTempFolder = tempFolder;
        config.plugin = plugin;
        svgConfigs.put(name, config);
    }

    /**
     * 
     * @see #svgImageChecked(String, String)
     * @param path The path to the image inside the bundle. This can be relative to
     *        the bundle root or relative to {@link #svgBasePath}.
     * @param configName
     * @return Newly generated are cached image. Must no be used outside current user session!
     * @throws IllegalArgumentException(FileNotFoundException) If the given path does not exist. 
     */
    public Image svgImage(String path, String configName) {
        return svgImageOpt(path, configName).orElseThrow(() -> new IllegalArgumentException(
                new FileNotFoundException("No such SVG file/resource found: " + path)));
    }

    /**
     * 
     * @see #svgImageChecked(String, String)
     * @param path The path to the image inside the bundle. This can be relative to
     *        the bundle root or relative to {@link #svgBasePath}.
     * @param configName
     * @return Newly generated are cached image. Must no be used outside current user session!
     * @throws FileNotFoundException If the given path does not exist. 
     */
    public Image svgImageChecked(String path, String configName) throws FileNotFoundException {
        return svgImageOpt(path, configName)
                .orElseThrow(() -> new FileNotFoundException("No such SVG file/resource found: " + path));
    }

    /**
     * 
     *
     * @param path The path to the image inside the bundle. This can be relative to
     *        the bundle root or relative to {@link #svgBasePath}.
     * @param configName
     * @return Newly generated are cached image. Must no be used outside current user session!
     * @throws FileNotFoundException 
     */
    public Optional<Image> svgImageOpt(String path, String configName) {
        final String key = configName + "-" + path;

        Image image = registry.get().get(key);
        if (image == null || image.isDisposed()) {
            // create
            Optional<ImageDescriptor> desc = createSvgImage(configName, path);
            if (!desc.isPresent()) {
                return Optional.empty();
            }

            // check again, maybe anybody else did it meanwhile
            synchronized (registryLock) {
                image = registry.get().get(key);
                if (image == null || image.isDisposed()) {
                    registry.get().put(key, desc.get());
                    image = registry.get().get(key);
                }
            }
        }
        return Optional.of(image);
    }

    /**
     * 
     *
     * @param baseImagePath
     * @param baseConfigName
     * @param ovrImagePath
     * @param ovrConfigName
     * @param quadrant
     * @return Newly generated are cached image. Must no be used outside current user session!
     */
    public Image svgOverlayedImage(String baseImagePath, String baseConfigName, String ovrImagePath,
            String ovrConfigName, Quadrant quadrant) {
        String key = Joiner.on("-").join(baseImagePath, baseConfigName, ovrImagePath, ovrConfigName);
        Image image = registry.get().get(key);
        if (image == null || image.isDisposed()) {
            Image baseImage = svgImage(baseImagePath, baseConfigName);
            ImageDescriptor ovrImage = svgImageDescriptor(ovrImagePath, ovrConfigName);
            image = new DecorationOverlayIcon(baseImage, ovrImage, quadrant.iDecorationConstant).createImage();

            registry.get().put(key, image);
            image = registry.get().get(key);
        }
        return image;
    }

    /**
     * 
     *
     * @param path The path to the image inside the bundle. This can be relative to
     *        the bundle root or relative to {@link #svgBasePath}.
     * @param configName
     */
    public ImageDescriptor svgImageDescriptor(String path, String configName) {
        svgImage(path, configName);

        String key = configName + "-" + path;
        return registry.get().getDescriptor(key);
    }

    /**
     * 
     *
     * @param configName
     * @param path The path to the image inside the bundle. This can be relative to
     *        the bundle root or relative to {@link #svgBasePath}.
     * @return Newly created {@link Image}.
     * @throws FileNotFoundException 
     */
    protected Optional<ImageDescriptor> createSvgImage(String configName, String path) {
        if (plugin.getBundle().getResource(path) == null) {
            path = svgBasePath.get() + path;
        }
        if (plugin.getBundle().getResource(path) == null) {
            return Optional.empty();
        }

        try {
            return Optional.of(svgConfigs.get(configName).createImage(path));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 
     */
    public static abstract class SvgConfiguration {

        protected File baseTempFolder;

        protected AbstractUIPlugin plugin;

        protected abstract String colorScheme();

        protected abstract Scale scale();

        protected abstract ImageConfiguration imageConfiguration();

        protected File tempFolder() {
            File result = new File(new File(baseTempFolder, colorScheme()), scale().name());
            result.mkdirs();
            return result;
        }

        public ImageDescriptor createImage(String svgPath) throws TranscoderException, IOException {
            Timer timer = new Timer();
            File pngFile = new File(tempFolder(), FilenameUtils.getBaseName(svgPath) + ".png");
            if (!pngFile.exists()) {
                ImageConfiguration imageConfig = imageConfiguration();
                Scale scale = scale();
                URL svgInput = plugin.getBundle().getResource(svgPath);
                new Svg2Png().transcode(pngFile, svgInput, scale, imageConfig);
                log.debug("SVG -> " + pngFile.getAbsolutePath() + " (" + timer.elapsedTime() + "ms)");
            } else {
                log.debug("SVG -> " + pngFile.getAbsolutePath() + " (cached)");
            }
            ImageDescriptor result = ImageDescriptor.createFromURL(pngFile.toURI().toURL());
            return result;
        }
    }

    /**
     * 
     */
    public static class ReplaceBlackSvgConfiguration extends SvgConfiguration {

        private String colorScheme;

        private Scale scale;

        private RGB replace;

        public ReplaceBlackSvgConfiguration(RGB replace, int dim) {
            this.replace = replace;
            this.colorScheme = Joiner.on("-").join(replace.red, replace.green, replace.blue);
            this.scale = Scale.getAsScale(Integer.valueOf(dim));
        }

        @Override
        protected String colorScheme() {
            return colorScheme;
        }

        @Override
        protected Scale scale() {
            return scale;
        }

        @Override
        protected ImageConfiguration imageConfiguration() {
            ImageConfiguration imageConfig = new ImageConfiguration();
            imageConfig.setName(colorScheme());
            imageConfig.setRGB(replace);
            ReplaceConfiguration replaceConfig = new ReplaceConfiguration(new RGB(0, 0, 0), replace);
            imageConfig.getReplaceConfigurations().add(replaceConfig);
            imageConfig.setColorType(COLOR_TYPE.ARGB);
            return imageConfig;
        }
    }

}