Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid remaining trivial uses of new ImageIcon(Image), for HiDPI icons #8109

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

eirikbakke
Copy link
Contributor

@eirikbakke eirikbakke commented Jan 3, 2025

In previous PRs, e.g. #7463 and #8083, SVG versions of various NetBeans icons were added to look better on Retina/HiDPI monitors. ImageUtilities has supported automatic loading of SVG versions of icons since #1278 .

To render at full HiDPI resolution, Icon/Image instances must be created via the methods in ImageUtilities rather than, in particular, the constructors of ImageIcon. Uses of the latter must be replaced with e.g. ImageUtilities.image2Icon and ImageUtilities.loadImageIcon. Some such replacements were done in #7472 .

This PR replaces most of the remaining trivial uses of the ImageIcon(Image) constructor. This allows SVG icons, when/once present, to render in full resolution on HiDPI/Retina displays. Bitmap icons also benefit from the improved scaling hints applied by ImageUtilities.

While this PR touches many lines, the changes should be simple enough that they can be reviewed by simply reading over the diffs (rather than by opening every dialog, UI panel etc. in a running IDE). I will also run my working IDE with these changes for a while to see if I bump into any observable problems. It's not necessary to have a HiDPI screen to test this patch; merely seeing that there are no new exceptions etc. when running the IDE should be sufficient.

There are still many uses of the "new ImageIcon(String)" and "new ImageIcon(URL)" variations of the ImageIcon constructors. Removing these is left for future work.

Replace most of the remaining trivial uses of the ImageIcon(Image) constructor, replacing these with ImageUtilities.image2Icon or ImageUtilities.loadImageIcon. This allows SVG icons, when/once present, to render in full resolution on HiDPI/Retina displays. Bitmap icons also benefit from the improved scaling hints applied by ImageUtilities.

Similar work was done, for the platform module only, in apache#7472 . This commit covers most of the remaining trivial cases; I grepped the codebase for 'new ImageIcon' and adjusted the code to avoid ImageIcon whenever this could be done easily (not changing APIs, requiring only a visual code review).
@eirikbakke eirikbakke added Platform [ci] enable platform tests (platform/*) UI User Interface labels Jan 3, 2025
Copy link
Contributor

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks sane to me. Only eyeballed though. There is more cleanup possible, but that is a different task.

@eirikbakke
Copy link
Contributor Author

Thanks! I will run with this in my working IDE for a few days before merging.

Copy link
Member

@mbien mbien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got confused at the end so excuse if anything I commented on makes no sense ;)

some notes:

  • i think this PR would have benefited from 2 commits, one which refactors loading, the other which refactors conversion/wrapping problems
  • nitpick but most of the , false) postfixes could be probably removed, since most of the original code did also use the overloaded variants which set it to false by default.

@@ -108,60 +107,47 @@ public static ImageIcon getElementIcon( ElementKind elementKind, Collection<Modi
modifiers = Collections.<Modifier>emptyList();
}

Image img = null;
ImageIcon icon;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this line into the CONSTANT case?

Copy link
Contributor Author

@eirikbakke eirikbakke Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. (Declarations in a case statement will leak to the subsequent cases, so I left it at the top. But either style works.)

Comment on lines -94 to +101
private ImageIcon getImageIcon(String name, boolean error){
ImageIcon icon = ImageUtilities.loadImageIcon(name, false);
if(error)
return new ImageIcon(ImageUtilities.mergeImages( icon.getImage(), ERROR_IMAGE, 15, 7 ));
else
private Icon getIcon(String name, boolean error){
Icon icon = ImageUtilities.loadImageIcon(name, false);
if (error) {
return ImageUtilities.image2Icon(ImageUtilities.mergeImages(
ImageUtilities.icon2Image(icon), ERROR_IMAGE, 15, 7 ));
} else {
return icon;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one call less if the variable keeps the original ImageIcon type.

    private Icon getIcon(String name, boolean error){
        ImageIcon icon = ImageUtilities.loadImageIcon(name, false);
        if (error) {
            return ImageUtilities.image2Icon(ImageUtilities.mergeImages(icon.getImage(), ERROR_IMAGE, 15, 7 ));
        } else {
            return icon;
        }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's another change I think can be dropped overall since mergeImages is smart enough to preserve HiDPI resolution.

Comment on lines -67 to 73
ImageIcon imageIcon = new ImageIcon(DataObject.find(fo).getNodeDelegate().getIcon(BeanInfo.ICON_COLOR_16x16));
icon = ImageUtilities.image2Icon(DataObject.find(fo).getNodeDelegate().getIcon(BeanInfo.ICON_COLOR_16x16));
Boolean inTestFile = ElementGripFactory.getDefault().inTestFile(fo);
if (Boolean.TRUE == inTestFile) {
Image mergeImages = ImageUtilities.mergeImages(imageIcon.getImage(),
ImageUtilities.loadImageIcon("org/netbeans/modules/refactoring/java/resources/found_item_test.png", false).getImage(), 4, 4);
imageIcon = new ImageIcon(mergeImages);
icon = ImageUtilities.image2Icon(ImageUtilities.mergeImages(ImageUtilities.icon2Image(icon),
ImageUtilities.loadImage("org/netbeans/modules/refactoring/java/resources/found_item_test.png", false), 4, 4));
}
icon = imageIcon;
} catch (DataObjectNotFoundException ex) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar here. I think ImageIcon icon would allow to skip the call to icon2image.

although the lookup registration later would have to be changed to icon.getImage()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can simplify it a bit here; keeping the ImageIcon from loadImageIcon while keeping image2Icon instead of the "new ImageIcon" constructor.

Comment on lines -115 to +122
ImageIcon imageIcon = new ImageIcon(DataObject.find(fo).getNodeDelegate().getIcon(BeanInfo.ICON_COLOR_16x16));
Icon icon = ImageUtilities.image2Icon(DataObject.find(fo).getNodeDelegate().getIcon(BeanInfo.ICON_COLOR_16x16));
Boolean inTestFile = ElementGripFactory.getDefault().inTestFile(fo);
if(Boolean.TRUE == inTestFile) {
Image mergeImages = ImageUtilities.mergeImages(imageIcon.getImage(),
ImageUtilities.loadImageIcon("org/netbeans/modules/refactoring/java/resources/found_item_test.png", false).getImage(), 4, 4);
imageIcon = new ImageIcon(mergeImages);
icon = ImageUtilities.image2Icon(ImageUtilities.mergeImages(
ImageUtilities.icon2Image(icon),
ImageUtilities.loadImage("org/netbeans/modules/refactoring/java/resources/found_item_test.png", false), 4, 4));
}
return imageIcon;
return icon;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment on lines -517 to +518
return ImageUtilities.mergeImages(original, ProjectProperties.ICON_BROKEN_BADGE.getImage(), 8, 0);
return ImageUtilities.mergeImages(original,
ImageUtilities.icon2Image(ProjectProperties.ICON_BROKEN_BADGE), 8, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this change needed?

the utility method will simply call getImage() anyway, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some custom handling for certain Icon subclasses, but reading over the ImageUtilities code, it indeed seems that the change is unnecessary! mergeImages is smart enough to preserve vector graphics in these cases.

I will remove the changes around mergeImages.

Comment on lines -529 to +531
return ImageUtilities.mergeImages(original, ProjectProperties.ICON_BROKEN_BADGE.getImage(), 8, 0);
return ImageUtilities.mergeImages(original,
ImageUtilities.icon2Image(ProjectProperties.ICON_BROKEN_BADGE), 8, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I'll remove these from the patch, as mergeImages is smart enough to avoid the need.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

many instances where imageicon.getImage() was changed to icon2image(imageicon)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, will remove.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too. getImage() was changed to icon2Image()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to reduce the use of ImageIcon, as it assumes that icons are bitmaps rather than vector graphics. When ImageIcon is used it's harder to reason about whether vector graphics will be preserved or not.

@eirikbakke
Copy link
Contributor Author

eirikbakke commented Jan 5, 2025

Thanks for useful reviews!

i think this PR would have benefited from 2 commits, one which refactors loading, the other which refactors conversion/wrapping problems

I see now that the changes around mergeImages can probably be removed, since mergeImages is smart enough to pick up the special implementations of Image and Icon by itself.

nitpick but most of the , false) postfixes could be probably removed, since most of the original code did also use the overloaded variants which set it to false by default.

ImageUtilities has loadImage(String) and loadImage(String,boolean) but no loadIconImage(String), only loadIconImage(String,boolean).

I think I will make a separate PR with some proposed new methods in ImageUtilities that can simplify some of these changes:

  • loadIcon(String,boolean) would be like loadIconImage(String,boolean) but returns only a plain Icon (not IconImage). This would help discourage use of IconImage in the future.
  • loadIcon(String) would be like loadIcon(String,false)

@eirikbakke
Copy link
Contributor Author

nitpick but most of the , false) postfixes could be probably removed, since most of the original code did also use the overloaded variants which set it to false by default.

I proposed some new utility methods for ImageUtilities in #8114. One of these will let me change "loadImageIcon(path, false)" to just "loadIcon(path)" in most of the cases here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform [ci] enable platform tests (platform/*) UI User Interface
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants