diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java index c181972b84..3bab2121d1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/CompareFunctionsAction.java @@ -29,7 +29,7 @@ import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import resources.MultiIcon; import resources.ResourceManager; -import resources.icons.ScaledImageIconWrapper; +import resources.icons.ScaledImageIcon; import resources.icons.TranslateIcon; /** @@ -48,7 +48,7 @@ public abstract class CompareFunctionsAction extends DockingAction { private static final ImageIcon COMPARISON_ICON = ResourceManager.loadImage("images/page_white_c.png"); private static final Icon NEW_ICON = ResourceManager.loadImage("images/bullet_star.png"); - private static final Icon SCALED_NEW_ICON = new ScaledImageIconWrapper(NEW_ICON, 16, 16); + private static final Icon SCALED_NEW_ICON = new ScaledImageIcon(NEW_ICON, 16, 16); private static final Icon TRANSLATED_NEW_ICON = new TranslateIcon(SCALED_NEW_ICON, 4, -4); private static final Icon CREATE_NEW_COMPARISON_ICON = new MultiIcon(COMPARISON_ICON, TRANSLATED_NEW_ICON); @@ -101,8 +101,7 @@ public abstract class CompareFunctionsAction extends DockingAction { setPopupMenuData(new MenuData(new String[] { "Compare Selected Functions" }, getToolBarIcon(), CREATE_COMPARISON_GROUP)); - ToolBarData newToolBarData = - new ToolBarData(getToolBarIcon(), CREATE_COMPARISON_GROUP); + ToolBarData newToolBarData = new ToolBarData(getToolBarIcon(), CREATE_COMPARISON_GROUP); setToolBarData(newToolBarData); setHelpLocation(new HelpLocation("FunctionComparison", "Function_Comparison")); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/OpenFunctionTableAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/OpenFunctionTableAction.java index afd9f3480f..b4f3e5cffa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/OpenFunctionTableAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/functioncompare/actions/OpenFunctionTableAction.java @@ -37,7 +37,7 @@ import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; import resources.MultiIcon; import resources.ResourceManager; -import resources.icons.ScaledImageIconWrapper; +import resources.icons.ScaledImageIcon; import resources.icons.TranslateIcon; import util.CollectionUtils; @@ -50,7 +50,7 @@ import util.CollectionUtils; public class OpenFunctionTableAction extends DockingAction { private static final Icon ADD_ICON = ResourceManager.loadImage("images/Plus.png"); - private static final Icon SCALED_ADD_ICON = new ScaledImageIconWrapper(ADD_ICON, 10, 10); + private static final Icon SCALED_ADD_ICON = new ScaledImageIcon(ADD_ICON, 10, 10); private static final ImageIcon COMPARISON_ICON = ResourceManager.loadImage("images/page_white_c.png"); private static final Icon TRANSLATED_ADD_ICON = new TranslateIcon(SCALED_ADD_ICON, 8, 1); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java index 8ea353eafc..d0ccbde7f8 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/plugin/VTPlugin.java @@ -89,8 +89,8 @@ public class VTPlugin extends Plugin { protected Icon createIcon() { MultiIcon icon = new MultiIcon(new EmptyIcon(16, 16)); ImageIcon cancelIcon = ResourceManager.loadImage("images/dialog-cancel.png"); - ScaledImageIconWrapper scaledCancelIcon = - new ScaledImageIconWrapper(cancelIcon, 13, 13); + ScaledImageIcon scaledCancelIcon = + new ScaledImageIcon(cancelIcon, 13, 13); TranslateIcon translatedCancelIcon = new TranslateIcon(scaledCancelIcon, 3, 4); ImageIcon undoIcon = ResourceManager.loadImage("images/undo.png"); TranslateIcon translatedUndoIcon = new TranslateIcon(undoIcon, 0, -4); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java index 97c657dcd7..b06989062b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/action/DockingAction.java @@ -31,7 +31,6 @@ import ghidra.util.datastruct.WeakSet; import ghidra.util.exception.AssertException; import resources.ResourceManager; import resources.icons.FileBasedIcon; -import resources.icons.ImageIconWrapper; import utilities.util.reflection.ReflectionUtilities; /** @@ -493,9 +492,8 @@ public abstract class DockingAction implements DockingActionIf { } Icon icon = menuBarData.getMenuIcon(); - if (icon != null && icon instanceof ImageIconWrapper) { - ImageIconWrapper wrapper = (ImageIconWrapper) icon; - String filename = wrapper.getFilename(); + if (icon instanceof FileBasedIcon filebasedIcon) { + String filename = filebasedIcon.getFilename(); buffer.append(" MENU ICON: ").append(filename); buffer.append('\n'); } @@ -522,9 +520,8 @@ public abstract class DockingAction implements DockingActionIf { } Icon icon = popupMenuData.getMenuIcon(); - if (icon != null && icon instanceof ImageIconWrapper) { - ImageIconWrapper wrapper = (ImageIconWrapper) icon; - String filename = wrapper.getFilename(); + if (icon instanceof FileBasedIcon fileBasedIcon) { + String filename = fileBasedIcon.getFilename(); buffer.append(" POPUP ICON: ").append(filename); buffer.append('\n'); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/BadgedIcon.java b/Ghidra/Framework/Docking/src/main/java/docking/util/BadgedIcon.java index 02402cf29c..c48cc0151e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/util/BadgedIcon.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/util/BadgedIcon.java @@ -25,7 +25,7 @@ import javax.swing.ImageIcon; import resources.MultiIcon; import resources.ResourceManager; import resources.icons.EmptyIcon; -import resources.icons.ScaledImageIconWrapper; +import resources.icons.ScaledImageIcon; /** * An icon that allows sub-icons to be added at key perimeter locations. Each position can @@ -317,7 +317,7 @@ public class BadgedIcon implements Icon { MultiIcon icon = badgeMap.get(pos); - Icon scaled = new ScaledImageIconWrapper(icon, badgeSize.width, badgeSize.height); + Icon scaled = new ScaledImageIcon(icon, badgeSize.width, badgeSize.height); Point badgePaintLoc = getBadgePaintLocation(pos, badgeSize); diff --git a/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java b/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java index bb5eeb9750..808ffec712 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/ResourceManager.java @@ -324,7 +324,7 @@ public class ResourceManager { * @return A new, scaled ImageIcon */ public static ImageIcon getScaledIcon(Icon icon, int width, int height, int hints) { - return new ScaledImageIconWrapper(icon, width, height, hints); + return new ScaledImageIcon(icon, width, height, hints); } /** @@ -337,7 +337,7 @@ public class ResourceManager { * @return A new, scaled ImageIcon */ public static ImageIcon getScaledIcon(Icon icon, int width, int height) { - return new ScaledImageIconWrapper(icon, width, height); + return new ScaledImageIcon(icon, width, height); } /** @@ -346,7 +346,7 @@ public class ResourceManager { * @return disabled icon */ public static ImageIcon getDisabledIcon(Icon icon) { - return new DisabledImageIconWrapper(getImageIcon(icon)); + return new DisabledImageIcon(icon); } /** @@ -355,7 +355,7 @@ public class ResourceManager { * @return disabled icon */ public static ImageIcon getDisabledIcon(ImageIcon icon) { - return new DisabledImageIconWrapper(icon); + return new DisabledImageIcon(icon); } /** @@ -367,7 +367,7 @@ public class ResourceManager { * @return a disabled version of the original icon. */ public static ImageIcon getDisabledIcon(Icon icon, int brightnessPercent) { - return new DisabledImageIconWrapper(icon, brightnessPercent); + return new DisabledImageIcon(icon, brightnessPercent); } /** @@ -381,7 +381,7 @@ public class ResourceManager { * @return the new icon */ public static ImageIcon getImageIconFromImage(String imageName, Image image) { - return new ImageIconWrapper(image, imageName); + return new DerivedImageIcon(imageName, image); } /** @@ -396,7 +396,7 @@ public class ResourceManager { if (icon instanceof ImageIcon) { return (ImageIcon) icon; } - return new ImageIconWrapper(icon); + return new DerivedImageIcon(icon); } /** @@ -452,7 +452,7 @@ public class ResourceManager { if (icon != null) { return icon; } - icon = new ImageIconWrapper(imageBytes, imageName); + icon = new BytesImageIcon(imageName, imageBytes); iconMap.put(imageName, icon); return icon; } @@ -476,39 +476,47 @@ public class ResourceManager { /** * Load the image specified by filename; returns the default bomb icon * if problems occur trying to load the file. - *

* * @param filename name of file to load, e.g., "images/home.gif" * @return the image icon stored in the bytes */ public static ImageIcon loadImage(String filename) { - - // use the wrapper so that images are not loaded until they are needed ImageIcon icon = iconMap.get(filename); - if (icon != null) { - return icon; + if (icon == null) { + icon = doLoadIcon(filename, ResourceManager.getDefaultIcon()); + iconMap.put(filename, icon); + } + return icon; + } + + private static ImageIcon doLoadIcon(String filename, ImageIcon defaultIcon) { + // if only the name of an icon is given, but not a path, check to see if it is + // a resource that lives under our "images/" folder + if (!filename.contains("/")) { + URL url = getResource("images/" + filename); + if (url != null) { + return new UrlImageIcon(filename, url); + } } + // look for it directly with the given path + URL url = getResource(filename); + if (url != null) { + return new UrlImageIcon(filename, url); + } + + // try using the filename as a file path File imageFile = new File(filename); if (imageFile.exists()) { try { - icon = new ImageIconWrapper(imageFile.toURI().toURL()); - iconMap.put(filename, icon); - return icon; + return new UrlImageIcon(filename, imageFile.toURI().toURL()); } catch (MalformedURLException e) { // handled below } } - URL url = getResource(filename); - if (url != null) { - icon = new ImageIconWrapper(url); - iconMap.put(filename, icon); - return icon; - } - - return getDefaultIcon(); + return defaultIcon; } /** @@ -547,7 +555,7 @@ public class ResourceManager { Msg.error(ResourceManager.class, "Could not find default icon: " + DEFAULT_ICON_FILENAME); } - DEFAULT_ICON = new ImageIconWrapper(url); + DEFAULT_ICON = new UrlImageIcon(DEFAULT_ICON_FILENAME, url); } return DEFAULT_ICON; } diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/BytesImageIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/BytesImageIcon.java new file mode 100644 index 0000000000..35702038bb --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/BytesImageIcon.java @@ -0,0 +1,49 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.Image; +import java.awt.Toolkit; +import java.util.Objects; + +import javax.swing.ImageIcon; + +import generic.util.image.ImageUtils; + +/** + * {@link LazyImageIcon} that is created from a byte array + */ +public class BytesImageIcon extends LazyImageIcon { + private byte[] bytes; + + public BytesImageIcon(String name, byte[] imageBytes) { + super(name); + this.bytes = Objects.requireNonNull(imageBytes); + } + + protected ImageIcon createImageIcon() { + String name = getFilename(); + Image image = createImage(); + if (!ImageUtils.waitForImage(name, image)) { + return null; + } + return new ImageIcon(image, name); + } + + protected Image createImage() { + return Toolkit.getDefaultToolkit().createImage(bytes); + } +} diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/DerivedImageIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/DerivedImageIcon.java new file mode 100644 index 0000000000..61ffd4800d --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/DerivedImageIcon.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.util.Objects; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import generic.util.image.ImageUtils; +import resources.ResourceManager; + +/** + * {@link LazyImageIcon} that is created from an {@link Icon} or an {@link Image} + */ +public class DerivedImageIcon extends LazyImageIcon { + private Icon sourceIcon; + private Image sourceImage; + + /** + * Constructor for deriving from an icon + * @param icon the source icon + */ + public DerivedImageIcon(Icon icon) { + super(ResourceManager.getIconName(icon)); + this.sourceIcon = Objects.requireNonNull(icon); + } + + /** + * Constructor for deriving from an image + * @param name the name of the image + * @param image the source image + */ + public DerivedImageIcon(String name, Image image) { + super(name); + this.sourceImage = Objects.requireNonNull(image); + } + + protected ImageIcon createImageIcon() { + Image image = createImage(); + String imageName = getFilename(); + if (!ImageUtils.waitForImage(imageName, image)) { + return null; + } + return new ImageIcon(image, imageName); + } + + protected Image createImage() { + if (sourceImage != null) { + return sourceImage; + } + + // if sourceImage is null, then sourceIcon can't be null + if (sourceIcon instanceof ImageIcon) { + return ((ImageIcon) sourceIcon).getImage(); + } + BufferedImage bufferedImage = new BufferedImage(sourceIcon.getIconWidth(), + sourceIcon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics graphics = bufferedImage.getGraphics(); + sourceIcon.paintIcon(null, graphics, 0, 0); + graphics.dispose(); + return bufferedImage; + } +} diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIcon.java new file mode 100644 index 0000000000..58d519bb4f --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIcon.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.Image; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import generic.util.image.ImageUtils; + +/** + * {@link LazyImageIcon} that creates a disabled version of an icon + */ +public class DisabledImageIcon extends DerivedImageIcon { + + /** + * The inverse percentage of gray (higher percentage equals less gray) to apply to + * the disabled image; higher is brighter + */ + private int brightnessPercent; + + /** + * Construct wrapped disabled ImageIcon based upon specified baseIcon. + * A 50% brightness will be applied. + * @param baseIcon enabled icon to be rendered as disabled + */ + public DisabledImageIcon(Icon baseIcon) { + this(baseIcon, 50); // default to half gray + } + + /** + * Construct wrapped disabled ImageIcon based upon specified baseIcon + * using the specified brightness level + * @param baseIcon the icon to create a disabled version of + * @param brightnessPercent a brightness level specified using a + * value in the range of 0 thru 100. + */ + public DisabledImageIcon(Icon baseIcon, int brightnessPercent) { + super(baseIcon); + this.brightnessPercent = brightnessPercent; + } + + @Override + protected ImageIcon createImageIcon() { + Image image = createImage(); + Image disabledImage = ImageUtils.createDisabledImage(image, brightnessPercent); + return new ImageIcon(disabledImage, getFilename()); + } + +} diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIconWrapper.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIconWrapper.java index 145d7b504c..5068474817 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIconWrapper.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/DisabledImageIconWrapper.java @@ -22,6 +22,12 @@ import javax.swing.ImageIcon; import generic.util.image.ImageUtils; +/** + * Creates a disabled version of an icon + * @deprecated This class has been replaced by {@link DisabledImageIcon} since it + * extends {@link ImageIconWrapper} which has also been deprecated. + */ +@Deprecated(forRemoval = true, since = "11") public class DisabledImageIconWrapper extends ImageIconWrapper { /** diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/ImageIconWrapper.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/ImageIconWrapper.java index cde7aba68c..be3bd7c11e 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/icons/ImageIconWrapper.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/ImageIconWrapper.java @@ -36,7 +36,13 @@ import resources.ResourceManager; * it has the added benefit of allowing the use of static initialization * of ImageIcons without starting the Swing thread which can cause * problems when running headless. + * + * @deprecated This class has been replaced by a series of classes that extend + * {@link LazyImageIcon}: {@link UrlImageIcon}, {@link DerivedImageIcon}, {@link BytesImageIcon}, + * {@link DisabledImageIcon}, and {@link ScaledImageIcon}. Pick the one that matches + * the constructor that was being used to create an ImageIconWrapper */ +@Deprecated(forRemoval = true, since = "11") public class ImageIconWrapper extends ImageIcon implements FileBasedIcon { private boolean loaded; diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/LazyImageIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/LazyImageIcon.java new file mode 100644 index 0000000000..8c02405404 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/LazyImageIcon.java @@ -0,0 +1,134 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.*; +import java.awt.image.ImageObserver; + +import javax.accessibility.AccessibleContext; +import javax.swing.ImageIcon; + +import resources.ResourceManager; + +/** + * LazyImageIcon provides the ability to instantiate + * an ImageIcon with delayed loading. In addition to delayed loading + * it has the added benefit of allowing the use of static initialization + * of ImageIcons without starting the Swing thread which can cause + * problems when running headless. + */ +public abstract class LazyImageIcon extends ImageIcon implements FileBasedIcon { + + private boolean loaded; + + protected LazyImageIcon(String name) { + setDescription(name); + } + + private synchronized void init() { + if (!loaded) { + loaded = true; + ImageIcon imageIcon = createImageIcon(); + if (imageIcon == null) { + imageIcon = getDefaultIcon(); + } + super.setImage(imageIcon.getImage()); + super.setDescription(getDescription()); + } + } + + protected abstract ImageIcon createImageIcon(); + + @Override + public String getFilename() { + return getDescription(); + } + + @Override + public Image getImage() { + init(); + return super.getImage(); + } + + @Override + public AccessibleContext getAccessibleContext() { + init(); + return super.getAccessibleContext(); + } + + @Override + public String getDescription() { + init(); + return super.getDescription(); + } + + @Override + public int getIconHeight() { + init(); + return super.getIconHeight(); + } + + @Override + public int getIconWidth() { + init(); + return super.getIconWidth(); + } + + @Override + public int getImageLoadStatus() { + init(); + return super.getImageLoadStatus(); + } + + @Override + public ImageObserver getImageObserver() { + init(); + return super.getImageObserver(); + } + + @Override + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + init(); + super.paintIcon(c, g, x, y); + } + + @Override + public void setDescription(String description) { + super.setDescription(description); + } + + @Override + public void setImage(Image image) { + init(); + super.setImage(image); + } + + @Override + public String toString() { + init(); + return super.toString(); + } + + private ImageIcon getDefaultIcon() { + ImageIcon defaultIcon = ResourceManager.getDefaultIcon(); + if (this == defaultIcon) { + // this can happen under just the right conditions when loading the default + // icon's bytes fails (probably due to disk or network issues) + throw new IllegalStateException("Unexpected failure loading the default icon!"); + } + return defaultIcon; // some sort of initialization has failed + } +} diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIcon.java new file mode 100644 index 0000000000..1f60529a32 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIcon.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.*; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import generic.util.image.ImageUtils; + +/** + * {@link LazyImageIcon} that creates a scaled version of an icon + */ +public class ScaledImageIcon extends DerivedImageIcon { + + private int width; + private int height; + private int hints; + + /** + * Construct wrapped scaled ImageIcon based upon specified + * baseIcon and desired size. The rendering hints of + * {@link Image#SCALE_AREA_AVERAGING} will be applied. + * @param baseIcon base icon + * @param width new icon width + * @param height new icon height + */ + public ScaledImageIcon(Icon baseIcon, int width, int height) { + this(baseIcon, width, height, Image.SCALE_AREA_AVERAGING); + } + + /** + * Construct wrapped scaled ImageIcon based upon specified + * baseIcon and desired size + * @param baseIcon base icon + * @param width new icon width + * @param height new icon height + * @param hints {@link RenderingHints} used by {@link Graphics2D} + */ + public ScaledImageIcon(Icon baseIcon, int width, int height, int hints) { + super(baseIcon); + this.width = width; + this.height = height; + this.hints = hints; + } + + @Override + protected ImageIcon createImageIcon() { + Image image = createImage(); + Image scaledImage = ImageUtils.createScaledImage(image, width, height, hints); + return new ImageIcon(scaledImage, getFilename()); + } + +} diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIconWrapper.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIconWrapper.java index e5dca1e7fa..7ab34eef3a 100644 --- a/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIconWrapper.java +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/ScaledImageIconWrapper.java @@ -22,6 +22,12 @@ import javax.swing.ImageIcon; import generic.util.image.ImageUtils; +/** + * Creates a scaled version of an icon + * @deprecated This class has been replaced by {@link ScaledImageIcon} since it + * extends {@link ImageIconWrapper} which has also been deprecated. + */ +@Deprecated(forRemoval = true, since = "11") public class ScaledImageIconWrapper extends ImageIconWrapper { private int width; diff --git a/Ghidra/Framework/Generic/src/main/java/resources/icons/UrlImageIcon.java b/Ghidra/Framework/Generic/src/main/java/resources/icons/UrlImageIcon.java new file mode 100644 index 0000000000..9312d6ffe9 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/resources/icons/UrlImageIcon.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package resources.icons; + +import java.awt.Image; +import java.awt.Toolkit; +import java.io.*; +import java.net.URL; +import java.util.Objects; + +import javax.swing.ImageIcon; + +import generic.util.image.ImageUtils; +import ghidra.util.Msg; + +/** + * {@link LazyImageIcon} that is created from a URL to an icon file. + */ +public class UrlImageIcon extends LazyImageIcon { + private URL imageURL; + + /** + * Constructor + * @param path the path String used to create the URL + * @param url the {@link URL} to an icon resource file + */ + public UrlImageIcon(String path, URL url) { + super(path); + this.imageURL = Objects.requireNonNull(url); + } + + protected ImageIcon createImageIcon() { + String name = getFilename(); + Image image = createImage(); + if (image == null) { + return null; + } + if (!ImageUtils.waitForImage(name, image)) { + return null; + } + return new ImageIcon(image, name); + } + + protected Image createImage() { + byte[] imageBytes = loadBytesFromURL(imageURL); + if (imageBytes == null) { + return null; + } + return Toolkit.getDefaultToolkit().createImage(imageBytes); + } + + private byte[] loadBytesFromURL(URL url) { + try (InputStream is = url.openStream()) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int length = 0; + byte[] buf = new byte[1024]; + while ((length = is.read(buf)) > 0) { + os.write(buf, 0, length); + } + return os.toByteArray(); + } + catch (IOException e) { + Msg.error(this, "Exception loading image bytes: " + url.toExternalForm(), e); + } + return null; + } +}