--- old/src/macosx/classes/sun/lwawt/LWLightweightFramePeer.java 2014-05-13 21:17:44.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/LWLightweightFramePeer.java 2014-05-13 21:17:44.000000000 +0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,14 +26,17 @@ package sun.lwawt; import java.awt.Graphics; +import java.awt.GraphicsConfiguration; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Transparency; import java.awt.Window; import java.awt.dnd.DropTarget; import sun.awt.CausedFocusEvent; import sun.awt.LightweightFrame; +import sun.awt.image.OffScreenImage; import sun.swing.JLightweightFrame; import sun.swing.SwingAccessor; @@ -114,4 +117,17 @@ public void updateCursorImmediately() { SwingAccessor.getJLightweightFrameAccessor().updateCursor((JLightweightFrame)getLwTarget()); } + + @Override + public OffScreenImage createHiDPIImage(int width, int height) { + OffScreenImage img = OffScreenImage.create(getLwTarget(), (GraphicsConfiguration)getLWGC(), + width, height, Transparency.OPAQUE, + getLwTarget().getScaleFactor()); + return img; + } + + @Override + public void notifyScaleFactorChanged() { + displayChanged(); + } } --- old/src/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java 2014-05-13 21:17:44.000000000 +0400 +++ new/src/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java 2014-05-13 21:17:44.000000000 +0400 @@ -29,12 +29,18 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.Insets; import java.awt.MenuBar; import java.awt.Point; +import java.awt.Rectangle; import java.awt.Window; +import sun.awt.CGraphicsDevice; +import sun.awt.CGraphicsEnvironment; import sun.awt.CausedFocusEvent; +import sun.awt.LightweightFrame; import sun.java2d.SurfaceData; +import sun.lwawt.LWLightweightFramePeer; import sun.lwawt.LWWindowPeer; import sun.lwawt.PlatformWindow; @@ -73,11 +79,6 @@ } @Override - public GraphicsDevice getGraphicsDevice() { - return null; - } - - @Override public SurfaceData getScreenSurface() { return null; } @@ -199,4 +200,24 @@ public long getLayerPtr() { return 0; } + + @Override + public GraphicsDevice getGraphicsDevice() { + CGraphicsEnvironment ge = (CGraphicsEnvironment)GraphicsEnvironment. + getLocalGraphicsEnvironment(); + + LWLightweightFramePeer peer = (LWLightweightFramePeer)getPeer(); + int scale = ((LightweightFrame)peer.getTarget()).getScaleFactor(); + + Rectangle bounds = ((LightweightFrame)peer.getTarget()).getBounds(); + for (GraphicsDevice d : ge.getScreenDevices()) { + if (d.getDefaultConfiguration().getBounds().intersects(bounds) && + ((CGraphicsDevice)d).getScaleFactor() == scale) + { + return d; + } + } + // We shouldn't be here... + return ge.getDefaultScreenDevice(); + } } --- old/src/share/classes/java/awt/peer/FramePeer.java 2014-05-13 21:17:45.000000000 +0400 +++ new/src/share/classes/java/awt/peer/FramePeer.java 2014-05-13 21:17:45.000000000 +0400 @@ -28,6 +28,7 @@ import java.awt.*; import sun.awt.EmbeddedFrame; +import sun.awt.image.OffScreenImage; /** * The peer interface for {@link Frame}. This adds a couple of frame specific @@ -131,4 +132,17 @@ * @param activate activate or deactivate the window */ void emulateActivation(boolean activate); + + /** + * Notifies that the scale factor has changed. + * The method is used by the LightweightFrame Mac implementation. + */ + default void notifyScaleFactorChanged() {} + + /** + * Creates a HiDPI back buffer image with a scale factor matching + * the scale of the frame (which is an internal value). + * The method is currently used by the LightweightFrame Mac implementation. + */ + default OffScreenImage createHiDPIImage(int width, int height) { return null; } } --- old/src/share/classes/javax/swing/JViewport.java 2014-05-13 21:17:45.000000000 +0400 +++ new/src/share/classes/javax/swing/JViewport.java 2014-05-13 21:17:45.000000000 +0400 @@ -36,6 +36,10 @@ import javax.accessibility.*; import java.io.Serializable; +import sun.awt.image.OffScreenImage; +import sun.awt.image.SurfaceManager; +import sun.java2d.SunGraphics2D; +import sun.swing.JLightweightFrame; /** * The "viewport" or "porthole" through which you see the underlying @@ -739,8 +743,12 @@ if (!isOpaque()) { g.clipRect(0, 0, viewBounds.width, viewBounds.height); } + int scale = g instanceof SunGraphics2D ? + ((SunGraphics2D)g).surfaceData.getDefaultScale() : 1; - if (backingStoreImage == null) { + if (backingStoreImage == null || + SurfaceManager.getImageScale(backingStoreImage) != scale) + { // Backing store is enabled but this is the first call to paint. // Create the backing store, paint it and then copy to g. // The backing store image will be created with the size of @@ -748,7 +756,13 @@ // same size, otherwise when scrolling the backing image // the region outside of the clipped region will not be painted, // and result in empty areas. - backingStoreImage = createImage(width, height); + Window w = SwingUtilities.getWindowAncestor(this); + if (w instanceof JLightweightFrame) { + backingStoreImage = ((JLightweightFrame)w).createHiDPIImage(width, height); + ((OffScreenImage)backingStoreImage).setReturnLayoutSize(true); + } else { + backingStoreImage = createImage(width, height); + } Rectangle clip = g.getClipBounds(); if (clip.width != width || clip.height != height) { if (!isOpaque()) { --- old/src/share/classes/javax/swing/RepaintManager.java 2014-05-13 21:17:45.000000000 +0400 +++ new/src/share/classes/javax/swing/RepaintManager.java 2014-05-13 21:17:45.000000000 +0400 @@ -47,6 +47,10 @@ import com.sun.java.swing.SwingUtilities3; import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2.RepaintListener; +import sun.awt.image.OffScreenImage; +import sun.awt.image.SurfaceManager; +import sun.java2d.SunGraphics2D; +import sun.swing.JLightweightFrame; /** * This class manages repaint requests, allowing the number @@ -1063,7 +1067,7 @@ return null; } } - + if (standardDoubleBuffer == null) { standardDoubleBuffer = new DoubleBufferInfo(); } @@ -1086,10 +1090,20 @@ height = Math.max(doubleBuffer.size.height, height); } + Graphics g = c.getGraphics(); + int scale = g instanceof SunGraphics2D ? + ((SunGraphics2D)g).surfaceData.getDefaultScale() : 1; + Image result = doubleBuffer.image; - if (doubleBuffer.image == null) { - result = c.createImage(width , height); + if (doubleBuffer.image == null || + SurfaceManager.getImageScale(doubleBuffer.image) != scale) + { + if (w instanceof JLightweightFrame) { + result = ((JLightweightFrame)w).createHiDPIImage(width, height); + } else { + result = c.createImage(width, height); + } doubleBuffer.size = new Dimension(width, height); if (c instanceof JComponent) { ((JComponent)c).setCreatedDoubleBuffer(true); @@ -1552,10 +1566,16 @@ Graphics g, int clipX, int clipY, int clipW, int clipH) { Graphics osg = image.getGraphics(); - int bw = Math.min(clipW, image.getWidth(null)); - int bh = Math.min(clipH, image.getHeight(null)); + int lw = image.getWidth(null); + int lh = image.getHeight(null); + if (image instanceof OffScreenImage) { + lw = ((OffScreenImage)image).getLayoutWidth(); + lh = ((OffScreenImage)image).getLayoutHeight(); + } + int bw = Math.min(clipW, lw); + int bh = Math.min(clipH, lh); int x,y,maxx,maxy; - + try { for(x = clipX, maxx = clipX+clipW; x < maxx ; x += bw ) { for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) { @@ -1576,10 +1596,10 @@ final Graphics2D g2d = (Graphics2D) g; final Composite oldComposite = g2d.getComposite(); g2d.setComposite(AlphaComposite.Src); - g2d.drawImage(image, x, y, c); + g2d.drawImage(image, x, y, lw, lh, c); g2d.setComposite(oldComposite); } else { - g.drawImage(image, x, y, c); + g.drawImage(image, x, y, lw, lh, c); } osg.translate(x, y); } --- old/src/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java 2014-05-13 21:17:46.000000000 +0400 +++ new/src/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java 2014-05-13 21:17:46.000000000 +0400 @@ -31,6 +31,11 @@ import javax.swing.plaf.UIResource; import javax.swing.Painter; import java.awt.print.PrinterGraphics; +import sun.awt.image.BufferedImageGraphicsConfig; +import sun.awt.image.OffScreenImage; +import sun.awt.image.SunVolatileImage; +import sun.java2d.SunGraphics2D; +import sun.swing.JLightweightFrame; /** * Convenient base class for defining Painter instances for rendering a @@ -674,7 +679,7 @@ ImageCache imageCache = ImageCache.getInstance(); //get the buffer for this component VolatileImage buffer = (VolatileImage) imageCache.getImage(config, w, h, this, extendedCacheKeys); - + int renderCounter = 0; //to avoid any potential, though unlikely, infinite loop do { //validate the buffer so we can check for surface loss @@ -693,10 +698,15 @@ if (buffer != null) { buffer.flush(); buffer = null; - } + } //recreate the buffer - buffer = config.createCompatibleVolatileImage(w, h, - Transparency.TRANSLUCENT); + if (config instanceof OffScreenImage.GraphicsConfig) { + buffer = new OffScreenImage. + VolatileImage((OffScreenImage.GraphicsConfig)config, + w, h, Transparency.TRANSLUCENT); + } else { + buffer = config.createCompatibleVolatileImage(w, h, Transparency.TRANSLUCENT); + } // put in cache for future imageCache.setImage(buffer, config, w, h, this, extendedCacheKeys); } --- old/src/share/classes/sun/awt/LightweightFrame.java 2014-05-13 21:17:46.000000000 +0400 +++ new/src/share/classes/sun/awt/LightweightFrame.java 2014-05-13 21:17:46.000000000 +0400 @@ -124,4 +124,20 @@ * @see SunToolkit#ungrab(java.awt.Window) */ public abstract void ungrabFocus(); + + /** + * Returns the scale factor set with the {@link #setScaleFactor} method. + * The default value is 1. + * + * @return the scale factor + */ + public abstract int getScaleFactor(); + + /** + * Sets the scale factor which defines the HiDPI resolution + * of the offscreen buffer the frame is painted to. + * + * @param scale the factor + */ + public abstract void setScaleFactor(int scale); } --- old/src/share/classes/sun/awt/image/BufImgSurfaceData.java 2014-05-13 21:17:46.000000000 +0400 +++ new/src/share/classes/sun/awt/image/BufImgSurfaceData.java 2014-05-13 21:17:46.000000000 +0400 @@ -236,14 +236,22 @@ sData = createDataSC(bufImg, sType, icm); break; } - sData = new BufImgSurfaceData(raster.getDataBuffer(), - bufImg, SurfaceType.Custom); + sData = createData(raster.getDataBuffer(), bufImg, SurfaceType.Custom); } break; } ((BufImgSurfaceData) sData).initSolidLoops(); return sData; } + + private static BufImgSurfaceData createData(DataBuffer db, BufferedImage bufImg, + SurfaceType sType) + { + if (bufImg instanceof OffScreenImage) { + return ((OffScreenImage)bufImg).new SurfaceData(db, sType); + } + return new BufImgSurfaceData(db, bufImg, sType); + } public static SurfaceData createData(Raster ras, ColorModel cm) { throw new InternalError("SurfaceData not implemented for Raster/CM"); @@ -253,8 +261,7 @@ SurfaceType sType) { IntegerComponentRaster icRaster = (IntegerComponentRaster)bImg.getRaster(); - BufImgSurfaceData bisd = - new BufImgSurfaceData(icRaster.getDataBuffer(), bImg, sType); + BufImgSurfaceData bisd = createData(icRaster.getDataBuffer(), bImg, sType); bisd.initRaster(icRaster.getDataStorage(), icRaster.getDataOffset(0) * 4, 0, icRaster.getWidth(), @@ -270,8 +277,7 @@ IndexColorModel icm) { ShortComponentRaster scRaster = (ShortComponentRaster)bImg.getRaster(); - BufImgSurfaceData bisd = - new BufImgSurfaceData(scRaster.getDataBuffer(), bImg, sType); + BufImgSurfaceData bisd = createData(scRaster.getDataBuffer(), bImg, sType); bisd.initRaster(scRaster.getDataStorage(), scRaster.getDataOffset(0) * 2, 0, scRaster.getWidth(), @@ -287,8 +293,7 @@ int primaryBank) { ByteComponentRaster bcRaster = (ByteComponentRaster)bImg.getRaster(); - BufImgSurfaceData bisd = - new BufImgSurfaceData(bcRaster.getDataBuffer(), bImg, sType); + BufImgSurfaceData bisd = createData(bcRaster.getDataBuffer(), bImg, sType); ColorModel cm = bImg.getColorModel(); IndexColorModel icm = ((cm instanceof IndexColorModel) ? (IndexColorModel) cm @@ -307,8 +312,7 @@ SurfaceType sType) { BytePackedRaster bpRaster = (BytePackedRaster)bImg.getRaster(); - BufImgSurfaceData bisd = - new BufImgSurfaceData(bpRaster.getDataBuffer(), bImg, sType); + BufImgSurfaceData bisd = createData(bpRaster.getDataBuffer(), bImg, sType); ColorModel cm = bImg.getColorModel(); IndexColorModel icm = ((cm instanceof IndexColorModel) ? (IndexColorModel) cm --- old/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java 2014-05-13 21:17:47.000000000 +0400 +++ new/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java 2014-05-13 21:17:47.000000000 +0400 @@ -25,12 +25,10 @@ package sun.awt.image; -import java.awt.AWTException; import java.awt.Component; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; -import java.awt.ImageCapabilities; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.geom.AffineTransform; @@ -38,7 +36,6 @@ import java.awt.image.ColorModel; import java.awt.image.DirectColorModel; import java.awt.image.Raster; -import java.awt.image.VolatileImage; import java.awt.image.WritableRaster; public class BufferedImageGraphicsConfig @@ -57,13 +54,17 @@ return ret; } } - ret = new BufferedImageGraphicsConfig(bImg, null); + if (bImg instanceof OffScreenImage) { + ret = ((OffScreenImage)bImg).new GraphicsConfig(); + } else { + ret = new BufferedImageGraphicsConfig(bImg, null); + } if (type > 0 && type < numconfigs) { configs[type] = ret; } return ret; } - + GraphicsDevice gd; ColorModel model; Raster raster; @@ -88,7 +89,7 @@ public GraphicsDevice getDevice() { return gd; } - + /** * Returns a BufferedImage with channel layout and color model * compatible with this graphics configuration. This method @@ -102,7 +103,7 @@ WritableRaster wr = raster.createCompatibleWritableRaster(width, height); return new BufferedImage(model, wr, model.isAlphaPremultiplied(), null); } - + /** * Returns the color model associated with this configuration. */ --- old/src/share/classes/sun/awt/image/OffScreenImage.java 2014-05-13 21:17:47.000000000 +0400 +++ new/src/share/classes/sun/awt/image/OffScreenImage.java 2014-05-13 21:17:47.000000000 +0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,13 +31,17 @@ import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.ImageProducer; import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.ImageObserver; import java.awt.image.WritableRaster; import sun.java2d.SunGraphics2D; -import sun.java2d.SurfaceData; +import sun.java2d.loops.SurfaceType; /** * This is a special variant of BufferedImage that keeps a reference to @@ -49,7 +53,21 @@ protected Component c; private OffScreenImageSource osis; private Font defaultFont; - + + // The layout size of the image. + private int width; + private int height; + + // The scale factor which determines the physical size of the raster. + private int scale = 1; + + // Affects the value returned by the size methods. + // When {@code true}, the returned size value matches + // the layout size of the image, otherwise the returned + // size value matches the physical size of the raster, + // which is determined by the scale factor. + private boolean returnLayoutSize; + /** * Constructs an OffScreenImage given a color model and tile, * for offscreen rendering to be used with a given component. @@ -63,7 +81,115 @@ this.c = c; initSurface(raster.getWidth(), raster.getHeight()); } - + + private OffScreenImage(Component c, ColorModel cm, WritableRaster raster, + boolean isRasterPremultiplied, + int width, int height, + int scale) + { + this(c, cm, raster, isRasterPremultiplied); + this.width = width; + this.height = height; + this.scale = scale; + } + + /** + * Constructs an OffScreenImage with the {@code scale} factor. + * The scale determines the physical size of the raster, compared + * to the layout {@code width/height} size of the image. + */ + public OffScreenImage(Component c, int width, int height, + int imageType, + int scale) + { + super(width * scale, height * scale, imageType); + this.c = c; + this.width = width; + this.height = height; + this.scale = scale; + } + + /** + * Constructs an OffScreenImage with the {@code scale} factor. + * The scale determines the physical size of the raster, compared + * to the layout {@code width/height} size of the image. + */ + public static OffScreenImage create(Component c, GraphicsConfiguration gc, + int width, int height, + int transparency, + int scale) + { + ColorModel cm = gc.getColorModel(transparency); + WritableRaster wr = cm.createCompatibleWritableRaster(width * scale, height * scale); + return new OffScreenImage(c, cm, wr, cm.isAlphaPremultiplied(), + width, height, scale); + } + + /** + * Sets whether the {@code getWidth/Height} methods should + * return the layout or physical size of the image. + * @param value + */ + public void setReturnLayoutSize(boolean value) { + returnLayoutSize = value; + } + + /** + * Gets whether the {@code getWidth/Height} methods should + * return the layout or physical size of the image. + * @param value + */ + public boolean isReturnLayoutSize() { + return returnLayoutSize; + } + + @Override + public int getWidth() { + return returnLayoutSize ? width : super.getWidth(); + } + + @Override + public int getWidth(ImageObserver o) { + return getWidth(); + } + + @Override + public int getHeight() { + return returnLayoutSize ? height : super.getHeight(); + } + + @Override + public int getHeight(ImageObserver o) { + return getHeight(); + } + + /** + * Gets the layout width of the image. + * + * @return the layout width of the image + */ + public int getLayoutWidth() { + return width; + } + + /** + * Gets the layout height of the image. + * + * @return the layout height of the image + */ + public int getLayoutHeight() { + return height; + } + + /** + * Gets the scale factor used to scale the image on HiDPI devices + * + * @return the HiDPI scale factor + */ + public int getHiDPIScale() { + return scale; + } + public Graphics getGraphics() { return createGraphics(); } @@ -112,4 +238,62 @@ } return osis; } + + /** + * Creates a scaled OffScreenImage. + */ + public class GraphicsConfig extends BufferedImageGraphicsConfig { + public GraphicsConfig() { + super(OffScreenImage.this, null); + } + + public BufferedImage createCompatibleHiDPIImage(int width, int height, int transparency) { + return OffScreenImage.create(null, this, + width, height, + transparency, + OffScreenImage.this.getHiDPIScale()); + } + } + + /** + * Returns correct physical bounds and the scale factor. + */ + public class SurfaceData extends BufImgSurfaceData { + public SurfaceData(DataBuffer db, SurfaceType sType) { + super(db, OffScreenImage.this, sType); + } + + @Override + public Rectangle getBounds() { + return new Rectangle(OffScreenImage.this.getRaster().getWidth(), + OffScreenImage.this.getRaster().getHeight()); + } + + @Override + public int getDefaultScale() { + return OffScreenImage.this.getHiDPIScale(); + } + } + + /** + * Backed by a scaled OffScreenImage. + */ + public static class VolatileImage extends SunVolatileImage { + public VolatileImage(OffScreenImage.GraphicsConfig graphicsConfig, + int width, int height, + int transparency) + { + super(graphicsConfig, width, height, transparency, null); + } + + @Override + public BufferedImage getBackupImage() { + OffScreenImage img = (OffScreenImage) + ((OffScreenImage.GraphicsConfig)getGraphicsConfig()). + createCompatibleHiDPIImage(getWidth(), getHeight(), transparency); + img.setReturnLayoutSize(true); + return img; + } + } + } --- old/src/share/classes/sun/awt/image/SurfaceManager.java 2014-05-13 21:17:47.000000000 +0400 +++ new/src/share/classes/sun/awt/image/SurfaceManager.java 2014-05-13 21:17:47.000000000 +0400 @@ -25,7 +25,6 @@ package sun.awt.image; -import java.awt.Color; import java.awt.GraphicsEnvironment; import java.awt.GraphicsConfiguration; import java.awt.Image; @@ -36,7 +35,6 @@ import java.util.Iterator; import sun.java2d.SurfaceData; import sun.java2d.SurfaceDataProxy; -import sun.java2d.loops.CompositeType; /** * The abstract base class that manages the various SurfaceData objects that @@ -64,7 +62,7 @@ } imgaccessor = ia; } - + /** * Returns the SurfaceManager object contained within the given Image. */ @@ -296,7 +294,9 @@ * @see SurfaceData#getDefaultScale */ public static int getImageScale(final Image img) { - if (!(img instanceof VolatileImage)) { + if (!(img instanceof VolatileImage) && + !(img instanceof OffScreenImage)) + { return 1; } final SurfaceManager sm = getManager(img); --- old/src/share/classes/sun/java2d/SunGraphics2D.java 2014-05-13 21:17:48.000000000 +0400 +++ new/src/share/classes/sun/java2d/SunGraphics2D.java 2014-05-13 21:17:48.000000000 +0400 @@ -95,6 +95,7 @@ import java.lang.annotation.Native; import sun.awt.image.MultiResolutionImage; +import sun.awt.image.OffScreenImage; import static java.awt.geom.AffineTransform.TYPE_FLIP; import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE; @@ -2108,12 +2109,12 @@ if (theData.copyArea(this, x, y, w, h, dx, dy)) { return; } - if (transformState >= TRANSFORM_TRANSLATESCALE) { + if (transformState > TRANSFORM_TRANSLATESCALE) { throw new InternalError("transformed copyArea not implemented yet"); } // REMIND: This method does not deal with missing data from the // source object (i.e. it does not send exposure events...) - + Region clip = getCompClip(); Composite comp = composite; @@ -2129,9 +2130,26 @@ lastCAcomp = comp; } - x += transX; - y += transY; + double[] coords = {x, y, x + w, y + h, x + dx, y + dy}; + transform.transform(coords, 0, coords, 0, 3); + x = (int)Math.ceil(coords[0] - 0.5); + y = (int)Math.ceil(coords[1] - 0.5); + w = ((int)Math.ceil(coords[2] - 0.5)) - x; + h = ((int)Math.ceil(coords[3] - 0.5)) - y; + dx = ((int)Math.ceil(coords[4] - 0.5)) - x; + dy = ((int)Math.ceil(coords[5] - 0.5)) - y; + + // In case of negative scale transform, reflect the rect coords. + if (w < 0) { + w *= -1; + x -= w; + } + if (h < 0) { + h *= -1; + y -= h; + } + Blit ob = lastCAblit; if (dy == 0 && dx > 0 && dx < w) { while (w > 0) { @@ -3073,6 +3091,11 @@ // end of text rendering methods private boolean isHiDPIImage(final Image img) { + if (img instanceof OffScreenImage && + !((OffScreenImage)img).isReturnLayoutSize()) + { + return false; + } return (SurfaceManager.getImageScale(img) != 1) || (resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_OFF && img instanceof MultiResolutionImage); @@ -3191,6 +3214,35 @@ return resolutionVariant; } + private boolean drawHiDPIImage(BufferedImage img, BufferedImageOp op, + int x, int y) + { + final int scale = SurfaceManager.getImageScale(img); + if (op != null) { + if (op instanceof AffineTransformOp) { + AffineTransformOp atop = (AffineTransformOp)op; + AffineTransform at = atop.getTransform(); + try { + // The "atop" transform may move the bounding rect of the image, + // so the scale transform should be applied before. + at.concatenate(AffineTransform.getScaleInstance(scale, scale).createInverse()); + } catch (Exception e) { + // the inverse is always possible + } + atop = new AffineTransformOp(at, atop.getInterpolationType()); + imagepipe.transformImage(this, img, atop, x, y); + return true; + } + img = op.filter(img, null); + } + int w = img.getWidth(); + int h = img.getHeight(); + return drawHiDPIImage(img, + x, y, x + w, y + h, + 0, 0, w, h, + backgroundColor, null); + } + /** * Draws an image scaled to x,y,w,h in nonblocking mode with a * callback object. @@ -3468,7 +3520,10 @@ if (bImg == null) { return; } - + if (isHiDPIImage(bImg)) { + drawHiDPIImage(bImg, op, x, y); + return; + } try { imagepipe.transformImage(this, bImg, op, x, y); } catch (InvalidPipeException e) { --- old/src/share/classes/sun/swing/JLightweightFrame.java 2014-05-13 21:17:48.000000000 +0400 +++ new/src/share/classes/sun/swing/JLightweightFrame.java 2014-05-13 21:17:48.000000000 +0400 @@ -41,6 +41,7 @@ import java.awt.event.ContainerListener; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; +import java.awt.peer.FramePeer; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.security.AccessController; @@ -55,6 +56,7 @@ import javax.swing.SwingUtilities; import sun.awt.LightweightFrame; +import sun.awt.image.OffScreenImage; import sun.security.action.GetPropertyAction; import sun.swing.SwingUtilities2.RepaintListener; @@ -78,8 +80,10 @@ private Component component; private JPanel contentPane; - private BufferedImage bbImage; + private OffScreenImage bbImage; + private volatile int scaleFactor = 1; + /** * {@code copyBufferEnabled}, true by default, defines the following strategy. * A duplicating (copy) buffer is created for the original pixel buffer. @@ -90,7 +94,7 @@ * by the lock (managed with the {@link LightweightContent#paintLock()}, * {@link LightweightContent#paintUnlock()} methods). */ - private boolean copyBufferEnabled; + private static boolean copyBufferEnabled; private int[] copyBuffer; private PropertyChangeListener layoutSizeListener; @@ -103,6 +107,8 @@ frame.updateClientCursor(); } }); + copyBufferEnabled = "true".equals(AccessController. + doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); } /** @@ -111,9 +117,7 @@ */ public JLightweightFrame() { super(); - copyBufferEnabled = "true".equals(AccessController. - doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); - + add(rootPane, BorderLayout.CENTER); setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); if (getGraphicsConfiguration().isTranslucencyCapable()) { @@ -220,19 +224,54 @@ public void ungrabFocus() { if (content != null) content.focusUngrabbed(); } + + @Override + public void setScaleFactor(int scaleFactor) { + if (scaleFactor != this.scaleFactor) { + if (!copyBufferEnabled) content.paintLock(); + try { + if (bbImage != null) { + resizeBuffer(getWidth(), getHeight(), scaleFactor); + } + } finally { + if (!copyBufferEnabled) content.paintUnlock(); + } + this.scaleFactor = scaleFactor; + if (getPeer() != null) { + ((FramePeer)getPeer()).notifyScaleFactorChanged(); + } + repaint(); + } + } + + @Override + public void addNotify() { + super.addNotify(); + ((FramePeer)getPeer()).notifyScaleFactorChanged(); + } + + @Override + public int getScaleFactor() { + return scaleFactor; + } - private void syncCopyBuffer(boolean reset, int x, int y, int w, int h) { + private void syncCopyBuffer(boolean reset, int x, int y, int w, int h, int scale) { content.paintLock(); try { int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); if (reset) { copyBuffer = new int[srcBuffer.length]; } - int linestride = bbImage.getWidth(); - + int linestride = bbImage.getWidth() * scale; + + x *= scale; + y *= scale; + w *= scale; + h *= scale; + for (int i=0; i= newW) && (oldH >= newH)) { - createBB = false; - } else { - if (oldW >= newW) { - newW = oldW; + try { + boolean createBB = (bbImage == null); + int newW = width; + int newH = height; + if (bbImage != null) { + int imgWidth = bbImage.getWidth(); + int imgHeight = bbImage.getHeight(); + if (width != imgWidth || height != imgHeight) { + createBB = true; + if (bbImage != null) { + int oldW = imgWidth; + int oldH = imgHeight; + if ((oldW >= newW) && (oldH >= newH)) { + createBB = false; } else { - newW = Math.max((int)(oldW * 1.2), width); - } - if (oldH >= newH) { - newH = oldH; - } else { - newH = Math.max((int)(oldH * 1.2), height); + if (oldW >= newW) { + newW = oldW; + } else { + newW = Math.max((int)(oldW * 1.2), width); + } + if (oldH >= newH) { + newH = oldH; + } else { + newH = Math.max((int)(oldH * 1.2), height); + } } } } - if (createBB) { - BufferedImage oldBB = bbImage; - bbImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB_PRE); - if (oldBB != null) { - Graphics g = bbImage.getGraphics(); - try { - g.drawImage(oldBB, 0, 0, newW, newH, null); - } finally { - g.dispose(); - oldBB.flush(); - } - } - int[] pixels = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); - if (copyBufferEnabled) { - syncCopyBuffer(true, 0, 0, width, height); - pixels = copyBuffer; - } - content.imageBufferReset(pixels, 0, 0, width, height, bbImage.getWidth()); - return; - } + } + if (createBB) { + resizeBuffer(newW, newH, scaleFactor); + return; } content.imageReshaped(0, 0, width, height); @@ -374,6 +403,21 @@ } } } + + private void resizeBuffer(int width, int height, int newScaleFactor) { + bbImage = new OffScreenImage(this, width, height, + BufferedImage.TYPE_INT_ARGB_PRE, + newScaleFactor); + bbImage.setReturnLayoutSize(true); + + int[] pixels = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); + if (copyBufferEnabled) { + syncCopyBuffer(true, 0, 0, width, height, newScaleFactor); + pixels = copyBuffer; + } + content.imageBufferReset(pixels, 0, 0, width, height, + width * newScaleFactor, newScaleFactor); + } @Override public JRootPane getRootPane() { @@ -410,7 +454,19 @@ return getRootPane().getGlassPane(); } - + /** + * Creates an {@link OffScreenImage} with a scale factor + * matching the {@link #getScaleFactor()} value. + * + * @param width the layout width of the image + * @param height the layout height of the image + * @return the HiDPI image + */ + public OffScreenImage createHiDPIImage(int width, int height) { + if (getPeer() == null) return null; + return ((FramePeer)getPeer()).createHiDPIImage(width, height); + } + /* * Notifies client toolkit that it should change a cursor. * --- old/src/share/classes/sun/swing/LightweightContent.java 2014-05-13 21:17:49.000000000 +0400 +++ new/src/share/classes/sun/swing/LightweightContent.java 2014-05-13 21:17:49.000000000 +0400 @@ -85,31 +85,57 @@ * {@code JLightweightFrame} calls this method to notify the client * application that a new data buffer has been set as a content pixel * buffer. Typically this occurs when a buffer of a larger size is - * created in response to a content resize event. The method reports - * a reference to the pixel data buffer, the content image bounds - * within the buffer and the line stride of the buffer. These values - * have the following correlation. + * created in response to a content resize event. *

- * The {@code width} and {@code height} matches the size of the content + * When a scale factor greater than one is set on a {@code JLightweightFrame} + * ({@link JLightweightFrame#setScaleFactor}), an {@link OffScreenHiDPIImage} + * is created to server the pixel buffer. + *

+ * The method reports a reference to the pixel data buffer, the content + * image bounds within the buffer and the line stride of the buffer. + * These values have the following correlation. + * The {@code width} and {@code height} matches the layout size of the content * (the component returned from the {@link #getComponent} method). The * {@code x} and {@code y} is the origin of the content, {@code (0, 0)} - * in the coordinate space of the content, appearing at - * {@code data[y * linestride + x]} in the buffer. All indices - * {@code data[(y + j) * linestride + (x + i)]} where - * {@code (0 <= i < width)} and {@code (0 <= j < height)} will represent - * valid pixel data, {@code (i, j)} in the coordinate space of the content. + * in the layout coordinate space of the content, appearing at + * {@code data[y * scale * linestride + x * scale]} in the buffer. + * A pixel with indices {@code (i, j)}, where {@code (0 <= i < width)} and + * {@code (0 <= j < height)}, in the layout coordinate space of the content + * is represented by a {@code scale^2} square of pixels in the physical + * coordinate space of the buffer. The top-left corner of the square has the + * following physical coordinate in the buffer: + * {@code data[(y + j) * scale * linestride + (x + i) * scale]}. * * @param data the content pixel data buffer of INT_ARGB_PRE type - * @param x the x coordinate of the image - * @param y the y coordinate of the image - * @param width the width of the image - * @param height the height of the image + * @param x the logical x coordinate of the image + * @param y the logical y coordinate of the image + * @param width the logical width of the image + * @param height the logical height of the image * @param linestride the line stride of the pixel buffer + * @param scale the scale factor of the pixel buffer + */ + default public void imageBufferReset(int[] data, + int x, int y, + int width, int height, + int linestride, + int scale) + { + imageBufferReset(data, x, y, width, height, linestride); + } + + /** + * The default implementation for #imageBufferReset uses a hard-coded value + * of 1 for the scale factor. Both the old and the new methods provide + * default implementations in order to allow a client application to run + * with any JDK version without breaking backward compatibility. */ - public void imageBufferReset(int[] data, + default public void imageBufferReset(int[] data, int x, int y, int width, int height, - int linestride); + int linestride) + { + imageBufferReset(data, x, y, width, height, linestride, 1); + } /** * {@code JLightweightFrame} calls this method to notify the client