# HG changeset patch
# User John Swensen <jpswensen@gmail.com>
# Date 1516415414 28800
#      Fri Jan 19 18:30:14 2018 -0800
# Node ID 87669ee0b3466140bbb135f78d5ad83d3c26d09e
# Parent  f39ae754a7d88191a6f0fb248866577133d2413b
Fixed scaling problems in OpenGL for HiDPI displays by scaling the viewport and
projection for OpenGL drawing operations (bug #45093, bug #52878).

* display.h (screen_scale, set_screen_scale, do_screen_scale, do_set_screen_scale):
Create a member variable and static member functions for storing and retrieving
the screen scale. This value is normally 1.0 for most devices, however the value
can change for systems supporting HiDPI displays.

* GLCanvas.cc (GLCanvas::GLCanvas): Added calls to QT to retrieve the screen
pixel ratio and update the screen scale accordingly in the display_info
singleton.

* gl-render.cc (opengl_renderer::setup_opengl_transformation,
opengl_renderer::draw_text_background, opengl_renderer::init_marker): Updated
the orthographic transformation for the drawing operation to take the screen
scale into account.
(opengl_renderer::draw_image): Updated the pixel zoom factor based on the screen
scale for the raster of an image to the OpenGL canvas.
(opengl_renderer::set_viewport): Updated the viewport size based on the screen
scale.

* graphics.cc (axes::properties::update_font, text::properties::update_font):
Update the true font size in points based on the screen scale. The value stored
in the property is still as expected for a non-scaled screen, and the scaling is
only taken into account for drawing purposes.

* acinclude.m4 (OCTAVE_CHECK_FUNC_QSCREEN_DEVICEPIXELRATIO): New macro to test
if HiDPI support is needed.

diff --git a/libgui/graphics/GLCanvas.cc b/libgui/graphics/GLCanvas.cc
--- a/libgui/graphics/GLCanvas.cc
+++ b/libgui/graphics/GLCanvas.cc
@@ -26,10 +26,15 @@ along with Octave; see the file COPYING.
 
 #include "gl-render.h"
 #include "graphics.h"
+#include "display.h"
 
 #include "GLCanvas.h"
 #include "gl-select.h"
 
+#include <QtGlobal>
+#include <QApplication>
+#include <QScreen>
+
 namespace QtHandles
 {
 #if defined (HAVE_QOPENGLWIDGET)
@@ -51,6 +56,12 @@ namespace QtHandles
   {
     setFocusPolicy (Qt::ClickFocus);
     setFocus ();
+
+#if defined (HAVE_QSCREEN_DEVICEPIXELRATIO)
+    QScreen *screen = QApplication::primaryScreen ();
+    double screen_scale = screen->devicePixelRatio ();
+    octave::display_info::set_screen_scale (screen_scale);
+#endif
   }
 
   GLCanvas::~GLCanvas (void)
diff --git a/libinterp/corefcn/display.h b/libinterp/corefcn/display.h
--- a/libinterp/corefcn/display.h
+++ b/libinterp/corefcn/display.h
@@ -39,28 +39,32 @@ namespace octave
 
     display_info (void)
       : m_ht (1), m_wd (1), m_dp (0),
-        m_rx (72), m_ry (72), m_dpy_avail (false), m_err_msg ()
+        m_rx (72), m_ry (72), m_screen_scale (1.0),
+        m_dpy_avail (false), m_err_msg ()
     {
       init ();
     }
 
     explicit display_info (const std::string& dpy_name)
       : m_ht (1), m_wd (1), m_dp (0),
-        m_rx (72), m_ry (72), m_dpy_avail (false), m_err_msg ()
+        m_rx (72), m_ry (72), m_screen_scale (1.0),
+        m_dpy_avail (false), m_err_msg ()
     {
       init (dpy_name);
     }
 
     explicit display_info (bool query)
       : m_ht (1), m_wd (1), m_dp (0),
-        m_rx (72), m_ry (72), m_dpy_avail (false), m_err_msg ()
+        m_rx (72), m_ry (72), m_screen_scale (1.0),
+        m_dpy_avail (false), m_err_msg ()
     {
       init ("", query);
     }
 
     explicit display_info (const std::string& dpy_name, bool query)
       : m_ht (1), m_wd (1), m_dp (0),
-        m_rx (72), m_ry (72), m_dpy_avail (false), m_err_msg ()
+        m_rx (72), m_ry (72), m_screen_scale (1.0),
+        m_dpy_avail (false), m_err_msg ()
     {
       init (dpy_name, query);
     }
@@ -92,6 +96,17 @@ namespace octave
       return instance_ok () ? instance->do_y_dpi () : 0;
     }
 
+    static double screen_scale (void)
+    {
+      return instance_ok () ? instance->do_screen_scale () : 0;
+    }
+
+    static void set_screen_scale (double screen_scale)
+    {
+      if (instance_ok ())
+        instance->do_set_screen_scale (screen_scale);
+    }
+
     static bool display_available (void)
     {
       std::string msg;
@@ -125,6 +140,9 @@ namespace octave
     double m_rx;
     double m_ry;
 
+    // The pixel scale factor for HiDPI devices (used to draw OpenGL properly).
+    double m_screen_scale;
+
     bool m_dpy_avail;
 
     std::string m_err_msg;
@@ -135,6 +153,11 @@ namespace octave
 
     double do_x_dpi (void) const { return m_rx; }
     double do_y_dpi (void) const { return m_ry; }
+    double do_screen_scale (void) const { return m_screen_scale; }
+    void do_set_screen_scale (double screen_scale)
+    {
+      m_screen_scale = screen_scale;
+    }
 
     bool do_display_available (std::string& msg) const
     {
diff --git a/libinterp/corefcn/gl-render.cc b/libinterp/corefcn/gl-render.cc
--- a/libinterp/corefcn/gl-render.cc
+++ b/libinterp/corefcn/gl-render.cc
@@ -37,6 +37,7 @@ along with Octave; see the file COPYING.
 
 #include "errwarn.h"
 #include "gl-render.h"
+#include "display.h"
 #include "oct-opengl.h"
 #include "text-renderer.h"
 
@@ -1156,7 +1157,13 @@ namespace octave
     glMultMatrixd (x_mat1.data ());
     glMatrixMode (GL_PROJECTION);
     glLoadIdentity ();
-    glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+    
+    float screen_scale_inverse = 1.0 / display_info::screen_scale ();
+    vw[2] = octave::math::round (screen_scale_inverse *
+      static_cast<float> (vw[2]));
+    vw[3] = octave::math::round (screen_scale_inverse *
+      static_cast<float> (vw[3]));
+    glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);    
     glMultMatrixd (x_mat2.data ());
     glMatrixMode (GL_MODELVIEW);
 
@@ -3538,7 +3545,14 @@ namespace octave
     glMatrixMode (GL_PROJECTION);
     glPushMatrix ();
     glLoadIdentity ();
+
+    float screen_scale_inverse = 1.0 / display_info::screen_scale ();
+    vp[2] = octave::math::round (screen_scale_inverse *
+      static_cast<float> (vp[2]));
+    vp[3] = octave::math::round (screen_scale_inverse *
+      static_cast<float> (vp[3]));
     glOrtho (0, vp[2], vp[3], 0, xZ1, xZ2);
+
     glMatrixMode (GL_MODELVIEW);
     glPushMatrix ();
     glLoadIdentity ();
@@ -3705,10 +3719,12 @@ namespace octave
         // FIXME: actually add the code to do it!
       }
 
+
     if (i0 >= i1 || j0 >= j1)
       return;
 
-    glPixelZoom (pix_dx, -pix_dy);
+    glPixelZoom (display_info::screen_scale () * pix_dx,
+      -display_info::screen_scale () * pix_dy);
     glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0);
 
     // by default this is 4
@@ -3818,7 +3834,10 @@ namespace octave
   {
 #if defined (HAVE_OPENGL)
 
-    glViewport (0, 0, w, h);
+    glViewport (0,
+    0,
+    display_info::screen_scale () * w,
+    display_info::screen_scale () * h);
 
 #else
 
@@ -3923,6 +3942,7 @@ namespace octave
     txt_renderer.set_font (props.get ("fontname").string_value (),
                            props.get ("fontweight").string_value (),
                            props.get ("fontangle").string_value (),
+                           display_info::screen_scale () *
                            props.get ("__fontsize_points__").double_value ());
   }
 
@@ -4111,7 +4131,14 @@ namespace octave
     glMatrixMode (GL_PROJECTION);
     glPushMatrix ();
     glLoadIdentity ();
+
+    float screen_scale_inverse = 1.0 / display_info::screen_scale ();
+    vw[2] = octave::math::round (screen_scale_inverse *
+      static_cast<float> (vw[2]));
+    vw[3] = octave::math::round (screen_scale_inverse *
+      static_cast<float> (vw[3]));
     glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+    
     glMatrixMode (GL_MODELVIEW);
     glPushMatrix ();
 
diff --git a/libinterp/corefcn/graphics.cc b/libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc
+++ b/libinterp/corefcn/graphics.cc
@@ -6591,6 +6591,7 @@ axes::properties::update_font (std::stri
   txt_renderer.set_font (get ("fontname").string_value (),
                          get ("fontweight").string_value (),
                          get ("fontangle").string_value (),
+                         octave::display_info::screen_scale () *
                          get ("__fontsize_points__").double_value ());
 }
 
@@ -6686,7 +6687,7 @@ axes::properties::get_extent (bool with_
                   else if (text_rotation == 90. || text_rotation == 270.)
                     ignore_vertical = true;
                 }
-
+                                                                      
               if (! ignore_horizontal)
                 {
                   ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
@@ -8747,6 +8748,7 @@ text::properties::update_font (void)
   txt_renderer.set_font (get ("fontname").string_value (),
                          get ("fontweight").string_value (),
                          get ("fontangle").string_value (),
+                         octave::display_info::screen_scale () *
                          get ("__fontsize_points__").double_value ());
 
   txt_renderer.set_color (get_color_rgb ());
diff --git a/m4/acinclude.m4 b/m4/acinclude.m4
--- a/m4/acinclude.m4
+++ b/m4/acinclude.m4
@@ -416,6 +416,36 @@ AC_DEFUN([OCTAVE_CHECK_FUNC_QSCI_FINDSEL
   fi
 ])
 dnl
+dnl Check whether the Qt class QScreen has the devicePixelRatio member function.
+dnl This member function was introduced in Qt 5.5.
+dnl
+AC_DEFUN([OCTAVE_CHECK_FUNC_QSCREEN_DEVICEPIXELRATIO], [
+  AC_CACHE_CHECK([for QScreen::devicePixelRatio in <QScreen>],
+    [octave_cv_func_qscreen_devicepixelratio],
+    [AC_LANG_PUSH(C++)
+    ac_octave_save_CPPFLAGS="$CPPFLAGS"
+    ac_octave_save_CXXFLAGS="$CXXFLAGS"
+    CPPFLAGS="$QT_CPPFLAGS $CXXPICFLAG $CPPFLAGS"
+    CXXFLAGS="$CXXPICFLAG $CXXFLAGS"
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+        #include <QApplication>
+        #include <QScreen>
+        ]], [[
+        QScreen *screen = QApplication::primaryScreen ();
+        qreal ratio = screen->devicePixelRatio ();
+        ]])],
+      octave_cv_func_qscreen_devicepixelratio=yes,
+      octave_cv_func_qscreen_devicepixelratio=no)
+    CPPFLAGS="$ac_octave_save_CPPFLAGS"
+    CXXFLAGS="$ac_octave_save_CXXFLAGS"
+    AC_LANG_POP(C++)
+  ])
+  if test $octave_cv_func_qscreen_devicepixelratio = yes; then
+    AC_DEFINE(HAVE_QSCREEN_DEVICEPIXELRATIO, 1,
+      [Define to 1 if you have the `QScreen::devicePixelRatio' member function.])
+  fi
+])
+dnl
 dnl Check whether the Qt class QTabWidget has the setMovable member function.
 dnl This member function was introduced in Qt 4.5.
 dnl
@@ -1716,6 +1746,7 @@ AC_DEFUN([OCTAVE_CHECK_QT_VERSION], [AC_
     ## version that we are testing now will be the one used.
 
     OCTAVE_CHECK_FUNC_QLINEEDIT_SETPLACEHOLDERTEXT
+    OCTAVE_CHECK_FUNC_QSCREEN_DEVICEPIXELRATIO
     OCTAVE_CHECK_FUNC_QTABWIDGET_SETMOVABLE
     OCTAVE_CHECK_MEMBER_QFONT_FORCE_INTEGER_METRICS
     OCTAVE_CHECK_MEMBER_QFONT_MONOSPACE
