Browse Source

REPORT-42238 【10.0.13】JDK11设计器图标模糊问题优化

No.1 将转化SVG图标的功能先上传
research/11.0
Yvan 4 years ago
parent
commit
95221b9e7c
  1. 59
      designer-base/src/main/java/com/fr/base/svg/SVGIcon.java
  2. 74
      designer-base/src/main/java/com/fr/base/svg/SVGLoader.java
  3. 170
      designer-base/src/main/java/com/fr/base/svg/SVGTranscoder.java
  4. 96
      designer-base/src/main/java/com/fr/base/svg/SystemScaleUtils.java

59
designer-base/src/main/java/com/fr/base/svg/SVGIcon.java

@ -0,0 +1,59 @@
package com.fr.base.svg;
import javax.swing.Icon;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
/**
* 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();
public SVGIcon(BufferedImage image) {
this.image = image;
}
@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 new SVGIcon((BufferedImage) SVGLoader.load(url));
}
}

74
designer-base/src/main/java/com/fr/base/svg/SVGLoader.java

@ -0,0 +1,74 @@
package com.fr.base.svg;
import com.fr.general.IOUtils;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.xmlgraphics.java2d.Dimension2DDouble;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
/**
* SVG图标加载器
* @author Yvan
* @version 10.0
* Created by Yvan on 2020/12/17
*/
public class SVGLoader {
public static final int ICON_DEFAULT_SIZE = 16;
public SVGLoader() {
}
@Nullable
public static Image load(@NotNull String url) {
try {
return load(IOUtils.getResource(url, SVGLoader.class), SVGIcon.SYSTEM_SCALE);
} catch (IOException ignore) {
return null;
}
}
@Nullable
public static Image load(@NotNull URL url) throws IOException {
return load(url, SVGIcon.SYSTEM_SCALE);
}
@Nullable
public static Image load(@NotNull URL url, double scale) throws IOException {
try {
String svgUri = url.toString();
TranscoderInput input = new TranscoderInput(svgUri);
return SVGTranscoder.createImage(scale, input).getImage();
} catch (TranscoderException e) {
throw new IOException(e);
}
}
@Nullable
public static Image load(@NotNull URL url, double scale, Dimension2DDouble dimension) throws IOException {
try {
String svgUri = url.toString();
TranscoderInput input = new TranscoderInput(svgUri);
return SVGTranscoder.createImage(scale, input,
(float) (dimension.getWidth() * scale), (float) (dimension.getHeight() * scale)).getImage();
} catch (TranscoderException e) {
throw new IOException(e);
}
}
@Nullable
public static Image load(@NotNull URL url, double scale, double width, double height) throws IOException {
try {
String svgUri = url.toString();
TranscoderInput input = new TranscoderInput(svgUri);
return SVGTranscoder.createImage(scale, input, (float) (width * scale), (float) (height * scale)).getImage();
} catch (TranscoderException e) {
throw new IOException(e);
}
}
}

170
designer-base/src/main/java/com/fr/base/svg/SVGTranscoder.java

@ -0,0 +1,170 @@
package com.fr.base.svg;
import com.fr.stable.AssistUtils;
import com.fr.value.AtomicNotNullLazyValue;
import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
import org.apache.batik.anim.dom.SVGOMDocument;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.transcoder.SVGAbstractTranscoder;
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.util.XMLResourceDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGDocument;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.StringReader;
/**
* 可以根据某个缩放倍数scale将SVG图片转化为Image对象
* @author Yvan
* @version 10.0
* Created by Yvan on 2020/12/17
*/
public class SVGTranscoder extends ImageTranscoder {
private static final float DEFAULT_VALUE = -1.0F;
public static final float ICON_DEFAULT_SIZE = 16F;
private float origDocWidth;
private float origDocHeight;
@Nullable
private BufferedImage image;
private final double scale;
@NotNull
private static AtomicNotNullLazyValue<Double> iconMaxSize = new AtomicNotNullLazyValue<Double>() {
@NotNull
@Override
protected Double compute() {
double maxSize = Double.MAX_VALUE;
if (!GraphicsEnvironment.isHeadless()) {
GraphicsDevice defaultScreenDevice = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice();
Rectangle bounds = defaultScreenDevice.getDefaultConfiguration().getBounds();
AffineTransform tx = defaultScreenDevice
.getDefaultConfiguration()
.getDefaultTransform();
maxSize = Math.max(bounds.width * tx.getScaleX(), bounds.height * tx.getScaleY());
}
return maxSize;
}
};
public SVGTranscoder(double scale) {
this.scale = scale;
this.width = ICON_DEFAULT_SIZE;
this.height = ICON_DEFAULT_SIZE;
}
public final float getOrigDocWidth() {
return this.origDocWidth;
}
public final void setOrigDocWidth(float origDocWidth) {
this.origDocWidth = origDocWidth;
}
public final float getOrigDocHeight() {
return this.origDocHeight;
}
public final void setOrigDocHeight(float origDocHeight) {
this.origDocHeight = origDocHeight;
}
public static double getIconMaxSize() {
return iconMaxSize.getValue();
}
@Nullable
public final BufferedImage getImage() {
return this.image;
}
@NotNull
public static SVGTranscoder createImage(double scale, @NotNull TranscoderInput input) throws TranscoderException {
return createImage(scale, input, -1, -1);
}
@NotNull
public static SVGTranscoder createImage(double scale, @NotNull TranscoderInput input, float overriddenWidth, float overriddenHeight) throws TranscoderException {
SVGTranscoder transcoder = new SVGTranscoder(scale);
if (!AssistUtils.equals(overriddenWidth, DEFAULT_VALUE)) {
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, overriddenWidth);
}
if (!AssistUtils.equals(overriddenHeight, DEFAULT_VALUE)) {
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, overriddenHeight);
}
double iconMaxSize = SVGTranscoder.iconMaxSize.getValue();
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_MAX_WIDTH, (float) iconMaxSize);
transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_MAX_HEIGHT, (float) iconMaxSize);
transcoder.transcode(input, null);
return transcoder;
}
private static SVGDocument createFallbackPlaceholder() {
try {
String fallbackIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\">\n" +
" <rect x=\"1\" y=\"1\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"red\" stroke-width=\"2\"/>\n" +
" <line x1=\"1\" y1=\"1\" x2=\"15\" y2=\"15\" stroke=\"red\" stroke-width=\"2\"/>\n" +
" <line x1=\"1\" y1=\"15\" x2=\"15\" y2=\"1\" stroke=\"red\" stroke-width=\"2\"/>\n" +
"</svg>\n";
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
return (SVGDocument) factory.createDocument(null, new StringReader(fallbackIcon));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override
protected void setImageSize(float docWidth, float docHeight) {
super.setImageSize((float) (docWidth * this.scale), (float) (docHeight * this.scale));
this.origDocWidth = docWidth;
this.origDocHeight = docHeight;
}
@Override
@NotNull
public BufferedImage createImage(int width, int height) {
return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
}
@Override
public void writeImage(@NotNull BufferedImage image, @Nullable TranscoderOutput output) {
this.image = image;
}
@Override
@NotNull
protected UserAgent createUserAgent() {
return new SVGAbstractTranscoderUserAgent() {
@Override
@NotNull
public SVGDocument getBrokenLinkDocument(@NotNull Element e, @NotNull String url, @NotNull String message) {
return createFallbackPlaceholder();
}
};
}
/**
* 开放访问权限
*/
@Override
public BridgeContext createBridgeContext(SVGOMDocument doc) {
return super.createBridgeContext(doc);
}
}

96
designer-base/src/main/java/com/fr/base/svg/SystemScaleUtils.java

@ -0,0 +1,96 @@
package com.fr.base.svg;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StableUtils;
import com.fr.stable.os.OperatingSystem;
import org.jetbrains.annotations.NotNull;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;
/**
* 获取系统Scale相关的工具类
* @author Yvan
* @version 10.0
* Created by Yvan on 2020/12/17
*/
public class SystemScaleUtils {
private static final AtomicReference<Boolean> JRE_HIDPI = new AtomicReference<>();
private static final String HI_DPI = "hidpi";
/**
* 判断是否支持高清
* @return
*/
public static boolean isJreHiDPIEnabled() {
if (JRE_HIDPI.get() != null) {
return JRE_HIDPI.get();
}
if (OperatingSystem.isMacos()) {
// 如果是mac os系统,直接返回true
return true;
}
if (OperatingSystem.isWindows() && StableUtils.getMajorJavaVersion() <= 8) {
// 如果是jdk8 + Windows系统,直接返回false
return false;
}
synchronized (JRE_HIDPI) {
if (JRE_HIDPI.get() != null) {
return JRE_HIDPI.get();
}
boolean result = false;
if (getBooleanProperty(HI_DPI, true)) {
try {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Class<?> sunGraphicsEnvironmentClass = Class.forName("sun.java2d.SunGraphicsEnvironment");
if (sunGraphicsEnvironmentClass.isInstance(ge)) {
try {
Method method = sunGraphicsEnvironmentClass.getDeclaredMethod("isUIScaleEnabled");
method.setAccessible(true);
result = (Boolean)method.invoke(ge);
}
catch (NoSuchMethodException e) {
FineLoggerFactory.getLogger().error(e.getMessage());
}
}
}
catch (Throwable ignore) {
}
}
JRE_HIDPI.set(result);
return result;
}
}
public static boolean getBooleanProperty(@NotNull final String key, final boolean defaultValue) {
final String value = System.getProperty(key);
return value == null ? defaultValue : Boolean.parseBoolean(value);
}
/**
* 获取系统Scale
* @return
*/
public static float sysScale() {
float scale = 1.0f;
// 先判断是否支持高清,不支持代表此时是Windows + jdk8 的设计器,返回的scale值为1.0
if (isJreHiDPIEnabled()) {
// 获取屏幕图形设备对象
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (graphicsDevice != null) {
// 获取图形配置对象
GraphicsConfiguration configuration = graphicsDevice.getDefaultConfiguration();
if (configuration != null && configuration.getDevice().getType() != GraphicsDevice.TYPE_PRINTER) {
// 获取屏幕缩放率,mac下固定为2,Windows+jdk11则将得到用户设置的dpi值
scale = (float) configuration.getDefaultTransform().getScaleX();
}
}
}
return scale;
}
}
Loading…
Cancel
Save