From 6516a52d3da5cced924db63b64af911d55355325 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Tue, 27 Jan 2015 00:49:51 +0100 Subject: Bug 1120 - Refine HiDPI Support ( Part-2 ) (API CHANGE) - Use float[2] for pixel-scale. Utilize simple integer rounding: int-pixel-units = (int) ( int-window-units * pixel-scale + 0.5f ) - Provide minimum and maximum allowed pixel-scale values to be set by platform, supporting generic pixel-scale validation. - Remove 'OSXUtil.GetPixelScale(final RectangleImmutable r, final int[] screenIndexOut)', implementation for all platforms would cause huge redundancy of Screen and MonitorDevice code (duplication of NEWT). - instead, add 'float[2] pixelScale' to NEWT's MonitorDevice - Detect change of pixel-scale and propagate accordingly. This allows GLCanvas, GLJPanel and NewtCanvasAWT instances to be dragged between monitor devices w/ different pixel-scale. - OSX: Handle native triggered reshape events off-thread to avoid EDT congestion due to locked window when consuming deferred events on EDT. --- .../classes/javax/media/opengl/awt/GLCanvas.java | 103 +++++++++++----- .../classes/javax/media/opengl/awt/GLJPanel.java | 132 ++++++++++++++------- 2 files changed, 159 insertions(+), 76 deletions(-) (limited to 'src/jogl/classes/javax/media/opengl') diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index a648e3bf6..a3b84a838 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -178,9 +178,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private volatile GLContextImpl context; // volatile: avoid locking for read-only access private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking - private final int[] nativePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; // copy of the cstr args, mainly for recreation private final GLCapabilitiesImmutable capsReqUser; @@ -642,38 +643,73 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } @Override - public final void setSurfaceScale(final int[] pixelScale) { - SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null); - if( isRealized() ) { - final ScalableSurface ns = jawtWindow; - if( null != ns ) { - ns.setSurfaceScale(reqPixelScale); - final int hadPixelScaleX = hasPixelScale[0]; - final int hadPixelScaleY = hasPixelScale[1]; - ns.getCurrentSurfaceScale(hasPixelScale); - if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) { - reshapeImpl(getWidth(), getHeight()); - display(); - } - } + public final boolean setSurfaceScale(final float[] pixelScale) { + System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); + if( isRealized() && isShowing ) { + Threading.invoke(true, setSurfaceScaleOnEDTAction, getTreeLock()); + return true; + } else { + return false; + } + } + private final Runnable setSurfaceScaleOnEDTAction = new Runnable() { + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( null != drawable && drawable.isRealized() ) { + if( setSurfaceScaleImpl(jawtWindow) ) { + reshapeImpl(getWidth(), getHeight()); + if( !helper.isAnimatorAnimatingOnOtherThread() ) { + helper.invokeGL(drawable, context, displayAction, initAction); // display + } + } + } + } finally { + _lock.unlock(); + } + } }; + private final boolean setSurfaceScaleImpl(final ScalableSurface ns) { + if( ns.setSurfaceScale(reqPixelScale) ) { + ns.getCurrentSurfaceScale(hasPixelScale); + return true; + } else { + return false; + } + } + + private final boolean updatePixelScale() { + if( jawtWindow.hasPixelScaleChanged() ) { + jawtWindow.getMaximumSurfaceScale(maxPixelScale); + jawtWindow.getMinimumSurfaceScale(minPixelScale); + return setSurfaceScaleImpl(jawtWindow); + } else { + return false; } } @Override - public final int[] getRequestedSurfaceScale(final int[] result) { + public final float[] getRequestedSurfaceScale(final float[] result) { System.arraycopy(reqPixelScale, 0, result, 0, 2); return result; } @Override - public final int[] getCurrentSurfaceScale(final int[] result) { + public final float[] getCurrentSurfaceScale(final float[] result) { System.arraycopy(hasPixelScale, 0, result, 0, 2); return result; } @Override - public int[] getNativeSurfaceScale(final int[] result) { - System.arraycopy(nativePixelScale, 0, result, 0, 2); + public float[] getMinimumSurfaceScale(final float[] result) { + System.arraycopy(minPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public float[] getMaximumSurfaceScale(final float[] result) { + System.arraycopy(maxPixelScale, 0, result, 0, 2); return result; } @@ -681,13 +717,14 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing if ( !Beans.isDesignTime() ) { jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); - jawtWindow.setSurfaceScale(reqPixelScale); jawtWindow.lockSurface(); try { + jawtWindow.setSurfaceScale(reqPixelScale); drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); createContextImpl(drawable); jawtWindow.getCurrentSurfaceScale(hasPixelScale); - jawtWindow.getNativeSurfaceScale(nativePixelScale); + jawtWindow.getMinimumSurfaceScale(minPixelScale); + jawtWindow.getMaximumSurfaceScale(maxPixelScale); } finally { jawtWindow.unlockSurface(); } @@ -785,10 +822,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing reshapeImpl(width, height); } } - private void reshapeImpl(final int width, final int height) { - final int scaledWidth = width * hasPixelScale[0]; - final int scaledHeight = height * hasPixelScale[1]; + final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]); + final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]); if(DEBUG) { final NativeSurface ns = getNativeSurface(); @@ -1187,12 +1223,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public int getSurfaceWidth() { - return getWidth() * hasPixelScale[0]; + return SurfaceScaleUtils.scale(getWidth(), hasPixelScale[0]); } @Override public int getSurfaceHeight() { - return getHeight() * hasPixelScale[1]; + return SurfaceScaleUtils.scale(getHeight(), hasPixelScale[1]); } @Override @@ -1239,7 +1275,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // Internals only below this point // - private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; } + private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; } private final Runnable destroyOnEDTAction = new Runnable() { @Override @@ -1339,8 +1375,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; if(null != awtConfig) { final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); @@ -1391,6 +1429,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing _lock.lock(); try { if( null != drawable && drawable.isRealized() ) { + if( GLCanvas.this.updatePixelScale() ) { + GLCanvas.this.reshapeImpl(getWidth(), getHeight()); + } helper.invokeGL(drawable, context, displayAction, initAction); } } finally { diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 6e9e28c19..65c4c5537 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -262,9 +262,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private boolean handleReshape = false; private boolean sendReshape = true; - private final int[] nativePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - private final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; /** For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width in pixel units (scaled) */ private int reshapeWidth; @@ -450,20 +451,20 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public void display() { - if( isShowing || ( printActive && isVisible() ) ) { - if (EventQueue.isDispatchThread()) { - // Want display() to be synchronous, so call paintImmediately() - paintImmediately(0, 0, getWidth(), getHeight()); - } else { - // Multithreaded redrawing of Swing components is not allowed, - // so do everything on the event dispatch thread - try { - EventQueue.invokeAndWait(paintImmediatelyAction); - } catch (final Exception e) { - throw new GLException(e); + if( isShowing || ( printActive && isVisible() ) ) { + if (EventQueue.isDispatchThread()) { + // Want display() to be synchronous, so call paintImmediately() + paintImmediatelyAction.run(); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + EventQueue.invokeAndWait(paintImmediatelyAction); + } catch (final Exception e) { + throw new GLException(e); + } } - } - } + } } protected void dispose(final Runnable post) { @@ -556,6 +557,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // re-creating it -- tricky to do properly while the context is // current if( !printActive ) { + updatePixelScale(backend); if ( handleReshape ) { handleReshape = false; sendReshape = handleReshape(); @@ -579,36 +581,76 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override - public final void setSurfaceScale(final int[] pixelScale) { // HiDPI support - SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null); - final int hadPixelScaleX = hasPixelScale[0]; - final int hadPixelScaleY = hasPixelScale[1]; - SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, nativePixelScale, DEBUG ? getClass().getSimpleName() : null); - if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) { - reshapeImpl(getWidth(), getHeight()); - final Backend b = backend; - if ( isInitialized && null != b ) { - updateWrappedSurfaceScale(b.getDrawable()); - display(); + public final boolean setSurfaceScale(final float[] pixelScale) { // HiDPI support + System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); + final Backend b = backend; + if ( isInitialized && null != b && isShowing ) { + if( isShowing || ( printActive && isVisible() ) ) { + if (EventQueue.isDispatchThread()) { + setSurfaceScaleAction.run(); + } else { + try { + EventQueue.invokeAndWait(setSurfaceScaleAction); + } catch (final Exception e) { + throw new GLException(e); + } + } } + return true; + } else { + return false; + } + } + private final Runnable setSurfaceScaleAction = new Runnable() { + @Override + public void run() { + final Backend b = backend; + if( null != b && setSurfaceScaleImpl(b) ) { + if( !helper.isAnimatorAnimatingOnOtherThread() ) { + paintImmediatelyAction.run(); // display + } + } + } + }; + + private final boolean setSurfaceScaleImpl(final Backend b) { + if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null) ) { + reshapeImpl(getWidth(), getHeight()); + updateWrappedSurfaceScale(b.getDrawable()); + return true; + } + return false; + } + + private final boolean updatePixelScale(final Backend b) { + if( JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale) ) { + return setSurfaceScaleImpl(b); + } else { + return false; } } @Override - public final int[] getRequestedSurfaceScale(final int[] result) { + public final float[] getRequestedSurfaceScale(final float[] result) { System.arraycopy(reqPixelScale, 0, result, 0, 2); return result; } @Override - public final int[] getCurrentSurfaceScale(final int[] result) { + public final float[] getCurrentSurfaceScale(final float[] result) { System.arraycopy(hasPixelScale, 0, result, 0, 2); return result; } @Override - public int[] getNativeSurfaceScale(final int[] result) { - System.arraycopy(nativePixelScale, 0, result, 0, 2); + public float[] getMinimumSurfaceScale(final float[] result) { + System.arraycopy(minPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public float[] getMaximumSurfaceScale(final float[] result) { + System.arraycopy(maxPixelScale, 0, result, 0, 2); return result; } @@ -624,12 +666,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing awtWindowClosingProtocol.addClosingListener(); // HiDPI support - { - final int ps = JAWTUtil.getPixelScale(getGraphicsConfiguration()); - nativePixelScale[0] = ps; - nativePixelScale[1] = ps; - } - SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, nativePixelScale, DEBUG ? getClass().getSimpleName() : null); + JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale); + SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null); if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.addNotify()"); @@ -649,8 +687,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing dispose(null); hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; super.removeNotify(); } @@ -670,8 +710,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } private void reshapeImpl(final int width, final int height) { - final int scaledWidth = width * hasPixelScale[0]; - final int scaledHeight = height * hasPixelScale[1]; + final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]); + final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]); if( !printActive && ( handleReshape || scaledWidth != panelWidth || scaledHeight != panelHeight ) ) { reshapeWidth = scaledWidth; reshapeHeight = scaledHeight; @@ -826,8 +866,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! final int awtWidth = GLJPanel.this.getWidth(); final int awtHeight= GLJPanel.this.getHeight(); - final int scaledAWTWidth = awtWidth * hasPixelScale[0]; - final int scaledAWTHeight= awtHeight * hasPixelScale[1]; + final int scaledAWTWidth = SurfaceScaleUtils.scale(awtWidth, hasPixelScale[0]); + final int scaledAWTHeight= SurfaceScaleUtils.scale(awtHeight, hasPixelScale[1]); final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable(); if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight || drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) { @@ -1342,7 +1382,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } - private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; } + private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; } @Override public WindowClosingMode getDefaultCloseOperation() { @@ -2058,7 +2098,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount); } // Draw resulting image in one shot - g.drawImage(alignedImage, 0, 0, alignedImage.getWidth()/hasPixelScale[0], alignedImage.getHeight()/hasPixelScale[1], null); // Null ImageObserver since image data is ready. + g.drawImage(alignedImage, 0, 0, + SurfaceScaleUtils.scaleInv(alignedImage.getWidth(), hasPixelScale[0]), + SurfaceScaleUtils.scaleInv(alignedImage.getHeight(), hasPixelScale[1]), null); // Null ImageObserver since image data is ready. } frameCount++; } -- cgit v1.2.3