diff --git a/src/lib/app/RvCommon/GLView.cpp b/src/lib/app/RvCommon/GLView.cpp index a6c470ea3..1b312dda5 100644 --- a/src/lib/app/RvCommon/GLView.cpp +++ b/src/lib/app/RvCommon/GLView.cpp @@ -330,10 +330,85 @@ namespace Rv #endif } + bool GLView::validateReadPixels(int x, int y, int w, int h) + { + int r = x + w; + int t = y + h; + + // are the extents of the read region out of bounds? + if (x < 0 || y < 0 || r > width() || t > height()) + return false; + + return true; + } + + QImage GLView::readPixels(int x, int y, int w, int h) + { + + // If out of bounds, return an empty image. + if (validateReadPixels(x, y, w, h) == false) + { + QImage image(0, 0, QImage::Format_RGBA8888); + return image; + } + + // + // Why this method exists, and why saving and restoring the context is + // important: + // + // With the old QGLWidget implement with Qt 5.15.2, the current context + // stayed bounded to the main view (eg: the instance of GLView) after + // each paint of the GLView. This made it possible to call gl functions + // (eg: glReadPixels) even when not in the context of a render call. + // + // For example, in Mu: + // method: render (void; Event event) + // + // Now with the new QOpenGL paradigm, after the application is done + // rendering, the current OpenGL context is no longer bound to the + // context of the GLView, and is instead bound to the context of the + // main Qt frame window. + // + // This caused a problem with some tools, like the dropperSample() tool + // of the mu annotation toolset, which blindly called glReadPixels + // whenever the mouse was clicked/dragged over the view. Since the + // context was no longer bound to the view, but to the qapplication's + // main window, glReadPixels would return invalid colors. + // + // Therefore we implemented this readPixels on the glView, and exposed a + // "framebufferPixelValue(x,y)" method which ends up here, allowing us + // to perform some save/restore operations of the context before + // returning. + // + // Note that we can't simply make the GLiew's context current without + // restoring the old context, since this creates side effects and causes + // the glDebug() to complain about invalid states elsewhere. + // + + // Make the current context the one of the GL view + makeCurrent(); + + QImage image(w, h, QImage::Format_RGBA8888); + glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.bits()); + + return image; + } + + void GLView::debugSaveFramebuffer() + { + // Create a QImage with the same size as the FBO + QImage image(width(), height(), QImage::Format_RGBA8888); + glReadPixels(0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_BYTE, + image.bits()); + + // image.save("/home/>//fbo.png"); + } + void GLView::swapBuffersNoSync() { glFlush(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, + context()->defaultFramebufferObject()); glReadBuffer(GL_BACK); glDrawBuffer(GL_FRONT); glBlitFramebufferEXT(0, 0, width(), height(), 0, 0, width(), height(), @@ -423,9 +498,8 @@ namespace Rv // Not sure why it is not complaining on Linux or Windows, but this // make sure that we are drawing onto the default framebuffer with // those OpenGL functions below. - glBindFramebuffer( - GL_FRAMEBUFFER, - QOpenGLContext::currentContext()->defaultFramebufferObject()); + glBindFramebuffer(GL_FRAMEBUFFER, + context()->defaultFramebufferObject()); glPushAttrib(GL_COLOR_BUFFER_BIT); TWK_GLDEBUG; diff --git a/src/lib/app/RvCommon/MuUICommands.cpp b/src/lib/app/RvCommon/MuUICommands.cpp index 594bc24bf..529776eb3 100644 --- a/src/lib/app/RvCommon/MuUICommands.cpp +++ b/src/lib/app/RvCommon/MuUICommands.cpp @@ -146,6 +146,11 @@ namespace Rv context->listType(context->tupleType(types)); commands->addSymbols( + new Function(c, "framebufferPixelValue", framebufferPixelValue, + None, Return, "vector float[4]", Parameters, + new Param(c, "px", "float"), + new Param(c, "py", "float"), End), + new Function(c, "resizeFit", resizeFit, None, Return, "void", End), new Function(c, "setViewSize", setViewSize, None, Return, "void", @@ -507,6 +512,47 @@ namespace Rv EndArguments); } + NODE_IMPLEMENTATION(framebufferPixelValue, Mu::Vector4f) + { + Process* p = NODE_THREAD.process(); + MuLangContext* c = static_cast(p->context()); + Session* s = Session::currentSession(); + RvDocument* doc = reinterpret_cast(s->opaquePointer()); + QWidget* w = doc->view(); + + GLView* glview = dynamic_cast(w); + + Mu::Vector4f v; + v[0] = 0; + v[1] = 0; + v[2] = 0; + v[3] = 0; + + if (glview != NULL) + { + float x = NODE_ARG(0, float); + float y = NODE_ARG(1, float); + + int ix = (int)(x + 0.5f); + int iy = (int)(y + 0.5f); + + QImage image = glview->readPixels(ix, iy, 1, 1); + + if ((image.width() > 0) && (image.height() > 0)) + { + QRgb rgba = image.pixel(0, 0); + QColor qc(rgba); + + v[0] = qc.redF(); + v[1] = qc.greenF(); + v[2] = qc.blueF(); + v[3] = qc.alphaF(); + } + } + + NODE_RETURN(v); + } + NODE_IMPLEMENTATION(queryDriverAttribute, Pointer) { Process* p = NODE_THREAD.process(); diff --git a/src/lib/app/RvCommon/RvCommon/GLView.h b/src/lib/app/RvCommon/RvCommon/GLView.h index fd82e055b..4ee75538a 100644 --- a/src/lib/app/RvCommon/RvCommon/GLView.h +++ b/src/lib/app/RvCommon/RvCommon/GLView.h @@ -64,6 +64,8 @@ namespace Rv void* syncClosure() const { return m_syncThreadData; } + QImage readPixels(int x, int y, int w, int h); + public slots: void eventProcessingTimeout(); @@ -72,6 +74,8 @@ namespace Rv void resizeGL(int w, int h); void paintGL(); void swapBuffersNoSync(); + bool validateReadPixels(int x, int y, int w, int h); + void debugSaveFramebuffer(); private: RvDocument* m_doc; diff --git a/src/lib/app/RvCommon/RvCommon/MuUICommands.h b/src/lib/app/RvCommon/RvCommon/MuUICommands.h index 73d377274..33cc0c83b 100644 --- a/src/lib/app/RvCommon/RvCommon/MuUICommands.h +++ b/src/lib/app/RvCommon/RvCommon/MuUICommands.h @@ -89,6 +89,7 @@ namespace Rv NODE_DECLARATION(launchTLI, void); NODE_DECLARATION(rvioSetup, void); NODE_DECLARATION(javascriptMuExport, void); + NODE_DECLARATION(framebufferPixelValue, Mu::Vector4f); } // namespace Rv diff --git a/src/lib/app/py_rvui/rv_commands_setup.py b/src/lib/app/py_rvui/rv_commands_setup.py index 01c6be500..3cb9a676b 100644 --- a/src/lib/app/py_rvui/rv_commands_setup.py +++ b/src/lib/app/py_rvui/rv_commands_setup.py @@ -233,6 +233,7 @@ "beginCompoundCommand", "setCacheMode", "sourcePixelValue", + "framebufferPixelValue", "setByteProperty", "inc", "getCurrentAttributes", diff --git a/src/lib/ip/IPCore/ImageRenderer.cpp b/src/lib/ip/IPCore/ImageRenderer.cpp index 7a71427b0..31545c46f 100644 --- a/src/lib/ip/IPCore/ImageRenderer.cpp +++ b/src/lib/ip/IPCore/ImageRenderer.cpp @@ -2969,6 +2969,7 @@ namespace IPCore // is this needed ? YES this is needed, otherwise fonts wont render // right with FTGL GLPushAttrib attr1(GL_ALL_ATTRIB_BITS); + TWK_GLDEBUG; GLPushClientAttrib attr2(GL_CLIENT_ALL_ATTRIB_BITS); TWK_GLDEBUG; diff --git a/src/plugins/rv-packages/annotate/annotate_mode.mu b/src/plugins/rv-packages/annotate/annotate_mode.mu index b2757d310..264cbde08 100644 --- a/src/plugins/rv-packages/annotate/annotate_mode.mu +++ b/src/plugins/rv-packages/annotate/annotate_mode.mu @@ -672,8 +672,7 @@ class: AnnotateMinorMode : MinorMode sName = sourceNameWithoutFrame(pinfo.name), ip = state.pointerPosition; - float[] pixels; - glReadPixels(ip.x, ip.y, 1, 1, GL_RGBA, pixels); + let pixels = framebufferPixelValue(ip.x, ip.y); let c = Color(pixels[0], pixels[1], pixels[2], pixels[3]); _sampleColor += c; _sampleCount++;