# HG changeset patch # User Pantxo Diribarne # Date 1519392835 -3600 # Node ID 45470049a43f0bbbbcbec2615b67916f4f3f36e4 # Parent b901d3123745085cfe651b7e3fb0168945d5b268 Allow getframe and print to work without osmesa (bug #53186). * NEWS: Announce support for printing invisible qt figures without osmesa. * __opengl_print__.m: Allow invisible figures to be printed with qt if QOFFSCREENSURFACE is present. * getframe.m: Allow using __get_frame__ on invisible qt figures if QOFFSCREENSURFACE is present. * Backenc.cc (Backend::get_pixels): Don't restrict to visible figures. * Canvas.h/cc (Canvas::do_print): New pure virtual function (Canvas::print): Call do_print. * GLCanvas.h/cc (GLCanvas::begin_rendering): New methods to make sure a valid OpenGL context is made current before rendering. (GLCanvas::end_rendering): Call doneCurrent. (GLCanvas::getPixels): Make use of begin/end_rendering. If the figure is not visible or its size is frozen, draw on a framebuffer object of suitable size. (GLCanvas::do_print): Move code for OpenGL printing here. Make use of begin/end_rendering. * acinclude.m4: Add test for presence and usability of QOffscreenSurface. * configure.ac: Allow building the manual with qt if QOffscreenSurface is OK. * geometryimages.m, interpimages.m, plotimages.m, sparseimages.m, splineimages.m: Allow using qt toolkit to generate images for the manual when QOFFSCREENSURFACE is present. diff -r b901d3123745 -r 45470049a43f NEWS --- a/NEWS Mon Feb 26 15:43:28 2018 -0800 +++ b/NEWS Fri Feb 23 14:33:55 2018 +0100 @@ -63,6 +63,9 @@ explicitly instructed to perform an economy factorization by using a final argument of 0. + ** The Qt graphics toolkit now supports offscreen printing without osmesa + if Octave was built with Qt >= 5.1. + ** The FLTK toolkit is no longer prioritized for development. The number of Octave Maintainers is too small to support three different graphic toolkits. New development will target the Qt toolkit. diff -r b901d3123745 -r 45470049a43f configure.ac --- a/configure.ac Mon Feb 26 15:43:28 2018 -0800 +++ b/configure.ac Fri Feb 23 14:33:55 2018 +0100 @@ -2791,7 +2791,7 @@ OCTAVE_CONFIGURE_WARNING([warn_docs]) fi], []) if test $ENABLE_DOCS = yes; then - if test $opengl_graphics = no || test -n "$warn_OSMesa"; then + if test $opengl_graphics = no || test -n "$warn_OSMesa" || test -n "$have_qt_opengl_offscreen"; then if test -n "$warn_gnuplot"; then ENABLE_DOCS=no warn_docs_graphics="building documentation disabled because no suitable graphics toolkit is available; make dist will fail." diff -r b901d3123745 -r 45470049a43f doc/interpreter/geometryimages.m --- a/doc/interpreter/geometryimages.m Mon Feb 26 15:43:28 2018 -0800 +++ b/doc/interpreter/geometryimages.m Fri Feb 23 14:33:55 2018 +0100 @@ -142,6 +142,9 @@ function set_graphics_toolkit () if (isempty (available_graphics_toolkits ())) error ("no graphics toolkit available for plotting"); + elseif (strcmp ("qt", graphics_toolkit ()) + && __have_feature__ ("QOFFSCREENSURFACE")) + ## Use qt with QOffscreenSurface for plot elseif (! strcmp ("gnuplot", graphics_toolkit ()) ... && ! __have_feature__ ("OSMESA")) if (! any (strcmp ("gnuplot", available_graphics_toolkits ()))) diff -r b901d3123745 -r 45470049a43f doc/interpreter/interpimages.m --- a/doc/interpreter/interpimages.m Mon Feb 26 15:43:28 2018 -0800 +++ b/doc/interpreter/interpimages.m Fri Feb 23 14:33:55 2018 +0100 @@ -85,6 +85,9 @@ function set_graphics_toolkit () if (isempty (available_graphics_toolkits ())) error ("no graphics toolkit available for plotting"); + elseif (strcmp ("qt", graphics_toolkit ()) + && __have_feature__ ("QOFFSCREENSURFACE")) + ## Use qt with QOffscreenSurface for plot elseif (! strcmp ("gnuplot", graphics_toolkit ()) && ! __have_feature__ ("OSMESA")) if (! any (strcmp ("gnuplot", available_graphics_toolkits ()))) diff -r b901d3123745 -r 45470049a43f doc/interpreter/plotimages.m --- a/doc/interpreter/plotimages.m Mon Feb 26 15:43:28 2018 -0800 +++ b/doc/interpreter/plotimages.m Fri Feb 23 14:33:55 2018 +0100 @@ -123,6 +123,9 @@ function set_graphics_toolkit () if (isempty (available_graphics_toolkits ())) error ("no graphics toolkit available for plotting"); + elseif (strcmp ("qt", graphics_toolkit ()) + && __have_feature__ ("QOFFSCREENSURFACE")) + ## Use qt with QOffscreenSurface for plot elseif (! strcmp ("gnuplot", graphics_toolkit ()) ... && ! __have_feature__ ("OSMESA")) if (! any (strcmp ("gnuplot", available_graphics_toolkits ()))) diff -r b901d3123745 -r 45470049a43f doc/interpreter/sparseimages.m --- a/doc/interpreter/sparseimages.m Mon Feb 26 15:43:28 2018 -0800 +++ b/doc/interpreter/sparseimages.m Fri Feb 23 14:33:55 2018 +0100 @@ -244,6 +244,9 @@ function set_graphics_toolkit () if (isempty (available_graphics_toolkits ())) error ("no graphics toolkit available for plotting"); + elseif (strcmp ("qt", graphics_toolkit ()) + && __have_feature__ ("QOFFSCREENSURFACE")) + ## Use qt with QOffscreenSurface for plot elseif (! strcmp ("gnuplot", graphics_toolkit ()) && ! __have_feature__ ("OSMESA")) if (! any (strcmp ("gnuplot", available_graphics_toolkits ()))) diff -r b901d3123745 -r 45470049a43f doc/interpreter/splineimages.m --- a/doc/interpreter/splineimages.m Mon Feb 26 15:43:28 2018 -0800 +++ b/doc/interpreter/splineimages.m Fri Feb 23 14:33:55 2018 +0100 @@ -175,6 +175,9 @@ function set_graphics_toolkit () if (isempty (available_graphics_toolkits ())) error ("no graphics toolkit available for plotting"); + elseif (strcmp ("qt", graphics_toolkit ()) + && __have_feature__ ("QOFFSCREENSURFACE")) + ## Use qt with QOffscreenSurface for plot elseif (! strcmp ("gnuplot", graphics_toolkit ()) && ! __have_feature__ ("OSMESA")) if (! any (strcmp ("gnuplot", available_graphics_toolkits ()))) diff -r b901d3123745 -r 45470049a43f libgui/graphics/Backend.cc --- a/libgui/graphics/Backend.cc Mon Feb 26 15:43:28 2018 -0800 +++ b/libgui/graphics/Backend.cc Fri Feb 23 14:33:55 2018 +0100 @@ -197,7 +197,7 @@ { uint8NDArray retval; - if (go.get_properties ().is_visible () && go.isa ("figure")) + if (go.isa ("figure")) { ObjectProxy *proxy = toolkitObjectProxy (go); diff -r b901d3123745 -r 45470049a43f libgui/graphics/Canvas.cc --- a/libgui/graphics/Canvas.cc Mon Feb 26 15:43:28 2018 -0800 +++ b/libgui/graphics/Canvas.cc Fri Feb 23 14:33:55 2018 +0100 @@ -41,7 +41,6 @@ #include "annotation-dialog.h" -#include "gl2ps-print.h" #include "oct-opengl.h" #include "octave-qt-link.h" @@ -101,27 +100,6 @@ } } - void - Canvas::print (const QString& file_cmd, const QString& term) - { - gh_manager::auto_lock lock; - graphics_object obj = gh_manager::get_object (m_handle); - - if (obj.valid_object ()) - { - graphics_object figObj (obj.get_ancestor ("figure")); - try - { - octave::gl2ps_print (figObj, file_cmd.toStdString (), - term.toStdString ()); - } - catch (octave::execution_exception e) - { - octave_link::post_exception (std::current_exception ()); - } - } - } - /* Two updateCurrentPoint() routines are required: 1) Used for QMouseEvents where cursor position data is in callback from Qt. diff -r b901d3123745 -r 45470049a43f libgui/graphics/Canvas.h --- a/libgui/graphics/Canvas.h Mon Feb 26 15:43:28 2018 -0800 +++ b/libgui/graphics/Canvas.h Fri Feb 23 14:33:55 2018 +0100 @@ -54,7 +54,10 @@ void redraw (bool sync = false); void blockRedraw (bool block = true); - void print (const QString& file_cmd, const QString& term); + void print (const QString& file_cmd, const QString& term) + { + do_print (file_cmd, term, m_handle); + } void addEventMask (int m) { m_eventMask |= m; } void clearEventMask (int m) { m_eventMask &= (~m); } @@ -80,6 +83,8 @@ virtual graphics_object selectFromAxes (const graphics_object& ax, const QPoint& pt) = 0; virtual uint8NDArray do_getPixels (const graphics_handle& handle) = 0; + virtual void do_print (const QString& file_cmd, const QString& term, + const graphics_handle& handle) = 0; protected: Canvas (const graphics_handle& handle) diff -r b901d3123745 -r 45470049a43f libgui/graphics/GLCanvas.cc --- a/libgui/graphics/GLCanvas.cc Mon Feb 26 15:43:28 2018 -0800 +++ b/libgui/graphics/GLCanvas.cc Fri Feb 23 14:33:55 2018 +0100 @@ -25,7 +25,9 @@ #endif #include "gl-render.h" +#include "gl2ps-print.h" #include "graphics.h" +#include "octave-link.h" #include "GLCanvas.h" #include "gl-select.h" @@ -76,18 +78,73 @@ { uint8NDArray retval; graphics_object go = gh_manager::get_object (gh); - - if (go) + + if (go && go.isa ("figure")) { - octave::opengl_renderer r; + Matrix pos = go.get ("position").matrix_value (); + + // Make sure we have a valid current context + if (! begin_rendering ()) + return retval; - r.set_viewport (width (), height ()); - r.draw (go); - retval = r.get_pixels (width (), height ()); + // When the figure is not visible or its size is frozen for printing, + // we use a framebuffer object to make sure we are rendering on a + // suitably large frame. + if (go.get ("visible").string_value () == "off" + || go.get ("__printing__").string_value () == "on") + { + OCTAVE_QT_OPENGL_FBO + fbo (pos(2), pos(3),OCTAVE_QT_OPENGL_FBO::Attachment::Depth); + + fbo.bind (); + + octave::opengl_renderer r; + r.set_viewport (pos(2), pos(3)); + r.draw (go); + retval = r.get_pixels (pos(2), pos(3)); + + fbo.release (); + } + else + { + octave::opengl_renderer r; + r.set_viewport (pos(2), pos(3)); + r.draw (go); + retval = r.get_pixels (pos(2), pos(3)); + } + + end_rendering (); } return retval; } + + void + GLCanvas::do_print (const QString& file_cmd, const QString& term, + const graphics_handle& handle) + { + gh_manager::auto_lock lock; + graphics_object obj = gh_manager::get_object (handle); + + if (obj.valid_object ()) + { + graphics_object figObj (obj.get_ancestor ("figure")); + try + { + // Make sure we have a valid current context + if (! begin_rendering ()) + error ("print: no valid OpenGL offscreen context"); + + octave::gl2ps_print (figObj, file_cmd.toStdString (), + term.toStdString ()); + } + catch (octave::execution_exception e) + { + octave_link::post_exception (std::current_exception ()); + end_rendering (); + } + } + } void GLCanvas::toggleAxes (const graphics_handle& gh) @@ -219,4 +276,40 @@ OCTAVE_QT_OPENGL_WIDGET::keyReleaseEvent (xevent); } + bool + GLCanvas::begin_rendering (void) + { + bool retval = true; + + if (! isValid ()) + { +# if defined (HAVE_QOFFSCREENSURFACE) + static bool os_ctx_ok = true; + if (os_ctx_ok && ! m_os_context.isValid ()) + { + // Try to initialize offscreen context + m_os_surface.create (); + if (! m_os_context.create ()) + { + os_ctx_ok = false; + return false; + } + } + + retval = m_os_context.makeCurrent (&m_os_surface); +# else + retval = false; +# endif + } + else + makeCurrent (); + + return retval; + } + + void + GLCanvas::end_rendering (void) + { + doneCurrent (); + } } diff -r b901d3123745 -r 45470049a43f libgui/graphics/GLCanvas.h --- a/libgui/graphics/GLCanvas.h Mon Feb 26 15:43:28 2018 -0800 +++ b/libgui/graphics/GLCanvas.h Fri Feb 23 14:33:55 2018 +0100 @@ -26,9 +26,17 @@ #if defined (HAVE_QOPENGLWIDGET) # include # define OCTAVE_QT_OPENGL_WIDGET QOpenGLWidget +# include +# define OCTAVE_QT_OPENGL_FBO QOpenGLFramebufferObject +# if defined (HAVE_QOFFSCREENSURFACE) +# include +# include +# endif #elif defined (HAVE_QGLWIDGET) # include # define OCTAVE_QT_OPENGL_WIDGET QGLWidget +# include +# define OCTAVE_QT_OPENGL_FBO QGLFramebufferObject #else # error "configuration error: must have or ." #endif @@ -45,6 +53,8 @@ void draw (const graphics_handle& handle); uint8NDArray do_getPixels (const graphics_handle& handle); + void do_print (const QString& file_cmd, const QString& term, + const graphics_handle& handle); void toggleAxes (const graphics_handle& handle); void toggleGrid (const graphics_handle& handle); void autoAxes (const graphics_handle& handle); @@ -64,6 +74,16 @@ void wheelEvent (QWheelEvent *event); void keyPressEvent (QKeyEvent *event); void keyReleaseEvent (QKeyEvent *event); + + private: + + bool begin_rendering (void); + void end_rendering (void); + +# if defined (HAVE_QOFFSCREENSURFACE) + QOpenGLContext m_os_context; + QOffscreenSurface m_os_surface; +# endif }; } diff -r b901d3123745 -r 45470049a43f m4/acinclude.m4 --- a/m4/acinclude.m4 Mon Feb 26 15:43:28 2018 -0800 +++ b/m4/acinclude.m4 Fri Feb 23 14:33:55 2018 +0100 @@ -1470,6 +1470,60 @@ AM_CONDITIONAL([WIN32_TERMINAL], [test $win32_terminal = yes]) ]) dnl +dnl Check whether QOffscreenSurface is present. +dnl +AC_DEFUN([OCTAVE_CHECK_QT_OPENGL_OFFSCREEN_OK], [ + dnl Normally the language and compiler flags would be set and restored + dnl inside of the AC_CACHE_CHECK body. Because we also need to check for + dnl Qt header files associated with the compilation test, set and restore + dnl these values outside of the AC_CACHE_CHECK for this macro only. + AC_LANG_PUSH(C++) + ac_octave_save_CPPFLAGS="$CPPFLAGS" + ac_octave_save_CXXFLAGS="$CXXFLAGS" + CPPFLAGS="$QT_CPPFLAGS $CXXPICFLAG $CPPFLAGS" + CXXFLAGS="$CXXPICFLAG $CXXFLAGS" + AC_CHECK_HEADERS([QOffscreenSurface]) + AC_CACHE_CHECK([whether Qt supports full offscreen OpenGL rendering], + [octave_cv_qt_opengl_os_ok], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #if HAVE_WINDOWS_H + # include + #endif + #if defined (HAVE_GL_GL_H) + # include + #elif defined (HAVE_OPENGL_GL_H) + # include + #endif + #if defined (HAVE_GL_GLU_H) + # include + #elif defined HAVE_OPENGL_GLU_H || defined HAVE_FRAMEWORK_OPENGL + # include + #endif + #if defined (HAVE_QOPENGLWIDGET) + # include + # include + #endif + #if defined (HAVE_QOFFSCREENSURFACE) + # include + #endif + QOpenGLContext ctx; + QOffscreenSurface surf; + ]])], + octave_cv_qt_opengl_os_ok=yes, + octave_cv_qt_opengl_os_ok=no) + ]) + CPPFLAGS="$ac_octave_save_CPPFLAGS" + CXXFLAGS="$ac_octave_save_CXXFLAGS" + AC_LANG_POP(C++) + if test $octave_cv_qt_opengl_os_ok = yes; then + $1 + : + else + $2 + : + fi +]) +dnl dnl Check whether Qt works with full OpenGL support dnl AC_DEFUN([OCTAVE_CHECK_QT_OPENGL_OK], [ @@ -1532,7 +1586,7 @@ ]) dnl dnl Check whether Qt VERSION is present, supports QtOpenGL and -dnl QScintilla and will work for Octave. +dnl QScintilla, and will work for Octave. dnl dnl OCTAVE_CHECK_QT_VERSION(VERSION) dnl @@ -1545,6 +1599,7 @@ build_qt_gui=yes build_qt_graphics=no + have_qt_opengl_offscreen=no win32_terminal=no warn_qt_libraries="" @@ -1725,6 +1780,7 @@ [warn_qt_opengl="Qt does not work with the OpenGL libs (GL and GLU); disabling OpenGL graphics with Qt GUI"]) if test $build_qt_graphics = yes; then + OCTAVE_CHECK_QT_OPENGL_OFFSCREEN_OK([have_qt_opengl_offscreen=yes]) AC_DEFINE(HAVE_QT_GRAPHICS, 1, [Define to 1 if Qt works with OpenGL libs (GL and GLU)]) fi fi diff -r b901d3123745 -r 45470049a43f scripts/image/getframe.m --- a/scripts/image/getframe.m Mon Feb 26 15:43:28 2018 -0800 +++ b/scripts/image/getframe.m Fri Feb 23 14:33:55 2018 +0100 @@ -95,7 +95,10 @@ pos = rect; endif - if (strcmp (get (hf, "visible"), "on")) + if (strcmp (get (hf, "visible"), "on") + || (strcmp (get (hf, "__graphics_toolkit__"), "qt") + && (strcmp (get (hf, "__gl_window__"), "on") + || __have_feature__ ("QOFFSCREENSURFACE")))) cdata = __get_frame__ (hf); else ## Use OpenGL offscreen rendering with OSMesa for non-visible figures diff -r b901d3123745 -r 45470049a43f scripts/plot/util/private/__opengl_print__.m --- a/scripts/plot/util/private/__opengl_print__.m Mon Feb 26 15:43:28 2018 -0800 +++ b/scripts/plot/util/private/__opengl_print__.m Fri Feb 23 14:33:55 2018 +0100 @@ -152,7 +152,8 @@ if (strcmp (get (opts.figure, "visible"), "on") || (strcmp (get (opts.figure, "__graphics_toolkit__"), "qt") - && strcmp (get (opts.figure, "__gl_window__"), "on"))) + && (strcmp (get (opts.figure, "__gl_window__"), "on") + || __have_feature__ ("QOFFSCREENSURFACE")))) ## Use toolkits "print_figure" method drawnow (gl2ps_device{n}, ['|' pipeline{n}]); else