Java 9 introduced a new interface called MultiResolutionImage (JEP 251) to encapsulate a set of images with different resolutions into a single multi-resolution image. The APIs related to multi-resolution images are available under java.awt.image package and helps us to programmatically:
- Get all variants of a particular image.
- Get an image specific to the resolution required based on DPI metric and image transformations.
Let us learn more about the APIs for multi-resolution images introduced in Java 9.
MultiResolutionImage Interface
There are two important functions in the MultiResolutionImage.
- getResolutionVariant() – This method returns an instance of java.awt.Image from the set of variants based on the width and height of the image given as parameters to the function. It will throw an IllegalArgumentException if the double values representing the dimensions are zero, negative, infinity or Not A Number.
- getResolutionVariants() – This method returns all the variants available for the given image. It returns a List containing objects of type java.awt.Image.
AbstractMultiResolutionImage Class
As the name suggests, the AbstractMultiResolutionImage class is an extension of java.awt.Image class. It implements the MultiResolutionImage interface. It gives default implementations for many methods from java.awt.Image class and enables the users to extend this class to provide a custom concrete class defining additional methods to handle multi-resolution images.
Important methods of this class are:
- getBaseImage() – returns the base image, which is of type java.awt.Image with the best resolution available among the list of variants. It is the only abstract method in this class and should be implemented by all concrete classes that extends the AbstractMultiResolutionImage class. Other methods of this class delegates the respective calls to corresponding methods invoked on the base image object returned by this method. It is a protected method.
- getHeight() – delegates method call to getBaseImage().getHeight() and returns an integer value.
- getWidth() – delegates method call to getBaseImage().getWidth() and returns an integer value.
- getProperty() – delegates method call to getBaseImage().getProperty() and returns the value for the String property name that we pass as parameter to this method.
- getSource() – delegates method call to getBaseImage().getSource() and returns the object that produces the pixels on the image. The object returned will be of type java.awt.image.ImageProducer.
- getGraphics() – will always throw UnsupportedOperationException in contrast to what happens when java.awt.Image.getGraphics() is called. This is because only off-screen images can return a Graphics object.
BaseMultiResolutionImage Class
This is a concrete class that extends the AbstractMultiResolutionImage class. It implements the methods available in MultiResolutionImage interface – getResolutionVariant() and getResolutionVariants().
MultiResolutionImage In Action
Let us learn more about MultiResolutionImages by creating a sample project.
Before we jump into code development, we need to keep test data ready using the method that is described next.
From an internet browser, access Wikipedia page for eagle. Click on the image of an eagle on the right-hand side. We should be able to navigate to the page containing the file details.
Right below the image, we can see various resolutions for the given images. Download the files that we would like to include in our sample project and save them to disk.
Now, create a new java project in Eclipse IDE with JRE set to java 9. Under the java project base folder, copy the images that we downloaded from Wikimedia.
Create a new class named MRImageDemo.java. The project structure should look as follows:
Notice that the image with the largest resolution is named without adding prefix for the resolution. This is to show that it is the highest resolution image available.
Modify the MRImageDemo.java file to include the code required for processing multi-resolution images that we have in the project:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.javatutorials.imageprocessing; import java.awt.Image; import java.awt.image.BaseMultiResolutionImage; import java.awt.image.MultiResolutionImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; public class MRImageDemo { public static void main(String[] args) throws IOException { String BASEDIR = System.getProperty("user.dir"); System.out.println("Base Directory of the project: " + BASEDIR); List<String> imageLocations = List.of(BASEDIR + File.separator + "320px-Eagle.jpg", BASEDIR + File.separator + "800px-Eagle.jpg", BASEDIR + File.separator + "1024px-Eagle.jpg", BASEDIR + File.separator + "1280px-Eagle.jpg", BASEDIR + File.separator + "Eagle.jpg"); List<Image> imgList = new ArrayList<Image>(); for(String loc: imageLocations) { Image currentImg = ImageIO.read(new File(loc)); imgList.add(currentImg); } MultiResolutionImage mrImages = new BaseMultiResolutionImage(imgList.toArray(new Image[0])); List<Image> diffResolutions = mrImages.getResolutionVariants(); System.out.println("Number of available resolutions for the same image: " + diffResolutions.size()); Image img1 = mrImages.getResolutionVariant(1920, 1080); System.out.printf("\nRetrieved Image variant for a 21.5 inch monitor with high resolution [%d,%d] has resolution [%d,%d]\n", 1920, 1080, img1.getWidth(null), img1.getHeight(null)); Image img2 = mrImages.getResolutionVariant(1280, 768); System.out.printf("\nRetrieved Image variant for a simple desktop monitor [%d,%d] has resolution [%d,%d]\n", 1280, 768, img2.getWidth(null), img2.getHeight(null)); Image img3 = mrImages.getResolutionVariant(640, 360); System.out.printf("\nRetrieved Image variant for a small device resolution [%d,%d] has resolution [%d,%d]\n", 768, 600, img3.getWidth(null), img3.getHeight(null)); Image img4 = mrImages.getResolutionVariant(240, 120); System.out.printf("\nRetrieved Image variant for an extra small device resolution [%d,%d] has resolution [%d,%d]\n", 240, 120, img4.getWidth(null), img4.getHeight(null)); } } |
Output for the above code will be as follows:
1 2 3 4 5 6 7 8 9 10 |
Base Directory of the project: G:\workspaces\java9ws\MRImageDemo number of different resolutions of same image: 5 Retrieved Image variant for a 21.5 inch monitor with high resolution [1920,1080] has resolution [1696,1130] Retrieved Image variant for a simple desktop monitor [1280,768] has resolution [1280,853] Retrieved Image variant for a small device resolution [768,600] has resolution [800,533] Retrieved Image variant for an extra small device resolution [240,120] has resolution [320,213] |
From the above example, it is evident that for a given resolution, the image variant is returned in such a way that there is very less amount of pixel size difference between the intended resolution and the variant obtained. This is done so that the returned image can fit properly in the device from which the image variant was requested. The MultiResolutionImage object mrImages has retained data of all the five variants fed to it in the sample program.
We have used files available on the local machine as input to simplify the example. But that is not the only way we can fetch input data for creating a MultiReolutionImage object.
It is possible to use images from the internet also as input for the multiresolution image. To do so, the imageLocations and images lists in the above example need to be changed as shown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
List<String> imageLocations = List.of( "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Adler_jagt.jpg/1024px-Adler_jagt.jpg", "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Adler_jagt.jpg/1280px-Adler_jagt.jpg", "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Adler_jagt.jpg/320px-Adler_jagt.jpg", "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Adler_jagt.jpg/800px-Adler_jagt.jpg", "https://upload.wikimedia.org/wikipedia/commons/a/a7/Adler_jagt.jpg" ); List<Image> images = imageLocations.stream().map(url -> { try { return ImageIO.read(new URL(url)); } catch (IOException e) { System.out.println("Error Reading The Image From File Location. Reason : " + e.getMessage()); } return null; }).collect(Collectors.toList()); |
The sample program will give the same output as mentioned earlier with the above changes.
Summary
The Multi-resolution API will allow the encapsulation of a set of images having different resolutions into a single multi-resolution image object. Thus, a developer can retrieve an image that is resolution-specific. And can also retrieve all variants within the image. This will make it very easy to have different resolution images for different displays and hence would be a very helpful feature for developers.
It is also ported back to java 8
Thanks, That is correct.
I would rather see it in a library.
Image was already a part of java.awt. This feature is only an enhancement.