changeset 25889:2da65009cc7f

Fix graphics object selection on high resolution screens (bug #49053) * gl-render.cc/h (opengl_renderer::set_device_pixel_ratio ()): New public method to set device pixel ratio independent of the actual __device_pixel_ratio__. (opengl_renderer::get_viewport_scaled): New convenience method for fetching the current viewport and scaling it (avoids code duplication). (opengl_renderer::setup_opengl_transformation, opengl_renderer::init_marker, opengl_renderer::draw_text_background): Make use of get_viewport_scaled. (opengl_renderer::draw_text_background): unscale the text bbox before computing background coordinates. * GLCanvas.cc (GLCanvas::selectFromAxes): Draw at scale 1 since mouse event return logical coordinates, not actual device pixels. * gl-select (opengl_selector::apply_pick_matrix): Make use of get_viewport_scaled. * Figure.cc (Figure::screenChanged): Force redraw after a screen change.
author Pantxo Diribarne <pantxo.diribarne@gmail.com>
date Thu, 20 Sep 2018 21:15:40 +0200
parents 6109f302cf43
children 042a1d2a7769
files libgui/graphics/Figure.cc libgui/graphics/GLCanvas.cc libgui/graphics/gl-select.cc libinterp/corefcn/gl-render.cc libinterp/corefcn/gl-render.h libinterp/corefcn/gl2ps-print.cc
diffstat 6 files changed, 81 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/graphics/Figure.cc	Tue Sep 18 21:53:14 2018 +0200
+++ b/libgui/graphics/Figure.cc	Thu Sep 20 21:15:40 2018 +0200
@@ -504,7 +504,7 @@
           win->setWindowModality (Qt::NonModal);
 
         break;
-        
+
       case figure::properties::ID___MOUSE_MODE__:
         m_container->canvas (m_handle)->setCursor (mouseMode ());
         break;
@@ -555,7 +555,7 @@
         m_blockUpdates = true;
         qWidget<QWidget> ()->setGeometry (r);
         m_blockUpdates = false;
-        
+
         updateBoundingBox (false);
       }
   }
@@ -904,14 +904,15 @@
 #if defined (HAVE_QSCREEN_DEVICEPIXELRATIO)
     QWindow* window = qWidget<QMainWindow> ()->windowHandle ();
     QScreen* screen = window->screen ();
-    
+
     gh_manager::auto_lock lock;
-    
+
     figure::properties& fp = properties<figure> ();
     fp.set___device_pixel_ratio__ (screen->devicePixelRatio ());
 
     connect (window, SIGNAL (screenChanged (QScreen*)),
              this, SLOT (screenChanged (QScreen*)));
+
 #endif
   }
 
@@ -920,9 +921,18 @@
   {
 #if defined (HAVE_QSCREEN_DEVICEPIXELRATIO)
     gh_manager::auto_lock lock;
-    
+
     figure::properties& fp = properties<figure> ();
-    fp.set___device_pixel_ratio__ (screen->devicePixelRatio ());
+    double old_dpr = fp.get___device_pixel_ratio__ ();
+    double new_dpr = screen->devicePixelRatio ();
+    if (old_dpr != new_dpr)
+      {
+        fp.set___device_pixel_ratio__ (new_dpr);
+
+        // For some obscure reason, changing the __device_pixel_ratio__ property
+        // from the GUI thread does not necessarily trigger a redraw. Force it.
+        redraw ();
+      }
 #endif
   }
 
--- a/libgui/graphics/GLCanvas.cc	Tue Sep 18 21:53:14 2018 +0200
+++ b/libgui/graphics/GLCanvas.cc	Thu Sep 20 21:15:40 2018 +0200
@@ -75,6 +75,7 @@
         graphics_object fig = go.get_ancestor ("figure");
         double dpr = fig.get ("__device_pixel_ratio__").double_value ();
         m_renderer.set_viewport (dpr * width (), dpr * height ());
+        m_renderer.set_device_pixel_ratio (dpr);
         m_renderer.draw (go);
       }
   }
@@ -177,10 +178,7 @@
       {
         octave::opengl_selector s (m_glfcns);
 
-        graphics_object fig = ax.get_ancestor ("figure");
-        double dpr = fig.get ("__device_pixel_ratio__").double_value ();
-
-        s.set_viewport (dpr * width (), dpr * height ());
+        s.set_viewport (width (), height ());
         return s.select (ax, pt.x (), height () - pt.y (),
                          octave::select_ignore_hittest);
       }
--- a/libgui/graphics/gl-select.cc	Tue Sep 18 21:53:14 2018 +0200
+++ b/libgui/graphics/gl-select.cc	Thu Sep 20 21:15:40 2018 +0200
@@ -35,10 +35,8 @@
   opengl_selector::apply_pick_matrix (void)
   {
     GLdouble p_matrix[16];
-    GLint viewport[4];
 
     m_glfcns.glGetDoublev (GL_PROJECTION_MATRIX, p_matrix);
-    m_glfcns.glGetIntegerv (GL_VIEWPORT, viewport);
     m_glfcns.glMatrixMode (GL_PROJECTION);
     m_glfcns.glLoadIdentity ();
 
@@ -47,12 +45,14 @@
     // the QOpenGLFunctions class so that the OpenGL implementation may
     // be selected dynamically.
 
+    Matrix viewport = get_viewport_scaled ();
+
     if (size > 0)
       {
-        m_glfcns.glTranslatef ((viewport[2] - 2 * (xp - viewport[0])) / size,
-                               (viewport[3] - 2 * (yp - viewport[1])) / size, 0);
+        m_glfcns.glTranslatef ((viewport(2) - 2 * (xp - viewport(0))) / size,
+                               (viewport(3) - 2 * (yp - viewport(1))) / size, 0);
 
-        m_glfcns.glScalef (viewport[2] / size, viewport[3] / size, 1.0);
+        m_glfcns.glScalef (viewport(2) / size, viewport(3) / size, 1.0);
       }
 
     m_glfcns.glMultMatrixd (p_matrix);
--- a/libinterp/corefcn/gl-render.cc	Tue Sep 18 21:53:14 2018 +0200
+++ b/libinterp/corefcn/gl-render.cc	Thu Sep 20 21:15:40 2018 +0200
@@ -613,12 +613,11 @@
 #endif
 
   opengl_renderer::opengl_renderer (opengl_functions& glfcns)
-    : m_glfcns (glfcns), toolkit (), xform (), xmin (), xmax (),
-      ymin (), ymax (), zmin (), zmax (), xZ1 (), xZ2 (),
-      marker_id (), filled_marker_id (), camera_pos (), camera_dir (),
-      view_vector (), interpreter ("none"), txt_renderer (),
-      m_current_light (0), m_max_lights (0), selecting (false),
-      m_devpixratio (1.)
+    : m_glfcns (glfcns), toolkit (), xform (), xmin (), xmax (), ymin (),
+      ymax (), zmin (), zmax (), xZ1 (), xZ2 (), marker_id (),
+      filled_marker_id (), camera_pos (), camera_dir (), view_vector (),
+      interpreter ("none"), txt_renderer (), m_current_light (0),
+      m_max_lights (0), selecting (false), m_devpixratio (1.)
   {
     // This constructor will fail if we don't have OpenGL or if the data
     // types we assumed in our public interface aren't compatible with the
@@ -703,11 +702,8 @@
   void
   opengl_renderer::draw_figure (const figure::properties& props)
   {
-    // Current physical-pixel to toolkit-pixel factor
-    m_devpixratio = props.get___device_pixel_ratio__ ();
-    
     // Initialize OpenGL context
-    
+
     init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ());
 
 #if defined (HAVE_OPENGL)
@@ -1212,14 +1208,6 @@
     Matrix x_mat1 = props.get_opengl_matrix_1 ();
     Matrix x_mat2 = props.get_opengl_matrix_2 ();
 
-#if defined (HAVE_FRAMEWORK_OPENGL)
-    GLint vw[4];
-#else
-    int vw[4];
-#endif
-
-    m_glfcns.glGetIntegerv (GL_VIEWPORT, vw);
-
     m_glfcns.glMatrixMode (GL_MODELVIEW);
     m_glfcns.glLoadIdentity ();
     m_glfcns.glScaled (1, 1, -1);
@@ -1227,11 +1215,8 @@
     m_glfcns.glMatrixMode (GL_PROJECTION);
     m_glfcns.glLoadIdentity ();
 
-     // Use abstract Octave-pixels for transformation, not physical-pixels
-     vw[2] = octave::math::round (static_cast<float> (vw[2]) / m_devpixratio);
-     vw[3] = octave::math::round (static_cast<float> (vw[3]) / m_devpixratio);
-
-    m_glfcns.glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+    Matrix vp = get_viewport_scaled ();
+    m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
     m_glfcns.glMultMatrixd (x_mat2.data ());
     m_glfcns.glMatrixMode (GL_MODELVIEW);
 
@@ -3678,28 +3663,17 @@
     if (bgcol.isempty () && ecol.isempty ())
       return;
 
-    Matrix pos = xform.scale (props.get_data_position ());
+    Matrix pos = props.get_data_position ();
     ColumnVector pixpos = get_transform ().transform (pos(0), pos(1),
-                                                      pos(2), false);
-    const Matrix bbox = props.get_extent_matrix ();
-
-#  if defined (HAVE_FRAMEWORK_OPENGL)
-    GLint vp[4];
-#  else
-    int vp[4];
-#  endif
-
-    m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
+                                                      pos(2), true);
 
     // Save current transform matrices and set orthogonal window coordinates
     m_glfcns.glMatrixMode (GL_PROJECTION);
     m_glfcns.glPushMatrix ();
     m_glfcns.glLoadIdentity ();
 
-    vp[2] = octave::math::round (static_cast<float> (vp[2]) / m_devpixratio);
-    vp[3] = octave::math::round (static_cast<float> (vp[3]) / m_devpixratio);
-
-    m_glfcns.glOrtho (0, vp[2], vp[3], 0, xZ1, xZ2);
+    Matrix vp = get_viewport_scaled ();
+    m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
     m_glfcns.glMatrixMode (GL_MODELVIEW);
     m_glfcns.glPushMatrix ();
     m_glfcns.glLoadIdentity ();
@@ -3716,10 +3690,11 @@
       m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
 
     double m = props.get_margin ();
-    double x0 = bbox (0) - m;
-    double x1 = x0 + bbox(2) + 2 * m;
-    double y0 = -(bbox (1) - m);
-    double y1 = y0 - (bbox(3) + 2 * m);
+    const Matrix bbox = props.get_extent_matrix ();
+    double x0 = bbox (0) / m_devpixratio - m;
+    double x1 = x0 + bbox(2) / m_devpixratio + 2 * m;
+    double y0 = -(bbox (1) / m_devpixratio - m);
+    double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m);
 
     if (! bgcol.isempty ())
       {
@@ -3863,8 +3838,6 @@
       }
     else // clip to viewport
       {
-        GLfloat vp[4];
-        m_glfcns.glGetFloatv (GL_VIEWPORT, vp);
         // FIXME: actually add the code to do it!
       }
 
@@ -3997,6 +3970,35 @@
 #endif
   }
 
+  Matrix
+  opengl_renderer::get_viewport_scaled (void) const
+  {
+    Matrix retval (1, 4, 0.0);
+
+#if defined (HAVE_OPENGL)
+#if defined (HAVE_FRAMEWORK_OPENGL)
+    GLint vp[4];
+#else
+    int vp[4];
+#endif
+
+    m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
+
+    for (int i = 0; i < 4; i++)
+      retval(i) = static_cast<double> (vp[i]) / m_devpixratio;
+
+#else
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+
+    return retval;
+  }
+
   void
   opengl_renderer::draw_pixels (int width, int height, const float *data)
   {
@@ -4083,7 +4085,7 @@
 
   void
   opengl_renderer::set_font (const base_properties& props)
-  {    
+  {
     txt_renderer.set_font (props.get ("fontname").string_value (),
                            props.get ("fontweight").string_value (),
                            props.get ("fontangle").string_value (),
@@ -4264,23 +4266,12 @@
   opengl_renderer::init_marker (const std::string& m, double size, float width)
   {
 #if defined (HAVE_OPENGL)
-
-#  if defined (HAVE_FRAMEWORK_OPENGL)
-    GLint vw[4];
-#  else
-    int vw[4];
-#  endif
-
-    m_glfcns.glGetIntegerv (GL_VIEWPORT, vw);
-
     m_glfcns.glMatrixMode (GL_PROJECTION);
     m_glfcns.glPushMatrix ();
     m_glfcns.glLoadIdentity ();
 
-    vw[2] = octave::math::round (static_cast<float> (vw[2]) / m_devpixratio);
-    vw[3] = octave::math::round (static_cast<float> (vw[3]) / m_devpixratio);
-
-    m_glfcns.glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+    Matrix vp = get_viewport_scaled ();
+    m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
     m_glfcns.glMatrixMode (GL_MODELVIEW);
     m_glfcns.glPushMatrix ();
 
--- a/libinterp/corefcn/gl-render.h	Tue Sep 18 21:53:14 2018 +0200
+++ b/libinterp/corefcn/gl-render.h	Thu Sep 20 21:15:40 2018 +0200
@@ -65,6 +65,8 @@
     }
 
     virtual void set_viewport (int w, int h);
+    virtual void set_device_pixel_ratio (double dpr) { m_devpixratio = dpr; }
+    virtual Matrix get_viewport_scaled (void) const;
     virtual graphics_xform get_transform (void) const { return xform; }
     virtual uint8NDArray get_pixels (int width, int height);
 
@@ -247,6 +249,7 @@
 
     // Factor used for translating Octave pixels to actual device pixels
     double m_devpixratio;
+
   private:
     class patch_tesselator;
   };
--- a/libinterp/corefcn/gl2ps-print.cc	Tue Sep 18 21:53:14 2018 +0200
+++ b/libinterp/corefcn/gl2ps-print.cc	Thu Sep 20 21:15:40 2018 +0200
@@ -413,6 +413,9 @@
             Matrix c = fprop.get_color_rgb ();
             m_glfcns.glClearColor (c(0), c(1), c(2), 1);
 
+            // Allow figures to be printed at arbitrary resolution
+            set_device_pixel_ratio (fprop.get___device_pixel_ratio__ ());
+
             // GL2PS_SILENT was removed to allow gl2ps to print errors on stderr
             GLint ret = gl2psBeginPage ("gl2ps_renderer figure", "Octave",
                                         nullptr, gl2ps_term, gl2ps_sort,
@@ -920,11 +923,11 @@
             const std::string tmpstr = txtobj.get_string ();
             const uint8_t *c =
               reinterpret_cast<const uint8_t *> (tmpstr.c_str ());
-            
+
             for (size_t i = 0; i < tmpstr.size ();)
               {
                 int mblen = octave_u8_strmblen_wrapper (c + i);
-                
+
                 if (mblen > 1)
                   {
                     str += " ";
@@ -939,7 +942,7 @@
                   }
                 else
                   str += tmpstr.at (i);
-                
+
                 i += mblen;
               }
           }