<AWT Dev> [OpenJDK 2D-Dev]  Review request for 8011059 [macosx] Make JDK demos look perfect on retina displays
james.graham at oracle.com
Wed Nov 20 03:17:59 PST 2013
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...
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...
> 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.
More information about the awt-dev