In looking through the ToolkitImage code some more it occurs to me that it was 
already designed (moreso in a previous life) to hold onto multiple 
representations of the image anyway.  In a prior life in 1.0 and 1.1 it held 
separate ImageRepresentation objects for each size it was scaled to (via 
drawImage(w,h)), but that was simplified to a single ImageRep when we converted 
to Java2D and started doing scaling on the fly for all drawImage() operations.  
Still, the concept of an alternate resolution version of the image is probably 
more in line with the ImageRepresentation object within the ToolkitImage than 
in having multiple ToolkitImage objects and a wrapper for them.

This would also deal more naturally with the ImageObserver issue since there 
really would be only one Image and SG2D would not deal with the 
sub-representations, it would be dealt with in the DrawImage pipeline code when 
it queries the ToolkitImage for the ImageRepresentation object.

In the end, I think that design would be simpler overall, would it be possible 
to shift those gears for this fix?  If not, we should consider it for a 
near-term future cleanup task perhaps...

                        ...jim

On 11/19/2013 7:58 PM, Jim Graham wrote:
Based on the information below, I have the following suggestions...

We should probably allow asynchronous loading of the scaled resolution 
variants.  This is a minor variation of what the ImageObserver was originally 
designed for, but technically might violate some developer's expectations if 
they have only dealt with a post-Java2D version of Java.  Some education might 
be helpful here.

The wording on drawImage(dxy12, sxy12) should probably be reworded to indicate 
that asynchronous scaling would not happen, but alternate versions of the image 
may be accessed.

In all cases, if the version of the image that we would ideally want to show 
hasn't been loaded, but the standard version has (or if @2x was loaded, but we 
want the regular version too?) then we should probably go with the version that 
was loaded, but still trigger the loading of the alternate version and notify 
their Observer as it is loaded.

I also examined the places in the code where we notify the ImageObserver.  A 
search for the observer method should show all places we call it, but the 
primary ones look like they are fairly few places.  If we tag the resolution 
variant images with the composite image from which they came, then we can have 
those few places do something like:

Image obsimg = (img instanceof SunToolkitImage) ? (() img).getObservedImage() : 
img;

I think in most cases the img is already known to be our internal SunToolkit 
image and so we don't even need to check instanceof...

             ...jim

On 11/19/2013 7:01 PM, Jim Graham wrote:
I did some more reading about the various ways in which the ImageObserver is 
used and noticed the following points, some of which may be to our advantage.

Many of the drawImage() calls mention that they will notify the observer as the image is 
"scaled or converted for the output device".  I believe in the world before 2D, 
we would often have to load or convert the image asynchronously for various changes in 
the output.  When we created the 2D interface and added some on-the-fly image scaling 
code, we mentioned that scaling was no longer asynchronous, but technically the API is 
still designed for asynchronous operations when the rendering parameters change.

However, the drawImage() call that takes 8 parameters dxy12, sxy12 - 
specifically includes the words:

      * This method always uses the unscaled version of the image
      * to render the scaled rectangle and performs the required
      * scaling on the fly. It does not use a cached, scaled version
      * of the image for this operation. Scaling of the image from source
      * to destination is performed such that the first coordinate
      * of the source rectangle is mapped to the first coordinate of
      * the destination rectangle, and the second source coordinate is
      * mapped to the second destination coordinate. The subimage is
      * scaled and flipped as needed to preserve those mappings.
     public abstract boolean drawImage(Image img,
                                       int dx1, int dy1, int dx2, int dy2,
                                       int sx1, int sy1, int sx2, int sy2,
                                       ImageObserver observer);

which basically, at the time it was created in 1.1, was an opt "out" of the 
asynchronous operations that might happen when you scaled an image with drawImage(xywh).  
As worded, this would suggest that this method does not use the @2x version and always 
uses the default resolution version, but those words were not put there for this 
particular intent, they were put there to indicate that we weren't going to do an 
off-line scaling of the pixels which sometimes happened in 1.0.x and possibly 1.1 (for 
the other calls that predated 1.1 until Java2D).

I also found the Component.prepareImage() and Component.checkImage() methods 
which also take an ImageObserver.  Those methods imply that they will possibly 
start the process and return information on a particular version of an image 
appropriate for the current output device.  There is even a prepareImage(w,h) 
method that gets an image ready to render at the indicated size.  Given that 
these methods are on component, it probably makes sense to have that code look 
up which representation would be used for those indicated dimensions on the 
indicated output device that the Component is displayed on.

             ...jim

Reply via email to