Fix SVG viewer: use url if possible, handle scale correctly

This commit is contained in:
Anton Tarasov
2018-04-28 12:53:59 +03:00
parent 5b03f2197d
commit d8e12515e4
9 changed files with 88 additions and 30 deletions
@@ -18,7 +18,7 @@ package org.intellij.images.editor;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.function.Function;
import java.util.function.BiFunction;
/**
* Image document to show or edit in {@link ImageEditor}.
@@ -30,7 +30,7 @@ public interface ImageDocument {
/**
* A scaled image provider.
*/
interface ScaledImageProvider extends Function<Double/* scale */, BufferedImage> {}
interface ScaledImageProvider extends BiFunction<Double/* scale */, Component, BufferedImage> {}
/**
* Return image for rendering
@@ -39,7 +39,7 @@ public final class ImageEditorManagerImpl {
ui.getImageComponent().setGridVisible(gridOptions.isShowDefault());
ui.getImageComponent().setTransparencyChessboardVisible(transparencyChessboardOptions.isShowDefault());
ui.setImageProvider(ignore -> image, null);
ui.setImageProvider((scale, ancestor) -> image, null);
return ui;
}
}
@@ -382,8 +382,8 @@ final class ThumbnailViewUI extends JPanel implements DataProvider, Disposable {
// File rendering
setFileSize(file.getLength());
try {
BufferedImage image = IfsUtil.getImage(file);
ImageComponent imageComponent = getImageComponent();
BufferedImage image = IfsUtil.getImage(file, imageComponent);
imageComponent.getDocument().setValue(image);
setFormat(IfsUtil.getFormat(file));
} catch (Exception e) {
@@ -73,7 +73,7 @@ public class ImageComponent extends JComponent {
UIManager.getDefaults().put(uiClassID, ImageComponentUI.class.getName());
}
private final ImageDocument document = new ImageDocumentImpl();
private final ImageDocument document = new ImageDocumentImpl(this);
private final Grid grid = new Grid();
private final Chessboard chessboard = new Chessboard();
private boolean myFileSizeVisible = true;
@@ -247,6 +247,11 @@ public class ImageComponent extends JComponent {
private ScaledImageProvider imageProvider;
private String format;
private Image renderer;
private Component myComponent;
public ImageDocumentImpl(Component component) {
this.myComponent = component;
}
public Image getRenderer() {
return renderer;
@@ -263,12 +268,12 @@ public class ImageComponent extends JComponent {
@Override
public BufferedImage getValue(double scale) {
return imageProvider != null ? imageProvider.apply(scale) : null;
return imageProvider != null ? imageProvider.apply(scale, myComponent) : null;
}
public void setValue(BufferedImage image) {
this.renderer = image != null ? Toolkit.getDefaultToolkit().createImage(image.getSource()) : null;
setValue(image != null ? ignore -> image : null);
setValue(image != null ? (scale, anchor) -> image : null);
}
@Override
+43 -16
View File
@@ -18,14 +18,15 @@
package org.intellij.images.vfs;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.reference.SoftReference;
import com.intellij.util.ImageLoader;
import com.intellij.util.LogicalRoot;
import com.intellij.util.LogicalRootsManager;
import com.intellij.util.SVGLoader;
@@ -41,6 +42,7 @@ import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -50,12 +52,16 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import static com.intellij.util.ui.JBUI.ScaleType.OBJ_SCALE;
/**
* Image loader utility.
*
* @author <a href="mailto:aefimov.box@gmail.com">Alexey Efimov</a>
*/
public final class IfsUtil {
private static final Logger LOG = Logger.getInstance("#org.intellij.images.vfs.IfsUtil");
public static final String ICO_FORMAT = "ico";
public static final String SVG_FORMAT = "svg";
@@ -82,27 +88,43 @@ public final class IfsUtil {
try {
final BufferedImage image = ICO_IMAGE_PARSER.getBufferedImage(new ByteSourceArray(content), null);
file.putUserData(FORMAT_KEY, ICO_FORMAT);
file.putUserData(IMAGE_PROVIDER_REF_KEY, new SoftReference<>(zoom -> image));
file.putUserData(IMAGE_PROVIDER_REF_KEY, new SoftReference<>((scale, ancestor) -> image));
return true;
}
catch (ImageReadException ignore) { }
}
if (isScalableImage(file)) {
final Ref<URL> url = Ref.create();
try {
URL url = new File(file.getPath()).toURI().toURL();
// ensure that image can be displayed (or throw exception otherwise)
SVGLoader.load(url, new ByteArrayInputStream(content), 1.0f);
file.putUserData(FORMAT_KEY, SVG_FORMAT);
file.putUserData(IMAGE_PROVIDER_REF_KEY, new SoftReference<>(zoom -> {
ScaleContext ctx = ScaleContext.create(); // todo: lack component, defaults to main monitor
return (BufferedImage)ImageLoader.loadFromUrl(url, true, false, null, ctx);
}));
return true;
url.set(new File(file.getPath()).toURI().toURL());
}
catch (MalformedURLException ignored) {}
catch (MalformedURLException ex) {
LOG.warn(ex.getMessage());
}
try {
// ensure svg can be displayed
SVGLoader.load(url.get(), new ByteArrayInputStream(content), 1.0f);
}
catch (Throwable t) {
LOG.warn(url.get() + " " + t.getMessage());
return false;
}
file.putUserData(FORMAT_KEY, SVG_FORMAT);
file.putUserData(IMAGE_PROVIDER_REF_KEY, new SoftReference<>((zoom, ancestor) -> {
try {
final ScaleContext ctx = ScaleContext.create(ancestor);
ctx.update(OBJ_SCALE.of(zoom));
return SVGLoader.loadHiDPI(url.get(), new ByteArrayInputStream(content), ctx);
}
catch (Throwable t) {
LOG.warn(url.get() + " " + t.getMessage());
return null;
}
}));
return true;
}
InputStream inputStream = new ByteArrayInputStream(content, 0, content.length);
@@ -117,7 +139,7 @@ public final class IfsUtil {
imageReader.setInput(imageInputStream, true, true);
int minIndex = imageReader.getMinIndex();
BufferedImage image = imageReader.read(minIndex, param);
file.putUserData(IMAGE_PROVIDER_REF_KEY, new SoftReference<>(zoom -> image));
file.putUserData(IMAGE_PROVIDER_REF_KEY, new SoftReference<>((zoom, ancestor) -> image));
return true;
} finally {
imageReader.dispose();
@@ -136,9 +158,14 @@ public final class IfsUtil {
@Nullable
public static BufferedImage getImage(@NotNull VirtualFile file) throws IOException {
return getImage(file, null);
}
@Nullable
public static BufferedImage getImage(@NotNull VirtualFile file, @Nullable Component ancestor) throws IOException {
ScaledImageProvider imageProvider = getImageProvider(file);
if (imageProvider == null) return null;
return imageProvider.apply(1d);
return imageProvider.apply(1d, ancestor);
}
@Nullable
@@ -46,7 +46,6 @@ import java.util.concurrent.ConcurrentMap;
import static com.intellij.util.ImageLoader.ImageDesc.Type.IMG;
import static com.intellij.util.ImageLoader.ImageDesc.Type.SVG;
import static com.intellij.util.ui.JBUI.ScaleType.PIX_SCALE;
import static com.intellij.util.ui.JBUI.ScaleType.SYS_SCALE;
public class ImageLoader implements Serializable {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.ImageLoader");
@@ -294,10 +293,7 @@ public class ImageLoader implements Serializable {
return with(new ImageConverter() {
@Override
public Image convert(Image source, ImageDesc desc) {
if (source != null && UIUtil.isJreHiDPI(ctx)) {
return RetinaImage.createFrom(source, ctx.getScale(SYS_SCALE), ourComponent);
}
return source;
return ImageUtil.ensureHiDPI(source, ctx);
}
});
}
@@ -318,6 +314,7 @@ public class ImageLoader implements Serializable {
public static final Component ourComponent = new Component() {
};
@SuppressWarnings("UnusedReturnValue")
private static boolean waitForImage(Image image) {
if (image == null) return false;
if (image.getWidth(null) > 0) return true;
@@ -18,6 +18,8 @@ package com.intellij.util;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ui.ImageUtil;
import com.intellij.util.ui.JBUI.ScaleContext;
import org.apache.batik.anim.dom.*;
import org.apache.batik.dom.AbstractDocument;
import org.apache.batik.transcoder.SVGAbstractTranscoder;
@@ -33,6 +35,7 @@ import org.w3c.dom.Element;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
@@ -40,6 +43,8 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.util.ui.JBUI.ScaleType.PIX_SCALE;
/**
* @author tav
*/
@@ -147,6 +152,12 @@ public class SVGLoader {
}
}
public static <T extends BufferedImage> T loadHiDPI(@Nullable URL url, @NotNull InputStream stream , ScaleContext ctx) throws IOException {
BufferedImage image = (BufferedImage)load(url, stream, ctx.getScale(PIX_SCALE));
//noinspection unchecked
return (T)ImageUtil.ensureHiDPI(image, ctx);
}
public static Couple<Integer> loadInfo(@Nullable URL url, @NotNull InputStream stream , double scale) throws IOException {
SVGLoader loader = new SVGLoader(url, stream, scale);
return Couple.of((int)loader.size.width, (int)loader.size.height);
@@ -17,11 +17,17 @@ package com.intellij.util.ui;
import com.intellij.util.ImageLoader;
import com.intellij.util.JBHiDPIScaledImage;
import com.intellij.util.RetinaImage;
import com.intellij.util.ui.JBUI.ScaleContext;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.image.*;
import static com.intellij.util.ui.JBUI.ScaleType.SYS_SCALE;
/**
* @author Konstantin Bulenkov
*/
@@ -127,4 +133,16 @@ public class ImageUtil {
public static Image scaleImage(Image image, double scale) {
return ImageLoader.scaleImage(image, scale);
}
/**
* Wraps the {@code image} with {@link JBHiDPIScaledImage} according to {@code ctx} when applicable.
*/
@Contract("null, _ -> null; !null, _ -> !null")
public static Image ensureHiDPI(@Nullable Image image, @NotNull ScaleContext ctx) {
if (image == null) return null;
if (UIUtil.isJreHiDPI(ctx)) {
return RetinaImage.createFrom(image, ctx.getScale(SYS_SCALE), null);
}
return image;
}
}
@@ -998,9 +998,9 @@ public class JBUI {
* Creates a context based on the comp's system scale and sticks to it via the {@link #update()} method.
*/
@NotNull
public static ScaleContext create(@NotNull Component comp) {
public static ScaleContext create(@Nullable Component comp) {
final ScaleContext ctx = new ScaleContext(SYS_SCALE.of(sysScale(comp)));
ctx.compRef = new WeakReference<Component>(comp);
if (comp != null) ctx.compRef = new WeakReference<Component>(comp);
return ctx;
}