Blob Blame History Raw
From d1f6fc47af86a5db0a07effdefb10573f49928fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Br=C3=BCns?= <stefan.bruens@rwth-aachen.de>
Date: Mon, 18 Dec 2017 17:39:31 +0100
Subject: [PATCH] Use QOpenGLFunctions for GL extensions, drop GLEW and
 obsolete GL stuff
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Instead of relying on GLEW for resolution of GL extension function entry
points use QOpenGLFunctions, which provides the same, but also allows to
run on both OpenGL desktop and GLES platforms.

Also drop extension based shader support. Shaders are supported since
OpenGL 2.0, so ARB shaders are likely no longer used and make the code
harder to read. Assume PBOs are available, which is true since OpenGL 1.5.

Text rendering in the pixelview overlay was apparently broken (no text
drawn at all), switch to QPainter based text drawing, as
QGLWidget::renderText() is not supported by QOpenGLWidget.

Signed-off-by: Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de>
---
 CMakeLists.txt                   |   1 -
 INSTALL.md                       |   8 +-
 Makefile                         |   5 -
 src/cmake/externalpackages.cmake |  27 ----
 src/iv/CMakeLists.txt            |   9 +-
 src/iv/imageviewer.cpp           |  17 +-
 src/iv/imageviewer.h             |   1 -
 src/iv/ivgl.cpp                  | 336 +++++++++++++--------------------------
 src/iv/ivgl.h                    |  19 +--
 9 files changed, 128 insertions(+), 295 deletions(-)

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,7 +70,6 @@ option (OIIO_BUILD_TOOLS "Build the comm
 option (OIIO_BUILD_TESTS "Build the unit tests" ON)
 option (USE_OPENGL "Include OpenGL support" ON)
 option (USE_QT "Include Qt support" ON)
-option (FORCE_OPENGL_1 "Force iv to use OpenGL's fixed pipeline")
 option (USE_PYTHON "Build the Python bindings" ON)
 set (PYTHON_VERSION "2.7" CACHE STRING "Target version of python to find")
 option (PYLIB_INCLUDE_SONAME "If ON, soname/soversion will be set for Python module library")
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -43,8 +43,7 @@ Building OpenImageIO on Linux or OS X
 The following dependencies must be installed to build the core of
 OpenImageIO: Boost, libjpeg, libtiff, libpng and OpenEXR.  These can be
 installed using the standard package managers on your system.
-Optionally, to build the image viewing tools, you will need Qt, OpenGL,
-and GLEW.
+Optionally, to build the image viewing tools, you will need Qt and OpenGL.
 
 On OS X, these dependencies can be installed using Fink, MacPorts or
 Homebrew.  After installation of any of these package installers, use
@@ -99,7 +98,6 @@ Additionally, a few helpful modifiers al
 | make USE_OPENGL=0 ...     |  Skip anything that needs OpenGL
 | make USE_QT=0 ...         |  Skip anything that needs Qt
 | make MYCC=xx MYCXX=yy ... |  Use custom compilers
-| make FORCE_OPENGL_1=1 ... |  Force iv to use OpenGL's fixed pipeline
 | make USE_PYTHON=0 ...     |  Don't build the Python binding
 | make BUILDSTATIC=1 ...    |  Build static library instead of shared
 | make LINKSTATIC=1 ...     |  Link with static external libraries when possible
@@ -218,9 +216,6 @@ D:\OIIO\external\dist\windows\zlib-1.2.3
 ZLIB and PNG, add
 D:\OIIO\external\dist\windows\zlib-1.2.3;D:\OIIO\external\dist\windows\libpng-1.2.3.
 
-Also, if cmake won't find GLEW set up GLEW_INCLUDES and GLEW_LIBRARIES
-in cmake-gui. Don't add them (they are already added), just set them.
-
 
 Test Images
 -----------
--- a/Makefile
+++ b/Makefile
@@ -79,10 +79,6 @@ ifneq (${USE_QT},)
 MY_CMAKE_FLAGS += -DUSE_QT:BOOL=${USE_QT}
 endif
 
-ifneq (${FORCE_OPENGL_1},)
-MY_CMAKE_FLAGS += -DFORCE_OPENGL_1:BOOL=${FORCE_OPENGL_1}
-endif
-
 ifneq (${OIIO_THREAD_ALLOW_DCLP},)
 MY_CMAKE_FLAGS += -DOIIO_THREAD_ALLOW_DCLP:BOOL=${OIIO_THREAD_ALLOW_DCLP}
 endif
@@ -473,7 +469,6 @@ help:
 	@echo "      USE_EXTERNAL_PUGIXML=1   Use the system PugiXML, not the one in OIIO"
 	@echo "      USE_QT=0                 Skip anything that needs Qt"
 	@echo "      USE_OPENGL=0             Skip anything that needs OpenGL"
-	@echo "      FORCE_OPENGL_1=1         Force iv to use OpenGL's fixed pipeline"
 	@echo "      USE_PYTHON=0             Don't build the Python binding"
 	@echo "      USE_PYTHON3=1            If 1, try to build against Python3, not 2.x"
 	@echo "      PYTHON_VERSION=2.6       Specify the Python version"
--- a/src/cmake/externalpackages.cmake
+++ b/src/cmake/externalpackages.cmake
@@ -9,7 +9,6 @@ if (NOT VERBOSE)
     set (Field3D_FIND_QUIETLY true)
     set (Freetype_FIND_QUIETLY true)
     set (GIF_FIND_QUIETLY true)
-    set (GLEW_FIND_QUIETLY true)
     set (HDF5_FIND_QUIETLY true)
     set (IlmBase_FIND_QUIETLY true)
     set (JPEG_FIND_QUIETLY true)
@@ -209,32 +208,6 @@ endif ()
 # end Qt setup
 ###########################################################################
 
-###########################################################################
-# GL Extension Wrangler library setup
-
-if (USE_OPENGL)
-    set (GLEW_VERSION 1.5.1)
-    find_library (GLEW_LIBRARIES
-                  NAMES GLEW glew32)
-    find_path (GLEW_INCLUDES
-               NAMES glew.h
-               PATH_SUFFIXES GL)
-    if (GLEW_INCLUDES AND GLEW_LIBRARIES)
-        set (GLEW_FOUND TRUE)
-        if (NOT GLEW_FIND_QUIETLY)
-            message (STATUS "GLEW includes = ${GLEW_INCLUDES}")
-            message (STATUS "GLEW library = ${GLEW_LIBRARIES}")
-        endif ()
-    else ()
-        message (STATUS "GLEW not found")
-    endif ()
-else ()
-    message (STATUS "USE_OPENGL=0, skipping components that need OpenGL")
-endif (USE_OPENGL)
-
-# end GL Extension Wrangler library setup
-###########################################################################
-
 
 ###########################################################################
 # BZIP2 - used by ffmped and freetype
--- a/src/iv/CMakeLists.txt
+++ b/src/iv/CMakeLists.txt
@@ -1,9 +1,9 @@
-if (Qt5_FOUND AND OPENGL_FOUND AND GLEW_FOUND)
+if (Qt5_FOUND AND OPENGL_FOUND)
     set (CMAKE_AUTOMOC ON)
 	if (Qt5_POSITION_INDEPENDENT_CODE)
         set (CMAKE_POSITION_INDEPENDENT_CODE ON)
 	endif()
-    include_directories (${OPENGL_INCLUDE_DIR} ${GLEW_INCLUDES})
+    include_directories (${OPENGL_INCLUDE_DIR})
     set (iv_srcs imageviewer.cpp ivimage.cpp ivgl.cpp ivinfowin.cpp 
                  ivpref.cpp ivmain.cpp)
     if (FORCE_OPENGL_1)
@@ -14,10 +14,9 @@ if (Qt5_FOUND AND OPENGL_FOUND AND GLEW_
     target_link_libraries (iv OpenImageIO
                                Qt5::Core Qt5::Gui Qt5::Widgets Qt5::OpenGL
                                ${OPENGL_LIBRARIES}
-                               ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}
-                               ${GLEW_LIBRARIES})
+                               ${Boost_LIBRARIES} ${CMAKE_DL_LIBS})
     oiio_install_targets (iv)
 
 else ()
-    message (STATUS "\n\n   WARNING: Qt, OpenGL, or GLEW not found -- 'iv' will not be built!\n")
+    message (STATUS "\n\n   WARNING: Qt or OpenGL not found -- 'iv' will not be built!\n")
 endif ()
--- a/src/iv/imageviewer.cpp
+++ b/src/iv/imageviewer.cpp
@@ -1025,8 +1025,7 @@ ImageViewer::displayCurrentImage (bool u
         }
     } else {
         m_current_image = m_last_image = -1;
-        repaint(); // add repaint event to Qt queue
-        glwin->trigger_redraw(); // then make sure GL canvas is cleared
+        ((QOpenGLWidget*)(glwin))->update ();
     }
 
     if (update) {
@@ -1795,8 +1794,10 @@ void ImageViewer::zoomIn()
         float z = Imath::lerp (oldzoom, newzoom, a);
         float zoomratio = z / oldzoom;
         view (xm + xoffset/zoomratio, ym + yoffset/zoomratio, z, false);
-        if (i != nsteps)
+        if (i != nsteps) {
+            QApplication::processEvents();
             Sysutil::usleep (1000000 / 4 / nsteps);
+        }
     }
 
     fitImageToWindowAct->setChecked (false);
@@ -1827,8 +1828,10 @@ void ImageViewer::zoomOut()
         float z = Imath::lerp (oldzoom, newzoom, a);
         float zoomratio = z / oldzoom;
         view (xmpel + xoffset/zoomratio, ympel + yoffset/zoomratio, z, false);
-        if (i != nsteps)
+        if (i != nsteps) {
+            QApplication::processEvents();
             Sysutil::usleep (1000000 / 4 / nsteps);
+        }
     }
 
     fitImageToWindowAct->setChecked (false);
@@ -2037,8 +2040,10 @@ ImageViewer::view (float xcenter, float
         m_zoom = Imath::lerp (oldzoom, newzoom, a);
 
         glwin->view (xc, yc, m_zoom, redraw);  // Triggers redraw automatically
-        if (i != nsteps)
+        if (i != nsteps) {
+            QApplication::processEvents();
             Sysutil::usleep (1000000 / 4 / nsteps);
+        }
     }
 
     if (img->auto_subimage ()) {
@@ -2090,7 +2095,7 @@ ImageViewer::showInfoWindow ()
 void
 ImageViewer::showPixelviewWindow ()
 {
-    glwin->trigger_redraw ();
+    ((QOpenGLWidget*)(glwin))->update ();
 }
 
 
--- a/src/iv/imageviewer.h
+++ b/src/iv/imageviewer.h
@@ -46,7 +46,6 @@
 
 #include <vector>
 
-#include <glew.h>
 #include <QGLWidget>
 #include <QAction>
 #include <QCheckBox>
--- a/src/iv/ivgl.cpp
+++ b/src/iv/ivgl.cpp
@@ -40,7 +40,6 @@
 #include <QLabel>
 #include <QMouseEvent>
 #include <QProgressBar>
-#include <QGLFormat>
 
 #include "ivutils.h"
 #include <OpenImageIO/strutil.h>
@@ -71,12 +70,12 @@ gl_err_to_string (GLenum err)
 
 
 IvGL::IvGL (QWidget *parent, ImageViewer &viewer)
-    : QGLWidget(parent), m_viewer(viewer), 
+    : QOpenGLWidget(parent), m_viewer(viewer),
       m_shaders_created(false), m_tex_created(false),
       m_zoom(1.0), m_centerx(0), m_centery(0), m_dragging(false),
-      m_use_shaders(false), m_shaders_using_extensions(false), 
+      m_use_shaders(false),
       m_use_halffloat(false), m_use_float(false),
-      m_use_srgb(false), m_use_pbo(false), 
+      m_use_srgb(false),
       m_texture_width(1), m_texture_height(1), m_last_pbo_used(0), 
       m_current_image(NULL), m_pixelview_left_corner(true),
       m_last_texbuf_used(0)
@@ -106,20 +105,13 @@ IvGL::~IvGL ()
 void
 IvGL::initializeGL ()
 {
-    GLenum glew_error = glewInit ();
-    if (glew_error != GLEW_OK) {
-        std::cerr << "GLEW init error " << glewGetErrorString (glew_error) << "\n";
-    }
+    initializeOpenGLFunctions();
 
     glClearColor (0.05f, 0.05f, 0.05f, 1.0f);
-    glShadeModel (GL_FLAT);
-    glEnable (GL_DEPTH_TEST);
-    glDisable (GL_CULL_FACE);
     glEnable (GL_BLEND);
     glEnable (GL_TEXTURE_2D);
     // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-    glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
     // Make sure initial matrix is identity (returning to this stack level loads
     // back this matrix).
     glLoadIdentity();
@@ -202,12 +194,10 @@ IvGL::create_textures (void)
     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
-    if (m_use_pbo) {
-        glGenBuffersARB(2, m_pbo_objects);
-        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, m_pbo_objects[0]);
-        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, m_pbo_objects[1]);
-        glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
-    }
+    glGenBuffers (2, m_pbo_objects);
+    glBindBuffer (GL_PIXEL_UNPACK_BUFFER, m_pbo_objects[0]);
+    glBindBuffer (GL_PIXEL_UNPACK_BUFFER, m_pbo_objects[1]);
+    glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
 
     m_tex_created = true;
 }
@@ -349,90 +339,50 @@ IvGL::create_shaders (void)
     // entry points (which is actually done by GLEW) and then call them. So
     // we have to get the functions through the right symbols otherwise
     // extension-based shaders won't work.
-    if (m_shaders_using_extensions) {
-        m_shader_program = glCreateProgramObjectARB ();
-    }
-    else {
-        m_shader_program = glCreateProgram ();
-    }
+    m_shader_program = glCreateProgram ();
+
     GLERRPRINT ("create progam");
 
     // This holds the compilation status
     GLint status;
 
-    if (m_shaders_using_extensions) {
-        m_vertex_shader = glCreateShaderObjectARB (GL_VERTEX_SHADER_ARB);
-        glShaderSourceARB (m_vertex_shader, 1, &vertex_source, NULL);
-        glCompileShaderARB (m_vertex_shader);
-        glGetObjectParameterivARB (m_vertex_shader,
-                GL_OBJECT_COMPILE_STATUS_ARB, &status);
-    } else {
-        m_vertex_shader = glCreateShader (GL_VERTEX_SHADER);
-        glShaderSource (m_vertex_shader, 1, &vertex_source, NULL);
-        glCompileShader (m_vertex_shader);
-        glGetShaderiv (m_vertex_shader, GL_COMPILE_STATUS, &status);
-    }
+    m_vertex_shader = glCreateShader (GL_VERTEX_SHADER);
+    glShaderSource (m_vertex_shader, 1, &vertex_source, NULL);
+    glCompileShader (m_vertex_shader);
+    glGetShaderiv (m_vertex_shader, GL_COMPILE_STATUS, &status);
+
     if (! status) {
         std::cerr << "vertex shader compile status: " << status << "\n";
         print_shader_log (std::cerr, m_vertex_shader);
         create_shaders_abort ();
         return;
     }
-    if (m_shaders_using_extensions) {
-        glAttachObjectARB (m_shader_program, m_vertex_shader);
-    } else {
-        glAttachShader (m_shader_program, m_vertex_shader);
-    }
+    glAttachShader (m_shader_program, m_vertex_shader);
     GLERRPRINT ("After attach vertex shader.");
 
-    if (m_shaders_using_extensions) {
-        m_fragment_shader = glCreateShaderObjectARB (GL_FRAGMENT_SHADER_ARB);
-        glShaderSourceARB (m_fragment_shader, 1, &fragment_source, NULL);
-        glCompileShaderARB (m_fragment_shader);
-        glGetObjectParameterivARB (m_fragment_shader,
-                GL_OBJECT_COMPILE_STATUS_ARB, &status);
-    } else {
-        m_fragment_shader = glCreateShader (GL_FRAGMENT_SHADER);
-        glShaderSource (m_fragment_shader, 1, &fragment_source, NULL);
-        glCompileShader (m_fragment_shader);
-        glGetShaderiv (m_fragment_shader, GL_COMPILE_STATUS, &status);
-    }
+    m_fragment_shader = glCreateShader (GL_FRAGMENT_SHADER);
+    glShaderSource (m_fragment_shader, 1, &fragment_source, NULL);
+    glCompileShader (m_fragment_shader);
+    glGetShaderiv (m_fragment_shader, GL_COMPILE_STATUS, &status);
     if (! status) {
         std::cerr << "fragment shader compile status: " << status << "\n";
         print_shader_log(std::cerr, m_fragment_shader);
         create_shaders_abort ();
         return;
     }
-    if (m_shaders_using_extensions) {
-        glAttachObjectARB (m_shader_program, m_fragment_shader);
-    } else {
-        glAttachShader (m_shader_program, m_fragment_shader);
-    }
+    glAttachShader (m_shader_program, m_fragment_shader);
     GLERRPRINT ("After attach fragment shader");
 
-    if (m_shaders_using_extensions) {
-        glLinkProgramARB (m_shader_program);
-    } else {
-        glLinkProgram (m_shader_program);
-    }
+    glLinkProgram (m_shader_program);
     GLERRPRINT ("link");
     GLint linked;
-    if (m_shaders_using_extensions) {
-        glGetObjectParameterivARB (m_shader_program,
-                GL_OBJECT_LINK_STATUS_ARB, &linked);
-    } else {
-        glGetProgramiv (m_shader_program, GL_LINK_STATUS, &linked);
-    }
+    glGetProgramiv (m_shader_program, GL_LINK_STATUS, &linked);
     if (! linked) {
         std::cerr << "NOT LINKED\n";
         char buf[10000];
         buf[0] = 0;
         GLsizei len;
-        if (m_shaders_using_extensions) {
-            glGetInfoLogARB (m_shader_program, sizeof(buf), &len, buf);
-        } else {
-            glGetProgramInfoLog (m_shader_program, sizeof(buf), &len, buf);
-        }
+        glGetProgramInfoLog (m_shader_program, sizeof(buf), &len, buf);
         std::cerr << "link log:\n" << buf << "---\n";
         create_shaders_abort ();
         return;
@@ -446,25 +396,13 @@ IvGL::create_shaders (void)
 void
 IvGL::create_shaders_abort (void)
 {
-    if (m_shaders_using_extensions) {
-        glUseProgramObjectARB (0);
-        if (m_shader_program)
-            //this will also detach related shaders
-            glDeleteObjectARB (m_shader_program);
-        if (m_vertex_shader)
-            glDeleteObjectARB (m_vertex_shader);
-        if (m_fragment_shader)
-            glDeleteObjectARB (m_fragment_shader);
-    }
-    else {
-        glUseProgram (0);        
-        if (m_shader_program)
-            glDeleteProgram (m_shader_program);
-        if (m_vertex_shader)
-            glDeleteShader (m_vertex_shader);
-        if (m_fragment_shader)
-            glDeleteShader (m_fragment_shader);
-    }
+    glUseProgram (0);
+    if (m_shader_program)
+        glDeleteProgram (m_shader_program);
+    if (m_vertex_shader)
+        glDeleteShader (m_vertex_shader);
+    if (m_fragment_shader)
+        glDeleteShader (m_fragment_shader);
     
     GLERRPRINT ("After delete shaders");    
     m_use_shaders = false;
@@ -697,16 +635,23 @@ void
 IvGL::shadowed_text (float x, float y, float z, const std::string &s,
                      const QFont &font)
 {
-    QString q (s.c_str());
-#if 0
-    glColor4f (0, 0, 0, 1);
-    const int b = 2;  // blur size
-    for (int i = -b;  i <= b;  ++i)
-        for (int j = -b;  j <= b;  ++j)
-            renderText (x+i, y+j, q, font);
-#endif
-    glColor4f (1, 1, 1, 1);
-    renderText (x, y, z, q, font);
+    /*
+     * Paint on intermediate QImage, AA text on QOpenGLWidget based
+     * QPaintDevice requires MSAA
+     */
+    QImage t(size(), QImage::Format_ARGB32_Premultiplied);
+    t.fill(qRgba(0, 0, 0, 0));
+    {
+        QPainter painter(&t);
+        painter.setRenderHint(QPainter::TextAntialiasing, true);
+
+        painter.setFont(font);
+
+        painter.setPen(QPen(Qt::white, 1.0));
+        painter.drawText(QPointF(x, y), QString(s.c_str()));
+    }
+    QPainter painter(this);
+    painter.drawImage(rect(), t);
 }
 
 
@@ -837,9 +782,7 @@ IvGL::paint_pixelview ()
         GLenum glformat, gltype, glinternalformat;
         typespec_to_opengl (spec, nchannels, gltype, glformat, glinternalformat);
         // Use pixelview's own texture, and upload the corresponding image patch.
-        if (m_use_pbo) {
-            glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
-        }
+        glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
         glBindTexture (GL_TEXTURE_2D, m_pixelview_tex);
         glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 
                          xend-xbegin, yend-ybegin,
@@ -880,7 +823,7 @@ IvGL::paint_pixelview ()
     glDisable (GL_TEXTURE_2D);
     if (m_use_shaders) {
         // Disable shaders for this.
-        gl_use_program (0);
+        glUseProgram (0);
     }
     float extraspace = yspacing * (1 + spec.nchannels) + 4;
     glColor4f (0.1f, 0.1f, 0.1f, 0.5f);
@@ -893,11 +836,11 @@ IvGL::paint_pixelview ()
         QFont font;
         font.setFixedPitch (true);
         float *fpixel = (float *) alloca (spec.nchannels*sizeof(float));
-        int textx = - closeupsize/2 + 4;
-        int texty = - closeupsize/2 - yspacing;
+        int textx = xw - closeupsize/2 + 4;
+        int texty = yw + closeupsize/2 + yspacing;
         std::string s = Strutil::format ("(%d, %d)", (int) real_xp+spec.x, (int) real_yp+spec.y);
         shadowed_text (textx, texty, 0.0f, s, font);
-        texty -= yspacing;
+        texty += yspacing;
         img->getpixel ((int) real_xp+spec.x, (int) real_yp+spec.y, fpixel);
         for (int i = 0;  i < spec.nchannels;  ++i) {
             switch (spec.format.basetype) {
@@ -920,7 +863,7 @@ IvGL::paint_pixelview ()
                                      spec.channelnames[i].c_str(), fpixel[i]);
             }
             shadowed_text (textx, texty, 0.0f, s, font);
-            texty -= yspacing;
+            texty += yspacing;
         }
     }
 
@@ -955,47 +898,47 @@ IvGL::useshader (int tex_width, int tex_
 
     const ImageSpec &spec (img->spec());
 
-    gl_use_program (m_shader_program);
+    glUseProgram (m_shader_program);
     GLERRPRINT ("After use program");
 
     GLint loc;
 
-    loc = gl_get_uniform_location ("startchannel");
+    loc = glGetUniformLocation (m_shader_program, "startchannel");
     if (m_viewer.current_channel()>=spec.nchannels) {
-        gl_uniform (loc, -1);
+        glUniform1i (loc, -1);
         return;
     }
-    gl_uniform (loc, 0);
+    glUniform1i (loc, 0);
 
-    loc = gl_get_uniform_location ("imgtex");
+    loc = glGetUniformLocation (m_shader_program, "imgtex");
     // This is the texture unit, not the texture object
-    gl_uniform (loc, 0);
+    glUniform1i (loc, 0);
 
-    loc = gl_get_uniform_location ("gain");
+    loc = glGetUniformLocation (m_shader_program, "gain");
 
     float gain = powf (2.0, img->exposure ());
-    gl_uniform (loc, gain);
+    glUniform1f (loc, gain);
 
-    loc = gl_get_uniform_location ("gamma");
-    gl_uniform (loc, img->gamma ());
+    loc = glGetUniformLocation (m_shader_program, "gamma");
+    glUniform1f (loc, img->gamma ());
 
-    loc = gl_get_uniform_location ("colormode");
-    gl_uniform (loc, m_viewer.current_color_mode());
+    loc = glGetUniformLocation (m_shader_program, "colormode");
+    glUniform1i (loc, m_viewer.current_color_mode());
 
-    loc = gl_get_uniform_location ("imgchannels");
-    gl_uniform (loc, spec.nchannels);
+    loc = glGetUniformLocation (m_shader_program, "imgchannels");
+    glUniform1i (loc, spec.nchannels);
 
-    loc = gl_get_uniform_location ("pixelview");
-    gl_uniform (loc, pixelview);
+    loc = glGetUniformLocation (m_shader_program, "pixelview");
+    glUniform1i (loc, pixelview);
 
-    loc = gl_get_uniform_location ("linearinterp");
-    gl_uniform (loc, m_viewer.linearInterpolation ());
+    loc = glGetUniformLocation (m_shader_program, "linearinterp");
+    glUniform1i (loc, m_viewer.linearInterpolation ());
 
-    loc = gl_get_uniform_location ("width");
-    gl_uniform (loc, tex_width);
+    loc = glGetUniformLocation (m_shader_program, "width");
+    glUniform1i (loc, tex_width);
 
-    loc = gl_get_uniform_location ("height");
-    gl_uniform (loc, tex_height);
+    loc = glGetUniformLocation (m_shader_program, "height");
+    glUniform1i (loc, tex_height);
     GLERRPRINT ("After settting uniforms");
 }
 
@@ -1032,11 +975,7 @@ IvGL::update ()
     m_texture_width = clamp (pow2roundup(spec.width), 1, m_max_texture_size);
     m_texture_height= clamp (pow2roundup(spec.height), 1, m_max_texture_size);
 
-    if (m_use_pbo) {
-        // Otherwise OpenGL will confuse the NULL with an index into one of
-        // the PBOs.
-        glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
-    }
+    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 
     for (auto&& tb : m_texbufs) {
         tb.width = 0;
@@ -1073,7 +1012,7 @@ IvGL::view (float xcenter, float ycenter
     m_zoom = zoom;
 
     if (redraw)
-        trigger_redraw ();
+        parent_t::update();
 }
 
 
@@ -1256,7 +1195,7 @@ IvGL::mouseMoveEvent (QMouseEvent *event
     }
     remember_mouse (pos);
     if (m_viewer.pixelviewOn())
-        trigger_redraw ();
+        parent_t::update();
     parent_t::mouseMoveEvent (event);
 }
 
@@ -1332,63 +1271,14 @@ IvGL::get_focus_image_pixel (int &x, int
 }
 
 
-
-inline void
-IvGL::gl_use_program (int program)
-{
-    if (m_shaders_using_extensions) 
-        glUseProgramObjectARB (program);
-    else
-        glUseProgram (program);
-}
-
-
-
-inline GLint
-IvGL::gl_get_uniform_location (const char *uniform)
-{
-    if (m_shaders_using_extensions)
-        return glGetUniformLocationARB (m_shader_program, uniform);
-    else
-        return glGetUniformLocation (m_shader_program, uniform);
-}
-
-
-
-inline void
-IvGL::gl_uniform (GLint location, float value)
-{
-    if (m_shaders_using_extensions)
-        glUniform1fARB (location, value);
-    else
-        glUniform1f (location, value);
-}
-
-
-
-inline void
-IvGL::gl_uniform (GLint location, int value)
-{
-    if (m_shaders_using_extensions)
-        glUniform1iARB (location, value);
-    else
-        glUniform1i (location, value);
-}
-
-
-
 void
-IvGL::print_shader_log (std::ostream& out, const GLuint shader_id) const
+IvGL::print_shader_log (std::ostream& out, const GLuint shader_id)
 {
     GLint size = 0;
     glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &size);
     if (size > 0) {    
         GLchar* log = new GLchar[size];
-        if (m_shaders_using_extensions) {
-            glGetInfoLogARB (shader_id, size, NULL, log);
-        } else {
-            glGetShaderInfoLog (shader_id, size, NULL, log);
-        }
+        glGetShaderInfoLog (shader_id, size, NULL, log);
         out << "compile log:\n" << log << "---\n";
         delete[] log;
     }
@@ -1399,32 +1289,26 @@ IvGL::print_shader_log (std::ostream& ou
 void
 IvGL::check_gl_extensions (void)
 {
-#ifndef FORCE_OPENGL_1
-    m_use_shaders = glewIsSupported("GL_VERSION_2_0");
+    m_use_shaders = hasOpenGLFeature(QOpenGLFunctions::Shaders);
 
-    if (!m_use_shaders && glewIsSupported("GL_ARB_shader_objects "
-                                          "GL_ARB_vertex_shader "
-                                          "GL_ARB_fragment_shader")) {
-        m_use_shaders = true;
-        m_shaders_using_extensions = true;
-    }
-
-    m_use_srgb = glewIsSupported("GL_VERSION_2_1") ||
-                 glewIsSupported("GL_EXT_texture_sRGB");
-
-    m_use_halffloat = glewIsSupported("GL_VERSION_3_0") ||
-                      glewIsSupported("GL_ARB_half_float_pixel") ||
-                      glewIsSupported("GL_NV_half_float_pixel");
-
-    m_use_float = glewIsSupported("GL_VERSION_3_0") ||
-                  glewIsSupported("GL_ARB_texture_float") ||
-                  glewIsSupported("GL_ATI_texture_float");
-
-    m_use_pbo = glewIsSupported("GL_VERSION_1_5") ||
-                glewIsSupported("GL_ARB_pixel_buffer_object");
-#else
-    std::cerr << "Not checking GL extensions\n";
-#endif
+    QOpenGLContext* context = QOpenGLContext::currentContext();
+    QSurfaceFormat format = context->format();
+    bool isGLES = format.renderableType() == QSurfaceFormat::OpenGLES;
+
+    m_use_srgb = (isGLES && format.majorVersion() >= 3) ||
+                 (!isGLES && format.version() >= qMakePair(2, 1)) ||
+                 context->hasExtension("GL_EXT_texture_sRGB") ||
+                 context->hasExtension("GL_EXT_sRGB");
+
+    m_use_halffloat = (!isGLES && format.version() >= qMakePair(3, 0)) ||
+                      context->hasExtension("GL_ARB_half_float_pixel") ||
+                      context->hasExtension("GL_NV_half_float_pixel") ||
+                      context->hasExtension("GL_OES_texture_half_float");
+
+    m_use_float = (!isGLES && format.version() >= qMakePair(3, 0)) ||
+                  context->hasExtension("GL_ARB_texture_float") ||
+                  context->hasExtension("GL_ATI_texture_float") ||
+                  context->hasExtension("GL_OES_texture_float");
 
     m_max_texture_size = 0;
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_texture_size);
@@ -1436,13 +1320,9 @@ IvGL::check_gl_extensions (void)
 #ifndef NDEBUG
     // Report back...
     std::cerr << "OpenGL Shading Language supported: " << m_use_shaders << "\n";
-    if (m_shaders_using_extensions) {
-        std::cerr << "\t(with extensions)\n";
-    }
     std::cerr << "OpenGL sRGB color space textures supported: " << m_use_srgb << "\n";
     std::cerr << "OpenGL half-float pixels supported: " << m_use_halffloat << "\n";
     std::cerr << "OpenGL float texture storage supported: " << m_use_float << "\n";
-    std::cerr << "OpenGL pixel buffer object supported: " << m_use_pbo << "\n";
     std::cerr << "OpenGL max texture dimension: " << m_max_texture_size << "\n";
 #endif
 }
@@ -1603,21 +1483,18 @@ IvGL::load_texture (int x, int y, int wi
                                           m_viewer.current_channel() + nchannels),
                                      spec.format, &m_tex_buffer[0]);
     }
-    if (m_use_pbo) {
-        glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 
-                         m_pbo_objects[m_last_pbo_used]);
-        glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, 
-                         width * height * spec.pixel_bytes(),
-                         &m_tex_buffer[0],
-                         GL_STREAM_DRAW_ARB);
-        GLERRPRINT ("After buffer data");
-        m_last_pbo_used = (m_last_pbo_used + 1) & 1;
-    }
+
+    glBindBuffer (GL_PIXEL_UNPACK_BUFFER,
+                     m_pbo_objects[m_last_pbo_used]);
+    glBufferData (GL_PIXEL_UNPACK_BUFFER,
+                     width * height * spec.pixel_bytes(),
+                     &m_tex_buffer[0],
+                     GL_STREAM_DRAW);
+    GLERRPRINT ("After buffer data");
+    m_last_pbo_used = (m_last_pbo_used + 1) & 1;
 
     // When using PBO this is the offset within the buffer.
     void *data = 0;
-    if (! m_use_pbo)
-        data = &m_tex_buffer[0];
 
     glBindTexture (GL_TEXTURE_2D, tb.tex_object);
     GLERRPRINT ("After bind texture");
@@ -1628,6 +1505,7 @@ IvGL::load_texture (int x, int y, int wi
                      data);
     GLERRPRINT ("After loading sub image");
     m_last_texbuf_used = (m_last_texbuf_used + 1) % m_texbufs.size();
+    glBindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
 }
 
 
--- a/src/iv/ivgl.h
+++ b/src/iv/ivgl.h
@@ -46,7 +46,8 @@
 
 #include <vector>
 
-#include <QGLWidget>
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
 
 #include <OpenImageIO/imageio.h>
 #include <OpenImageIO/imagebuf.h>
@@ -58,7 +59,7 @@ class ImageViewer;
 
 
 
-class IvGL : public QGLWidget
+class IvGL : public QOpenGLWidget, protected QOpenGLFunctions
 {
 Q_OBJECT
 public:
@@ -103,8 +104,6 @@ public:
     /// widget boundaries)
     void get_focus_window_pixel (int &x, int &y);
 
-    void trigger_redraw (void) { glDraw(); }
-
     /// Returns true if OpenGL is capable of loading textures in the sRGB color
     /// space.
     bool is_srgb_capable (void) const { return m_use_srgb; }
@@ -137,12 +136,10 @@ protected:
     int m_mousex, m_mousey;           ///< Last mouse position
     Qt::MouseButton m_drag_button;    ///< Button on when dragging
     bool m_use_shaders;               ///< Are shaders supported?
-    bool m_shaders_using_extensions;  ///< Are we using ARB_*_shader?
     bool m_use_halffloat;             ///< Are half-float textures supported?
     bool m_use_float;                 ///< Are float textures supported?
     bool m_use_srgb;                  ///< Are sRGB-space textures supported?
     bool m_use_npot_texture;          ///< Can we handle NPOT textures?
-    bool m_use_pbo;                   ///< Can we use PBO to upload the texture?
     GLint m_max_texture_size;         ///< Maximum allowed texture dimension.
     GLsizei m_texture_width;
     GLsizei m_texture_height;
@@ -190,7 +187,7 @@ protected:
                         const QFont &font);
 
 private:
-    typedef QGLWidget parent_t;
+    typedef QOpenGLWidget parent_t;
     /// ncloseuppixels is the number of big pixels (in each direction)
     /// visible in our closeup window.
     const static int ncloseuppixels = 9;
@@ -207,19 +204,13 @@ private:
 
     void clamp_view_to_window ();
 
-    // Small wrappers to handle ARB shaders.
-    void gl_use_program (int program);
-    GLint gl_get_uniform_location (const char*);
-    void gl_uniform (GLint location, float value);
-    void gl_uniform (GLint location, int value);
-
     /// checks what OpenGL extensions we have
     ///
     void check_gl_extensions (void);
 
     /// print shader info to out stream
     ///
-    void print_shader_log (std::ostream& out, const GLuint shader_id) const;
+    void print_shader_log (std::ostream& out, const GLuint shader_id);
 
     /// Loads the given patch of the image, but first figures if it's already
     /// been loaded.