diff --git a/designer-base/src/main/java/com/fr/base/svg/IconUtils.java b/designer-base/src/main/java/com/fr/base/svg/IconUtils.java new file mode 100644 index 0000000000..fd99f0e994 --- /dev/null +++ b/designer-base/src/main/java/com/fr/base/svg/IconUtils.java @@ -0,0 +1,147 @@ +package com.fr.base.svg; + +import com.fr.general.IOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.bridge.StableFactory; +import com.fr.stable.fun.ResourcePathTransformer; +import com.fr.stable.plugin.ExtraClassManagerProvider; + +import javax.swing.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * 主要是用来读取svgIcon的工具类 + * @author Yvan + * @version 10.0 + * Created by Yvan on 2020/12/23 + */ +public class IconUtils { + + private static final String ICON_SUFFIX_SVG = ".svg"; + private static final String ICON_SUFFIX_PNG = ".png"; + private static final String ICON_SUFFIX_GIF = ".gif"; + private static final String SUFFIX_SEPARATOR = "."; + + public static final String ICON_TYPE_NORMAL= "_normal.svg"; + public static final String ICON_TYPE_DISABLED= "_disabled.svg"; + public static final String ICON_TYPE_PRESSED= "_pressed.svg"; + + + /** + * 可以读取SVG图标或者普通图标,并且可以读取不带扩展名的文件 + * 不带扩展名时以svg优先、其次png,最后gif + * @param resource 图片路径 + * @return 图标 + */ + public static Icon readIcon(String resource) { + // 判断是否有.XXX文件后缀 + if (resource.contains(SUFFIX_SEPARATOR)) { + // 判断是否以.svg结尾 + if (resource.endsWith(ICON_SUFFIX_SVG)) { + if (IOUtils.readResource(resource) != null) { + return SVGIcon.readSVGIcon(transformPath(resource)); + } + // 适配插件 + return adjustPluginsPng(resource); + } + return IOUtils.readIcon(resource); + } + // 文件无后缀时 + return readNoSuffixResource(resource, ICON_TYPE_NORMAL); + } + + /** + * 适配插件中使用_normal.png、_selected.png、_disabled.png的情况 + * @param resource 图片路径 + * @return Icon + */ + private static Icon adjustPluginsPng(String resource) { + String pngResource = resource.replace(ICON_SUFFIX_SVG, ICON_SUFFIX_PNG); + // 考虑到某些插件可能只会使用三种图标中的一部分,这里做个判断,不然就会因为资源不存在而报错 + return IOUtils.readResource(pngResource) == null ? new ImageIcon() : IOUtils.readIcon(pngResource); + } + + /** + * 尝试读取不带扩展名的图标,svg优先,其次png,最后gif,都没读到就打印错误日志,返回空白Icon + * @param resource 图片路径 + * @param svgIconType 针对svg来说的图标类型 + * 取值为:ICON_TYPE_NORMAL、ICON_TYPE_DISABLED、ICON_TYPE_PRESSED + * @return 图标 + */ + private static Icon readNoSuffixResource(String resource, String svgIconType) { + String svgPath = resource + svgIconType; + if (IOUtils.readResource(svgPath) != null) { + return SVGIcon.readSVGIcon(transformPath(svgPath)); + } + String pngPath = resource + ICON_SUFFIX_PNG; + if (IOUtils.readResource(pngPath) != null) { + return IOUtils.readIcon(transformPath(pngPath)); + } + String gifPath = resource + ICON_SUFFIX_GIF; + if (IOUtils.readResource(gifPath) != null) { + return IOUtils.readIcon(transformPath(gifPath)); + } + FineLoggerFactory.getLogger().error("File not exists:{}", resource); + return new ImageIcon(); + } + + /** + * 读取指定类型的svgIcon + * @param resource + * @param svgIconType + * @return + */ + public static Icon readSVGIcon(String resource, String svgIconType) { + // 判断下是否有后缀 + if (!resource.contains(SUFFIX_SEPARATOR)) { + return readNoSuffixResource(resource, svgIconType); + } + // 如果是".png"后缀,就替换为传入的svgIconType,然后读取图标 + if (resource.endsWith(ICON_SUFFIX_PNG)) { + return readSpecifiedTypeIcon(resource, ICON_SUFFIX_PNG, svgIconType); + } + // 如果是"_XXXXXX.svg"后缀 + if (resource.endsWith(ICON_TYPE_NORMAL)) { + return readSpecifiedTypeIcon(resource, ICON_TYPE_NORMAL, svgIconType); + } + if (resource.endsWith(ICON_TYPE_DISABLED)) { + return readSpecifiedTypeIcon(resource, ICON_TYPE_DISABLED, svgIconType); + } + if (resource.endsWith(ICON_TYPE_PRESSED)) { + return readSpecifiedTypeIcon(resource, ICON_TYPE_PRESSED, svgIconType); + } + return readIcon(resource); + } + + private static Icon readSpecifiedTypeIcon(String resource, String oldSuffix, String newSuffix) { + String iconPath = resource.replace(oldSuffix, newSuffix); + if (IOUtils.readResource(iconPath) != null) { + return SVGIcon.readSVGIcon(transformPath(iconPath)); + } + return readIcon(resource); + } + + private static String transformPath(String path) { + Set set = getResourcePathTransformers(); + for (ResourcePathTransformer transformer : set) { + if (transformer.accept(path)) { + return transformer.transform(path); + } + } + return path; + } + + private static Set getResourcePathTransformers() { + Set set = new HashSet(); + ExtraClassManagerProvider provider = StableFactory.getExtraClassManager(); + if (provider != null) { + Set pluginProvided = provider.getArray(ResourcePathTransformer.MARK_STRING); + set.addAll(pluginProvided); + } + ResourcePathTransformer[] oemSet = StableFactory.getMarkedObjectsFromCollection(ResourcePathTransformer.MARK_STRING, ResourcePathTransformer.class); + set.addAll(Arrays.asList(oemSet)); + return set; + } +} diff --git a/designer-base/src/main/java/com/fr/base/svg/SVGIcon.java b/designer-base/src/main/java/com/fr/base/svg/SVGIcon.java new file mode 100644 index 0000000000..172b10dac5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/base/svg/SVGIcon.java @@ -0,0 +1,99 @@ +package com.fr.base.svg; + +import com.fr.general.IOUtils; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * SVG转化而来的Icon + * @author Yvan + * @version 10.0 + * Created by Yvan on 2020/12/17 + */ +public class SVGIcon implements Icon { + + private BufferedImage image; + + private static final boolean HI_DPI_SURPORT = SystemScaleUtils.isJreHiDPIEnabled(); + + public static final float SYSTEM_SCALE = SystemScaleUtils.sysScale(); + + private static final String ICON_PREFIX = "/"; + + public SVGIcon(BufferedImage image) { + this.image = image; + } + + private static Map iconCache = new ConcurrentHashMap<>(); + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (HI_DPI_SURPORT) { + Graphics2D graphics = (Graphics2D) g.create(x, y, image.getWidth(null), image.getHeight(null)); + float scale = SYSTEM_SCALE; + graphics.scale(1 / scale, 1 / scale); + graphics.drawImage(image, 0, 0, null); + graphics.scale(1.0D, 1.0D); + graphics.dispose(); + } else { + g.drawImage(image, x, y, null); + } + } + + @Override + public int getIconWidth() { + return HI_DPI_SURPORT ? (int) (image.getWidth() / SYSTEM_SCALE) : image.getWidth(); + } + + @Override + public int getIconHeight() { + return HI_DPI_SURPORT ? (int) (image.getHeight() / SYSTEM_SCALE) : image.getHeight(); + } + + /** + * 读取高清图标 + * @param url + * @return + */ + public static Icon readSVGIcon(String url) { + return readSVGIconWithCache(url, true); + } + + public static Icon readSVGIconWithCache(String url, boolean cacheRead) { + Icon icon = null; + if (cacheRead) { + icon = iconCache.get(url); + } + if (icon == null) { + if (!url.startsWith(ICON_PREFIX)) { + url = ICON_PREFIX + url; + } + BufferedImage image = (BufferedImage) SVGLoader.load(url); + icon = image == null ? IOUtils.readIcon(url) : new SVGIcon(image); + //只缓存svg图标 + if (image != null){ + iconCache.put(url, icon); + } + } + return icon; + } + + /** + * 读取指定尺寸的图标 + * @param url 资源路径 + * @param width 宽度 + * @param height 高度 + * @return + */ + public static Icon readSVGIcon(String url, float width, float height) { + if (!url.startsWith(ICON_PREFIX)) { + url = ICON_PREFIX + url; + } + BufferedImage image = (BufferedImage) SVGLoader.load(url, width, height); + return image == null ? IOUtils.readIcon(url) : new SVGIcon(image); + } +}