Mercurial > octave
changeset 24798:45470049a43f
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.
author | Pantxo Diribarne <pantxo.diribarne@gmail.com> |
---|---|
date | Fri, 23 Feb 2018 14:33:55 +0100 |
parents | b901d3123745 |
children | 74a596fd6bab |
files | NEWS configure.ac doc/interpreter/geometryimages.m doc/interpreter/interpimages.m doc/interpreter/plotimages.m doc/interpreter/sparseimages.m doc/interpreter/splineimages.m libgui/graphics/Backend.cc libgui/graphics/Canvas.cc libgui/graphics/Canvas.h libgui/graphics/GLCanvas.cc libgui/graphics/GLCanvas.h m4/acinclude.m4 scripts/image/getframe.m scripts/plot/util/private/__opengl_print__.m |
diffstat | 15 files changed, 208 insertions(+), 34 deletions(-) [+] |
line wrap: on
line diff
--- 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.
--- 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."
--- 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 ())))
--- 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 ())))
--- 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 ())))
--- 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 ())))
--- 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 ())))
--- 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);
--- 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.
--- 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)
--- 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 (); + } }
--- 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 <QOpenGLWidget> # define OCTAVE_QT_OPENGL_WIDGET QOpenGLWidget +# include <QOpenGLFramebufferObject> +# define OCTAVE_QT_OPENGL_FBO QOpenGLFramebufferObject +# if defined (HAVE_QOFFSCREENSURFACE) +# include <QOpenGLContext> +# include <QOffscreenSurface> +# endif #elif defined (HAVE_QGLWIDGET) # include <QGLWidget> # define OCTAVE_QT_OPENGL_WIDGET QGLWidget +# include <QGLFramebufferObject> +# define OCTAVE_QT_OPENGL_FBO QGLFramebufferObject #else # error "configuration error: must have <QOpenGLWidget> or <QGLWidget>." #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 }; }
--- 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 <windows.h> + #endif + #if defined (HAVE_GL_GL_H) + # include <GL/gl.h> + #elif defined (HAVE_OPENGL_GL_H) + # include <OpenGL/gl.h> + #endif + #if defined (HAVE_GL_GLU_H) + # include <GL/glu.h> + #elif defined HAVE_OPENGL_GLU_H || defined HAVE_FRAMEWORK_OPENGL + # include <OpenGL/glu.h> + #endif + #if defined (HAVE_QOPENGLWIDGET) + # include <QOpenGLWidget> + # include <QOpenGLContext> + #endif + #if defined (HAVE_QOFFSCREENSURFACE) + # include <QOffscreenSurface> + #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
--- 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
--- 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