[OpenJDK 2D-Dev] Review request for 8029339 Custom MultiResolution image support on HiDPI displays

Alexander Scherbatiy alexandr.scherbatiy at oracle.com
Thu Jan 22 14:49:42 UTC 2015


   Hi Phil,

   I have prepared two versions of the proposed API:

   I) Resolution variants are added directly to the Image:
    http://cr.openjdk.java.net/~alexsch/8029339/list/webrev.00

   II)  MultiResolutionImage interface is used:
     http://cr.openjdk.java.net/~alexsch/8029339/webrev.05

   It could help to decide which way should be chosen for the the 
multi-resolution image support.

   Below are some comments:

   1. High level goal:
      Introduce an API that allows to create and handle an image with 
resolution variants.

   2. What is not subject of the provided API
     - Scale naming convention for high-resolution images
     - Providing pixel scale factor for the screen/window

   3. Use cases
    3.1 Loading and drawing high-resolution icons in IntelliJ IDEA
      A high-resolution image is loaded from resources and stored in 
JBHiDPIScaledImage class  which is a subclass of the buffered image.
      The high-resolution image is used to create a disabled icon in the 
IconLoader.getDisabledIcon(icon) method.
https://github.com/JetBrains/intellij-community/blob/master/platform/util/src/com/intellij/openapi/util/IconLoader.java

    3.2 Loading and drawing high-resolution icons in NetBeans
      NetBeans does not have support for the high-resolution icons loading.
      It loads an icon from the file system using 
Toolkit.getDefaultToolkit().getImage(url) method or from resources
      by  ImageReader  and store it in ToolTipImage class which is 
subclass of the buffered image.
      ImageUtilities.createDisabledIcon(icon) method creates a disabled 
icon by applying  RGBImageFilter to the icon.
http://hg.netbeans.org/main/file/97dcf49eb4a7/openide.util/src/org/openide/util/ImageUtilities.java

    3.3 Loading system icons in JDK 1.8
      JDK requests icons from the native system for system L&Fs and 
applies filters for them.
      See for example AquaUtils.generateLightenedImage() method:
http://hg.openjdk.java.net/jdk9/client/jdk/file/e6f48c4fad38/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java

   4. HiDPI support for Images on different OSes

     4.1 Mac OS X
       Cocoa API contains NSImage that allows to work with image 
representations: add/remove/get all representations.
       It picks up an image with necessary resolution based on the 
screen backing store pixel scale factor and applied transforms.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSImage_Class/Reference/Reference.html

     4.2 Linux
       GTK+ 3 API has gtkcssimagescaled lib (it seems that it is not 
public/stable)
       that parses the -gtk-scaled css property and draws a GtkCssImage 
according to the given scale factor.

       I have not found information about the HiDPI support in Xlib.

     4.3 Windows
       I have only found the tutorial that suggests to select and draw a 
bitmap using the queried DPI
       and scale the coordinates for drawing a rectangular frame
http://msdn.microsoft.com/en-us/library/dd464659%28v=vs.85%29.aspx

       Windows also provides the horizontal and vertical DPI of the desktop
http://msdn.microsoft.com/en-us/library/windows/apps/dd371316

   5. Pseudo API
      Below are some ways which illustrates how multi-resolution images 
can be created and used.

     5.1 Resolution variants are stored directly in Image class.
     To query a resolution variant it needs to compare the resolution 
variant width/height
     with the requested high-resolution size.
     ------------
     public abstract class Image {

         public void addResolutionVariant(Image image) {...}
         public List<Image> getResolutionVariants() {...}
     }
     ------------
     // create a disabled image with resolution variants

     Image disabledImage = getDisabledImage(image);

     for (Image rv : image.getResolutionVariants()) {
         disabledImage.addResolutionVariant(getDisabledImage(rv));
     }
     ------------
     This approach requires that all resolution variants have been 
created even not of them are really used.

     5.2  Resolution variants are stored in a separate object that 
allows to create them by demand.
     To query a resolution variant it needs to compare the resolution 
variant scale factor
     with the requested scale (that can include both screen DPI scale 
and applied transforms).
     ------------
     public abstract class Image {

         public static interface ResolutionVariant {
             Image getImage();
             float getScaleFactor();
         }

         public void addResolutionVariant(ResolutionVariant 
resolutionVariant) {...}
         public List<ResolutionVariant> getResolutionVariants() {...}
     }
     ------------
     // create a disabled image with resolution variants
     Image disabledImage = getDisabledImage(image);

     for (Image.ResolutionVariant rv : image.getResolutionVariants()) {
         disabledImage.addResolutionVariant(new Image.ResolutionVariant() {

             public Image getImage() {
                 return getDisabledImage(rv.getImage());
             }

             public float getScaleFactor() {
                 return rv.getScaleFactor();
             }
         });
     }
     ------------

     It does not have problem if a predefined set of images is provided 
(like image.png and image at 2x.png on the file system).
     This does not cover cases where a resolution variant can be created 
using the exact requested size (like loading icons from the native system).
     A resolution variant can be queried based on a scale factor and 
applied transforms.

     5.3 The provided example allows to create a resolution variant 
using the requested high-resolution image size.
     ------------
     public interface MultiResolutionImage {
         Image getResolutionVariant(float width, float height);
     }
     ------------
     // create a multi-resolution image
     Image mrImage = new AbstractMultiResolutionImage() {

             public Image getResolutionVariant(float width, float height) {
                 // create and return a resolution variant with exact 
requested width/height size
             }

             protected Image getBaseImage() {
                 return baseImage;
             }
         };
     ------------
     // create a disabled image with resolution variants
     Image disabledImage = null;
     if (image instanceof MultiResolutionImage) {
         final MultiResolutionImage mrImage = (MultiResolutionImage) image;
         disabledImage = new AbstractMultiResolutionImage(){

             public Image getResolutionVariant(float width, float height) {
                 return 
getDisabledImage(mrImage.getResolutionVariant(width, height));
             }

             protected Image getBaseImage() {
                 return getDisabledImage(mrImage);
             }
         };
     } else {
         disabledImage = getDisabledImage(image);
     }
     ------------

   Thanks,
   Alexandr.



More information about the 2d-dev mailing list