From b867a6a016c89fe0cb6049dbaf3ca1ccdbcfb715 Mon Sep 17 00:00:00 2001 From: serb Date: Thu, 12 Jun 2014 00:32:33 +0400 Subject: [PATCH] 8029455: [JLightweightFrame] support scaled painting Reviewed-by: anthony, ant --- .../sun/lwawt/macosx/CPlatformLWWindow.java | 31 +++- .../classes/sun/awt/LightweightFrame.java | 45 ++++++ .../classes/sun/java2d/SunGraphics2D.java | 23 ++- .../classes/sun/swing/JLightweightFrame.java | 132 ++++++++++++------ .../classes/sun/swing/LightweightContent.java | 54 ++++--- .../ScaledCopyArea/ScaledCopyArea.java | 60 ++++++++ .../HangNonVolatileBuffer.java | 53 +++++++ 7 files changed, 331 insertions(+), 67 deletions(-) create mode 100644 test/java/awt/Graphics2D/ScaledCopyArea/ScaledCopyArea.java create mode 100644 test/javax/swing/JFrame/HangNonVolatileBuffer/HangNonVolatileBuffer.java diff --git a/src/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java b/src/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java index d7a912643..9c2edf227 100644 --- a/src/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java +++ b/src/macosx/classes/sun/lwawt/macosx/CPlatformLWWindow.java @@ -29,12 +29,18 @@ import java.awt.Font; 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; @@ -72,11 +78,6 @@ public class CPlatformLWWindow extends CPlatformWindow { return null; } - @Override - public GraphicsDevice getGraphicsDevice() { - return null; - } - @Override public SurfaceData getScreenSurface() { return null; @@ -199,4 +200,24 @@ public class CPlatformLWWindow extends CPlatformWindow { 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()).getHostBounds(); + 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(); + } } diff --git a/src/share/classes/sun/awt/LightweightFrame.java b/src/share/classes/sun/awt/LightweightFrame.java index 71e3dd303..85d05924a 100644 --- a/src/share/classes/sun/awt/LightweightFrame.java +++ b/src/share/classes/sun/awt/LightweightFrame.java @@ -31,6 +31,7 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.MenuBar; import java.awt.MenuComponent; +import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.peer.FramePeer; @@ -124,4 +125,48 @@ public abstract class LightweightFrame extends Frame { * @see SunToolkit#ungrab(java.awt.Window) */ public abstract void ungrabFocus(); + + /** + * Returns the scale factor of this frame. The default value is 1. + * + * @return the scale factor + * @see #notifyDisplayChanged(int) + */ + public abstract int getScaleFactor(); + + /** + * Called when display of the hosted frame is changed. + * + * @param scaleFactor the scale factor + */ + public abstract void notifyDisplayChanged(int scaleFactor); + + /** + * Host window absolute bounds. + */ + private int hostX, hostY, hostW, hostH; + + /** + * Returns the absolute bounds of the host (embedding) window. + * + * @return the host window bounds + */ + public Rectangle getHostBounds() { + if (hostX == 0 && hostY == 0 && hostW == 0 && hostH == 0) { + // The client app is probably unaware of the setHostBounds. + // A safe fall-back: + return getBounds(); + } + return new Rectangle(hostX, hostY, hostW, hostH); + } + + /** + * Sets the absolute bounds of the host (embedding) window. + */ + public void setHostBounds(int x, int y, int w, int h) { + hostX = x; + hostY = y; + hostW = w; + hostH = h; + } } diff --git a/src/share/classes/sun/java2d/SunGraphics2D.java b/src/share/classes/sun/java2d/SunGraphics2D.java index 287549c20..0ad5e1655 100644 --- a/src/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/share/classes/sun/java2d/SunGraphics2D.java @@ -2105,7 +2105,7 @@ public final class SunGraphics2D 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 @@ -2126,8 +2126,25 @@ public final class SunGraphics2D 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) { diff --git a/src/share/classes/sun/swing/JLightweightFrame.java b/src/share/classes/sun/swing/JLightweightFrame.java index abc701918..6566741de 100644 --- a/src/share/classes/sun/swing/JLightweightFrame.java +++ b/src/share/classes/sun/swing/JLightweightFrame.java @@ -54,6 +54,7 @@ import javax.swing.RepaintManager; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; +import sun.awt.DisplayChangedListener; import sun.awt.LightweightFrame; import sun.security.action.GetPropertyAction; import sun.swing.SwingUtilities2.RepaintListener; @@ -79,6 +80,8 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan private BufferedImage 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. @@ -89,7 +92,7 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan * 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; @@ -102,6 +105,8 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan frame.updateClientCursor(); } }); + copyBufferEnabled = "true".equals(AccessController. + doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); } /** @@ -143,7 +148,8 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan } Point p = SwingUtilities.convertPoint(c, x, y, jlf); Rectangle r = new Rectangle(p.x, p.y, w, h).intersection( - new Rectangle(0, 0, bbImage.getWidth(), bbImage.getHeight())); + new Rectangle(0, 0, bbImage.getWidth() / scaleFactor, + bbImage.getHeight() / scaleFactor)); if (!r.isEmpty()) { notifyImageUpdated(r.x, r.y, r.width, r.height); @@ -197,6 +203,7 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan g.setBackground(getBackground()); g.setColor(getForeground()); g.setFont(getFont()); + g.scale(scaleFactor, scaleFactor); return g; } @@ -220,7 +227,39 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan if (content != null) content.focusUngrabbed(); } - private void syncCopyBuffer(boolean reset, int x, int y, int w, int h) { + @Override + public int getScaleFactor() { + return scaleFactor; + } + + @Override + public void notifyDisplayChanged(final 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() instanceof DisplayChangedListener) { + ((DisplayChangedListener)getPeer()).displayChanged(); + } + repaint(); + } + + @Override + public void addNotify() { + super.addNotify(); + if (getPeer() instanceof DisplayChangedListener) { + ((DisplayChangedListener)getPeer()).displayChanged(); + } + } + + 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(); @@ -229,6 +268,11 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan } int linestride = bbImage.getWidth(); + x *= scale; + y *= scale; + w *= scale; + h *= scale; + for (int i=0; i= newW) && (oldH >= newH)) { - createBB = false; - } else { - if (oldW >= newW) { - newW = oldW; + boolean createBB = (bbImage == null); + int newW = width; + int newH = height; + if (bbImage != null) { + int imgWidth = bbImage.getWidth() / scaleFactor; + int imgHeight = bbImage.getHeight() / scaleFactor; + 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 (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(); + 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); + } } } - 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); } finally { @@ -373,6 +407,18 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan } } + private void resizeBuffer(int width, int height, int newScaleFactor) { + bbImage = new BufferedImage(width*newScaleFactor,height*newScaleFactor, + BufferedImage.TYPE_INT_ARGB_PRE); + 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() { return rootPane; diff --git a/src/share/classes/sun/swing/LightweightContent.java b/src/share/classes/sun/swing/LightweightContent.java index dd3373aea..2296a173f 100644 --- a/src/share/classes/sun/swing/LightweightContent.java +++ b/src/share/classes/sun/swing/LightweightContent.java @@ -85,31 +85,53 @@ public interface LightweightContent { * {@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 + * 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 diff --git a/test/java/awt/Graphics2D/ScaledCopyArea/ScaledCopyArea.java b/test/java/awt/Graphics2D/ScaledCopyArea/ScaledCopyArea.java new file mode 100644 index 000000000..c42529058 --- /dev/null +++ b/test/java/awt/Graphics2D/ScaledCopyArea/ScaledCopyArea.java @@ -0,0 +1,60 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +/** + * @test + * @bug 8029455 + * @summary Tests that copyarea on offscreen images works as expected when + * scaled transform is set + * @run main ScaledCopyArea + */ +public final class ScaledCopyArea { + + public static void main(final String[] args) { + final BufferedImage bi = new BufferedImage(100, 300, + BufferedImage.TYPE_INT_RGB); + final Graphics2D g = bi.createGraphics(); + g.scale(2, 2); + g.setColor(Color.RED); + g.fillRect(0, 0, 100, 300); + g.setColor(Color.GREEN); + g.fillRect(0, 100, 100, 100); + g.copyArea(0, 100, 100, 100, 0, -100); + g.dispose(); + for (int x = 0; x < 100; ++x) { + for (int y = 0; y < 100; ++y) { + final int actual = bi.getRGB(x, y); + final int exp = Color.GREEN.getRGB(); + if (actual != exp) { + System.err.println("Expected:" + Integer.toHexString(exp)); + System.err.println("Actual:" + Integer.toHexString(actual)); + throw new RuntimeException("Test " + "failed"); + } + } + } + } +} diff --git a/test/javax/swing/JFrame/HangNonVolatileBuffer/HangNonVolatileBuffer.java b/test/javax/swing/JFrame/HangNonVolatileBuffer/HangNonVolatileBuffer.java new file mode 100644 index 000000000..c61be1cb0 --- /dev/null +++ b/test/javax/swing/JFrame/HangNonVolatileBuffer/HangNonVolatileBuffer.java @@ -0,0 +1,53 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.InvocationTargetException; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/** + * @test + * @bug 8029455 + * @summary Swing should not hang if non-volatile image is used as a backbuffer. + * @run main/othervm -Dswing.volatileImageBufferEnabled=false HangNonVolatileBuffer + */ +public final class HangNonVolatileBuffer { + + private static JFrame f; + + public static void main(final String[] args) + throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("JFrame"); + f.setSize(300, 300); + f.setLocationRelativeTo(null); + f.setVisible(true); + }); + SwingUtilities.invokeAndWait(() -> { + // flush the EDT + }); + Thread.sleep(1000); + SwingUtilities.invokeAndWait(f::dispose); + } +} -- GitLab