8029455: [JLightweightFrame] support scaled painting
Reviewed-by: anthony, ant
This commit is contained in:
parent
4a9ad9f15f
commit
652e8b5ffb
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -2108,7 +2108,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
|
||||
@ -2129,8 +2129,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) {
|
||||
|
@ -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;
|
||||
@ -80,6 +81,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.
|
||||
@ -90,7 +93,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;
|
||||
@ -103,6 +106,8 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan
|
||||
frame.updateClientCursor();
|
||||
}
|
||||
});
|
||||
copyBufferEnabled = "true".equals(AccessController.
|
||||
doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true")));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,7 +149,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);
|
||||
@ -198,6 +204,7 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan
|
||||
g.setBackground(getBackground());
|
||||
g.setColor(getForeground());
|
||||
g.setFont(getFont());
|
||||
g.scale(scaleFactor, scaleFactor);
|
||||
return g;
|
||||
}
|
||||
|
||||
@ -221,7 +228,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();
|
||||
@ -230,6 +269,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<h; i++) {
|
||||
int from = (y + i) * linestride + x;
|
||||
System.arraycopy(srcBuffer, from, copyBuffer, from, w);
|
||||
@ -241,7 +285,7 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan
|
||||
|
||||
private void notifyImageUpdated(int x, int y, int width, int height) {
|
||||
if (copyBufferEnabled) {
|
||||
syncCopyBuffer(false, x, y, width, height);
|
||||
syncCopyBuffer(false, x, y, width, height, scaleFactor);
|
||||
}
|
||||
content.imageUpdated(x, y, width, height);
|
||||
}
|
||||
@ -269,7 +313,8 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyImageUpdated(clip.x, clip.y, clip.width, clip.height);
|
||||
Rectangle c = contentPane.getBounds().intersection(clip);
|
||||
notifyImageUpdated(c.x, c.y, c.width, c.height);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
@ -323,48 +368,37 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan
|
||||
content.paintLock();
|
||||
}
|
||||
try {
|
||||
if ((bbImage == null) || (width != bbImage.getWidth()) || (height != bbImage.getHeight())) {
|
||||
boolean createBB = true;
|
||||
int newW = width;
|
||||
int newH = height;
|
||||
if (bbImage != null) {
|
||||
int oldW = bbImage.getWidth();
|
||||
int oldH = bbImage.getHeight();
|
||||
if ((oldW >= 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 (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);
|
||||
|
||||
@ -375,6 +409,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;
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
public void imageBufferReset(int[] data,
|
||||
default public void imageBufferReset(int[] data,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
int linestride);
|
||||
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.
|
||||
*/
|
||||
default public void imageBufferReset(int[] data,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
int linestride)
|
||||
{
|
||||
imageBufferReset(data, x, y, width, height, linestride, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code JLightweightFrame} calls this method to notify the client
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user