From 6516a52d3da5cced924db63b64af911d55355325 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Tue, 27 Jan 2015 00:49:51 +0100
Subject: Bug 1120 - Refine HiDPI Support ( Part-2 ) (API CHANGE)

- Use float[2] for pixel-scale.
  Utilize simple integer rounding:
    int-pixel-units = (int) ( int-window-units * pixel-scale + 0.5f )

- Provide minimum and maximum allowed pixel-scale values
  to be set by platform, supporting generic pixel-scale validation.

- Remove 'OSXUtil.GetPixelScale(final RectangleImmutable r, final int[] screenIndexOut)',
  implementation for all platforms would cause huge redundancy of
  Screen and MonitorDevice code (duplication of NEWT).

- instead, add 'float[2] pixelScale' to NEWT's MonitorDevice

- Detect change of pixel-scale and propagate accordingly.
  This allows GLCanvas, GLJPanel and NewtCanvasAWT instances
  to be dragged between monitor devices w/ different pixel-scale.

- OSX: Handle native triggered reshape events off-thread to avoid EDT congestion
       due to locked window when consuming deferred events on EDT.
---
 .../classes/javax/media/opengl/awt/GLCanvas.java   | 103 +++++++++++-----
 .../classes/javax/media/opengl/awt/GLJPanel.java   | 132 ++++++++++++++-------
 2 files changed, 159 insertions(+), 76 deletions(-)

(limited to 'src/jogl/classes/javax/media/opengl/awt')

diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
index a648e3bf6..a3b84a838 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
@@ -178,9 +178,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle
   private volatile GLContextImpl context; // volatile: avoid locking for read-only access
   private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking
-  private final int[] nativePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
-  private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
-  final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+  private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
   // copy of the cstr args, mainly for recreation
   private final GLCapabilitiesImmutable capsReqUser;
@@ -642,38 +643,73 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   }
 
   @Override
-  public final void setSurfaceScale(final int[] pixelScale) {
-      SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
-      if( isRealized() ) {
-          final ScalableSurface ns = jawtWindow;
-          if( null != ns ) {
-              ns.setSurfaceScale(reqPixelScale);
-              final int hadPixelScaleX = hasPixelScale[0];
-              final int hadPixelScaleY = hasPixelScale[1];
-              ns.getCurrentSurfaceScale(hasPixelScale);
-              if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) {
-                  reshapeImpl(getWidth(), getHeight());
-                  display();
-              }
-          }
+  public final boolean setSurfaceScale(final float[] pixelScale) {
+      System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2);
+      if( isRealized() && isShowing ) {
+          Threading.invoke(true, setSurfaceScaleOnEDTAction, getTreeLock());
+          return true;
+      } else {
+          return false;
+      }
+  }
+  private final Runnable setSurfaceScaleOnEDTAction = new Runnable() {
+    @Override
+    public void run() {
+        final RecursiveLock _lock = lock;
+        _lock.lock();
+        try {
+            if( null != drawable && drawable.isRealized() ) {
+                if( setSurfaceScaleImpl(jawtWindow) ) {
+                    reshapeImpl(getWidth(), getHeight());
+                    if( !helper.isAnimatorAnimatingOnOtherThread() ) {
+                        helper.invokeGL(drawable, context, displayAction, initAction); // display
+                    }
+                }
+            }
+        } finally {
+            _lock.unlock();
+        }
+    }  };
+  private final boolean setSurfaceScaleImpl(final ScalableSurface ns) {
+      if( ns.setSurfaceScale(reqPixelScale) ) {
+          ns.getCurrentSurfaceScale(hasPixelScale);
+          return true;
+      } else {
+          return false;
+      }
+  }
+
+  private final boolean updatePixelScale() {
+      if( jawtWindow.hasPixelScaleChanged() ) {
+          jawtWindow.getMaximumSurfaceScale(maxPixelScale);
+          jawtWindow.getMinimumSurfaceScale(minPixelScale);
+          return setSurfaceScaleImpl(jawtWindow);
+      } else {
+          return false;
       }
   }
 
   @Override
-  public final int[] getRequestedSurfaceScale(final int[] result) {
+  public final float[] getRequestedSurfaceScale(final float[] result) {
       System.arraycopy(reqPixelScale, 0, result, 0, 2);
       return result;
   }
 
   @Override
-  public final int[] getCurrentSurfaceScale(final int[] result) {
+  public final float[] getCurrentSurfaceScale(final float[] result) {
       System.arraycopy(hasPixelScale, 0, result, 0, 2);
       return result;
   }
 
   @Override
-  public int[] getNativeSurfaceScale(final int[] result) {
-      System.arraycopy(nativePixelScale, 0, result, 0, 2);
+  public float[] getMinimumSurfaceScale(final float[] result) {
+      System.arraycopy(minPixelScale, 0, result, 0, 2);
+      return result;
+  }
+
+  @Override
+  public float[] getMaximumSurfaceScale(final float[] result) {
+      System.arraycopy(maxPixelScale, 0, result, 0, 2);
       return result;
   }
 
@@ -681,13 +717,14 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
     if ( !Beans.isDesignTime() ) {
         jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
         jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer);
-        jawtWindow.setSurfaceScale(reqPixelScale);
         jawtWindow.lockSurface();
         try {
+            jawtWindow.setSurfaceScale(reqPixelScale);
             drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow);
             createContextImpl(drawable);
             jawtWindow.getCurrentSurfaceScale(hasPixelScale);
-            jawtWindow.getNativeSurfaceScale(nativePixelScale);
+            jawtWindow.getMinimumSurfaceScale(minPixelScale);
+            jawtWindow.getMaximumSurfaceScale(maxPixelScale);
         } finally {
             jawtWindow.unlockSurface();
         }
@@ -785,10 +822,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
         reshapeImpl(width, height);
     }
   }
-
   private void reshapeImpl(final int width, final int height) {
-    final int scaledWidth = width * hasPixelScale[0];
-    final int scaledHeight = height * hasPixelScale[1];
+    final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]);
+    final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]);
 
     if(DEBUG) {
         final NativeSurface ns = getNativeSurface();
@@ -1187,12 +1223,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
 
   @Override
   public int getSurfaceWidth() {
-      return getWidth() * hasPixelScale[0];
+      return SurfaceScaleUtils.scale(getWidth(), hasPixelScale[0]);
   }
 
   @Override
   public int getSurfaceHeight() {
-      return getHeight() * hasPixelScale[1];
+      return SurfaceScaleUtils.scale(getHeight(), hasPixelScale[1]);
   }
 
   @Override
@@ -1239,7 +1275,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   // Internals only below this point
   //
 
-  private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; }
+  private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; }
 
   private final Runnable destroyOnEDTAction = new Runnable() {
     @Override
@@ -1339,8 +1375,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
         }
         hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
         hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
-        nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
-        nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+        minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+        maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+        maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
 
         if(null != awtConfig) {
             final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration();
@@ -1391,6 +1429,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
         _lock.lock();
         try {
             if( null != drawable && drawable.isRealized() ) {
+                if( GLCanvas.this.updatePixelScale() ) {
+                    GLCanvas.this.reshapeImpl(getWidth(), getHeight());
+                }
                 helper.invokeGL(drawable, context, displayAction, initAction);
             }
         } finally {
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
index 6e9e28c19..65c4c5537 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
@@ -262,9 +262,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   private boolean handleReshape = false;
   private boolean sendReshape = true;
 
-  private final int[] nativePixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
-  private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
-  private final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
+  private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE };
+  private final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE };
 
   /** For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width in pixel units (scaled) */
   private int reshapeWidth;
@@ -450,20 +451,20 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
 
   @Override
   public void display() {
-    if( isShowing || ( printActive && isVisible() ) ) {
-        if (EventQueue.isDispatchThread()) {
-          // Want display() to be synchronous, so call paintImmediately()
-          paintImmediately(0, 0, getWidth(), getHeight());
-        } else {
-          // Multithreaded redrawing of Swing components is not allowed,
-          // so do everything on the event dispatch thread
-          try {
-            EventQueue.invokeAndWait(paintImmediatelyAction);
-          } catch (final Exception e) {
-            throw new GLException(e);
+      if( isShowing || ( printActive && isVisible() ) ) {
+          if (EventQueue.isDispatchThread()) {
+              // Want display() to be synchronous, so call paintImmediately()
+              paintImmediatelyAction.run();
+          } else {
+              // Multithreaded redrawing of Swing components is not allowed,
+              // so do everything on the event dispatch thread
+              try {
+                  EventQueue.invokeAndWait(paintImmediatelyAction);
+              } catch (final Exception e) {
+                  throw new GLException(e);
+              }
           }
-        }
-    }
+      }
   }
 
   protected void dispose(final Runnable post) {
@@ -556,6 +557,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
         // re-creating it -- tricky to do properly while the context is
         // current
         if( !printActive ) {
+            updatePixelScale(backend);
             if ( handleReshape ) {
                 handleReshape = false;
                 sendReshape = handleReshape();
@@ -579,36 +581,76 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   }
 
   @Override
-  public final void setSurfaceScale(final int[] pixelScale) { // HiDPI support
-      SurfaceScaleUtils.validateReqPixelScale(reqPixelScale, pixelScale, DEBUG ? getClass().getSimpleName() : null);
-      final int hadPixelScaleX = hasPixelScale[0];
-      final int hadPixelScaleY = hasPixelScale[1];
-      SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, nativePixelScale, DEBUG ? getClass().getSimpleName() : null);
-      if( hadPixelScaleX != hasPixelScale[0] || hadPixelScaleY != hasPixelScale[1] ) {
-          reshapeImpl(getWidth(), getHeight());
-          final Backend b = backend;
-          if ( isInitialized && null != b ) {
-              updateWrappedSurfaceScale(b.getDrawable());
-              display();
+  public final boolean setSurfaceScale(final float[] pixelScale) { // HiDPI support
+      System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2);
+      final Backend b = backend;
+      if ( isInitialized && null != b && isShowing ) {
+          if( isShowing || ( printActive && isVisible() ) ) {
+              if (EventQueue.isDispatchThread()) {
+                  setSurfaceScaleAction.run();
+              } else {
+                  try {
+                      EventQueue.invokeAndWait(setSurfaceScaleAction);
+                  } catch (final Exception e) {
+                      throw new GLException(e);
+                  }
+              }
           }
+          return true;
+      } else {
+          return false;
+      }
+  }
+  private final Runnable setSurfaceScaleAction = new Runnable() {
+    @Override
+    public void run() {
+        final Backend b = backend;
+        if( null != b && setSurfaceScaleImpl(b) ) {
+            if( !helper.isAnimatorAnimatingOnOtherThread() ) {
+                paintImmediatelyAction.run(); // display
+            }
+        }
+    }
+  };
+
+  private final boolean setSurfaceScaleImpl(final Backend b) {
+      if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null) ) {
+          reshapeImpl(getWidth(), getHeight());
+          updateWrappedSurfaceScale(b.getDrawable());
+          return true;
+      }
+      return false;
+  }
+
+  private final boolean updatePixelScale(final Backend b) {
+      if( JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale) ) {
+          return setSurfaceScaleImpl(b);
+      } else {
+          return false;
       }
   }
 
   @Override
-  public final int[] getRequestedSurfaceScale(final int[] result) {
+  public final float[] getRequestedSurfaceScale(final float[] result) {
       System.arraycopy(reqPixelScale, 0, result, 0, 2);
       return result;
   }
 
   @Override
-  public final int[] getCurrentSurfaceScale(final int[] result) {
+  public final float[] getCurrentSurfaceScale(final float[] result) {
       System.arraycopy(hasPixelScale, 0, result, 0, 2);
       return result;
   }
 
   @Override
-  public int[] getNativeSurfaceScale(final int[] result) {
-      System.arraycopy(nativePixelScale, 0, result, 0, 2);
+  public float[] getMinimumSurfaceScale(final float[] result) {
+      System.arraycopy(minPixelScale, 0, result, 0, 2);
+      return result;
+  }
+
+  @Override
+  public float[] getMaximumSurfaceScale(final float[] result) {
+      System.arraycopy(maxPixelScale, 0, result, 0, 2);
       return result;
   }
 
@@ -624,12 +666,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
     awtWindowClosingProtocol.addClosingListener();
 
     // HiDPI support
-    {
-        final int ps = JAWTUtil.getPixelScale(getGraphicsConfiguration());
-        nativePixelScale[0] = ps;
-        nativePixelScale[1] = ps;
-    }
-    SurfaceScaleUtils.computePixelScale(hasPixelScale, hasPixelScale, reqPixelScale, nativePixelScale, DEBUG ? getClass().getSimpleName() : null);
+    JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale);
+    SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null);
 
     if (DEBUG) {
         System.err.println(getThreadName()+": GLJPanel.addNotify()");
@@ -649,8 +687,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
     dispose(null);
     hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
     hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
-    nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
-    nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+    minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+    minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
+    maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE;
+    maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE;
 
     super.removeNotify();
   }
@@ -670,8 +710,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   }
 
   private void reshapeImpl(final int width, final int height) {
-    final int scaledWidth = width * hasPixelScale[0];
-    final int scaledHeight = height * hasPixelScale[1];
+    final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]);
+    final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]);
     if( !printActive && ( handleReshape || scaledWidth != panelWidth || scaledHeight != panelHeight ) ) {
         reshapeWidth = scaledWidth;
         reshapeHeight = scaledHeight;
@@ -826,8 +866,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
               // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
               final int awtWidth = GLJPanel.this.getWidth();
               final int awtHeight= GLJPanel.this.getHeight();
-              final int scaledAWTWidth = awtWidth * hasPixelScale[0];
-              final int scaledAWTHeight= awtHeight * hasPixelScale[1];
+              final int scaledAWTWidth = SurfaceScaleUtils.scale(awtWidth, hasPixelScale[0]);
+              final int scaledAWTHeight= SurfaceScaleUtils.scale(awtHeight, hasPixelScale[1]);
               final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable();
               if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight ||
                   drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) {
@@ -1342,7 +1382,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
     }
   }
 
-  private final String getPixelScaleStr() { return hasPixelScale[0]+"x"+hasPixelScale[1]; }
+  private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; }
 
   @Override
   public WindowClosingMode getDefaultCloseOperation() {
@@ -2058,7 +2098,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
             System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount);
         }
         // Draw resulting image in one shot
-        g.drawImage(alignedImage, 0, 0, alignedImage.getWidth()/hasPixelScale[0], alignedImage.getHeight()/hasPixelScale[1], null); // Null ImageObserver since image data is ready.
+        g.drawImage(alignedImage, 0, 0,
+                    SurfaceScaleUtils.scaleInv(alignedImage.getWidth(), hasPixelScale[0]),
+                    SurfaceScaleUtils.scaleInv(alignedImage.getHeight(), hasPixelScale[1]), null); // Null ImageObserver since image data is ready.
       }
       frameCount++;
     }
-- 
cgit v1.2.3