diff options
author | Sven Gothel <[email protected]> | 2014-06-08 08:11:57 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-06-08 08:11:57 +0200 |
commit | 2571ed0b5ef14155d204540d38b564a7d4cd47b6 (patch) | |
tree | 8aaf1171af1b95f1cb1ebe706771a4aff3752c2f | |
parent | ff7bf3122fd2007bbe70cfadca9f0f978ee96456 (diff) |
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
Add ScalableSurface interface
- To set pixelScale before and after realization
- To get pixelScale
- Implemented on:
- NEWT Window
- Generic impl. in WindowImpl
- OSX WindowDriver impl.
- Also propagetes pixelScale to parent JAWTWindow if offscreen (NewtCanvasAWT)
- AWT WindowDriver impl.
- JAWTWindow / OSXCalayer
- AWT GLCanvas
- AWT GLJPanel
- NEWTCanvasAWT:
- Propagates NEWT Window's pixelScale to underlying JAWTWindow
- WrappedSurface for pixelScale propagation
using offscreen drawables, i.e. GLJPanel
- Generic helper in SurfaceScaleUtils (nativewindow package)
- Fully implemented on OSX
- Capable to switch pixelScale before realization,
i.e. native-creation, as well as on-the-fly.
- Impl. uses int[2] for pixelScale to support
non-uniform scale.
Test cases:
- com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT
- com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT
- com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT
- com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT
- Press 'x' to toggle HiDPI
- Commandline '-pixelScale <value>'
- Added basic auto unit test (setting pre-realization)
25 files changed, 1098 insertions, 241 deletions
diff --git a/.gitignore b/.gitignore index 1cd3375b4..71e3b7e68 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build javadoc_public build-temp make/temp* +temp/* .idea/workspace.xml .settings/* **~ diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 1d2234ad9..3c79ebba6 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -206,7 +206,7 @@ function jrun() { #D_ARGS="-Dnewt.debug.EDT" #D_ARGS="-Dnewt.debug.Window -Dnewt.debug.Display -Dnewt.debug.EDT -Djogl.debug.GLContext" #D_ARGS="-Dnewt.debug.Window -Djogl.debug.Animator -Dnewt.debug.Screen" - #D_ARGS="-Dnativewindow.debug.JAWT -Dnewt.debug.Window" + #D_ARGS="-Dnativewindow.debug.JAWT -Dnewt.debug.Window -Djogl.debug.GLJPanel -Djogl.debug.GLCanvas" #D_ARGS="-Dnewt.debug.Window.KeyEvent" #D_ARGS="-Dnewt.debug.Window.MouseEvent" #D_ARGS="-Dnewt.debug.Window.MouseEvent -Dnewt.debug.Window.KeyEvent" @@ -358,6 +358,15 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestVersionSemanticsNOUI $* # +# HiDPI +# +#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $* +#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $* +#testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $* +testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $* +#testawt com.jogamp.opengl.test.junit.jogl.awt.ManualHiDPIBufferedImage01AWT $* + +# # demos (any TK, more user driven tests) # #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestGearsES1NEWT $* @@ -365,8 +374,7 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es1.newt.TestRedSquareES1NEWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2AWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelAWT $* -#testawt com.jogamp.opengl.test.junit.jogl.awt.TestHiDPIBufferedImage01AWT $* -#testawt com.jogamp.opengl.test.junit.jogl.awt.TestHiDPIBufferedImage02AWT $* +#testawt com.jogamp.opengl.test.junit.jogl.awt.ManualHiDPIBufferedImage01AWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.awt.TestGearsES2GLJPanelsAWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasAWT $* #testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestLandscapeES2NewtCanvasAWT $* @@ -763,7 +771,7 @@ function testawtswt() { #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUTextNewtDemo $* #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPURegionNewtDemo $* #testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtDemo $* -testawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtCanvasAWTDemo $* +#testawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtCanvasAWTDemo $* #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube $* #testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple $* diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index d06b61624..bdd12dfea 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -63,6 +63,7 @@ import java.util.List; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.OffscreenLayerOption; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -101,6 +102,7 @@ import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.util.GLDrawableUtil; import com.jogamp.opengl.util.TileRenderer; +import jogamp.nativewindow.SurfaceScaleUtils; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; @@ -156,7 +158,8 @@ import jogamp.opengl.awt.AWTTilePainter; */ @SuppressWarnings("serial") -public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle, GLSharedContextSetter { +public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, + AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface { private static final boolean DEBUG = Debug.debug("GLCanvas"); @@ -167,7 +170,8 @@ 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 volatile int pixelScale; + private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; // copy of the cstr args, mainly for recreation private final GLCapabilitiesImmutable capsReqUser; @@ -308,7 +312,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing this.addHierarchyListener(hierarchyListener); this.isShowing = isShowing(); - this.pixelScale = 1; } @Override @@ -659,15 +662,45 @@ 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.getSurfaceScale(hasPixelScale); + if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) { + reshapeImpl(getWidth(), getHeight()); + display(); + } + } + } + } + + @Override + public final int[] getSurfaceScale(final int[] result) { + final ScalableSurface ns = jawtWindow; + if( null != ns ) { + return ns.getSurfaceScale(result); + } else { + System.arraycopy(reqPixelScale, 0, result, 0, 2); + return result; + } + } + private void createJAWTDrawableAndContext() { if ( !Beans.isDesignTime() ) { jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); + jawtWindow.setSurfaceScale(reqPixelScale); jawtWindow.lockSurface(); try { drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); createContextImpl(drawable); - pixelScale = jawtWindow.getPixelScale(); + jawtWindow.getSurfaceScale(hasPixelScale); } finally { jawtWindow.unlockSurface(); } @@ -762,37 +795,39 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing public void reshape(int x, int y, int width, int height) { synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape super.reshape(x, y, width, height); + reshapeImpl(width, height); + } + } - final int scale = getPixelScale(); - final int scaledWidth = scale * width; - final int scaledHeight = scale * height; + private void reshapeImpl(final int width, final int height) { + final int scaledWidth = width * hasPixelScale[0]; + final int scaledHeight = height * hasPixelScale[1]; - if(DEBUG) { - final NativeSurface ns = getNativeSurface(); - final long nsH = null != ns ? ns.getSurfaceHandle() : 0; - System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+ - " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+scale+ - "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+scale+" -> "+scaledWidth+"x"+scaledHeight+ - " - surfaceHandle 0x"+Long.toHexString(nsH)); - // Thread.dumpStack(); - } - if( validateGLDrawable() && !printActive ) { - final GLDrawableImpl _drawable = drawable; - if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { - final RecursiveLock _lock = lock; - _lock.lock(); - try { - final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight); - if(_drawable != _drawableNew) { - // write back - drawable = _drawableNew; - } - } finally { - _lock.unlock(); + if(DEBUG) { + final NativeSurface ns = getNativeSurface(); + final long nsH = null != ns ? ns.getSurfaceHandle() : 0; + System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+ + " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+ + "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight+ + " - surfaceHandle 0x"+Long.toHexString(nsH)); + // Thread.dumpStack(); + } + if( validateGLDrawable() && !printActive ) { + final GLDrawableImpl _drawable = drawable; + if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, scaledWidth, scaledHeight); + if(_drawable != _drawableNew) { + // write back + drawable = _drawableNew; } + } finally { + _lock.unlock(); } - sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock } } @@ -1133,12 +1168,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @Override public int getSurfaceWidth() { - return getWidth() * getPixelScale(); + return getWidth() * hasPixelScale[0]; } @Override public int getSurfaceHeight() { - return getHeight() * getPixelScale(); + return getHeight() * hasPixelScale[1]; } @Override @@ -1185,7 +1220,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // Internals only below this point // - private final int getPixelScale() { return pixelScale; } + private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; } private final Runnable destroyOnEDTAction = new Runnable() { @Override @@ -1269,7 +1304,8 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } jawtWindow=null; } - pixelScale = 1; + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; if(null != awtConfig) { final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index e712f2fa2..18de5a791 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -60,6 +60,7 @@ import java.util.List; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.opengl.GL; @@ -83,6 +84,8 @@ import javax.media.opengl.GLSharedContextSetter; import javax.media.opengl.Threading; import javax.swing.JPanel; +import jogamp.nativewindow.SurfaceScaleUtils; +import jogamp.nativewindow.WrappedSurface; import jogamp.nativewindow.jawt.JAWTUtil; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; @@ -169,7 +172,7 @@ import com.jogamp.opengl.util.texture.TextureState; */ @SuppressWarnings("serial") -public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter { +public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface { private static final boolean DEBUG; private static final boolean DEBUG_VIEWPORT; private static final boolean USE_GLSL_TEXTURE_RASTERIZER; @@ -244,7 +247,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private boolean handleReshape = false; private boolean sendReshape = true; - private volatile int pixelScale; + private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private volatile int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; // For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width private int reshapeWidth; @@ -368,7 +372,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing this.setFocusable(true); // allow keyboard input! this.addHierarchyListener(hierarchyListener); this.isShowing = isShowing(); - this.pixelScale = 1; } /** @@ -483,7 +486,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing animator.resume(); } } - pixelScale = 1; + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; if(DEBUG) { System.err.println(getThreadName()+": GLJPanel.dispose() - stop"); @@ -552,6 +556,40 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } + private final void updateWrappedSurfaceScale(final GLDrawable d) { + final NativeSurface s = d.getNativeSurface(); + if( s instanceof WrappedSurface ) { + ((WrappedSurface)s).setSurfaceScale( hasPixelScale ); + } + } + + @Override + public final void setSurfaceScale(final int[] pixelScale) { // HiDPI support + SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null); + final Backend b = backend; + if ( isInitialized && null != b ) { + final int[] pixelScaleInt; + { + final int ps = JAWTUtil.getPixelScale(getGraphicsConfiguration()); + pixelScaleInt = new int[] { ps, ps }; + } + final int hadPixelScaleX = hasPixelScale[0]; + final int hadPixelScaleY = hasPixelScale[1]; + SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG ? getClass().getSimpleName() : null); + if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) { + updateWrappedSurfaceScale(b.getDrawable()); + reshapeImpl(getWidth(), getHeight()); + display(); + } + } + } + + @Override + public final int[] getSurfaceScale(final int[] result) { + System.arraycopy(isInitialized ? hasPixelScale : reqPixelScale, 0, result, 0, 2); + return result; + } + /** Overridden to track when this component is added to a container. Subclasses which override this method must call super.addNotify() in their addNotify() method in order to @@ -562,10 +600,15 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing public void addNotify() { super.addNotify(); awtWindowClosingProtocol.addClosingListener(); + + // HiDPI support + final int[] pixelScaleInt; { - final int s = JAWTUtil.getPixelScale(getGraphicsConfiguration()); - pixelScale = 0 < s ? s : 1; + final int ps = JAWTUtil.getPixelScale(getGraphicsConfiguration()); + pixelScaleInt = new int[] { ps, ps }; } + SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG ? getClass().getSimpleName() : null); + if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.addNotify()"); } @@ -596,16 +639,18 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); + reshapeImpl(width, height); + } - final int scale = getPixelScale(); - final int scaledWidth = scale * width; - final int scaledHeight = scale * height; + private void reshapeImpl(final int width, final int height) { + final int scaledWidth = width * hasPixelScale[0]; + final int scaledHeight = height * hasPixelScale[1]; if( DEBUG ) { System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+ - " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+scale+ + " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+ ", panel "+panelWidth+"x"+panelHeight + ", reshape: " +reshapeWidth+"x"+reshapeHeight + - "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+scale+" -> "+scaledWidth+"x"+scaledHeight); + "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight); } if( !printActive ) { reshapeWidth = scaledWidth; @@ -724,17 +769,16 @@ 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 scale = getPixelScale(); - final int scaledAWTWidth = scale * awtWidth; - final int scaledAWTHeight= scale * awtHeight; + final int scaledAWTWidth = awtWidth * hasPixelScale[0]; + final int scaledAWTHeight= awtHeight * hasPixelScale[1]; final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable(); if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight || drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) { // -> !( awtSize == panelSize == drawableSize ) if ( DEBUG ) { - System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScale()+ + System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+ ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+ - " -> " + awtWidth+"x"+awtHeight+" * "+scale+" -> "+scaledAWTWidth+"x"+scaledAWTHeight); + " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight); } reshapeWidth = scaledAWTWidth; reshapeHeight = scaledAWTHeight; @@ -1020,12 +1064,12 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public int getSurfaceWidth() { - return getWidth() * getPixelScale(); + return panelWidth; // FIXME HiDPI: Accurate or: getWidth() * hasPixelScale[0]; } @Override public int getSurfaceHeight() { - return getHeight() * getPixelScale(); + return panelHeight; // FIXME HiDPI: Accurate or: getHeight() * hasPixelScale[1]; } /** @@ -1153,8 +1197,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend: " + - panelWidth+"x"+panelHeight+" @ scale "+getPixelScale() + " -> " + - reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScale()); + panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " + + reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr()); } // Pull down reshapeWidth and reshapeHeight into panelWidth and // panelHeight eagerly in order to complete initialization, and @@ -1182,7 +1226,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } - private final int getPixelScale() { return pixelScale; } + private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; } @Override public WindowClosingMode getDefaultCloseOperation() { @@ -1197,8 +1241,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private boolean handleReshape() { if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.handleReshape: "+ - panelWidth+"x"+panelHeight+" @ scale "+getPixelScale() + " -> " + - reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScale()); + panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " + + reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr()); } panelWidth = reshapeWidth; panelHeight = reshapeHeight; @@ -1236,7 +1280,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } if (sendReshape) { if (DEBUG) { - System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + " @ scale "+getPixelScale()+")"); + System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + " @ scale "+getPixelScaleStr()+")"); } helper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight); sendReshape = false; @@ -1484,6 +1528,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing offscreenCaps, chooser, panelWidth, panelHeight); + updateWrappedSurfaceScale(offscreenDrawable); offscreenDrawable.setRealized(true); if( DEBUG ) { offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { @@ -1675,7 +1720,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBufferProvider isSingletonBufferProvider "+useSingletonBuffer+", 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName()); System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment); System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster)); - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScale()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr()); } } if( offscreenDrawable.getSurfaceWidth() != panelWidth || offscreenDrawable.getSurfaceHeight() != panelHeight ) { @@ -1686,7 +1731,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing !pixelBuffer.isDataBufferSource(alignedImage) ) { alignedImage = pixelBuffer.getAlignedImage(panelWidth, panelHeight); if(DEBUG) { - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+" @ scale "+getPixelScale()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+" @ scale "+getPixelScaleStr()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer); } } final IntBuffer readBackInts; @@ -1813,7 +1858,7 @@ 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()/getPixelScale(), alignedImage.getHeight()/getPixelScale(), null); // Null ImageObserver since image data is ready. + g.drawImage(alignedImage, 0, 0, alignedImage.getWidth()/hasPixelScale[0], alignedImage.getHeight()/hasPixelScale[1], null); // Null ImageObserver since image data is ready. } frameCount++; } @@ -1832,10 +1877,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // write back _drawable = _drawableNew; offscreenDrawable = _drawableNew; + updateWrappedSurfaceScale(offscreenDrawable); } } if (DEBUG) { - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScale() + " -> " + _drawable.getSurfaceWidth()+"x"+_drawable.getSurfaceHeight()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr() + " -> " + _drawable.getSurfaceWidth()+"x"+_drawable.getSurfaceHeight()); } panelWidth = _drawable.getSurfaceWidth(); panelHeight = _drawable.getSurfaceHeight(); diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m index fb6fd18e9..652b0545e 100644 --- a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m +++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m @@ -292,13 +292,17 @@ static const GLfloat gl_verts[] = { gl_texCoords[i] = 0.0f; } } - if( _texWidth != _winWidth ) { + /** + * Set via + * - OSXUtil_SetCALayerPixelScale0 + * - OSXUtil_AddCASublayer0 NS_DURING - // Available >= 10.7 - [self setContentsScale: (CGFloat)_texWidth/(CGFloat)_winWidth]; + // Available >= 10.7 + [self setContentsScale: (CGFloat)_texWidth/(CGFloat)_winWidth]; NS_HANDLER NS_ENDHANDLER - } + */ + parentPixelFmt = [_parentPixelFmt retain]; // until destruction glContext = [[MyNSOpenGLContext alloc] initWithFormat:parentPixelFmt shareContext:parentCtx]; gl3ShaderProgramName = _gl3ShaderProgramName; diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java index 690d77901..efe9409ac 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java @@ -61,6 +61,7 @@ import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.OffscreenLayerOption; import javax.media.nativewindow.OffscreenLayerSurface; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; @@ -70,13 +71,14 @@ import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.Rectangle; import javax.media.nativewindow.util.RectangleImmutable; +import jogamp.nativewindow.SurfaceScaleUtils; import jogamp.nativewindow.SurfaceUpdatedHelper; import jogamp.nativewindow.awt.AWTMisc; import jogamp.nativewindow.jawt.JAWT; import jogamp.nativewindow.jawt.JAWTUtil; import jogamp.nativewindow.jawt.JAWT_Rectangle; -public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, OffscreenLayerOption { +public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, OffscreenLayerOption, ScalableSurface { protected static final boolean DEBUG = JAWTUtil.DEBUG; // user properties @@ -98,7 +100,10 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, protected Rectangle bounds; protected Insets insets; private volatile long offscreenSurfaceLayer; - private volatile int pixelScale; + + private volatile int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + protected final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + private long drawable_old; /** @@ -122,7 +127,6 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, invalidate(); this.isApplet = false; this.offscreenSurfaceLayer = 0; - this.pixelScale = 1; } private static String id(Object obj) { return ( null!=obj ? toHexString(obj.hashCode()) : "nil" ); } private String jawtStr() { return "JAWTWindow["+id(JAWTWindow.this)+"]"; } @@ -260,22 +264,37 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, drawable_old = 0; bounds = new Rectangle(); insets = new Insets(); - pixelScale = 1; + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; } protected abstract void invalidateNative(); /** + * {@inheritDoc} + * <p> + * Per default impl. only works for not yet {@link #isRealized() realized} instances, + * current exception OSX. + * </p> + */ + @Override + public void setSurfaceScale(final int[] pixelScale) { + SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null); + } + + @Override + public final int[] getSurfaceScale(final int[] result) { + // 0 != drawable -> locked at least once ! + System.arraycopy(0 != drawable ? hasPixelScale : reqPixelScale, 0, result, 0, 2); + return result; + } + + /** * Updates bounds and pixelScale + * @return true if bounds or pixelScale has changed, otherwise false */ protected final boolean updateLockedData(JAWT_Rectangle jawtBounds) { final Rectangle jb = new Rectangle(jawtBounds.getX(), jawtBounds.getY(), jawtBounds.getWidth(), jawtBounds.getHeight()); - final int newPixelScale; - { - final int s = JAWTUtil.getPixelScale(config.getAWTGraphicsConfiguration()); - newPixelScale = 0 < s ? s : 1; - } final boolean changedBounds = !bounds.equals(jb); - final boolean changedPixelScale = newPixelScale != pixelScale; if( changedBounds ) { if( DEBUG ) { @@ -288,20 +307,31 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, insets.set(contInsets.left, contInsets.right, contInsets.top, contInsets.bottom); } } - if( changedPixelScale ) { - if( DEBUG ) { - System.err.println("JAWTWindow.updatePixelScale: "+pixelScale+" -> "+newPixelScale); - } - pixelScale = newPixelScale; + + return updatePixelScale() || changedBounds; + } + + /** + * Update pixelScale + * @return true if pixelScale has changed, otherwise false + */ + protected final boolean updatePixelScale() { + final int[] pixelScaleInt; + { + final int ps = JAWTUtil.getPixelScale(config.getAWTGraphicsConfiguration()); + pixelScaleInt = new int[] { ps, ps }; } - return changedBounds || changedPixelScale; + return SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG ? getClass().getSimpleName() : null); } /** @return the JAWT_DrawingSurfaceInfo's (JAWT_Rectangle) bounds, updated with lock */ public final RectangleImmutable getBounds() { return bounds; } - /** @return the safe pixelScale value, i.e. never negative or zero. Updated with lock. */ - public final int getPixelScale() { return pixelScale; } + /** @return the safe pixelScale value for x-direction, i.e. never negative or zero. Updated with lock. */ + protected final int getPixelScaleX() { return hasPixelScale[0]; } + + /** @return the safe pixelScale value for y-direction, i.e. never negative or zero. Updated with lock. */ + protected final int getPixelScaleY() { return hasPixelScale[1]; } @Override public final InsetsImmutable getInsets() { return insets; } @@ -625,12 +655,26 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, @Override public final int getSurfaceWidth() { - return getWidth() * getPixelScale(); + return getWidth() * getPixelScaleX(); } @Override public final int getSurfaceHeight() { - return getHeight() * getPixelScale(); + return getHeight() * getPixelScaleY(); + } + + @Override + public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { + pixelUnitsAndResult[0] /= getPixelScaleX(); + pixelUnitsAndResult[1] /= getPixelScaleY(); + return pixelUnitsAndResult; + } + + @Override + public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { + windowUnitsAndResult[0] *= getPixelScaleX(); + windowUnitsAndResult[1] *= getPixelScaleY(); + return windowUnitsAndResult; } @Override @@ -651,22 +695,6 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } @Override - public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { - final int scale = getPixelScale(); - pixelUnitsAndResult[0] /= scale; - pixelUnitsAndResult[1] /= scale; - return pixelUnitsAndResult; - } - - @Override - public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { - final int scale = getPixelScale(); - windowUnitsAndResult[0] *= scale; - windowUnitsAndResult[1] *= scale; - return windowUnitsAndResult; - } - - @Override public void destroy() { surfaceLock.lock(); try { @@ -810,7 +838,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, append(", CA_LAYER: ").append(JAWTUtil.isJAWTUsingOffscreenLayer(jawt)). append(", isLayeredSurface ").append(isOffscreenLayerSurfaceEnabled()). append(", bounds ").append(bounds).append(", insets ").append(insets). - append(", pixelScale ").append(getPixelScale()); + append(", pixelScale ").append(getPixelScaleX()).append("x").append(getPixelScaleY()); } else { sb.append("JAWT n/a, bounds ").append(bounds).append(", insets ").append(insets); } @@ -830,7 +858,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, ", bounds "+bounds+", insets "+insets ); sb.append(", window ["+getX()+"/"+getY()+" "+getWidth()+"x"+getHeight()+ - "], pixels[x"+getPixelScale()+" -> "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+ + "], pixels[s "+getPixelScaleX()+"x"+getPixelScaleY()+" -> "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+ ", visible "+component.isVisible()); sb.append(", lockedExt "+isSurfaceLockedByOtherThread()+ ",\n\tconfig "+config+ diff --git a/src/nativewindow/classes/javax/media/nativewindow/ScalableSurface.java b/src/nativewindow/classes/javax/media/nativewindow/ScalableSurface.java new file mode 100644 index 000000000..024f22bc9 --- /dev/null +++ b/src/nativewindow/classes/javax/media/nativewindow/ScalableSurface.java @@ -0,0 +1,80 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package javax.media.nativewindow; + +/** + * Adding mutable surface pixel scale property to implementing class, usually to a {@link NativeSurface} implementation, + * see {@link #setSurfaceScale(int[])}. + */ +public interface ScalableSurface { + /** Setting surface-pixel-scale of {@value}, results in same pixel- and window-units. */ + public static final int IDENTITY_PIXELSCALE = 1; + /** Setting surface-pixel-scale of {@value}, results in maximum platform dependent pixel-scale, i.e. pixel-units >> window-units where available. */ + public static final int AUTOMAX_PIXELSCALE = 0; + + /** + * Request a pixel scale in x- and y-direction for the associated {@link NativeSurface}. + * <p> + * Default pixel scale request for both directions is {@link #AUTOMAX_PIXELSCALE}. + * </p> + * <p> + * In case platform only supports uniform pixel scale, i.e. one scale for both directions, + * either {@link #AUTOMAX_PIXELSCALE} or the maximum requested pixel scale component is used. + * </p> + * <p> + * The <i>requested</i> pixel scale will be validated against platform limits before native scale-setup, + * i.e. clipped to {@link #IDENTITY_PIXELSCALE} if not supported or the platform maximum. + * </p> + * <p> + * The actual <i>realized</i> pixel scale values of the {@link NativeSurface} + * can be queried via {@link #getSurfaceScale(int[])} or + * computed via <code>surface.{@link NativeSurface#convertToPixelUnits(int[]) convertToPixelUnits}(new int[] { 1, 1 })</code> + * </p> + * + * @param pixelScale <i>requested</i> surface pixel scale int[2] values for x- and y-direction. + */ + public void setSurfaceScale(final int[] pixelScale); + + /** + * Returns the pixel scale of the associated {@link NativeSurface}. + * <p> + * In case the {@link NativeSurface} is not yet realized, method returns the + * requested pixel scale as validated via {@link #setSurfaceScale(int[])} + * if called earlier or the implementation default, usually {@link #AUTOMAX_PIXELSCALE}. + * </p> + * <p> + * In case the {@link NativeSurface} is already realized, method returns the + * actual used pixel scale. + * </p> + * @param result int[2] storage for the result + * @return the passed storage for chaining + */ + public int[] getSurfaceScale(final int[] result); +} + diff --git a/src/nativewindow/classes/jogamp/nativewindow/SurfaceScaleUtils.java b/src/nativewindow/classes/jogamp/nativewindow/SurfaceScaleUtils.java new file mode 100644 index 000000000..22e67ecff --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/SurfaceScaleUtils.java @@ -0,0 +1,175 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.nativewindow; + +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.ScalableSurface; + +/** + * Basic {@link ScalableSurface} utility to validate and compute pixel-scale values. + */ +public class SurfaceScaleUtils { + + private static final int[] PlatformMaxPixelScale; + private static final boolean PlatformUniformPixelScale; + private static final boolean PlatformPixelScaleSupported; + + static { + if( NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(true) ) { + PlatformMaxPixelScale = new int[] { jogamp.nativewindow.macosx.OSXUtil.MAX_PIXELSCALE, jogamp.nativewindow.macosx.OSXUtil.MAX_PIXELSCALE }; + PlatformUniformPixelScale = true; + PlatformPixelScaleSupported = true; + } else { + PlatformMaxPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + PlatformUniformPixelScale = false; + PlatformPixelScaleSupported = false; + } + } + + /** + * Compute a new valid pixelScale to be used by {@link NativeSurface} implementations, + * based on the given request and surface's pixelScale + * + * @param result int[2] storage for result, maybe same as <code>prePixelScale</code> for in-place + * @param prePixelScale previous pixelScale + * @param reqPixelScale requested pixelScale, validated via {@link #validateReqPixelScale(int[], int, String)}. + * @param surfPixelScaleRaw raw surface pixelScale + * @param DEBUG_PREFIX if set, dumps debug info on stderr using this prefix + * @return true if pixelScale has changed, otherwise false + */ + public static boolean computePixelScale(int[] result, final int[] prePixelScale, final int[] reqPixelScale, final int[] surfPixelScaleRaw, final String DEBUG_PREFIX) { + final int surfPixelScaleSafeX = 0 < surfPixelScaleRaw[0] ? surfPixelScaleRaw[0] : ScalableSurface.IDENTITY_PIXELSCALE; + final int surfPixelScaleSafeY = 0 < surfPixelScaleRaw[1] ? surfPixelScaleRaw[1] : ScalableSurface.IDENTITY_PIXELSCALE; + final boolean useHiDPI = ScalableSurface.IDENTITY_PIXELSCALE != reqPixelScale[0] || ScalableSurface.IDENTITY_PIXELSCALE != reqPixelScale[1]; + final int prePixelScaleX = prePixelScale[0]; + final int prePixelScaleY = prePixelScale[1]; + + if( useHiDPI ) { + result[0] = surfPixelScaleSafeX; + result[1] = surfPixelScaleSafeY; + } else { + result[0] = ScalableSurface.IDENTITY_PIXELSCALE; + result[1] = ScalableSurface.IDENTITY_PIXELSCALE; + } + + if( result[0] != prePixelScaleX || result[1] != prePixelScaleY ) { + if( null != DEBUG_PREFIX ) { + System.err.println(DEBUG_PREFIX+".computePixelScale: useHiDPI "+useHiDPI+", ["+prePixelScaleX+"x"+prePixelScaleY+" (pre), "+ + reqPixelScale[0]+"x"+reqPixelScale[1]+" (req)] -> "+ + surfPixelScaleRaw[0]+"x"+surfPixelScaleRaw[1]+" (raw) -> "+ + surfPixelScaleSafeX+"x"+surfPixelScaleSafeY+" (safe) -> "+ + result[0]+"x"+result[1]+" (use)"); + } + return true; + } else { + return false; + } + } + + /** + * Validate the given requested pixelScale value pair, i.e. clip it to the + * limits of {@link ScalableSurface#AUTOMAX_PIXELSCALE} and {@link #getPlatformMaxPixelScale(int[])} + * <p> + * To be used by {@link ScalableSurface#setSurfaceScale(int[])} implementations. + * </p> + * + * @param result int[2] storage for result + * @param reqPixelScale requested pixelScale + * @param DEBUG_PREFIX if set, dumps debug info on stderr using this prefix + */ + public static void validateReqPixelScale(final int[] result, final int[] reqPixelScale, final String DEBUG_PREFIX) { + final int minPS = Math.min(reqPixelScale[0], reqPixelScale[1]); + if( ScalableSurface.AUTOMAX_PIXELSCALE >= minPS ) { + result[0] = ScalableSurface.AUTOMAX_PIXELSCALE; + result[1] = ScalableSurface.AUTOMAX_PIXELSCALE; + } else if( PlatformUniformPixelScale ) { + final int maxPS = Math.max(reqPixelScale[0], reqPixelScale[1]); + if( maxPS >= PlatformMaxPixelScale[0] ) { + result[0] = PlatformMaxPixelScale[0]; + result[1] = PlatformMaxPixelScale[1]; + } else { + result[0] = maxPS; + result[1] = maxPS; + } + } else { + if( reqPixelScale[0] >= PlatformMaxPixelScale[0] ) { + result[0] = PlatformMaxPixelScale[0]; + } else { + result[0] = reqPixelScale[0]; + } + if( reqPixelScale[1] >= PlatformMaxPixelScale[1] ) { + result[1] = PlatformMaxPixelScale[1]; + } else { + result[1] = reqPixelScale[1]; + } + } + if( null != DEBUG_PREFIX ) { + System.err.println(DEBUG_PREFIX+".validateReqPixelScale: ["+reqPixelScale[0]+"x"+reqPixelScale[1]+" (req), "+ + PlatformMaxPixelScale[0]+"x"+PlatformMaxPixelScale[1]+" (max)] -> "+ + result[0]+"x"+result[1]+" (valid)"); + } + } + + /** + * Replaces {@link ScalableSurface#AUTOMAX_PIXELSCALE} with {@link #getPlatformMaxPixelScale(int[])}, + * for each component. + * + * @param pixelScale int[2] value array to be tested and replaced + */ + public static void replaceAutoMaxWithPlatformMax(final int[] pixelScale) { + if( ScalableSurface.AUTOMAX_PIXELSCALE == pixelScale[0] ) { + pixelScale[0] = PlatformMaxPixelScale[0]; + } + if( ScalableSurface.AUTOMAX_PIXELSCALE == pixelScale[1] ) { + pixelScale[1] = PlatformMaxPixelScale[1]; + } + } + + /** + * Returns the maximum platform pixelScale + */ + public static int[] getPlatformMaxPixelScale(final int[] result) { + System.arraycopy(PlatformMaxPixelScale, 0, result, 0, 2); + return result; + } + + /** + * Returns true if platform pixelScale is uniform, i.e. same scale factor for x- and y-direction, otherwise false. + */ + public static boolean isPlatformPixelScaleUniform() { + return PlatformUniformPixelScale; + } + + /** + * Returns whether the platform supports pixelScale + */ + public static boolean isPlatformPixelScaleSupported() { + return PlatformPixelScaleSupported; + } + +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java b/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java index 902223c80..1a8772988 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java +++ b/src/nativewindow/classes/jogamp/nativewindow/WrappedSurface.java @@ -31,6 +31,7 @@ package jogamp.nativewindow; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.UpstreamSurfaceHook; import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; @@ -40,8 +41,9 @@ import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; * * @see ProxySurface */ -public class WrappedSurface extends ProxySurfaceImpl { - protected long surfaceHandle; +public class WrappedSurface extends ProxySurfaceImpl implements ScalableSurface { + private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private long surfaceHandle; /** * Utilizes a {@link UpstreamSurfaceHook.MutableSize} to hold the size information, @@ -76,6 +78,8 @@ public class WrappedSurface extends ProxySurfaceImpl { @Override protected void invalidateImpl() { surfaceHandle = 0; + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; } @Override @@ -97,14 +101,61 @@ public class WrappedSurface extends ProxySurfaceImpl { protected final void unlockSurfaceImpl() { } + /** + * {@inheritDoc} + * <p> + * {@link WrappedSurface}'s implementation uses the {@link #setSurfaceScale(int[]) given pixelScale} directly. + * </p> + */ @Override public final int[] convertToWindowUnits(final int[] pixelUnitsAndResult) { - return pixelUnitsAndResult; // FIXME HiDPI: use 'pixelScale' + pixelUnitsAndResult[0] /= hasPixelScale[0]; + pixelUnitsAndResult[1] /= hasPixelScale[1]; + return pixelUnitsAndResult; } + /** + * {@inheritDoc} + * <p> + * {@link WrappedSurface}'s implementation uses the {@link #setSurfaceScale(int[]) given pixelScale} directly. + * </p> + */ @Override public final int[] convertToPixelUnits(final int[] windowUnitsAndResult) { - return windowUnitsAndResult; // FIXME HiDPI: use 'pixelScale' + windowUnitsAndResult[0] *= hasPixelScale[0]; + windowUnitsAndResult[1] *= hasPixelScale[1]; + return windowUnitsAndResult; + } + + /** + * {@inheritDoc} + * <p> + * {@link WrappedSurface}'s implementation is to simply pass the given pixelScale + * from the caller <i>down</i> to this instance without validation to be applied in the {@link #convertToPixelUnits(int[]) conversion} {@link #convertToWindowUnits(int[]) methods} <b>only</b>.<br/> + * This allows the caller to pass down knowledge about window- and pixel-unit conversion and utilize mentioned conversion methods. + * </p> + * <p> + * The given pixelScale will not impact the actual {@link #getSurfaceWidth()} and {@link #getSurfaceHeight()}, + * which is determinated by this instances {@link #getUpstreamSurface() upstream surface}. + * </p> + * <p> + * Implementation uses the default pixelScale {@link ScalableSurface#IDENTITY_PIXELSCALE} + * and resets to default values on {@link #invalidateImpl()}, i.e. {@link #destroyNotify()}. + * </p> + * <p> + * Implementation returns the given pixelScale array. + * </p> + */ + @Override + public final void setSurfaceScale(final int[] pixelScale) { + hasPixelScale[0] = pixelScale[0]; + hasPixelScale[1] = pixelScale[1]; + } + + @Override + public final int[] getSurfaceScale(final int[] result) { + System.arraycopy(hasPixelScale, 0, result, 0, 2); + return result; } }
\ No newline at end of file diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java index 3b91fc8ab..3d88049b2 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java @@ -114,6 +114,27 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { } @Override + public void setSurfaceScale(final int[] pixelScale) { + super.setSurfaceScale(pixelScale); + if( 0 != drawable ) { // locked at least once ! + final int hadPixelScaleX = getPixelScaleX(); + updatePixelScale(); + + if( hadPixelScaleX != getPixelScaleX() && 0 != getAttachedSurfaceLayer() ) { + OSXUtil.RunOnMainThread(false, new Runnable() { + @Override + public void run() { + final long osl = getAttachedSurfaceLayer(); + if( 0 != osl ) { + OSXUtil.SetCALayerPixelScale(rootSurfaceLayer, osl, getPixelScaleX()); + } + } + }); + } + } + } + + @Override protected void attachSurfaceLayerImpl(final long layerHandle) { OSXUtil.RunOnMainThread(false, new Runnable() { @Override @@ -143,7 +164,8 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { } else if( DEBUG ) { System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds); } - OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), getPixelScale(), JAWTUtil.getOSXCALayerQuirks()); + // HiDPI: uniform pixel scale + OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), getPixelScaleX(), JAWTUtil.getOSXCALayerQuirks()); } } ); } @@ -309,7 +331,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { public void run() { String errMsg = null; if(0 == rootSurfaceLayer && 0 != jawtSurfaceLayersHandle) { - rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight(), getPixelScale()); + rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight(), getPixelScaleX()); // HiDPI: uniform pixel scale if(0 == rootSurfaceLayer) { errMsg = "Could not create root CALayer"; } else { diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index 12e574ced..a91698f62 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -44,6 +44,9 @@ public class OSXUtil implements ToolkitProperties { private static boolean isInit = false; private static final boolean DEBUG = Debug.debug("OSXUtil"); + /** FIXME HiDPI: OSX unique and maximum value {@value} */ + public static final int MAX_PIXELSCALE = 2; + /** * Called by {@link NativeWindowFactory#initSingleton()} * @see ToolkitProperties @@ -201,6 +204,20 @@ public class OSXUtil implements ToolkitProperties { } /** + * Set root and sub CALayer pixelScale / contentScale for HiDPI + * + * @param rootCALayer the root surface layer, maybe null. + * @param subCALayer the client surface layer, maybe null. + * @param contentsScale scale for HiDPI support: pixel-dim = window-dim x scale + */ + public static void SetCALayerPixelScale(final long rootCALayer, final long subCALayer, final float contentsScale) { + if( 0==rootCALayer && 0==subCALayer ) { + return; + } + SetCALayerPixelScale0(rootCALayer, subCALayer, contentsScale); + } + + /** * Detach a sub CALayer from the root CALayer. */ public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) { @@ -364,6 +381,7 @@ public class OSXUtil implements ToolkitProperties { private static native long CreateCALayer0(int width, int height, float contentsScale); private static native void AddCASublayer0(long rootCALayer, long subCALayer, int x, int y, int width, int height, float contentsScale, int caLayerQuirks); private static native void FixCALayerLayout0(long rootCALayer, long subCALayer, boolean visible, int x, int y, int width, int height, int caLayerQuirks); + private static native void SetCALayerPixelScale0(long rootCALayer, long subCALayer, float contentsScale); private static native void RemoveCASublayer0(long rootCALayer, long subCALayer); private static native void DestroyCALayer0(long caLayer); private static native void RunOnMainThread0(Runnable runnable); diff --git a/src/nativewindow/native/macosx/OSXmisc.m b/src/nativewindow/native/macosx/OSXmisc.m index 62d3d67bb..fa3bf026a 100644 --- a/src/nativewindow/native/macosx/OSXmisc.m +++ b/src/nativewindow/native/macosx/OSXmisc.m @@ -639,13 +639,11 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0 if(0 == width) { width = 32; } if(0 == height) { height = 32; } - if( 1.0 != contentsScale ) { NS_DURING - // Available >= 10.7 - [layer setContentsScale: (CGFloat)contentsScale]; + // Available >= 10.7 + [layer setContentsScale: (CGFloat)contentsScale]; NS_HANDLER NS_ENDHANDLER - } // initial dummy size ! CGRect lFrame = [layer frame]; @@ -692,12 +690,10 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_AddCASublayer0 caLayerQuirks, rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount], lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (float)contentsScale); - if( 1.0 != contentsScale ) { NS_DURING - [subLayer setContentsScale: (CGFloat)contentsScale]; + [subLayer setContentsScale: (CGFloat)contentsScale]; NS_HANDLER NS_ENDHANDLER - } [subLayer setFrame:lRectRoot]; [rootLayer addSublayer:subLayer]; @@ -753,6 +749,37 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_FixCALayerLayout0 /* * Class: Java_jogamp_nativewindow_macosx_OSXUtil + * Method: SetCALayerPixelScale0 + * Signature: (JJF)V + */ +JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_SetCALayerPixelScale0 + (JNIEnv *env, jclass unused, jlong rootCALayer, jlong subCALayer, jfloat contentsScale) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyCALayer* rootLayer = (MyCALayer*) ((intptr_t) rootCALayer); + if( NULL == rootLayer ) { + NativewindowCommon_throwNewRuntimeException(env, "Argument \"rootLayer\" is null"); + } + CALayer* subLayer = (CALayer*) ((intptr_t) subCALayer); + + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + +NS_DURING + [rootLayer setContentsScale: (CGFloat)contentsScale]; + if( NULL != subLayer ) { + [subLayer setContentsScale: (CGFloat)contentsScale]; + } +NS_HANDLER +NS_ENDHANDLER + + [CATransaction commit]; + + [pool release]; +} + +/* + * Class: Java_jogamp_nativewindow_macosx_OSXUtil * Method: RemoveCASublayer0 * Signature: (JJ)V */ diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index 82199e9b5..88134f3ef 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -45,6 +45,7 @@ import jogamp.newt.WindowImpl; import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.util.Rectangle; import javax.media.nativewindow.util.RectangleImmutable; @@ -94,7 +95,7 @@ import javax.media.nativewindow.util.RectangleImmutable; * </pre> * </p> */ -public interface Window extends NativeWindow, WindowClosingProtocol { +public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSurface { public static final boolean DEBUG_MOUSE_EVENT = Debug.debug("Window.MouseEvent"); public static final boolean DEBUG_KEY_EVENT = Debug.debug("Window.KeyEvent"); public static final boolean DEBUG_IMPLEMENTATION = Debug.debug("Window"); diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index 37e9f9813..baaa69e8e 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -865,6 +865,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto } newtChild.setVisible(false); newtChild.setSize(w, h); + jawtWindow.setSurfaceScale(newtChild.getSurfaceScale(new int[2])); newtChild.reparentWindow(jawtWindow, -1, -1, Window.REPARENT_HINT_BECOMES_VISIBLE); newtChild.addSurfaceUpdatedListener(jawtWindow); if( jawtWindow.isOffscreenLayerSurfaceEnabled() && diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index a61085fb0..6610bd74f 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -404,6 +404,16 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind } @Override + public final void setSurfaceScale(final int[] pixelScale) { + window.setSurfaceScale(pixelScale); + } + + @Override + public final int[] getSurfaceScale(final int[] result) { + return window.getSurfaceScale(result); + } + + @Override public final void setPosition(int x, int y) { window.setPosition(x, y); } diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 5288bfcc5..f02b9740d 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -48,6 +48,7 @@ import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.OffscreenLayerSurface; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.util.DimensionImmutable; @@ -59,6 +60,7 @@ import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.Rectangle; import javax.media.nativewindow.util.RectangleImmutable; +import jogamp.nativewindow.SurfaceScaleUtils; import jogamp.nativewindow.SurfaceUpdatedHelper; import com.jogamp.common.util.ArrayHashSet; @@ -150,6 +152,9 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private volatile boolean hasFocus = false; private volatile int pixWidth = 128, pixHeight = 128; // client-area size w/o insets in pixel units, default: may be overwritten by user private volatile int winWidth = 128, winHeight = 128; // client-area size w/o insets in window units, default: may be overwritten by user + protected int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + protected int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + private volatile int x = 64, y = 64; // client-area pos w/o insets in window units private volatile Insets insets = new Insets(); // insets of decoration (if top-level && decorated) private boolean blockInsetsChange = false; // block insets change (from same thread) @@ -1953,9 +1958,25 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */ - protected int getPixelScaleX() { return 1; } + protected final int getPixelScaleX() { + return hasPixelScale[0]; + } + /** HiDPI: We currently base scaling of window units to pixel units on an integer scale factor per component. */ - protected int getPixelScaleY() { return 1; } + protected final int getPixelScaleY() { + return hasPixelScale[1]; + } + + @Override + public void setSurfaceScale(final int[] pixelScale) { + SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getSimpleName() : null); + } + + @Override + public final int[] getSurfaceScale(final int[] result) { + System.arraycopy(isNativeValid() ? hasPixelScale : reqPixelScale, 0, result, 0, 2); + return result; + } protected final boolean autoPosition() { return autoPosition; } diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java index 5dab64e39..d01a2f21f 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTCanvas.java @@ -66,10 +66,16 @@ public class AWTCanvas extends Canvas { private volatile JAWTWindow jawtWindow=null; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private CapabilitiesChooser chooser=null; private final CapabilitiesImmutable capabilities; + private final UpstreamScalable upstreamScale; + + public static interface UpstreamScalable { + int[] getReqPixelScale(); + void setHasPixelScale(final int[] pixelScale); + } private boolean displayConfigChanged=false; - public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser) { + public AWTCanvas(CapabilitiesImmutable capabilities, CapabilitiesChooser chooser, UpstreamScalable upstreamScale) { super(); if(null==capabilities) { @@ -77,6 +83,7 @@ public class AWTCanvas extends Canvas { } this.capabilities=capabilities; this.chooser=chooser; + this.upstreamScale = upstreamScale; } public AWTGraphicsConfiguration getAWTGraphicsConfiguration() { @@ -139,7 +146,9 @@ public class AWTCanvas extends Canvas { { jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); // trigger initialization cycle + jawtWindow.setSurfaceScale( upstreamScale.getReqPixelScale() ); jawtWindow.lockSurface(); + upstreamScale.setHasPixelScale(jawtWindow.getSurfaceScale(new int[2])); jawtWindow.unlockSurface(); } @@ -152,10 +161,6 @@ public class AWTCanvas extends Canvas { } } - public int getPixelScale() { - final JAWTWindow _jawtWindow = jawtWindow; - return (null != _jawtWindow) ? _jawtWindow.getPixelScale() : 1; - } public NativeWindow getNativeWindow() { final JAWTWindow _jawtWindow = jawtWindow; return (null != _jawtWindow) ? _jawtWindow : null; diff --git a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java index cc92c4963..4064fdb05 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/awt/WindowDriver.java @@ -98,16 +98,17 @@ public class WindowDriver extends WindowImpl { } } - @Override - protected final int getPixelScaleX() { - final AWTCanvas _awtCanvas = awtCanvas; - return null != _awtCanvas ? _awtCanvas.getPixelScale() : 1; - } + private final AWTCanvas.UpstreamScalable upstreamScalable = new AWTCanvas.UpstreamScalable() { + @Override + public int[] getReqPixelScale() { + return WindowDriver.this.reqPixelScale; + } - @Override - protected final int getPixelScaleY() { - return getPixelScaleX(); - } + @Override + public void setHasPixelScale(final int[] pixelScale) { + System.arraycopy(pixelScale, 0, WindowDriver.this.hasPixelScale, 0, 2); + } + }; @Override protected void createNativeImpl() { @@ -130,7 +131,7 @@ public class WindowDriver extends WindowImpl { awtContainer.setLayout(new BorderLayout()); if( null == awtCanvas ) { - awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser); + awtCanvas = new AWTCanvas(capsRequested, WindowDriver.this.capabilitiesChooser, upstreamScalable); // canvas.addComponentListener(listener); awtContainer.add(awtCanvas, BorderLayout.CENTER); diff --git a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java index cd852bb09..0fa4739a3 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/WindowDriver.java @@ -39,11 +39,13 @@ import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.MutableSurface; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; +import jogamp.nativewindow.SurfaceScaleUtils; import jogamp.nativewindow.macosx.OSXUtil; import jogamp.newt.PointerIconImpl; import jogamp.newt.ScreenImpl; @@ -62,43 +64,42 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl DisplayDriver.initSingleton(); } - private int pixelScale; - public WindowDriver() { - pixelScale = 1; } - private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final float newPixelScaleRaw) { - final int newPixelScaleSafe = FloatUtil.isZero(newPixelScaleRaw, FloatUtil.EPSILON) ? 1 : (int) newPixelScaleRaw; - final boolean changed = pixelScale != newPixelScaleSafe; - if( DEBUG_IMPLEMENTATION ) { - System.err.println("WindowDriver.updatePixelScale.X: "+pixelScale+" -> "+newPixelScaleSafe+" (raw "+newPixelScaleRaw+") - changed "+changed); + private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final float pixelScaleRaw) { + final int[] pixelScaleInt; + { + final int ps = FloatUtil.isZero(pixelScaleRaw, FloatUtil.EPSILON) ? 1 : (int) pixelScaleRaw; + pixelScaleInt = new int[] { ps, ps }; } - if( changed ) { - pixelScale = newPixelScaleSafe; + + if( SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, pixelScaleInt, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) { if( sendEvent ) { super.sizeChanged(defer, getWidth(), getHeight(), true); } else { defineSize(getWidth(), getHeight()); } + return true; + } else { + return false; } - return changed; } private boolean updatePixelScaleByScreenIdx(final boolean sendEvent) { final float newPixelScaleRaw = (float) OSXUtil.GetPixelScale(getScreen().getIndex()); if( DEBUG_IMPLEMENTATION ) { - System.err.println("WindowDriver.updatePixelScale.1: "+pixelScale+" -> "+newPixelScaleRaw); + System.err.println("WindowDriver.updatePixelScale.1: "+hasPixelScale[0]+" -> "+newPixelScaleRaw); } return updatePixelScale(sendEvent, true /* defer */, newPixelScaleRaw); } private boolean updatePixelScaleByWindowHandle(final boolean sendEvent) { - final long wh = getWindowHandle(); - if( 0 != wh ) { - final float newPixelScaleRaw = (float)OSXUtil.GetPixelScale(wh); + final long handle = getWindowHandle(); + if( 0 != handle ) { + final float newPixelScaleRaw = (float)OSXUtil.GetPixelScale(handle); if( DEBUG_IMPLEMENTATION ) { - System.err.println("WindowDriver.updatePixelScale.2: "+pixelScale+" -> "+newPixelScaleRaw); + System.err.println("WindowDriver.updatePixelScale.2: "+hasPixelScale[0]+" -> "+newPixelScaleRaw); } return updatePixelScale(sendEvent, true /* defer */, newPixelScaleRaw); } else { @@ -109,10 +110,10 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl /** Called from native code */ protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw) { final long handle = getWindowHandle(); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (raw), drop "+(0==handle)); + } if( 0 != handle ) { - if( DEBUG_IMPLEMENTATION ) { - System.err.println("WindowDriver.updatePixelScale.3: "+pixelScale+" -> "+newPixelScaleRaw); - } updatePixelScale(true /* sendEvent*/, defer, newPixelScaleRaw); } } @@ -134,13 +135,43 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } @Override - protected final int getPixelScaleX() { - return pixelScale; - } - - @Override - protected final int getPixelScaleY() { - return pixelScale; + public final void setSurfaceScale(final int[] pixelScale) { + SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null); + + final int[] resPixelScale; + if( isNativeValid() ) { + if( isOffscreenInstance ) { + final NativeWindow pWin = getParent(); + if( pWin instanceof ScalableSurface ) { + final ScalableSurface sSurf = (ScalableSurface)pWin; + sSurf.setSurfaceScale(reqPixelScale); + final int[] pPixelScale = sSurf.getSurfaceScale(new int[2]); + updatePixelScale(true /* sendEvent */, true /* defer */, pPixelScale[0]); // HiDPI: uniformPixelScale + } else { + // just notify updated pixelScale if offscreen + SurfaceScaleUtils.replaceAutoMaxWithPlatformMax(reqPixelScale); + updatePixelScale(true /* sendEvent */, true /* defer */, reqPixelScale[0]); // HiDPI: uniformPixelScale + } + } else { + // set pixelScale in native code, will issue an update PixelScale + OSXUtil.RunOnMainThread(true, new Runnable() { + @Override + public void run() { + setPixelScale0(getWindowHandle(), surfaceHandle, reqPixelScale[0]); // HiDPI: uniformPixelScale + } + } ); + } + resPixelScale = hasPixelScale; + } else { + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + resPixelScale = reqPixelScale; + } + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.setPixelScale: "+pixelScale[0]+"x"+pixelScale[1]+" (req) -> "+ + reqPixelScale[0]+"x"+reqPixelScale[1]+" (validated) -> "+ + resPixelScale[0]+"x"+resPixelScale[1]+" (result) - realized "+isNativeValid()); + } } @Override @@ -351,6 +382,9 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } } + final boolean setVisible = 0 != ( FLAG_IS_VISIBLE & flags); + final boolean hasFocus = hasFocus(); + if(DEBUG_IMPLEMENTATION) { final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration(); final NativeWindow pWin = getParent(); @@ -362,12 +396,10 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl ", isOffscreenInstance(sscSurfaceHandle "+toHexString(sscSurfaceHandle)+ ", ioi: "+_isOffscreenInstance+ ") -> "+isOffscreenInstance+ - "\n\t, "+getReconfigureFlagsAsString(null, flags)); + "\n\t, "+getReconfigureFlagsAsString(null, flags)+", setVisible "+setVisible+", hasFocus "+hasFocus); // Thread.dumpStack(); } - final boolean setVisible = 0 != ( FLAG_IS_VISIBLE & flags); - if( 0 != ( FLAG_CHANGE_VISIBILITY & flags) && !setVisible ) { if ( !isOffscreenInstance ) { OSXUtil.RunOnMainThread(false, new Runnable() { @@ -395,6 +427,9 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl updatePixelScaleByWindowHandle(false /* sendEvent */); super.sizeChanged(false, width, height, true); visibleChanged(false, setVisible); + if( hasFocus ) { + requestFocusImpl(true); + } } else { if( width>0 && height>0 ) { if( !isOffscreenInstance ) { @@ -613,7 +648,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl if( 0 != surfaceHandle ) { throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView"); } - surfaceHandle = createView0(pS.getX(), pS.getY(), width, height, fullscreen); + surfaceHandle = createView0(pS.getX(), pS.getY(), width, height); if( 0 == surfaceHandle ) { throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this); } @@ -633,7 +668,7 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl OSXUtil.RunOnMainThread(true, new Runnable() { @Override public void run() { - initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, + initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */, isOpaque, visible && !offscreenInstance, surfaceHandle); if( offscreenInstance ) { orderOut0(0!=parentWinHandle ? parentWinHandle : newWin); @@ -648,11 +683,12 @@ public class WindowDriver extends WindowImpl implements MutableSurface, DriverCl } protected static native boolean initIDs0(); - private native long createView0(int x, int y, int w, int h, boolean fullscreen); + private native long createView0(int x, int y, int w, int h); private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view); /** Must be called on Main-Thread */ - private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, + private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale, boolean opaque, boolean visible, long view); + private native void setPixelScale0(long window, long view, float reqPixelScale); private native boolean lockSurface0(long window, long view); private native boolean unlockSurface0(long window, long view); /** Must be called on Main-Thread */ diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m index 80e70216e..2bd11da3c 100644 --- a/src/newt/native/MacWindow.m +++ b/src/newt/native/MacWindow.m @@ -747,24 +747,18 @@ JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initIDs0 /** * Class: jogamp_newt_driver_macosx_WindowDriver * Method: createView0 - * Signature: (IIIIZ)J + * Signature: (IIII)J */ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createView0 - (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h, - jboolean fullscreen) + (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - DBG_PRINT( "createView0 - %p (this), %d/%d %dx%d, fs %d (START)\n", - (void*)(intptr_t)jthis, (int)x, (int)y, (int)w, (int)h, (int)fullscreen); + DBG_PRINT( "createView0 - %p (this), %d/%d %dx%d (START)\n", + (void*)(intptr_t)jthis, (int)x, (int)y, (int)w, (int)h); NSRect rectView = NSMakeRect(0, 0, w, h); NewtView *myView = [[NewtView alloc] initWithFrame: rectView] ; -NS_DURING - // Available >= 10.7 - [myView setWantsBestResolutionOpenGLSurface: YES]; // HiDPI scaling: Always desired -NS_HANDLER -NS_ENDHANDLER DBG_PRINT( "createView0.X - new view: %p\n", myView); [pool release]; @@ -780,7 +774,7 @@ NS_ENDHANDLER * Signature: (IIIIZIIJ)J */ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow0 - (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h, + (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h, jboolean fullscreen, jint styleMask, jint bufferingType, jlong jview) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; @@ -816,10 +810,10 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_createWindow * * Class: jogamp_newt_driver_macosx_WindowDriver * Method: initWindow0 - * Signature: (JJIIIIZZZJ)V + * Signature: (JJIIIIFZZZJ)V */ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initWindow0 - (JNIEnv *env, jobject jthis, jlong parent, jlong window, jint x, jint y, jint w, jint h, + (JNIEnv *env, jobject jthis, jlong parent, jlong window, jint x, jint y, jint w, jint h, jfloat reqPixelScale, jboolean opaque, jboolean visible, jlong jview) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; @@ -827,10 +821,20 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_initWindow0 NewtView* myView = (NewtView*) (intptr_t) jview ; BOOL fullscreen = myWindow->isFullscreenWindow; - DBG_PRINT( "initWindow0 - %p (this), %p (parent), %p (window), %d/%d %dx%d, opaque %d, fs %d, visible %d, view %p (START)\n", - (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, (int)x, (int)y, (int)w, (int)h, + DBG_PRINT( "initWindow0 - %p (this), %p (parent), %p (window), %d/%d %dx%d, reqPixScale %f, opaque %d, fs %d, visible %d, view %p (START)\n", + (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, (int)x, (int)y, (int)w, (int)h, (float)reqPixelScale, (int) opaque, (int)fullscreen, (int)visible, myView); +NS_DURING + // HiDPI scaling: Setup - Available >= 10.7 + if( 1.0 == reqPixelScale ) { + [myView setWantsBestResolutionOpenGLSurface: NO]; + } else { + [myView setWantsBestResolutionOpenGLSurface: YES]; + } +NS_HANDLER +NS_ENDHANDLER + [myWindow setReleasedWhenClosed: NO]; // We control NSWindow destruction! [myWindow setPreservesContentDuringLiveResize: NO]; NS_DURING @@ -987,6 +991,44 @@ NS_ENDHANDLER * Method is called on Main-Thread, hence no special invocation required inside method. * * Class: jogamp_newt_driver_macosx_WindowDriver + * Method: setPixelScale0 + * Signature: (JJF)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_WindowDriver_setPixelScale0 + (JNIEnv *env, jobject jthis, jlong window, jlong view, jfloat reqPixelScale) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtMacWindow* myWindow = (NewtMacWindow*) ((intptr_t) window); + NewtView* myView = (NewtView*) (intptr_t) view ; +#ifdef VERBOSE_ON + int dbgIdx = 1; +#endif + + DBG_PRINT( "setPixelScale0 - %p (this), %p (window), view %p, reqPixScale %f (START)\n", + (void*)(intptr_t)jthis, myWindow, myView, (float)reqPixelScale); + +NS_DURING + // HiDPI scaling: Setup - Available >= 10.7 + if( 1.0 == reqPixelScale ) { + [myView setWantsBestResolutionOpenGLSurface: NO]; + } else { + [myView setWantsBestResolutionOpenGLSurface: YES]; + } +NS_HANDLER +NS_ENDHANDLER + + DBG_PRINT( "setPixelScale0.%d - %p (this), window: %p, view %p\n", + dbgIdx++, (void*)(intptr_t)jthis, myWindow, myView); + + [pool release]; + DBG_PRINT( "setPixelScale0.X - %p (this), window: %p, view %p\n", + (void*)(intptr_t)jthis, myWindow, myView); +} + +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * + * Class: jogamp_newt_driver_macosx_WindowDriver * Method: close0 * Signature: (J)V */ diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m index c4800bd26..fe761833d 100644 --- a/src/newt/native/NewtMacWindow.m +++ b/src/newt/native/NewtMacWindow.m @@ -778,8 +778,12 @@ static jmethodID windowRepaintID = NULL; { [super viewDidChangeBackingProperties]; - CGFloat pixelScale = [[self window] backingScaleFactor]; - [[self layer] setContentsScale: pixelScale]; + // HiDPI scaling + BOOL useHiDPI = [self wantsBestResolutionOpenGLSurface]; + CGFloat pixelScaleRaw = [[self window] backingScaleFactor]; + CGFloat pixelScaleUse = useHiDPI ? pixelScaleRaw : 1.0; + DBG_PRINT("viewDidChangeBackingProperties: PixelScale: HiDPI %d, raw %f -> use %f\n", useHiDPI, (float)pixelScaleRaw, (float)pixelScaleUse); + [[self layer] setContentsScale: pixelScaleUse]; if (javaWindowObject == NULL) { DBG_PRINT("viewDidChangeBackingProperties: null javaWindowObject\n"); @@ -792,7 +796,7 @@ static jmethodID windowRepaintID = NULL; return; } - (*env)->CallVoidMethod(env, javaWindowObject, updatePixelScaleID, JNI_TRUE, (jfloat)pixelScale); // defer + (*env)->CallVoidMethod(env, javaWindowObject, updatePixelScaleID, JNI_TRUE, (jfloat)pixelScaleUse); // defer // detaching thread not required - daemon // NewtCommon_ReleaseJNIEnv(shallBeDetached); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java index 622f51e45..3dbfeed8f 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2AWT.java @@ -28,6 +28,7 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.awt; +import javax.media.nativewindow.ScalableSurface; import javax.media.opengl.*; import com.jogamp.opengl.util.Animator; @@ -37,6 +38,7 @@ import javax.media.opengl.awt.GLCanvas; import com.jogamp.common.os.Platform; import com.jogamp.newt.event.awt.AWTKeyAdapter; import com.jogamp.newt.event.awt.AWTWindowAdapter; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.TraceKeyAdapter; import com.jogamp.newt.event.TraceWindowAdapter; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; @@ -78,6 +80,7 @@ public class TestGearsES2AWT extends UITestCase { static int xpos = 10, ypos = 10; static FrameLayout frameLayout = FrameLayout.None; static ResizeBy resizeBy = ResizeBy.Component; + static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; static boolean forceES2 = false; static boolean forceGL3 = false; @@ -167,6 +170,7 @@ public class TestGearsES2AWT extends UITestCase { final GLCanvas glCanvas = new GLCanvas(caps); Assert.assertNotNull(glCanvas); setSize(resizeBy, frame, false, glCanvas, new Dimension(width, height)); + glCanvas.setSurfaceScale(reqSurfacePixelScale); frame.setLocation(xpos, ypos); switch( frameLayout) { @@ -242,6 +246,33 @@ public class TestGearsES2AWT extends UITestCase { new AWTKeyAdapter(new TraceKeyAdapter(quitAdapter), glCanvas).addTo(glCanvas); new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glCanvas).addTo(frame); + final com.jogamp.newt.event.KeyListener kl = new com.jogamp.newt.event.KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + if( e.isAutoRepeat() ) { + return; + } + if(e.getKeyChar()=='x') { + final int[] hadSurfacePixelScale = glCanvas.getSurfaceScale(new int[2]); + final int[] reqSurfacePixelScale; + if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) { + reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + } else { + reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + } + System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]); + glCanvas.setSurfaceScale(reqSurfacePixelScale); + final int[] hasSurfacePixelScale0 = glCanvas.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glCanvas.getSurfaceScale(new int[2]); + System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+ + reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(frame, glCanvas, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + } + } }; + new AWTKeyAdapter(kl, glCanvas).addTo(glCanvas); + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { if( ResizeBy.Frame == resizeBy ) { @@ -254,6 +285,13 @@ public class TestGearsES2AWT extends UITestCase { Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glCanvas, true)); + final int[] hasSurfacePixelScale0 = glCanvas.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glCanvas.getSurfaceScale(new int[2]); + System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(frame, glCanvas, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + if( useAnimator ) { animator.start(); Assert.assertTrue(animator.isStarted()); @@ -365,6 +403,17 @@ public class TestGearsES2AWT extends UITestCase { runTestGL(caps, resizeBy, frameLayout); } + @Test + public void test99_PixelScale1_DefaultNorm() throws InterruptedException, InvocationTargetException { + if( mainRun ) return; + + reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + + GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + runTestGL(caps, resizeBy, frameLayout); + } + public static void main(String args[]) { boolean waitForKey = false; int rw=-1, rh=-1; @@ -392,6 +441,11 @@ public class TestGearsES2AWT extends UITestCase { } else if(args[i].equals("-rheight")) { i++; rh = MiscUtils.atoi(args[i], rh); + } else if(args[i].equals("-pixelScale")) { + i++; + final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]); + reqSurfacePixelScale[0] = pS; + reqSurfacePixelScale[1] = pS; } else if(args[i].equals("-layout")) { i++; frameLayout = FrameLayout.valueOf(args[i]); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java index 8172eb335..cfd099062 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java @@ -35,6 +35,7 @@ import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.lang.reflect.InvocationTargetException; +import javax.media.nativewindow.ScalableSurface; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLProfile; @@ -50,12 +51,14 @@ import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.TraceKeyAdapter; import com.jogamp.newt.event.TraceWindowAdapter; import com.jogamp.newt.event.awt.AWTKeyAdapter; import com.jogamp.newt.event.awt.AWTWindowAdapter; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.jogl.demos.gl2.Gears; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.QuitAdapter; import com.jogamp.opengl.test.junit.util.UITestCase; @@ -75,6 +78,7 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { static boolean manualTest = false; static boolean skipGLOrientationVerticalFlip = false; static int xpos = 10, ypos = 10; + static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; @BeforeClass public static void initClass() { @@ -120,6 +124,7 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { glJPanel.setMinimumSize(wsize); glJPanel.setPreferredSize(wsize); glJPanel.setSize(wsize); + glJPanel.setSurfaceScale(reqSurfacePixelScale); if( caps.isBitmap() || caps.getGLProfile().isGL2() ) { final Gears gears = new Gears(swapInterval); gears.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); @@ -161,6 +166,15 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { frame.pack(); frame.setVisible(true); } } ) ; + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glJPanel, true)); + + final int[] hasSurfacePixelScale0 = glJPanel.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glJPanel.getSurfaceScale(new int[2]); + System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(frame, glJPanel, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); if( useAnimator ) { animator.setUpdateFPSFrames(60, System.err); @@ -172,6 +186,33 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { new AWTKeyAdapter(new TraceKeyAdapter(quitAdapter), glJPanel).addTo(glJPanel); new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glJPanel).addTo(frame); + final com.jogamp.newt.event.KeyListener kl = new com.jogamp.newt.event.KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + if( e.isAutoRepeat() ) { + return; + } + if(e.getKeyChar()=='x') { + final int[] hadSurfacePixelScale = glJPanel.getSurfaceScale(new int[2]); + final int[] reqSurfacePixelScale; + if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) { + reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + } else { + reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + } + System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]); + glJPanel.setSurfaceScale(reqSurfacePixelScale); + final int[] hasSurfacePixelScale0 = glJPanel.getNativeSurface().convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glJPanel.getSurfaceScale(new int[2]); + System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+ + reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(frame, glJPanel, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + } + } }; + new AWTKeyAdapter(kl, glJPanel).addTo(glJPanel); + snap.setMakeSnapshot(); if( null != rwsize ) { @@ -198,11 +239,13 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { Assert.assertNotNull(frame); Assert.assertNotNull(glJPanel); - Assert.assertNotNull(animator); if( useAnimator ) { + Assert.assertNotNull(animator); animator.stop(); Assert.assertEquals(false, animator.isAnimating()); + } else { + Assert.assertNull(animator); } SwingUtilities.invokeAndWait(new Runnable() { public void run() { @@ -341,6 +384,20 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { runTestGL(caps); } + @Test + public void test99_PixelScale1_DefaultNorm() + throws AWTException, InterruptedException, InvocationTargetException + { + if( manualTest ) { + return; + } + reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + + GLCapabilities caps = new GLCapabilities(GLProfile.getDefault()); + runTestGL(caps); + } + static long duration = 500; // ms public static void main(String args[]) { @@ -374,6 +431,11 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { } else if(args[i].equals("-rheight")) { i++; rh = MiscUtils.atoi(args[i], rh); + } else if(args[i].equals("-pixelScale")) { + i++; + final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]); + reqSurfacePixelScale[0] = pS; + reqSurfacePixelScale[1] = pS; } else if(args[i].equals("-userVFlip")) { skipGLOrientationVerticalFlip = true; } else if(args[i].equals("-vsync")) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java index bc6b5e26f..b916e21d6 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java @@ -29,6 +29,7 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.newt; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.URLConnection; import com.jogamp.common.util.IOUtil; @@ -54,6 +55,7 @@ import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.util.Dimension; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; @@ -79,6 +81,7 @@ public class TestGearsES2NEWT extends UITestCase { static int screenIdx = 0; static PointImmutable wpos; static DimensionImmutable wsize, rwsize=null; + static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; static long duration = 500; // ms static boolean opaque = true; @@ -119,7 +122,7 @@ public class TestGearsES2NEWT extends UITestCase { private void setTitle(final Window win, final GLCapabilitiesImmutable caps) { final String capsA = caps.isBackgroundOpaque() ? "opaque" : "transl"; - win.setTitle("GLWindow["+capsA+"], swapI "+swapInterval+", win: "+win.getBounds()+", pix: "+win.getSurfaceBounds()); + win.setTitle("GLWindow["+capsA+"], swapI "+swapInterval+", win: "+win.getBounds()+", pix: "+win.getSurfaceWidth()+"x"+win.getSurfaceHeight()); } protected void runTestGL(final GLCapabilitiesImmutable caps, boolean undecorated) throws InterruptedException { System.err.println("requested: vsync "+swapInterval+", "+caps); @@ -127,6 +130,7 @@ public class TestGearsES2NEWT extends UITestCase { Screen screen = NewtFactory.createScreen(dpy, screenIdx); final GLWindow glWindow = GLWindow.create(screen, caps); Assert.assertNotNull(glWindow); + glWindow.setSurfaceScale(reqSurfacePixelScale); glWindow.setSize(wsize.getWidth(), wsize.getHeight()); if(null != wpos) { glWindow.setPosition(wpos.getX(), wpos.getY()); @@ -342,6 +346,23 @@ public class TestGearsES2NEWT extends UITestCase { System.err.println("[set mouse pos post]"); glWindow.setExclusiveContextThread(t); } }.start(); + } else if(e.getKeyChar()=='x') { + final int[] hadSurfacePixelScale = glWindow.getSurfaceScale(new int[2]); + final int[] reqSurfacePixelScale; + if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) { + reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + } else { + reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + } + System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]); + glWindow.setSurfaceScale(reqSurfacePixelScale); + final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]); + System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+ + reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(glWindow, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); } } }); @@ -414,6 +435,13 @@ public class TestGearsES2NEWT extends UITestCase { System.err.println("GL chosen: "+glWindow.getChosenCapabilities()); System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", "+glWindow.getInsets()); + final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]); + System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(glWindow, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + snap.setMakeSnapshot(); if( null != rwsize ) { @@ -520,6 +548,17 @@ public class TestGearsES2NEWT extends UITestCase { runTestGL(caps, undecorated); } + @Test + public void test99_PixelScale1_DefaultNorm() throws InterruptedException, InvocationTargetException { + if( mainRun ) return; + + reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + + GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + runTestGL(caps, undecorated); + } + public static void main(String args[]) throws IOException { mainRun = true; @@ -584,6 +623,11 @@ public class TestGearsES2NEWT extends UITestCase { i++; y = MiscUtils.atoi(args[i], y); usePos = true; + } else if(args[i].equals("-pixelScale")) { + i++; + final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]); + reqSurfacePixelScale[0] = pS; + reqSurfacePixelScale[1] = pS; } else if(args[i].equals("-rwidth")) { i++; rw = MiscUtils.atoi(args[i], rw); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java index c7a00350b..d3e695659 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NewtCanvasAWT.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.opengl.test.junit.jogl.demos.es2.newt; import java.awt.BorderLayout; @@ -34,6 +34,8 @@ import java.awt.Component; import java.awt.Container; import java.awt.Frame; import java.awt.TextArea; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -41,7 +43,9 @@ import com.jogamp.common.os.Platform; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; +import com.jogamp.newt.Window; import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.opengl.GLWindow; @@ -53,6 +57,7 @@ import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.newt.parenting.NewtAWTReparentingKeyAdapter; +import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.util.Dimension; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; @@ -71,16 +76,17 @@ import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGearsES2NewtCanvasAWT extends UITestCase { +public class TestGearsES2NewtCanvasAWT extends UITestCase { public enum FrameLayout { None, TextOnBottom, BorderBottom, BorderBottom2, BorderCenter, BorderCenterSurrounded, DoubleBorderCenterSurrounded }; public enum ResizeBy { GLWindow, Component, Frame }; - + static int screenIdx = 0; static PointImmutable wpos; static DimensionImmutable wsize, rwsize = null; static FrameLayout frameLayout = FrameLayout.None; static ResizeBy resizeBy = ResizeBy.Component; - + static int[] reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + static long duration = 500; // ms static boolean opaque = true; static int forceAlpha = -1; @@ -96,7 +102,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { static boolean mainRun = false; static boolean exclusiveContext = false; static boolean useAnimator = true; - + @BeforeClass public static void initClass() { if(null == wsize) { @@ -120,7 +126,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } catch( Throwable throwable ) { throwable.printStackTrace(); Assume.assumeNoException( throwable ); - } + } } static void setComponentSize(final Frame frame, final Component comp, final DimensionImmutable new_sz) { try { @@ -137,7 +143,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } catch( Throwable throwable ) { throwable.printStackTrace(); Assume.assumeNoException( throwable ); - } + } } static void setFrameSize(final Frame frame, final boolean frameLayout, final DimensionImmutable new_sz) { try { @@ -152,9 +158,9 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } catch( Throwable throwable ) { throwable.printStackTrace(); Assume.assumeNoException( throwable ); - } + } } - + static void setSize(final ResizeBy resizeBy, final Frame frame, final boolean frameLayout, final Component comp, final GLWindow glw, final DimensionImmutable new_sz) { switch( resizeBy ) { case GLWindow: @@ -166,9 +172,18 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { case Frame: setFrameSize(frame, frameLayout, new_sz); break; - } + } + } + + private void setTitle(final Frame frame, final NewtCanvasAWT glc, final Window win, final GLCapabilitiesImmutable caps) { + final String capsA = caps.isBackgroundOpaque() ? "opaque" : "transl"; + { + final java.awt.Rectangle b = glc.getBounds(); + frame.setTitle("NewtCanvasAWT["+capsA+"], swapI "+swapInterval+", win: ["+b.x+"/"+b.y+" "+b.width+"x"+b.height+"], pix: "+glc.getNativeWindow().getSurfaceWidth()+"x"+glc.getNativeWindow().getSurfaceHeight()); + } + win.setTitle("GLWindow["+capsA+"], swapI "+swapInterval+", win: "+win.getBounds()+", pix: "+win.getSurfaceWidth()+"x"+win.getSurfaceHeight()); } - + // public enum ResizeBy { GLWindow, Component, Frame }; protected void runTestGL(final GLCapabilitiesImmutable caps, final ResizeBy resizeBy, final FrameLayout frameLayout) throws InterruptedException, InvocationTargetException { System.err.println("requested: vsync "+swapInterval+", "+caps); @@ -176,14 +191,15 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { Screen screen = NewtFactory.createScreen(dpy, screenIdx); final GLWindow glWindow = GLWindow.create(screen, caps); Assert.assertNotNull(glWindow); - + glWindow.setSurfaceScale(reqSurfacePixelScale); + final NewtCanvasAWT newtCanvasAWT = new NewtCanvasAWT(glWindow); if ( shallUseOffscreenFBOLayer ) { newtCanvasAWT.setShallUseOffscreenLayer(true); } - + final Frame frame = new Frame("AWT Parent Frame"); - + setSize(resizeBy, frame, false, newtCanvasAWT, glWindow, wsize); switch( frameLayout) { @@ -199,7 +215,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { frame.setLayout(new BorderLayout()); frame.add(ta, BorderLayout.SOUTH); frame.add(newtCanvasAWT, BorderLayout.CENTER); - break; + break; case BorderBottom: frame.setLayout(new BorderLayout()); frame.add(newtCanvasAWT, BorderLayout.SOUTH); @@ -229,7 +245,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { c.add(new Button("east"), BorderLayout.EAST); c.add(new Button("west"), BorderLayout.WEST); c.add(newtCanvasAWT, BorderLayout.CENTER); - + frame.setLayout(new BorderLayout()); frame.add(new Button("NORTH"), BorderLayout.NORTH); frame.add(new Button("SOUTH"), BorderLayout.SOUTH); @@ -238,19 +254,35 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { frame.add(c, BorderLayout.CENTER); break; } - - frame.setTitle("Gears NewtCanvasAWT Test (translucent "+!caps.isBackgroundOpaque()+"), swapInterval "+swapInterval+", size "+wsize+", pos "+wpos); - + final GearsES2 demo = new GearsES2(swapInterval); demo.setPMVUseBackingArray(pmvUseBackingArray); glWindow.addGLEventListener(demo); - + + frame.addComponentListener(new ComponentListener() { + @Override + public void componentResized(ComponentEvent e) { + setTitle(frame, newtCanvasAWT, glWindow, caps); + } + + @Override + public void componentMoved(ComponentEvent e) { + setTitle(frame, newtCanvasAWT, glWindow, caps); + } + + @Override + public void componentShown(ComponentEvent e) { } + + @Override + public void componentHidden(ComponentEvent e) { } + }); + final Animator animator = useAnimator ? new Animator() : null; if( useAnimator ) { animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD); animator.setExclusiveContext(exclusiveContext); } - + final QuitAdapter quitAdapter = new QuitAdapter(); //glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter)); //glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter)); @@ -263,11 +295,36 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } public void windowMoved(WindowEvent e) { System.err.println("window moved: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()); - } + } }); - + glWindow.addKeyListener(new NewtAWTReparentingKeyAdapter(frame, newtCanvasAWT, glWindow, quitAdapter)); - + glWindow.addKeyListener(new com.jogamp.newt.event.KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + if( e.isAutoRepeat() ) { + return; + } + if(e.getKeyChar()=='x') { + final int[] hadSurfacePixelScale = glWindow.getSurfaceScale(new int[2]); + final int[] reqSurfacePixelScale; + if( hadSurfacePixelScale[0] == ScalableSurface.IDENTITY_PIXELSCALE ) { + reqSurfacePixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + } else { + reqSurfacePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + } + System.err.println("[set PixelScale pre]: had "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" -> req "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]); + glWindow.setSurfaceScale(reqSurfacePixelScale); + final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]); + System.err.println("[set PixelScale post]: "+hadSurfacePixelScale[0]+"x"+hadSurfacePixelScale[1]+" (had) -> "+ + reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(frame, newtCanvasAWT, glWindow, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + } + } } ); + if( useAnimator ) { animator.add(glWindow); animator.start(); @@ -281,28 +338,35 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { if( ResizeBy.Frame == resizeBy ) { frame.validate(); } else { - frame.pack(); - } - frame.setVisible(true); + frame.pack(); + } + frame.setVisible(true); } - }); + }); Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); - Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, true)); - + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glWindow, true)); + if( useAnimator ) { animator.setUpdateFPSFrames(60, showFPS ? System.err : null); } - + System.err.println("NW chosen: "+glWindow.getDelegatedWindow().getChosenCapabilities()); System.err.println("GL chosen: "+glWindow.getChosenCapabilities()); System.err.println("window pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", "+glWindow.getInsets()); - + + final int[] hasSurfacePixelScale0 = glWindow.convertToPixelUnits(new int[] { 1, 1 }); + final int[] hasSurfacePixelScale1 = glWindow.getSurfaceScale(new int[2]); + System.err.println("HiDPI PixelScale: "+reqSurfacePixelScale[0]+"x"+reqSurfacePixelScale[1]+" (req) -> "+ + hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); + setTitle(frame, newtCanvasAWT, glWindow, caps); + Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + if( null != rwsize ) { - Thread.sleep(500); // 500ms delay + Thread.sleep(500); // 500ms delay setSize(resizeBy, frame, true, newtCanvasAWT, glWindow, rwsize); System.err.println("window resize "+rwsize+" -> pos/siz: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", "+glWindow.getInsets()); } - + final long t0 = System.currentTimeMillis(); long t1 = t0; while(!quitAdapter.shouldQuit() && t1-t0<duration) { @@ -318,8 +382,8 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { } Assert.assertEquals(null, glWindow.getExclusiveContextThread()); SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - frame.dispose(); + public void run() { + frame.dispose(); } }); glWindow.destroy(); @@ -341,7 +405,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { final GLCapabilities caps = new GLCapabilities( glp ); caps.setBackgroundOpaque(opaque); if(-1 < forceAlpha) { - caps.setAlphaBits(forceAlpha); + caps.setAlphaBits(forceAlpha); } runTestGL(caps, resizeBy, frameLayout); if(loop_shutdown) { @@ -353,7 +417,7 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { @Test public void test02GL3() throws InterruptedException, InvocationTargetException { if(mainRun) return; - + if( !GLProfile.isAvailable(GLProfile.GL3) ) { System.err.println("GL3 n/a"); return; @@ -362,14 +426,25 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { final GLCapabilities caps = new GLCapabilities( glp ); runTestGL(caps, resizeBy, frameLayout); } - + + @Test + public void test99_PixelScale1_DefaultNorm() throws InterruptedException, InvocationTargetException { + if( mainRun ) return; + + reqSurfacePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + reqSurfacePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + + GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + runTestGL(caps, resizeBy, frameLayout); + } + public static void main(String args[]) throws IOException { mainRun = true; - + int x=0, y=0, w=640, h=480; int rw=-1, rh=-1; boolean usePos = false; - + for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { i++; @@ -424,6 +499,11 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { i++; y = MiscUtils.atoi(args[i], y); usePos = true; + } else if(args[i].equals("-pixelScale")) { + i++; + final int pS = MiscUtils.atoi(args[i], reqSurfacePixelScale[0]); + reqSurfacePixelScale[0] = pS; + reqSurfacePixelScale[1] = pS; } else if(args[i].equals("-screen")) { i++; screenIdx = MiscUtils.atoi(args[i], 0); @@ -438,11 +518,11 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { if( 0 < rw && 0 < rh ) { rwsize = new Dimension(rw, rh); } - + if(usePos) { wpos = new Point(x, y); } - + System.err.println("frameLayout "+frameLayout); System.err.println("resizeBy "+resizeBy); System.err.println("position "+wpos); @@ -450,9 +530,9 @@ public class TestGearsES2NewtCanvasAWT extends UITestCase { System.err.println("resize "+rwsize); System.err.println("screen "+screenIdx); System.err.println("translucent "+(!opaque)); - System.err.println("forceAlpha "+forceAlpha); + System.err.println("forceAlpha "+forceAlpha); System.err.println("fullscreen "+fullscreen); - System.err.println("pmvDirect "+(!pmvUseBackingArray)); + System.err.println("pmvDirect "+(!pmvUseBackingArray)); System.err.println("loops "+loops); System.err.println("loop shutdown "+loop_shutdown); System.err.println("shallUseOffscreenFBOLayer "+shallUseOffscreenFBOLayer); |