From f2f70f412ed22ff449ea315ab2c4457723e00f18 Mon Sep 17 00:00:00 2001 From: weisj Date: Sun, 17 May 2020 14:56:09 +0200 Subject: [PATCH] Back svg icons by cached renderer image. --- .../weisj/darklaf/graphics/ImageUtil.java | 33 ++++++++----- .../ui/list/DarkListCellRendererDelegate.java | 4 +- .../weisj/darklaf/icons/DarkSVGIcon.java | 47 +++++++++++++++---- .../weisj/darklaf/icons/ImageSource.java | 36 ++++++++++++++ .../weisj/darklaf/icons/ThemedSVGIcon.java | 13 +++-- .../com/github/weisj/darklaf/util/Scale.java | 21 +++++++++ 6 files changed, 125 insertions(+), 29 deletions(-) create mode 100644 property-loader/src/main/java/com/github/weisj/darklaf/icons/ImageSource.java diff --git a/core/src/main/java/com/github/weisj/darklaf/graphics/ImageUtil.java b/core/src/main/java/com/github/weisj/darklaf/graphics/ImageUtil.java index cbd1527b..24931f13 100644 --- a/core/src/main/java/com/github/weisj/darklaf/graphics/ImageUtil.java +++ b/core/src/main/java/com/github/weisj/darklaf/graphics/ImageUtil.java @@ -29,6 +29,7 @@ import java.awt.image.BufferedImage; import javax.swing.*; +import com.github.weisj.darklaf.icons.ImageSource; import com.github.weisj.darklaf.util.Scale; /** @@ -45,8 +46,8 @@ public final class ImageUtil { int w = icon.getIconWidth(); int h = icon.getIconHeight(); GraphicsConfiguration gc = c.getGraphicsConfiguration(); - double sx = gc != null ? Scale.getScaleX(gc) : Scale.SCALE_X; - double sy = gc != null ? Scale.getScaleY(gc) : Scale.SCALE_Y; + double sx = Scale.getScaleX(gc); + double sy = Scale.getScaleY(gc); double scaleX = sx * (((double) FRAME_ICON_SIZE) / w); double scaleY = sy * (((double) FRAME_ICON_SIZE) / h); return createScaledImage(icon, scaleX, scaleY); @@ -56,12 +57,16 @@ public final class ImageUtil { if (icon == null) return null; int w = (int) (scalex * icon.getIconWidth()); int h = (int) (scaley * icon.getIconHeight()); - BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D g = (Graphics2D) image.getGraphics(); - g.scale(scalex, scaley); - icon.paintIcon(null, g, 0, 0); - g.dispose(); - return image; + if (icon instanceof ImageSource) { + return ((ImageSource) icon).createImage(w, h); + } else { + BufferedImage image = createCompatibleTranslucentImage(w, h); + Graphics2D g = (Graphics2D) image.getGraphics(); + g.scale(scalex, scaley); + icon.paintIcon(null, g, 0, 0); + g.dispose(); + return image; + } } public static Image createDragImage(final Component c, final int lw, final Color borderColor) { @@ -130,10 +135,9 @@ public final class ImageUtil { BufferedImage image; boolean scale = scalex != 1.0 || scaley != 1.0; if (scale) { - image = new BufferedImage((int) (scalex * bounds.width), (int) (scaley * bounds.height), - BufferedImage.TYPE_INT_RGB); + image = createCompatibleImage((int) (scalex * bounds.width), (int) (scaley * bounds.height)); } else { - image = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_RGB); + image = createCompatibleImage(bounds.width, bounds.height); } final Graphics2D g2d = (Graphics2D) image.getGraphics(); if (scale) { @@ -150,6 +154,13 @@ public final class ImageUtil { return image; } + public static BufferedImage createCompatibleImage(final int width, + final int height) { + return isHeadless() ? new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) + : getGraphicsConfiguration().createCompatibleImage(width, height, + Transparency.OPAQUE); + } + public static BufferedImage createCompatibleTranslucentImage(final int width, final int height) { return isHeadless() ? new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/list/DarkListCellRendererDelegate.java b/core/src/main/java/com/github/weisj/darklaf/ui/list/DarkListCellRendererDelegate.java index 7e1e3ee2..31edda91 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/list/DarkListCellRendererDelegate.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/list/DarkListCellRendererDelegate.java @@ -28,8 +28,6 @@ import java.awt.*; import javax.swing.*; -import org.jdesktop.swingx.renderer.DefaultListRenderer; - import com.github.weisj.darklaf.delegate.ListCellRendererDelegate; import com.github.weisj.darklaf.ui.cell.CellUtil; import com.github.weisj.darklaf.util.PropertyUtil; @@ -43,7 +41,7 @@ public class DarkListCellRendererDelegate extends ListCellRendererDelegate delegate) { if (delegate == null) { - super.setDelegate(new DefaultListRenderer()); + super.setDelegate(new DefaultListCellRenderer()); } else { super.setDelegate(delegate); } diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java index 7c3aa801..40ae59f1 100644 --- a/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java +++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java @@ -30,9 +30,8 @@ import java.net.URI; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; -import javax.swing.*; - import com.github.weisj.darklaf.util.LogUtil; +import com.github.weisj.darklaf.util.Scale; import com.kitfox.svg.app.beans.SVGIcon; /** @@ -41,13 +40,16 @@ import com.kitfox.svg.app.beans.SVGIcon; * @author Jannis Weis * @since 2019 */ -public class DarkSVGIcon implements DerivableIcon, RotateIcon, Serializable { +public class DarkSVGIcon implements DerivableIcon, RotateIcon, Serializable, ImageSource { private static final Logger LOGGER = LogUtil.getLogger(DarkSVGIcon.class); private final Dimension size; private final SVGIcon icon; - private final URI uri; + protected final URI uri; private final AtomicBoolean loaded; + private double scaleX; + private double scaleY; + private Image image; /** * Method to fetch the SVG icon from a url. @@ -83,16 +85,38 @@ public class DarkSVGIcon implements DerivableIcon, RotateIcon, Seri @Override public void paintIcon(final Component c, final Graphics g, final int x, final int y) { - ensureLoaded(); paintIcon(c, g, x, y, 0); } - private void ensureLoaded() { + protected boolean ensureLoaded() { if (!loaded.get()) { LOGGER.fine(() -> "Loading icon '" + uri.toASCIIString() + "'."); icon.setSvgURI(uri); loaded.set(true); + return true; } + return false; + } + + protected void updateCache(final boolean update, final Component c) { + GraphicsConfiguration gc = c != null ? c.getGraphicsConfiguration() : null; + double sx = Scale.getScaleX(gc); + double sy = Scale.getScaleY(gc); + if (!update && Scale.equalWithError(scaleX, sx) && Scale.equalWithError(scaleY, sy) && image != null) return; + scaleX = sx; + scaleY = sy; + image = createImage(Scale.scale(scaleX, scaleY, size)); + } + + @Override + public Image createImage(final Dimension size) { + ensureLoaded(); + icon.setPreferredSize(size); + return icon.getImage(); + } + + protected void ensureImageLoaded(final Component c) { + updateCache(ensureLoaded(), c); } protected SVGIcon createSVGIcon() { @@ -102,14 +126,17 @@ public class DarkSVGIcon implements DerivableIcon, RotateIcon, Seri @Override public void paintIcon(final Component c, final Graphics g, final int x, final int y, final double rotation) { - ensureLoaded(); + ensureImageLoaded(c); Graphics2D g2 = (Graphics2D) g; g2.translate(x, y); + double sx = size.width / (double) image.getWidth(null); + double sy = size.height / (double) image.getWidth(null); + g2.scale(sx, sy); if (rotation != 0) { g2.rotate(rotation, size.width / 2.0, size.height / 2.0); } - icon.setPreferredSize(size); - icon.paintIcon(c, g2, 0, 0); + g2.drawImage(image, 0, 0, null); + g2.scale(1 / sx, 1 / sy); g2.translate(-x, -y); } @@ -124,7 +151,7 @@ public class DarkSVGIcon implements DerivableIcon, RotateIcon, Seri } public SVGIcon getSVGIcon() { - ensureLoaded(); + if (!loaded.get()) ensureLoaded(); return icon; } } diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/ImageSource.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/ImageSource.java new file mode 100644 index 00000000..76f9c25f --- /dev/null +++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/ImageSource.java @@ -0,0 +1,36 @@ +/* + * MIT License + * + * Copyright (c) 2020 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +package com.github.weisj.darklaf.icons; + +import java.awt.*; + +public interface ImageSource { + + Image createImage(final Dimension size); + + default Image createImage(final int width, final int height) { + return createImage(new Dimension(width, height)); + } +} diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/ThemedSVGIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/ThemedSVGIcon.java index 9f0d536d..7d8a6734 100644 --- a/property-loader/src/main/java/com/github/weisj/darklaf/icons/ThemedSVGIcon.java +++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/ThemedSVGIcon.java @@ -39,18 +39,21 @@ public class ThemedSVGIcon extends DarkSVGIcon { currentTheme = new Object(); } - @Override - public void paintIcon(final Component c, final Graphics g, final int x, final int y, final double rotation) { - ensureTheme(); - super.paintIcon(c, g, x, y, rotation); + protected boolean ensureLoaded() { + /* + * Use non-short-circuiting operand here to ensure the colors are actually patched. + */ + return super.ensureLoaded() | ensureTheme(); } - private void ensureTheme() { + protected boolean ensureTheme() { Object theme = IconLoader.getThemeStatus(); if (currentTheme != theme) { patchColors(); currentTheme = theme; + return true; } + return false; } protected void patchColors() { diff --git a/utils/src/main/java/com/github/weisj/darklaf/util/Scale.java b/utils/src/main/java/com/github/weisj/darklaf/util/Scale.java index 2d7da58c..85924f80 100644 --- a/utils/src/main/java/com/github/weisj/darklaf/util/Scale.java +++ b/utils/src/main/java/com/github/weisj/darklaf/util/Scale.java @@ -31,6 +31,7 @@ public class Scale { public static final double SCALE; public static final double SCALE_X; public static final double SCALE_Y; + private static final double EPSILON = 0.0001; static { DisplayMode mode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); @@ -41,11 +42,13 @@ public class Scale { } public static double scaleWidth(final double value, final GraphicsConfiguration gc) { + if (gc == null) return scaleWidth(value); AffineTransform transform = gc.getDefaultTransform(); return transform.getScaleX() * value; } public static double scaleHeight(final double value, final GraphicsConfiguration gc) { + if (gc == null) return scaleHeight(value); AffineTransform transform = gc.getDefaultTransform(); return transform.getScaleY() * value; } @@ -83,10 +86,28 @@ public class Scale { } public static double getScaleX(final GraphicsConfiguration gc) { + if (gc == null) return SCALE_X; return gc.getDefaultTransform().getScaleX(); } public static double getScaleY(final GraphicsConfiguration gc) { + if (gc == null) return SCALE_Y; return gc.getDefaultTransform().getScaleY(); } + + public static Dimension scale(final GraphicsConfiguration gc, final Dimension size) { + return new Dimension((int) scaleWidth(size.width, gc), (int) scaleHeight(size.height, gc)); + } + + public static double scale(final double scale, final double value) { + return scale * value; + } + + public static Dimension scale(final double scaleX, final double scaleY, final Dimension size) { + return new Dimension((int) scale(scaleX, size.width), (int) scale(scaleY, size.height)); + } + + public static boolean equalWithError(final double a, final double b) { + return Math.abs(a - b) < EPSILON; + } }