How does Java load native NSImages?
Asked Answered
S

1

7

I've been reading the OS X Java Developer Tools, in order to help make my application more "native" with the operating system. I found something interesting in this particular section. (emphasis mine)

To load a resolution-independent tiff, icns, or pdf file from the Resources folder of your application bundle into your Java application, use the getImage() method of java.awt.Toolkit. The string you pass into getImage() is of the form "NSImage://MyImage". Do not include the file extension of the image. Also be aware that the Sun 2D renderer is disabled when the user interface scale factor does not have a value of 1.0. Use the Quartz renderer so that your images scale smoothly.

Being familiar with javax.imageio, this comes as a complete surprise, as I hadn't known any other way to load other filetypes into images. Especially with an outdated platform and absolutely no support for files such as .tiff. For example, a quick test on my computer gives me this:

Supported read formats: [jpg, bmp, gif, png, wbmp, jpeg]
Supported write formats: [jpg, bmp, gif, png, wbmp, jpeg]
'JPEG' reader: com.sun.imageio.plugins.jpeg.JPEGImageReader@5e9f23b4
'JPEG' reader: com.sun.imageio.plugins.jpeg.JPEGImageWriter@378fd1ac

I tried loading a simple .tiff image and tested this out:

static Image n;

public static void main(String[] args) {
    JFrame f = new JFrame();
    JPanel p = new JPanel() {
        @Override
        public void paintComponent(Graphics graphics) {
            graphics.drawImage(n, 0, 0, null);
        }
    }
    f.add(p);
    n = Toolkit.getDefaultToolkit().getImage(("/Users/zinedine/Desktop/test_image.tiff");
    f.setVisible(true)
}

It yields nothing:

enter image description here

I tried again: This time, adding the image into the base folder of my java project, and typed this in as a string: "NSImage://test_image.tiff". Like everything I do, it doesn't work.

However, If I change my fancy path string to an NSImage one, such as "NSImage://NSApplicationIcon"...

enter image description here

It works. I did a quick spotlight search for NSImage, and found one. It looks like the file type for these images are .png. This is kind of disturbing, since I expected a proper image to come out of it. Mind you, I also kind of expected it: If it expects arguments of the form "NSImage://something", then it might just ignore anything else.

Obviously, I've got a couple questions:

  • How does the Toolkit load the image? If I try to load a .tiff image from my desktop, this is what I get if I call .toString():

    sun.awt.image.ToolkitImage@25f38edc // Also can't be cast to java.awt.BufferedImage
    
  • Are the readers (and writers if any) part of a public API? In other words, can I call something to load my .tiff file into an Image (which I can then cast into a `BufferedImage?

  • And then again, if the readers/writers are part of the API, why doesn't the javax.imageio package locate them?

This may look like a handful, (yes I'm sorry for ruining your day on this question), but to me, this looks like expected, but at the same time erroneous behaviour. Bonus marks: Is there any friendly (i.e. Open Source) imaging api (Not the JAI) that can process .tiff files (and others)?

Suburbanite answered 19/8, 2015 at 5:17 Comment(0)
S
1

You are asking multiple questions here, but I'll do my best at interpreting it all. :-)

Using the Toolkit to load images (java.awt.Image and friends) is part of the "old" asynchronous producer/consumer imaging API, and might feel a bit awkward to work with. It's completely okay for loading packaged icons and similar, but less suitable for loading large, user-supplied images, as you have no progress tracking, little error feedback if anything goes wrong, etc.

These images are also a lot less useful than BufferedImages, if you want to perform image manipulations of any kind. You cannot cast them to a BufferedImage but you can "convert" them, by painting them onto a BufferedImage.

The Toolkit class is abstract, and you obtain a concrete instance using Toolkit.getToolkit(). This concrete instance is platform specific, and ends up using systems specific native calls for most methods, like loading images.

The Apple Java implementation does have some extra features, like allowing you to load Apple system images using a special URI scheme. It seems it can also load bundled images this way, using the @2x naming convention, and even multi-scale TIFFs or scalable PDFs in OS X for resolution independent graphics for your application.

Note that for this to work, you need to package your application as an application bundle, and place your images in the Resources folder (ie. Contents/Resources) of the bundle. And you must refer to images using its base name, without extension. You can not use this functionality to read random TIFF files not packaged with your application (ie. user supplied content), as far as I know.

Furthermore, this functionality is specific to the Apple JRE, and will only work on Apple's own JRE, and on OS X. It is not part of the public Java API, and it will not work cross platform. I recommend using the functionality sparingly, and only to have better system integration (ie. make your application look more native) on OS X.

To read (and write) any TIFF (or any other format for that sake), you should instead use ImageIO and some proper plugins. The JRE does not come with plugins for the TIFF format for some reason, but several third party plugins exists.

If you don't want to use JAI (through jai_imageio.jar), I can recommend my own TwelveMonkeys library which supports TIFF along with quite a few other formats. It uses the business-friendly open source BSD license.

There are also Apache Commons Imaging, iCafe and probably others that can read/write TIFF, but these have their own custom APIs, which make them less flexible and more proprietary IMO.

Sosthena answered 7/10, 2015 at 18:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.