<AWT Dev> [9] Review Request: JDK-8029455 JLightweightFrame: support scaled painting
Anton V. Tarasov
anton.tarasov at oracle.com
Mon Dec 23 06:20:46 PST 2013
Unfortunately, I have to suspend the request, as I'm taking a vacation
for the rest of this year...
Thanks,
Anton.
On 12/10/13 6:22 PM, Anton V. Tarasov wrote:
> Hi Jim, Sergey and All,
>
> Please review the fix that adds support of Retina displays to
> JLightweightFrame (which javafx SwingNode is based on).
>
> webrev: http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1
> jira: https://bugs.openjdk.java.net/browse/JDK-8029455
>
> (After the fix goes into jdk9 it should be ported to 8u20 as well,
> because the functionality is essential for SwingNode.)
>
> The general idea of the fix is as follows.
>
> A BufferedImage instance, being created in the context in which the
> scale factor is determined and is different from one, is automatically
> created with appropriately extended size. The image itself becomes a
> scaled image (a "scale" private field is set on it). By the "context"
> I mean the circumstances where the BufferedImage is related to a
> JLightweightFrame, a GraphicsConfiguration, a SurfaceData, or a
> GraphicsDevice which determine the scale factor.
>
> Here are the related changes:
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/java/awt/image/BufferedImage.java.udiff.html
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/awt/image/OffScreenImage.java.udiff.html
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/swing/JLightweightFrame.java.udiff.html
> (the resizeBuffer method)
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/macosx/classes/sun/lwawt/LWLightweightFramePeer.java.udiff.html
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java.udiff.html
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java.udiff.html
>
> The "scale" value of a BufferedImage is used when 1)
> BufferedImageGraphicsConfig is created 2)
> BufImgSurfaceData.getDefaultScale() is called:
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java.udiff.html
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/awt/image/BufImgSurfaceData.java.udiff.html
>
> The former is used in the
> GraphicsConfiguration.createCompatibleImage() calls, and the latter is
> used in SurfaceManager.getImageScale(Image):
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/awt/image/SurfaceManager.java.udiff.html
>
> A scaled BufferedImage is supported by the SunGraphics2D.drawImage()
> primitives. Here's the pattern of how the image may be created and drawn:
>
> int scale = <get the scale factor from the context>;
> BufferedImage img = new BufferedImage(width * scale, height * scale,
> ...);
> img.setScale(scale); // an accessor is currently used instead
> <...>
> g2d.drawImage(img, x, y, ...); // 1) draw the image with auto-scale
> g2d.drawImage(img, x, y, dw, dh, ...) // 2) draw the image into a
> specified rect
>
> In the first case, if the BufferedImage is created with an extended
> size, the "scale" value of the image matters, it should be drawn as a
> HiDPI image.
> In the second case, if the BufferedImage is created with an extended
> size, the "scale" value of the image doesn't matter (it may not be
> evidently set) as the image will anyway be scaled from its physical
> bounds into provided logical bounds. This all should (as I suppose)
> provide backward compatibility for buffered images that were created
> in their logical bounds or without setting the "scale" field. For
> instance, the AquaPainter.paintFromSingleCachedImage(...) method
> creates & draws an image as follows:
>
> int scale = ((SunGraphics2D) g).surfaceData.getDefaultScale();
> int imgW = bounds.width * scale;
> int imgH = bounds.height * scale;
> BufferedImage img = new BufferedImage(imgW, imgH, ...);
> <paint into the img>
> g.drawImage(img, bounds.x, bounds.y, bounds.width, bounds.height, null);
>
> Here, the img.scale value is not set (I didn't modify this code), and
> SunGraphics2D doesn't treat the image as a HiDPI image, however it is
> drawn as expected. An alternative way to draw the image would be:
>
> int scale = ((SunGraphics2D) g).surfaceData.getDefaultScale();
> int imgW = bounds.width * scale;
> int imgH = bounds.height * scale;
> BufferedImage img = new BufferedImage(imgW, imgH, ...);
> img.setScale(scale);
> <paint into the img>
> g.drawImage(img, bounds.x, bounds.y, ...);
>
> The result would be the same.
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/java2d/SunGraphics2D.java.sdiff.html
>
> The following changes:
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/macosx/classes/sun/lwawt/macosx/CPlatformLWView.java.udiff.html
>
> are defined by this logic. Running Swing via JLightweightFrame (JLF)
> makes it "display agnostic". Swing is painted to an off-screen buffer
> and it's the host (e.g. SwingNode) that renders the buffer on a
> particular device. So, the host should detect the scale of the current
> display and set it on JLF.
>
> However, AWT in order to paint to a volatile image requires
> CGraphicsDevice and CGLSurfaceData to be created. By default AWT
> creates CGraphicsDevice instances matching all the detected display
> devices (CGraphicsEnvironment.initDevices()). But, as JLF doesn't have
> any platform window behind it, AWT can't match JLF to the exact device
> it's currently displayed on. So, on the one hand, AWT doesn't know
> which device is current and what is the current scale (the host passes
> this value), but from the other hand, AWT has a list of all the
> CGraphicsDevice instances.
>
> I tried to leverage from that fact. The
> CPlatformLWView.getGraphicsDevice() method takes the current scale
> from the JLF instance, and then tries to match it to an existent
> device from the list. In case it can't find a device with the
> specified scale (which should not actually happen, unless the host
> passes an arbitrary scale value, which is not the case for SwingNode)
> it takes a default device and changes its scale forcedly. I'm not sure
> if I should create a new dummy device instance instead. The scale
> factor of the device (which is then propagated to CGLSurfaceData on
> its creation) is the only info that JLF will take from the device to
> create a scaled volatile image.
>
> The following changes:
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/javax/swing/JViewport.java.udiff.html
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/javax/swing/RepaintManager.java.udiff.html
>
> were made to map a backing store image to a scale factor.
>
> The JViewPort.paint(...) method calls SunGraphics2D.copyArea(...) on
> scrolling. The method was not implemented for a graphics with a scale
> transform and a BufImgSurfaceData (it threw exceptions). I took that
> code, copied it to the BufImgSurfaceData.copyArea(...) and added a
> general translation for the coords:
>
> -
> http://cr.openjdk.java.net/~ant/JDK-8029455/webrev.1/src/share/classes/sun/awt/image/BufImgSurfaceData.java.udiff.html
>
> It works, but I'm not sure the implementation is eligible (I don't
> know the details of the Blit class, at least it warns not to use the
> same source and dest).
>
> The rest of the changes (not covered here) should be clear.
>
> Testing:
>
> - Using jfc/SwingSet2 and jfc/Java2D demos (in a standalone mode &
> embedded into SwingNode [1]).
> - Testing both Nimbus and Aqua L&F.
> - Setting swing.volatileImageBufferEnabled=false/true for all
> combinations.
>
> Currently, I see no regressions and no visual issues comparing a
> standalone mode and a SwingSet mode.
>
> At the end, I suspect there may be some intersection b/w this fix and
> the fix which introduced MultiResolutionToolkitImage. Unfortunately, I
> didn't yet read that review saga... Please tell me if I should
> incorporate anything from that fix.
>
> Thanks,
> Anton.
>
> [1] There's a SwingSet part of the fix which I'm going to post to the
> jfx alias separately.
>
More information about the awt-dev
mailing list