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 139bbecb..d88c0e85 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 @@ -101,12 +101,12 @@ public class DarkSVGIcon implements DerivableIcon, RotateIcon, Seri loaded = new AtomicBoolean(false); } - protected DarkSVGIcon(final int width, final int height, final DarkSVGIcon icon) { + protected DarkSVGIcon(final int width, final int height, final DarkSVGIcon parent) { this.size = new Dimension(width, height); - this.icon = icon.icon; - this.uri = icon.uri; - this.uriSupplier = icon.uriSupplier; - this.loaded = icon.loaded; + this.icon = parent.icon; + this.uri = parent.uri; + this.uriSupplier = parent.uriSupplier; + this.loaded = parent.loaded; } @Override @@ -174,15 +174,14 @@ public class DarkSVGIcon implements DerivableIcon, RotateIcon, Seri * If we get to here the issue was that the icon hasn't been patched because it isn't loaded as a themed * svg icon. */ - LOGGER.severe("Icon '" + getName() + "' that defines custom colors isn't loaded as themed icon."); + LOGGER.severe("Icon '" + getName(uri) + "' that defines custom colors isn't loaded as themed icon."); return img; } throw e; } } - protected String getName() { - ensureURILoaded(); + protected String getName(final URI uri) { String name = uri.toASCIIString(); name = name.substring(Math.min(name.length() - 1, name.lastIndexOf('/') + 1)); return name; diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/DerivableImageIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/DerivableImageIcon.java index 05a2fb0f..fa9d93ad 100644 --- a/property-loader/src/main/java/com/github/weisj/darklaf/icons/DerivableImageIcon.java +++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/DerivableImageIcon.java @@ -32,17 +32,18 @@ import java.util.function.Supplier; import javax.accessibility.*; import javax.swing.*; +import com.github.weisj.darklaf.util.LazyValue; + public class DerivableImageIcon implements DerivableIcon, Accessible { - private static final int DEFAULT_SCALING_MODE = Image.SCALE_SMOOTH; + private static final int DEFAULT_SCALING_MODE = Image.SCALE_DEFAULT; private final int scalingMode; private int width; private int height; - private Supplier imageSupplier; - private Image original; - private Image img; + private final LazyValue original; + private final LazyImageValue image; private String description; private AccessibleContext accessibleContext; @@ -125,7 +126,8 @@ public class DerivableImageIcon implements DerivableIcon, Ac this.width = width; this.height = height; this.scalingMode = scalingMode; - this.imageSupplier = imageSupplier; + this.original = new LazyValue<>(imageSupplier); + this.image = new LazyImageValue(this); } /** @@ -220,10 +222,8 @@ public class DerivableImageIcon implements DerivableIcon, Ac this.width = width; this.height = height; this.scalingMode = scalingMode; - this.original = img; - if (img != null && img.getWidth(null) == width && img.getHeight(null) == height) { - this.img = img; - } + this.original = new LazyValue<>(img); + this.image = new LazyImageValue(this); } protected DerivableImageIcon(final DerivableImageIcon parent, final int width, final int height) { @@ -232,16 +232,7 @@ public class DerivableImageIcon implements DerivableIcon, Ac this.scalingMode = parent.scalingMode; this.original = parent.original; this.description = parent.description; - this.imageSupplier = parent.imageSupplier; - if (this.original != null - && this.original.getWidth(null) == width - && this.original.getHeight(null) == height) { - this.img = this.original; - } else if (parent.img != null - && parent.img.getWidth(null) == width - && parent.img.getHeight(null) == height) { - this.img = parent.img; - } + this.image = parent.image.derive(this); } @Override @@ -249,31 +240,9 @@ public class DerivableImageIcon implements DerivableIcon, Ac return new DerivableImageIcon(this, width, height); } - protected void ensureOriginalLoaded() { - if (original == null && imageSupplier != null) { - original = imageSupplier.get(); - imageSupplier = null; - } - } - - protected void ensureLoaded() { - ensureOriginalLoaded(); - if (original != null && (width < 0 || height < 0)) { - if (width < 0) width = original.getWidth(null); - if (height < 0) height = original.getHeight(null); - } - if (img == null && original != null && width > 0 && height > 0) { - if (original.getWidth(null) != width || original.getHeight(null) != height) { - img = original.getScaledInstance(width, height, scalingMode); - } else { - img = original; - } - } - } - @Override public void paintIcon(final Component c, final Graphics g, final int x, final int y) { - ensureLoaded(); + Image img = image.get(); if (img != null) { g.drawImage(img, x, y, width, height, null); } @@ -285,19 +254,32 @@ public class DerivableImageIcon implements DerivableIcon, Ac * @return the image. */ public Image getImage() { - ensureLoaded(); + return image.get(); + } + + protected Image getOriginal() { + Image img = original.get(); + calculateSize(); return img; } + private void calculateSize() { + Image originalImage = original.get(); + if (originalImage != null && (width < 0 || height < 0)) { + if (width < 0) width = originalImage.getWidth(null); + if (height < 0) height = originalImage.getHeight(null); + } + } + @Override public int getIconWidth() { - if (width < 0) ensureLoaded(); + if (width < 0) calculateSize(); return width; } @Override public int getIconHeight() { - if (height < 0) ensureLoaded(); + if (height < 0) calculateSize(); return height; } @@ -392,4 +374,38 @@ public class DerivableImageIcon implements DerivableIcon, Ac return icon.getIconHeight(); } } + + protected static class LazyImageValue extends LazyValue { + + private final DerivableImageIcon icon; + + public LazyImageValue(final DerivableImageIcon icon) { + super((Image) null); + this.icon = icon; + } + + public LazyImageValue derive(final DerivableImageIcon icon) { + if (this.icon.width == icon.width && this.icon.height == icon.height) { + // Make sure all icons with the same dimension share one image. + return this; + } + return new LazyImageValue(icon); + } + + @Override + protected Image load() { + Image originalImage = icon.getOriginal(); + int width = icon.getIconWidth(); + int height = icon.getIconHeight(); + if (originalImage != null && width > 0 && height > 0) { + if (originalImage.getWidth(null) != width + || originalImage.getHeight(null) != height) { + return originalImage.getScaledInstance(width, height, icon.scalingMode); + } else { + return originalImage; + } + } + return null; + } + } } diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/IconLoader.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/IconLoader.java index 62e2de20..802c6c1d 100644 --- a/property-loader/src/main/java/com/github/weisj/darklaf/icons/IconLoader.java +++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/IconLoader.java @@ -263,8 +263,6 @@ public final class IconLoader { } // Icon not found or caching is disabled. - - key.w = w; // Restore key. if (isSVGIcon(path)) { Icon icon = loadSVGIcon(path, w, h, themed); cache(iconMap, key, icon); @@ -278,15 +276,16 @@ public final class IconLoader { private Icon getWildcardIcon(final Map iconMap, final IconKey iconKey, final int w, final int h) { - iconKey.w = -1; + iconKey.isWildcardEnabled = true; Icon icon = iconMap.get(iconKey); if (icon instanceof DerivableIcon) { @SuppressWarnings("unchecked") Icon derived = ((DerivableIcon) icon).derive(w, h); - iconKey.w = w; + iconKey.isWildcardEnabled = false; cache(iconMap, iconKey, derived); return derived; } + iconKey.isWildcardEnabled = false; return null; } @@ -374,7 +373,7 @@ public final class IconLoader { * @return the ImageIcon. */ ImageIcon createImageIcon(final String path, final String description) { - java.net.URL imgURL = getResource(path); + URL imgURL = getResource(path); if (imgURL != null) { return new ImageIcon(imgURL, description); } else { @@ -422,6 +421,7 @@ public final class IconLoader { final String path; int w; int h; + boolean isWildcardEnabled; private IconKey(final String path, final int w, final int h) { this.path = path; @@ -441,7 +441,7 @@ public final class IconLoader { IconKey iconKey = (IconKey) o; - if (iconKey.w == -1 || iconKey.h == -1 || this.h == -1 || this.w == -1) { + if (iconKey.isWildcardEnabled || this.isWildcardEnabled) { // Math any size. return Objects.equals(path, iconKey.path); } diff --git a/property-loader/src/test/java/com/github/weisj/darklaf/icons/DerivableImageIconTest.java b/property-loader/src/test/java/com/github/weisj/darklaf/icons/DerivableImageIconTest.java new file mode 100644 index 00000000..78d25dd0 --- /dev/null +++ b/property-loader/src/test/java/com/github/weisj/darklaf/icons/DerivableImageIconTest.java @@ -0,0 +1,54 @@ +/* + * 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.*; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class DerivableImageIconTest { + + @Test + public void testCache() { + IconLoader loader = IconLoader.get(DerivableImageIconTest.class); + DerivableImageIcon icon = (DerivableImageIcon) loader.getIcon("image_icon.png"); + Set imageSet = new HashSet<>(); + for (int i = 0; i < 100; i++) { + Image img = ((DerivableImageIcon) loader.getIcon("image_icon.png", 100, 100)).getImage(); + imageSet.add(img); + } + Assertions.assertEquals(1, imageSet.size()); + + imageSet.clear(); + icon = icon.derive(50, 50); + for (int i = 0; i < 100; i++) { + imageSet.add(icon.derive(50, 50).getImage()); + } + Assertions.assertEquals(1, imageSet.size()); + } +} diff --git a/property-loader/src/test/resources/com/github/weisj/darklaf/icons/image_icon.png b/property-loader/src/test/resources/com/github/weisj/darklaf/icons/image_icon.png new file mode 100644 index 00000000..42f6fa23 Binary files /dev/null and b/property-loader/src/test/resources/com/github/weisj/darklaf/icons/image_icon.png differ diff --git a/utils/src/main/java/com/github/weisj/darklaf/util/LazyValue.java b/utils/src/main/java/com/github/weisj/darklaf/util/LazyValue.java index 9a366a3b..7eb2ef44 100644 --- a/utils/src/main/java/com/github/weisj/darklaf/util/LazyValue.java +++ b/utils/src/main/java/com/github/weisj/darklaf/util/LazyValue.java @@ -31,8 +31,11 @@ public class LazyValue { private Supplier supplier; private T value; - public LazyValue(final Supplier supplier) { + public LazyValue(final T value) { + this.value = value; + } + public LazyValue(final Supplier supplier) { this.supplier = supplier; } @@ -40,11 +43,17 @@ public class LazyValue { return supplier == null; } - public T get() { - if (value == null && supplier != null) { - value = supplier.get(); + protected T load() { + if (supplier != null) { + T obj = supplier.get(); supplier = null; + return obj; } return value; } + + public T get() { + if (value == null) value = load(); + return value; + } }