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

Jim Graham james.graham at oracle.com
Fri Feb 13 22:52:08 UTC 2015


Hi Alexander,

The code that lets someone modify an existing image has an important and 
undesirable side effect of making images mutable.  Since we cache images 
internally, they are treated as immutable so that if two unrelated 
parties ask for an image that comes from a specific URL, we can give 
them back the same object (with a bit of code internally to make sure 
that each and both of them have permissions to access the specified 
URL/filename/source).  So, the first solution that adds a new method 
onto Image that lets someone add a resolution variant after the fact is 
not feasible.  (Arguably, we could override and sabotage attempts to add 
new variants, but that style of providing an API that is implemented in 
a common case by blocking code is not very interesting.)

The version that provides an interface allows us to implement automatic 
loading of resolution variants internally and expose that information in 
a read-only fashion without making a cached Image object (or its 
resolution variants) mutable - as long as we only ever provide immutable 
collections populated by other immutable images.  It can be combined 
with an Image subclass variant that lets a developer specify their own 
list of images, and potentially even add more images to that list on the 
fly after the image is created (because it is not shared by a hidden 
mechanism since they created it directly).  We could probably add that 
to BufferedImage since those are not shared, but we'd have to make sure 
that shared "Toolkit images" don't subclass from BufferedImage or expose 
that through their API - that's probably already true otherwise we'd 
have provided a way for someone to scribble on a shared image.

I'll look into the second proposal (the interface variant) in a little 
more detail, but I wanted to get this basic comment out there in advance 
of more specific feedback...

			...jim

On 1/22/2015 6:49 AM, Alexander Scherbatiy wrote:
>
>    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