Message ID | 20200320085029.17875-3-show.liu@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi Show, Thanks for your patch. On 2020-03-20 16:50:28 +0800, Show Liu wrote: > qcam: Added opengl widget class for qcam > > Signed-off-by: Show Liu <show.liu@linaro.org> > --- > src/qcam/glwidget.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++ > src/qcam/glwidget.h | 76 +++++++++++++++ > src/qcam/meson.build | 4 +- > 3 files changed, 292 insertions(+), 1 deletion(-) > create mode 100644 src/qcam/glwidget.cpp > create mode 100644 src/qcam/glwidget.h > > diff --git a/src/qcam/glwidget.cpp b/src/qcam/glwidget.cpp > new file mode 100644 > index 0000000..bf6add4 > --- /dev/null > +++ b/src/qcam/glwidget.cpp > @@ -0,0 +1,213 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2020, Linaro > + * > + * glwidget.cpp - Rendering YUV frame by OpenGL shader > + */ > + > +#include "glwidget.h" nit: add blank space > +#include <QImage> > + > +#define ATTRIB_VERTEX 0 > +#define ATTRIB_TEXTURE 1 > + > +GLWidget::GLWidget(QWidget *parent) > + : QOpenGLWidget(parent), > + textureY(QOpenGLTexture::Target2D), > + textureU(QOpenGLTexture::Target2D), > + textureV(QOpenGLTexture::Target2D), > + yuvDataPtr(NULL), > + glBuffer(QOpenGLBuffer::VertexBuffer) nit: align all lines with QOpenGLWidget() > +{ > +} > + > +GLWidget::~GLWidget() > +{ > + removeShader(); > + > + textureY.destroy(); > + textureU.destroy(); > + textureV.destroy(); > + > + glBuffer.destroy(); > +} > + > +void GLWidget::updateFrame(unsigned char *buffer) > +{ > + if(buffer) { > + yuvDataPtr = buffer; > + update(); > + } if (!buffer) return; yuvDataPtr = buffer; update(); > +} > + > +void GLWidget::setFrameSize(int width, int height) > +{ > + if(width > 0 && height > 0) { > + width_ = width; > + height_ = height; > + } Same pattern as above. > +} > + > +int GLWidget::configure(unsigned int format, unsigned int width, > + unsigned int height) > +{ > + switch (format) { > + case DRM_FORMAT_NV12: > + break; You can drop this break and lit it fall-thru to NV21. > + case DRM_FORMAT_NV21: > + break; > + default: > + return -EINVAL; > + }; newline > + format_ = format; > + width_ = width; > + height_ = height; > + > + return 0; > +} > + > +void GLWidget::createShader() > +{ > + bool ret = false; > + > + QString vsrc = "attribute vec4 vertexIn; \n" > + "attribute vec2 textureIn; \n" > + "varying vec2 textureOut; \n" > + "void main(void) \n" > + "{ \n" > + "gl_Position = vertexIn; \n" > + "textureOut = textureIn; \n" > + "} \n"; > + > + ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc); > + if(!ret) { > + qDebug() << __func__ << ":" << shaderProgram.log(); > + } > + > + QString fsrc = "#ifdef GL_ES \n" > + "precision highp float; \n" > + "#endif \n" > + "varying vec2 textureOut; \n" > + "uniform sampler2D tex_y; \n" > + "uniform sampler2D tex_uv; \n" > + "void main(void) \n" > + "{ \n" > + "vec3 yuv; \n" > + "vec3 rgb; \n" > + "yuv.x = texture2D(tex_y, textureOut).r - 0.0625; \n" > + "yuv.y = texture2D(tex_uv, textureOut).r - 0.5; \n" > + "yuv.z = texture2D(tex_uv, textureOut).g - 0.5; \n" > + "rgb = mat3( 1.0, 1.0, 1.0, \n" > + " 0.0, -0.39465, 2.03211, \n" > + " 1.13983, -0.58060, 0.0) * yuv; \n" > + "gl_FragColor = vec4(rgb, 1.0); \n" > + "}\n"; > + ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc); > + if(!ret) { > + qDebug() << __func__ << ":" << shaderProgram.log(); > + } I know to little of OpenGL and shaders to be able to review the above. > + > + // Link shader pipeline /* */ instead of // for comments /* Link shader pipeline. */ > + if (!shaderProgram.link()) { > + qDebug() << __func__ << ": shader program link failed.\n" << shaderProgram.log(); > + close(); > + } > + > + // Bind shader pipeline for use > + if (!shaderProgram.bind()) { > + qDebug() << __func__ << ": shader program binding failed.\n" << shaderProgram.log(); > + close(); > + } > + > +} > + > +void GLWidget::configureTexture(unsigned int id) > +{ > + glBindTexture(GL_TEXTURE_2D, id); > + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); > + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); > + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); > + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); > +} > + > +void GLWidget::removeShader() > +{ > + if (shaderProgram.isLinked()) { > + shaderProgram.release(); > + shaderProgram.removeAllShaders(); > + } > +} > + > +void GLWidget::initializeGL() > +{ > + initializeOpenGLFunctions(); > + > + > + glEnable(GL_TEXTURE_2D); > + glDisable(GL_DEPTH_TEST); > + > + static const GLfloat vertices[] { > + -1.0f,-1.0f, > + -1.0f,+1.0f, > + +1.0f,+1.0f, > + +1.0f,-1.0f, > + 0.0f,1.0f, > + 0.0f,0.0f, > + 1.0f,0.0f, > + 1.0f,1.0f, > + }; > + > + glBuffer.create(); > + glBuffer.bind(); > + glBuffer.allocate(vertices,sizeof(vertices)); > + > + createShader(); > + > + shaderProgram.enableAttributeArray(ATTRIB_VERTEX); > + shaderProgram.enableAttributeArray(ATTRIB_TEXTURE); > + > + shaderProgram.setAttributeBuffer(ATTRIB_VERTEX,GL_FLOAT,0,2,2*sizeof(GLfloat)); > + shaderProgram.setAttributeBuffer(ATTRIB_TEXTURE,GL_FLOAT,8*sizeof(GLfloat),2,2*sizeof(GLfloat)); > + > + textureUniformY = shaderProgram.uniformLocation("tex_y"); > + textureUniformU = shaderProgram.uniformLocation("tex_uv"); > + > + textureY.create(); > + textureU.create(); > + > + id_y = textureY.textureId(); > + id_u = textureU.textureId(); > + > + configureTexture(id_y); > + configureTexture(id_u); > + > + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); > +} > + > +void GLWidget::paintGL() > +{ > + if(yuvDataPtr) > + { if (yuvDataPtr) { > + glClearColor(0.0, 0.0, 0.0, 1.0); > + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); > + > + // activate texture Y > + glActiveTexture(GL_TEXTURE0); > + glBindTexture(GL_TEXTURE_2D, id_y); > + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width_, height_, 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr); > + glUniform1i(textureUniformY, 0); > + > + // activate texture UV > + glActiveTexture(GL_TEXTURE1); > + glBindTexture(GL_TEXTURE_2D, id_u); > + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width_/2, height_/2, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+width_*height_); > + glUniform1i(textureUniformU, 1); > + > + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); > + } > +} > + > +void GLWidget::resizeGL(int w, int h) > +{ > + glViewport(0,0,w,h); > +} > diff --git a/src/qcam/glwidget.h b/src/qcam/glwidget.h > new file mode 100644 > index 0000000..17f71b9 > --- /dev/null > +++ b/src/qcam/glwidget.h > @@ -0,0 +1,76 @@ > +#ifndef GLWINDOW_H > +#define GLWINDOW_H > + > +#include <fcntl.h> > +#include <iomanip> > +#include <iostream> > +#include <sstream> > +#include <string.h> > +#include <unistd.h> > + > +#include <QOpenGLWidget> > +#include <QOpenGLFunctions> > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2020, Linaro > + * > + * glwidget.h - Rendering YUV frame by OpenGL shader > + */ This header go at the top of the file. > +#include <QOpenGLTexture> > +#include <QOpenGLShader> > +#include <QOpenGLShaderProgram> > + > +#include <QOpenGLBuffer> > +#include <QImage> > + > +#include <linux/drm_fourcc.h> > + > +class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions I think we need to create a viewfinder base class and then build GLWidget on-top of that. If we rename the ViewFinder of today to ViewFinderQImage and reuse the name ViewFinder for a new base class. Then ViewFinderQImage and GLWidget (or maybe ViewFinderQPainter?) could inherit from it and implement the interface. By doing this it would be easier to integrate different renderers in qcam. > +{ > + Q_OBJECT > + > +public: > + GLWidget(QWidget *parent = 0); > + ~GLWidget(); > + > + void setFrameSize(int width, int height); > + void updateFrame(unsigned char *buffer); > + > + int configure(unsigned int format, unsigned int width, > + unsigned int height); > + > + void createShader(); > + > +protected: > + void initializeGL() Q_DECL_OVERRIDE; > + void paintGL() Q_DECL_OVERRIDE; > + void resizeGL(int w, int h) Q_DECL_OVERRIDE; > + > +private: > + > + void configureTexture(unsigned int id); > + void removeShader(); > + > + QOpenGLShader *pVShader; > + QOpenGLShader *pFShader; > + QOpenGLShaderProgram shaderProgram; > + > + GLuint textureUniformY; > + GLuint textureUniformU; > + GLuint textureUniformV; > + GLuint id_y; > + GLuint id_u; > + GLuint id_v; > + QOpenGLTexture textureY; > + QOpenGLTexture textureU; > + QOpenGLTexture textureV; > + > + unsigned int format_; > + unsigned int width_; > + unsigned int height_; > + > + unsigned char* yuvDataPtr; > + > + QOpenGLBuffer glBuffer; > +}; > +#endif // GLWINDOW_H > diff --git a/src/qcam/meson.build b/src/qcam/meson.build > index 5150631..8bbcfba 100644 > --- a/src/qcam/meson.build > +++ b/src/qcam/meson.build > @@ -5,10 +5,12 @@ qcam_sources = files([ > '../cam/options.cpp', > 'qt_event_dispatcher.cpp', > 'viewfinder.cpp', > + 'glwidget.cpp' > ]) > > qcam_moc_headers = files([ > 'main_window.h', > + 'glwidget.h' > ]) > > qcam_resources = files([ > @@ -18,7 +20,7 @@ qcam_resources = files([ > qt5 = import('qt5') > qt5_dep = dependency('qt5', > method : 'pkg-config', > - modules : ['Core', 'Gui', 'Widgets'], > + modules : ['Core', 'Gui', 'Widgets','OpenGL', 'OpenGLExtensions'], I think we need to make the GL modules optional and only build build them if they are available but still be able to build qcam with only only ViewFinderQImage support if they are not. > required : false) > > if qt5_dep.found() > -- > 2.20.1 > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
diff --git a/src/qcam/glwidget.cpp b/src/qcam/glwidget.cpp new file mode 100644 index 0000000..bf6add4 --- /dev/null +++ b/src/qcam/glwidget.cpp @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * glwidget.cpp - Rendering YUV frame by OpenGL shader + */ + +#include "glwidget.h" +#include <QImage> + +#define ATTRIB_VERTEX 0 +#define ATTRIB_TEXTURE 1 + +GLWidget::GLWidget(QWidget *parent) + : QOpenGLWidget(parent), + textureY(QOpenGLTexture::Target2D), + textureU(QOpenGLTexture::Target2D), + textureV(QOpenGLTexture::Target2D), + yuvDataPtr(NULL), + glBuffer(QOpenGLBuffer::VertexBuffer) +{ +} + +GLWidget::~GLWidget() +{ + removeShader(); + + textureY.destroy(); + textureU.destroy(); + textureV.destroy(); + + glBuffer.destroy(); +} + +void GLWidget::updateFrame(unsigned char *buffer) +{ + if(buffer) { + yuvDataPtr = buffer; + update(); + } +} + +void GLWidget::setFrameSize(int width, int height) +{ + if(width > 0 && height > 0) { + width_ = width; + height_ = height; + } +} + +int GLWidget::configure(unsigned int format, unsigned int width, + unsigned int height) +{ + switch (format) { + case DRM_FORMAT_NV12: + break; + case DRM_FORMAT_NV21: + break; + default: + return -EINVAL; + }; + format_ = format; + width_ = width; + height_ = height; + + return 0; +} + +void GLWidget::createShader() +{ + bool ret = false; + + QString vsrc = "attribute vec4 vertexIn; \n" + "attribute vec2 textureIn; \n" + "varying vec2 textureOut; \n" + "void main(void) \n" + "{ \n" + "gl_Position = vertexIn; \n" + "textureOut = textureIn; \n" + "} \n"; + + ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc); + if(!ret) { + qDebug() << __func__ << ":" << shaderProgram.log(); + } + + QString fsrc = "#ifdef GL_ES \n" + "precision highp float; \n" + "#endif \n" + "varying vec2 textureOut; \n" + "uniform sampler2D tex_y; \n" + "uniform sampler2D tex_uv; \n" + "void main(void) \n" + "{ \n" + "vec3 yuv; \n" + "vec3 rgb; \n" + "yuv.x = texture2D(tex_y, textureOut).r - 0.0625; \n" + "yuv.y = texture2D(tex_uv, textureOut).r - 0.5; \n" + "yuv.z = texture2D(tex_uv, textureOut).g - 0.5; \n" + "rgb = mat3( 1.0, 1.0, 1.0, \n" + " 0.0, -0.39465, 2.03211, \n" + " 1.13983, -0.58060, 0.0) * yuv; \n" + "gl_FragColor = vec4(rgb, 1.0); \n" + "}\n"; + ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc); + if(!ret) { + qDebug() << __func__ << ":" << shaderProgram.log(); + } + + // Link shader pipeline + if (!shaderProgram.link()) { + qDebug() << __func__ << ": shader program link failed.\n" << shaderProgram.log(); + close(); + } + + // Bind shader pipeline for use + if (!shaderProgram.bind()) { + qDebug() << __func__ << ": shader program binding failed.\n" << shaderProgram.log(); + close(); + } + +} + +void GLWidget::configureTexture(unsigned int id) +{ + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void GLWidget::removeShader() +{ + if (shaderProgram.isLinked()) { + shaderProgram.release(); + shaderProgram.removeAllShaders(); + } +} + +void GLWidget::initializeGL() +{ + initializeOpenGLFunctions(); + + + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + + static const GLfloat vertices[] { + -1.0f,-1.0f, + -1.0f,+1.0f, + +1.0f,+1.0f, + +1.0f,-1.0f, + 0.0f,1.0f, + 0.0f,0.0f, + 1.0f,0.0f, + 1.0f,1.0f, + }; + + glBuffer.create(); + glBuffer.bind(); + glBuffer.allocate(vertices,sizeof(vertices)); + + createShader(); + + shaderProgram.enableAttributeArray(ATTRIB_VERTEX); + shaderProgram.enableAttributeArray(ATTRIB_TEXTURE); + + shaderProgram.setAttributeBuffer(ATTRIB_VERTEX,GL_FLOAT,0,2,2*sizeof(GLfloat)); + shaderProgram.setAttributeBuffer(ATTRIB_TEXTURE,GL_FLOAT,8*sizeof(GLfloat),2,2*sizeof(GLfloat)); + + textureUniformY = shaderProgram.uniformLocation("tex_y"); + textureUniformU = shaderProgram.uniformLocation("tex_uv"); + + textureY.create(); + textureU.create(); + + id_y = textureY.textureId(); + id_u = textureU.textureId(); + + configureTexture(id_y); + configureTexture(id_u); + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); +} + +void GLWidget::paintGL() +{ + if(yuvDataPtr) + { + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // activate texture Y + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, id_y); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width_, height_, 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr); + glUniform1i(textureUniformY, 0); + + // activate texture UV + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, id_u); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width_/2, height_/2, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+width_*height_); + glUniform1i(textureUniformU, 1); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +} + +void GLWidget::resizeGL(int w, int h) +{ + glViewport(0,0,w,h); +} diff --git a/src/qcam/glwidget.h b/src/qcam/glwidget.h new file mode 100644 index 0000000..17f71b9 --- /dev/null +++ b/src/qcam/glwidget.h @@ -0,0 +1,76 @@ +#ifndef GLWINDOW_H +#define GLWINDOW_H + +#include <fcntl.h> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <string.h> +#include <unistd.h> + +#include <QOpenGLWidget> +#include <QOpenGLFunctions> +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * glwidget.h - Rendering YUV frame by OpenGL shader + */ +#include <QOpenGLTexture> +#include <QOpenGLShader> +#include <QOpenGLShaderProgram> + +#include <QOpenGLBuffer> +#include <QImage> + +#include <linux/drm_fourcc.h> + +class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + +public: + GLWidget(QWidget *parent = 0); + ~GLWidget(); + + void setFrameSize(int width, int height); + void updateFrame(unsigned char *buffer); + + int configure(unsigned int format, unsigned int width, + unsigned int height); + + void createShader(); + +protected: + void initializeGL() Q_DECL_OVERRIDE; + void paintGL() Q_DECL_OVERRIDE; + void resizeGL(int w, int h) Q_DECL_OVERRIDE; + +private: + + void configureTexture(unsigned int id); + void removeShader(); + + QOpenGLShader *pVShader; + QOpenGLShader *pFShader; + QOpenGLShaderProgram shaderProgram; + + GLuint textureUniformY; + GLuint textureUniformU; + GLuint textureUniformV; + GLuint id_y; + GLuint id_u; + GLuint id_v; + QOpenGLTexture textureY; + QOpenGLTexture textureU; + QOpenGLTexture textureV; + + unsigned int format_; + unsigned int width_; + unsigned int height_; + + unsigned char* yuvDataPtr; + + QOpenGLBuffer glBuffer; +}; +#endif // GLWINDOW_H diff --git a/src/qcam/meson.build b/src/qcam/meson.build index 5150631..8bbcfba 100644 --- a/src/qcam/meson.build +++ b/src/qcam/meson.build @@ -5,10 +5,12 @@ qcam_sources = files([ '../cam/options.cpp', 'qt_event_dispatcher.cpp', 'viewfinder.cpp', + 'glwidget.cpp' ]) qcam_moc_headers = files([ 'main_window.h', + 'glwidget.h' ]) qcam_resources = files([ @@ -18,7 +20,7 @@ qcam_resources = files([ qt5 = import('qt5') qt5_dep = dependency('qt5', method : 'pkg-config', - modules : ['Core', 'Gui', 'Widgets'], + modules : ['Core', 'Gui', 'Widgets','OpenGL', 'OpenGLExtensions'], required : false) if qt5_dep.found()
qcam: Added opengl widget class for qcam Signed-off-by: Show Liu <show.liu@linaro.org> --- src/qcam/glwidget.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++ src/qcam/glwidget.h | 76 +++++++++++++++ src/qcam/meson.build | 4 +- 3 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 src/qcam/glwidget.cpp create mode 100644 src/qcam/glwidget.h