I've got a Java desktop app that works, amongst other, on OS X.
Now the new MacBook Pro has a retina display and I'm concerned: how is it going to work regarding Swing?
What about when a Java app uses both Swing components and some bitmap graphics (like custom icons / ImageIcon)?
Shall all desktop Java apps be automatically resized (for example by quadrupling every pixel) or am I going to need to create two versions of my icons set (for example one with 24x24 icons and the other with 96x96 icons) and somehow determine that the app is running on a retina display?
Use IconLoader library. It supports HiDPI images http://bulenkov.com/iconloader/ It also provides a way to work with HiDPI images (drawing, etc)
On Apple's Java 6 you can provide multiple versions of the same image. Depending on the screen (retina or not), one or the other image is picked and drawn.
However, those images have to loaded in a special way:
Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension");
For example, if your (regular resolution) image is called: "scissor.png", you have to create a high resolution version "[email protected]" (following the Apple naming conventions) and place both images in the Resources
directory of your app bundle (yes, you need to bundle your app).
Then call:
Image img = Toolkit.getDefaultToolkit().getImage("NSImage://scissor");
You can use the resulting image in your buttons and it will be drawn with the right resolution magically.
There are two other "tricks" you can use:
The first "trick" (0.5 scaling) by now also works on Oracle's Java 7/8. I.e. if you draw an image with 0.5 scaling directly to the component's Graphics object, it will be rendered in high resolution on Retina displays (and also with half its original size).
Starting with Java 9, there is better built-in support for images with different resolutions via the MultiResolutionImage interface. For more details, please see this answer.
I can confirm that the scaling your images works with on Oracle Java 1.8. I cannot get the NSImage
hack to work on java 1.7 or 1.8. I think this only works with Java 6 from Mac...
Unless someone else has a better solution, what I do is the following:
Create two sets of icons.
If you have a 48pixel
width icon create one 48px
@normal DPI
and another at 96px
with 2x DPI
. Rename the 2xDPI
image as @2x.png
to conform with apple naming standards.
Subclass ImageIcon
and call it RetinaIcon
or whatever.
You can test for a Retina display as follows:
public static boolean isRetina() {
boolean isRetina = false;
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
try {
Field field = graphicsDevice.getClass().getDeclaredField("scale");
if (field != null) {
field.setAccessible(true);
Object scale = field.get(graphicsDevice);
if(scale instanceof Integer && ((Integer) scale).intValue() == 2) {
isRetina = true;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return isRetina;
}
Make sure to @Override
the width and height of the new ImageIcon
class as follows:
@Override
public int getIconWidth()
{
if(isRetina())
{
return super.getIconWidth()/2;
}
return super.getIconWidth();
}
@Override
public int getIconHeight()
{
if(isRetina())
{
return super.getIconHeight()/2;
}
return super.getIconHeight();
}
Once you have a test for the retina screen and your custom width/height methods overridden you can customise the painIcon
method as follows:
@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y)
{
ImageObserver observer = getImageObserver();
if (observer == null)
{
observer = c;
}
Image image = getImage();
int width = image.getWidth(observer);
int height = image.getHeight(observer);
final Graphics2D g2d = (Graphics2D)g.create(x, y, width, height);
if(isRetina())
{
g2d.scale(0.5, 0.5);
}
else
{
}
g2d.drawImage(image, 0, 0, observer);
g2d.scale(1, 1);
g2d.dispose();
}
I do not know how this will work with multiple screens though- is there anyone else that can help out with that???
Hope this code helps out anyway!
Jason Barraclough.
Here is an example of using the scaling as mentioned above: RetinaIcon is on the left. ImageIcon is on the right
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With