From patchwork Tue May 26 07:52:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Show Liu X-Patchwork-Id: 3857 Return-Path: Received: from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com [IPv6:2607:f8b0:4864:20::1035]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7ED94603D1 for ; Tue, 26 May 2020 09:52:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="UXMf2q7d"; dkim-atps=neutral Received: by mail-pj1-x1035.google.com with SMTP id q9so873019pjm.2 for ; Tue, 26 May 2020 00:52:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yc3LMIguN526ACc8SdOcpEENG/LYxTGRJR2OvNbnjJo=; b=UXMf2q7dNeaGE2WaGA0tRJ3n0usUsdfgPZw0osx57cet5JCAIiLCnxNvc3Iqid/ZVg 9q09xM3cVLn+7D70HDWrA6qwDmXORbMMGTnXr2tgnGk5NGqEwMMGF/6ACFAiP3Hf3hpz 0doPn95thXVipy13FFmXMy4OSo8VFQbDQ8Ad+5MhVmY2EIA9C5xcOqz2iYvW43AgqEJ3 CsY1C5csBfVr3Wl4ETyNpAR7+0OtL98upK+DKDa2VHQaJ9NDzwHJwlvb+CWdBYn8TbZr SvJVQRvcwBI1OeOFBigp+sk4kjRXXfdh0VeI/aMQoFzirm5Y0K8aWLhi4gVjsZz0nft8 GR5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yc3LMIguN526ACc8SdOcpEENG/LYxTGRJR2OvNbnjJo=; b=gwdfXccdE7Cg3r9TUwmnKB4aVoZ6anDUUgY2shK0F9TLBP/znYwixnWeKC07XWA3Ku B900R+jfGhu6pwMx23/BFTpSQdeDbcxmy+MzrzSdKJkwHSpc0nr9/SmpKhTaatpB9fUG N/Dv64KNER88u6e0Sha4P5a0JofOfNUg99pJBxZaYP2ZPYRIFTHojC7csjL7weqczU9W uiujoPtyvjSyIsy1qThDK/7ZyNJxqcFFLWDz4A3nXEn1kM/5KFrYXVvSWuCv+eEImQSB tMjIpYxIm8GvUoIiBBa8i7awUExEC64LtqiIVX/s7mArfPzfdRZ6a374isoxwZx2g4g1 KnDQ== X-Gm-Message-State: AOAM531YGmJFpnOIeYUhG8jL8NX5Y12/+eXPM2zq5Wm8AA5Rw+l4RC8u fg/nHcpjlJYJ3aQF/SLn4jwpF7kNXSB6VA== X-Google-Smtp-Source: ABdhPJy4hjb2dLWiL/ambhaziib2ssfee/UowutaS8xSq3xjd9HfWn9wxhkFabz1mojE1PcVVu9sNg== X-Received: by 2002:a17:902:b594:: with SMTP id a20mr10699092pls.187.1590479552867; Tue, 26 May 2020 00:52:32 -0700 (PDT) Received: from localhost.localdomain (211-20-20-223.HINET-IP.hinet.net. [211.20.20.223]) by smtp.gmail.com with ESMTPSA id v5sm14991091pjy.4.2020.05.26.00.52.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 May 2020 00:52:32 -0700 (PDT) From: Show Liu To: libcamera-devel@lists.libcamera.org Date: Tue, 26 May 2020 15:52:03 +0800 Message-Id: <20200526075203.4503-2-show.liu@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200526075203.4503-1-show.liu@linaro.org> References: <20200526075203.4503-1-show.liu@linaro.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC v2] [PATCH 1/1] qcam: Render YUV formats frame by OpenGL shader X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 26 May 2020 07:52:35 -0000 Signed-off-by: Show Liu --- src/qcam/fshader.h | 86 ++++++++++ src/qcam/main.cpp | 2 + src/qcam/main_window.cpp | 19 ++- src/qcam/main_window.h | 3 +- src/qcam/meson.build | 2 + src/qcam/viewfinder.cpp | 18 +- src/qcam/viewfinder.h | 23 ++- src/qcam/viewfinderGL.cpp | 346 ++++++++++++++++++++++++++++++++++++++ src/qcam/viewfinderGL.h | 102 +++++++++++ 9 files changed, 587 insertions(+), 14 deletions(-) create mode 100644 src/qcam/fshader.h create mode 100644 src/qcam/viewfinderGL.cpp create mode 100644 src/qcam/viewfinderGL.h diff --git a/src/qcam/fshader.h b/src/qcam/fshader.h new file mode 100644 index 0000000..5769308 --- /dev/null +++ b/src/qcam/fshader.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * fshader.h - Fragment shader code + */ +#ifndef __FSHADER_H__ +#define __FSHADER_H__ + +char NV_2_planes_UV[] = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec2 textureOut;\n" + "uniform sampler2D tex_y;\n" + "uniform sampler2D tex_u;\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_u, textureOut).r - 0.5;\n" + "yuv.z = texture2D(tex_u, 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"; + +char NV_2_planes_VU[] = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec2 textureOut;\n" + "uniform sampler2D tex_y;\n" + "uniform sampler2D tex_u;\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_u, textureOut).g - 0.5;\n" + "yuv.z = texture2D(tex_u, textureOut).r - 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"; + +char NV_3_planes_UV[] = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec2 textureOut;\n" + "uniform sampler2D tex_y;\n" + "uniform sampler2D tex_u;\n" + "uniform sampler2D tex_v;\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_u, textureOut).r - 0.5;\n" + "yuv.z = texture2D(tex_v, textureOut).g - 0.5;\n" + "rgb = mat3( 1, 1, 1,\n" + " 0, -0.39465, 2.03211,\n" + " 1.13983, -0.58060, 0) * yuv;\n" + "gl_FragColor = vec4(rgb, 1);\n" + "}\n"; + +char NV_3_planes_VU[] = "precision highp float;\n" + "#endif\n" + "varying vec2 textureOut;\n" + "uniform sampler2D tex_y;\n" + "uniform sampler2D tex_u;\n" + "uniform sampler2D tex_v;\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_u, textureOut).g - 0.5;\n" + "yuv.z = texture2D(tex_v, textureOut).r - 0.5;\n" + "rgb = mat3( 1, 1, 1,\n" + " 0, -0.39465, 2.03211,\n" + " 1.13983, -0.58060, 0) * yuv;\n" + "gl_FragColor = vec4(rgb, 1);\n" + "}\n"; +#endif // __FSHADER_H__ diff --git a/src/qcam/main.cpp b/src/qcam/main.cpp index b3468cb..a3ea471 100644 --- a/src/qcam/main.cpp +++ b/src/qcam/main.cpp @@ -35,6 +35,8 @@ OptionsParser::Options parseOptions(int argc, char *argv[]) "help"); parser.addOption(OptStream, &streamKeyValue, "Set configuration of a camera stream", "stream", true); + parser.addOption(OptOpenGL, OptionNone, "Render YUV formats frame via OpenGL shader", + "opengl"); OptionsParser::Options options = parser.parse(argc, argv); if (options.isSet(OptHelp)) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 7de0895..a49a15a 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -28,6 +28,9 @@ #include #include "dng_writer.h" +#include "main_window.h" +#include "viewfinder.h" +#include "viewfinderGL.h" using namespace libcamera; @@ -65,10 +68,18 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) setWindowTitle(title_); connect(&titleTimer_, SIGNAL(timeout()), this, SLOT(updateTitle())); - viewfinder_ = new ViewFinder(this); - connect(viewfinder_, &ViewFinder::renderComplete, - this, &MainWindow::queueRequest); - setCentralWidget(viewfinder_); + if (options_.isSet(OptOpenGL)) { + viewfinder_ = new ViewFinderGL(this); + connect(dynamic_cast(viewfinder_), &ViewFinderGL::renderComplete, + this, &MainWindow::queueRequest); + setCentralWidget(dynamic_cast(viewfinder_)); + } else { + viewfinder_ = new ViewFinder(this); + connect(dynamic_cast(viewfinder_), &ViewFinder::renderComplete, + this, &MainWindow::queueRequest); + setCentralWidget(dynamic_cast(viewfinder_)); + } + adjustSize(); /* Open the camera and start capture. */ diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 59fa2d9..2c9713c 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -36,6 +36,7 @@ enum { OptCamera = 'c', OptHelp = 'h', OptStream = 's', + OptOpenGL = 'o', }; class CaptureRequest @@ -96,7 +97,7 @@ private: QAction *startStopAction_; QComboBox *cameraCombo_; QAction *saveRaw_; - ViewFinder *viewfinder_; + ViewFinderHandler *viewfinder_; QIcon iconPlay_; QIcon iconStop_; diff --git a/src/qcam/meson.build b/src/qcam/meson.build index 045db52..6a58947 100644 --- a/src/qcam/meson.build +++ b/src/qcam/meson.build @@ -7,11 +7,13 @@ qcam_sources = files([ 'main.cpp', 'main_window.cpp', 'viewfinder.cpp', + 'viewfinderGL.cpp' ]) qcam_moc_headers = files([ 'main_window.h', 'viewfinder.h', + 'viewfinderGL.h' ]) qcam_resources = files([ diff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp index 0d68f62..2079e7e 100644 --- a/src/qcam/viewfinder.cpp +++ b/src/qcam/viewfinder.cpp @@ -31,22 +31,30 @@ static const QMap nativeFormats { libcamera::PixelFormat{ DRM_FORMAT_RGB888 }, QImage::Format_RGB888 }, }; -ViewFinder::ViewFinder(QWidget *parent) - : QWidget(parent), buffer_(nullptr) +ViewFinderHandler::ViewFinderHandler() { - icon_ = QIcon(":camera-off.svg"); } -ViewFinder::~ViewFinder() +ViewFinderHandler::~ViewFinderHandler() { } -const QList &ViewFinder::nativeFormats() const +const QList &ViewFinderHandler::nativeFormats() const { static const QList formats = ::nativeFormats.keys(); return formats; } +ViewFinder::ViewFinder(QWidget *parent) + : QWidget(parent), buffer_(nullptr) +{ + icon_ = QIcon(":camera-off.svg"); +} + +ViewFinder::~ViewFinder() +{ +} + int ViewFinder::setFormat(const libcamera::PixelFormat &format, const QSize &size) { diff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h index b3f1d25..3d0d725 100644 --- a/src/qcam/viewfinder.h +++ b/src/qcam/viewfinder.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -28,7 +30,23 @@ struct MappedBuffer { size_t size; }; -class ViewFinder : public QWidget +class ViewFinderHandler +{ +public: + ViewFinderHandler(); + virtual ~ViewFinderHandler(); + + const QList &nativeFormats() const; + + virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size) =0; + virtual void render(libcamera::FrameBuffer *buffer, MappedBuffer *map) =0; + virtual void stop() =0; + + virtual QImage getCurrentImage() =0; + +}; + +class ViewFinder : public QWidget, public ViewFinderHandler { Q_OBJECT @@ -36,8 +54,6 @@ public: ViewFinder(QWidget *parent); ~ViewFinder(); - const QList &nativeFormats() const; - int setFormat(const libcamera::PixelFormat &format, const QSize &size); void render(libcamera::FrameBuffer *buffer, MappedBuffer *map); void stop(); @@ -67,5 +83,4 @@ private: QImage image_; QMutex mutex_; /* Prevent concurrent access to image_ */ }; - #endif /* __QCAM_VIEWFINDER__ */ diff --git a/src/qcam/viewfinderGL.cpp b/src/qcam/viewfinderGL.cpp new file mode 100644 index 0000000..c24ccfb --- /dev/null +++ b/src/qcam/viewfinderGL.cpp @@ -0,0 +1,346 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * viewfinderGL.cpp - Render YUV format frame by OpenGL shader + */ +#include + +#include "fshader.h" +#include "viewfinderGL.h" + + +#define ATTRIB_VERTEX 0 +#define ATTRIB_TEXTURE 1 + +ViewFinderGL::ViewFinderGL(QWidget *parent) + : QOpenGLWidget(parent), + glBuffer(QOpenGLBuffer::VertexBuffer), + pFShader(nullptr), + pVShader(nullptr), + textureU(QOpenGLTexture::Target2D), + textureV(QOpenGLTexture::Target2D), + textureY(QOpenGLTexture::Target2D), + yuvDataPtr(nullptr) + +{ +} + +ViewFinderGL::~ViewFinderGL() +{ + removeShader(); + + if(textureY.isCreated()) + textureY.destroy(); + + if(textureU.isCreated()) + textureU.destroy(); + + if(textureV.isCreated()) + textureV.destroy(); + + glBuffer.destroy(); +} + +int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, + const QSize &size) +{ + format_ = format; + size_ = size; + + updateGeometry(); + return 0; +} + +void ViewFinderGL::stop() +{ + if (buffer_) { + renderComplete(buffer_); + buffer_ = nullptr; + } +} + +QImage ViewFinderGL::getCurrentImage() +{ + QMutexLocker locker(&mutex_); + + return(grabFramebuffer()); +} + +void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer *map) +{ + if (buffer->planes().size() != 1) { + qWarning() << "Multi-planar buffers are not supported"; + return; + } + + unsigned char *memory = static_cast(map->memory); + if(memory) { + yuvDataPtr = memory; + update(); + buffer_ = buffer; + } + + if (buffer) + renderComplete(buffer); +} + +void ViewFinderGL::updateFrame(unsigned char *buffer) +{ + if(buffer) { + yuvDataPtr = buffer; + update(); + } +} + +void ViewFinderGL::setFrameSize(int width, int height) +{ + if(width > 0 && height > 0) { + width_ = width; + height_ = height; + } +} + +char *ViewFinderGL::selectFragmentShader(unsigned int format) +{ + char *fsrc = nullptr; + + switch(format) { + case DRM_FORMAT_NV12: + horzSubSample_ = 2; + vertSubSample_ = 2; + fsrc = NV_2_planes_UV; + break; + case DRM_FORMAT_NV21: + horzSubSample_ = 2; + vertSubSample_ = 2; + fsrc = NV_2_planes_VU; + break; + case DRM_FORMAT_NV16: + horzSubSample_ = 2; + vertSubSample_ = 1; + fsrc = NV_2_planes_UV; + break; + case DRM_FORMAT_NV61: + horzSubSample_ = 2; + vertSubSample_ = 1; + fsrc = NV_2_planes_VU; + break; + case DRM_FORMAT_NV24: + horzSubSample_ = 1; + vertSubSample_ = 1; + fsrc = NV_2_planes_UV; + break; + case DRM_FORMAT_NV42: + horzSubSample_ = 1; + vertSubSample_ = 1; + fsrc = NV_2_planes_VU; + break; + case DRM_FORMAT_YUV420: + horzSubSample_ = 2; + vertSubSample_ = 2; + fsrc = NV_3_planes_UV; + break; + case DRM_FORMAT_YVU420: + horzSubSample_ = 2; + vertSubSample_ = 2; + fsrc = NV_3_planes_VU; + break; + default: + break; + }; + + if(fsrc == nullptr) { + qDebug() << __func__ << "[ERROR]:" <<" No suitable fragment shader can be select."; + } + return fsrc; +} + +void ViewFinderGL::createFragmentShader() +{ + bool bCompile; + + pFShader = new QOpenGLShader(QOpenGLShader::Fragment, this); + char *fsrc = selectFragmentShader(format_); + + bCompile = pFShader->compileSourceCode(fsrc); + if(!bCompile) + { + qDebug() << __func__ << ":" << pFShader->log(); + } + + shaderProgram.addShader(pFShader); + + // 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(); + } + + 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_u"); + textureUniformV = shaderProgram.uniformLocation("tex_v"); + + if(!textureY.isCreated()) + textureY.create(); + + if(!textureU.isCreated()) + textureU.create(); + + if(!textureV.isCreated()) + textureV.create(); + + id_y = textureY.textureId(); + id_u = textureU.textureId(); + id_v = textureV.textureId(); +} + +void ViewFinderGL::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 ViewFinderGL::removeShader() +{ + if (shaderProgram.isLinked()) { + shaderProgram.release(); + shaderProgram.removeAllShaders(); + } + + if(pFShader) + delete pFShader; + + if(pVShader) + delete pVShader; +} + +void ViewFinderGL::initializeGL() +{ + bool bCompile; + + 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)); + + /* Create Vertex Shader */ + pVShader = new QOpenGLShader(QOpenGLShader::Vertex, this); + const char *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"; + + bCompile = pVShader->compileSourceCode(vsrc); + if(!bCompile) { + qDebug() << __func__<< ":" << pVShader->log(); + } + + shaderProgram.addShader(pVShader); + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); +} + +void ViewFinderGL::render() +{ + switch(format_) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: + /* activate texture 0 */ + glActiveTexture(GL_TEXTURE0); + configureTexture(id_y); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size_.width(), size_.height(), 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr); + glUniform1i(textureUniformY, 0); + + /* activate texture 1 */ + glActiveTexture(GL_TEXTURE1); + configureTexture(id_u); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+size_.width()*size_.height()); + glUniform1i(textureUniformU, 1); + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + /* activate texture 0 */ + glActiveTexture(GL_TEXTURE0); + configureTexture(id_y); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size_.width(), size_.height(), 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr); + glUniform1i(textureUniformY, 0); + + /* activate texture 1 */ + glActiveTexture(GL_TEXTURE1); + configureTexture(id_u); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+size_.width()*size_.height()); + glUniform1i(textureUniformU, 1); + + /* activate texture 2 */ + glActiveTexture(GL_TEXTURE2); + configureTexture(id_v); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+size_.width()*size_.height()*5/4); + glUniform1i(textureUniformV, 2); + break; + default: + break; + }; +} + +void ViewFinderGL::paintGL() +{ + if(pFShader == nullptr) + createFragmentShader(); + + if(yuvDataPtr) + { + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + render(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +} + +void ViewFinderGL::resizeGL(int w, int h) +{ + glViewport(0,0,w,h); +} + +QSize ViewFinderGL::sizeHint() const +{ + return size_.isValid() ? size_ : QSize(640, 480); +} diff --git a/src/qcam/viewfinderGL.h b/src/qcam/viewfinderGL.h new file mode 100644 index 0000000..5366358 --- /dev/null +++ b/src/qcam/viewfinderGL.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * viewfinderGL.h - Render YUV format frame by OpenGL shader + */ +#ifndef __VIEWFINDERGL_H__ +#define __VIEWFINDERGL_H__ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "viewfinder.h" + +class ViewFinderGL : public QOpenGLWidget, + public ViewFinderHandler, + protected QOpenGLFunctions +{ + Q_OBJECT + +public: + ViewFinderGL(QWidget *parent = 0); + ~ViewFinderGL(); + + int setFormat(const libcamera::PixelFormat &format, const QSize &size); + void render(libcamera::FrameBuffer *buffer, MappedBuffer *map); + void stop(); + + QImage getCurrentImage(); + + void setFrameSize(int width, int height); + void updateFrame(unsigned char *buffer); + + char *selectFragmentShader(unsigned int format); + void createFragmentShader(); + void render(); + +Q_SIGNALS: + void renderComplete(libcamera::FrameBuffer *buffer); + +protected: + void initializeGL() Q_DECL_OVERRIDE; + void paintGL() Q_DECL_OVERRIDE; + void resizeGL(int w, int h) Q_DECL_OVERRIDE; + QSize sizeHint() const override; + +private: + + void configureTexture(unsigned int id); + void removeShader(); + + /* Captured image size, format and buffer */ + libcamera::FrameBuffer *buffer_; + libcamera::PixelFormat format_; + QOpenGLBuffer glBuffer; + QSize size_; + + GLuint id_u; + GLuint id_v; + GLuint id_y; + + QMutex mutex_; /* Prevent concurrent access to image_ */ + + QOpenGLShader *pFShader; + QOpenGLShader *pVShader; + QOpenGLShaderProgram shaderProgram; + + GLuint textureUniformU; + GLuint textureUniformV; + GLuint textureUniformY; + + QOpenGLTexture textureU; + QOpenGLTexture textureV; + QOpenGLTexture textureY; + + unsigned int width_; + unsigned int height_; + + unsigned char* yuvDataPtr; + + /* NV parameters */ + unsigned int horzSubSample_ ; + unsigned int vertSubSample_; +}; +#endif // __VIEWFINDERGL_H__