Show a patch.

GET /api/patches/3857/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 3857,
    "url": "https://patchwork.libcamera.org/api/patches/3857/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/3857/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20200526075203.4503-2-show.liu@linaro.org>",
    "date": "2020-05-26T07:52:03",
    "name": "[libcamera-devel,RFC,v2,1/1] qcam: Render YUV formats frame by OpenGL shader",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "78860c23a049e86d99490479d76bd684b3cecb68",
    "submitter": {
        "id": 24,
        "url": "https://patchwork.libcamera.org/api/people/24/?format=api",
        "name": "Show Liu",
        "email": "show.liu@linaro.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/3857/mbox/",
    "series": [
        {
            "id": 928,
            "url": "https://patchwork.libcamera.org/api/series/928/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=928",
            "date": "2020-05-26T07:52:02",
            "name": "qcam: Render YUV formats frame by OpenGL shader",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/928/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/3857/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/3857/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<show.liu@linaro.org>",
        "Received": [
            "from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com\n\t[IPv6:2607:f8b0:4864:20::1035])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7ED94603D1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 26 May 2020 09:52:35 +0200 (CEST)",
            "by mail-pj1-x1035.google.com with SMTP id q9so873019pjm.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 26 May 2020 00:52:35 -0700 (PDT)",
            "from localhost.localdomain (211-20-20-223.HINET-IP.hinet.net.\n\t[211.20.20.223]) by smtp.gmail.com with ESMTPSA id\n\tv5sm14991091pjy.4.2020.05.26.00.52.31\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 26 May 2020 00:52:32 -0700 (PDT)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=linaro.org\n\theader.i=@linaro.org header.b=\"UXMf2q7d\"; \n\tdkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=yc3LMIguN526ACc8SdOcpEENG/LYxTGRJR2OvNbnjJo=;\n\tb=UXMf2q7dNeaGE2WaGA0tRJ3n0usUsdfgPZw0osx57cet5JCAIiLCnxNvc3Iqid/ZVg\n\t9q09xM3cVLn+7D70HDWrA6qwDmXORbMMGTnXr2tgnGk5NGqEwMMGF/6ACFAiP3Hf3hpz\n\t0doPn95thXVipy13FFmXMy4OSo8VFQbDQ8Ad+5MhVmY2EIA9C5xcOqz2iYvW43AgqEJ3\n\tCsY1C5csBfVr3Wl4ETyNpAR7+0OtL98upK+DKDa2VHQaJ9NDzwHJwlvb+CWdBYn8TbZr\n\tSvJVQRvcwBI1OeOFBigp+sk4kjRXXfdh0VeI/aMQoFzirm5Y0K8aWLhi4gVjsZz0nft8\n\tGR5g==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=yc3LMIguN526ACc8SdOcpEENG/LYxTGRJR2OvNbnjJo=;\n\tb=gwdfXccdE7Cg3r9TUwmnKB4aVoZ6anDUUgY2shK0F9TLBP/znYwixnWeKC07XWA3Ku\n\tB900R+jfGhu6pwMx23/BFTpSQdeDbcxmy+MzrzSdKJkwHSpc0nr9/SmpKhTaatpB9fUG\n\tN/Dv64KNER88u6e0Sha4P5a0JofOfNUg99pJBxZaYP2ZPYRIFTHojC7csjL7weqczU9W\n\tuiujoPtyvjSyIsy1qThDK/7ZyNJxqcFFLWDz4A3nXEn1kM/5KFrYXVvSWuCv+eEImQSB\n\ttMjIpYxIm8GvUoIiBBa8i7awUExEC64LtqiIVX/s7mArfPzfdRZ6a374isoxwZx2g4g1\n\tKnDQ==",
        "X-Gm-Message-State": "AOAM531YGmJFpnOIeYUhG8jL8NX5Y12/+eXPM2zq5Wm8AA5Rw+l4RC8u\n\tfg/nHcpjlJYJ3aQF/SLn4jwpF7kNXSB6VA==",
        "X-Google-Smtp-Source": "ABdhPJy4hjb2dLWiL/ambhaziib2ssfee/UowutaS8xSq3xjd9HfWn9wxhkFabz1mojE1PcVVu9sNg==",
        "X-Received": "by 2002:a17:902:b594:: with SMTP id\n\ta20mr10699092pls.187.1590479552867; \n\tTue, 26 May 2020 00:52:32 -0700 (PDT)",
        "From": "Show Liu <show.liu@linaro.org>",
        "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",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [RFC v2] [PATCH 1/1] qcam: Render YUV formats\n\tframe by OpenGL shader",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "X-List-Received-Date": "Tue, 26 May 2020 07:52:35 -0000"
    },
    "content": "Signed-off-by: Show Liu <show.liu@linaro.org>\n---\n src/qcam/fshader.h        |  86 ++++++++++\n src/qcam/main.cpp         |   2 +\n src/qcam/main_window.cpp  |  19 ++-\n src/qcam/main_window.h    |   3 +-\n src/qcam/meson.build      |   2 +\n src/qcam/viewfinder.cpp   |  18 +-\n src/qcam/viewfinder.h     |  23 ++-\n src/qcam/viewfinderGL.cpp | 346 ++++++++++++++++++++++++++++++++++++++\n src/qcam/viewfinderGL.h   | 102 +++++++++++\n 9 files changed, 587 insertions(+), 14 deletions(-)\n create mode 100644 src/qcam/fshader.h\n create mode 100644 src/qcam/viewfinderGL.cpp\n create mode 100644 src/qcam/viewfinderGL.h",
    "diff": "diff --git a/src/qcam/fshader.h b/src/qcam/fshader.h\nnew file mode 100644\nindex 0000000..5769308\n--- /dev/null\n+++ b/src/qcam/fshader.h\n@@ -0,0 +1,86 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Linaro\n+ *\n+ * fshader.h - Fragment shader code\n+ */\n+#ifndef __FSHADER_H__\n+#define __FSHADER_H__\n+\n+char NV_2_planes_UV[] = \"#ifdef GL_ES\\n\"\n+                \"precision highp float;\\n\"\n+                \"#endif\\n\"\n+                \"varying vec2 textureOut;\\n\"\n+                \"uniform sampler2D tex_y;\\n\"\n+                \"uniform sampler2D tex_u;\\n\"\n+                \"void main(void)\\n\"\n+                \"{\\n\"\n+                \"vec3 yuv;\\n\"\n+                \"vec3 rgb;\\n\"\n+                \"yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\\n\"\n+                \"yuv.y = texture2D(tex_u, textureOut).r - 0.5;\\n\"\n+                \"yuv.z = texture2D(tex_u, textureOut).g - 0.5;\\n\"\n+                \"rgb = mat3( 1.0,       1.0,         1.0,\\n\"\n+                \"            0.0,       -0.39465,  2.03211,\\n\"\n+                \"            1.13983, -0.58060,  0.0) * yuv;\\n\"\n+                \"gl_FragColor = vec4(rgb, 1.0);\\n\"\n+                \"}\\n\";\n+\n+char NV_2_planes_VU[] = \"#ifdef GL_ES\\n\"\n+                \"precision highp float;\\n\"\n+                \"#endif\\n\"\n+                \"varying vec2 textureOut;\\n\"\n+                \"uniform sampler2D tex_y;\\n\"\n+                \"uniform sampler2D tex_u;\\n\"\n+                \"void main(void)\\n\"\n+                \"{\\n\"\n+                \"vec3 yuv;\\n\"\n+                \"vec3 rgb;\\n\"\n+                \"yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\\n\"\n+                \"yuv.y = texture2D(tex_u, textureOut).g - 0.5;\\n\"\n+                \"yuv.z = texture2D(tex_u, textureOut).r - 0.5;\\n\"\n+                \"rgb = mat3( 1.0,       1.0,         1.0,\\n\"\n+                \"            0.0,       -0.39465,  2.03211,\\n\"\n+                \"            1.13983, -0.58060,  0.0) * yuv;\\n\"\n+                \"gl_FragColor = vec4(rgb, 1.0);\\n\"\n+                \"}\\n\";\n+\n+char NV_3_planes_UV[] = \"#ifdef GL_ES\\n\"\n+                \"precision highp float;\\n\"\n+                \"#endif\\n\"\n+                \"varying vec2 textureOut;\\n\"\n+                \"uniform sampler2D tex_y;\\n\"\n+                \"uniform sampler2D tex_u;\\n\"\n+                \"uniform sampler2D tex_v;\\n\"\n+                \"void main(void)\\n\"\n+                \"{\\n\"\n+                \"vec3 yuv;\\n\"\n+                \"vec3 rgb;\\n\"\n+                \"yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\\n\"\n+                \"yuv.y = texture2D(tex_u, textureOut).r - 0.5;\\n\"\n+                \"yuv.z = texture2D(tex_v, textureOut).g - 0.5;\\n\"\n+                \"rgb = mat3( 1,       1,         1,\\n\"\n+                \"            0,       -0.39465,  2.03211,\\n\"\n+                \"            1.13983, -0.58060,  0) * yuv;\\n\"\n+                \"gl_FragColor = vec4(rgb, 1);\\n\"\n+                \"}\\n\";\n+\n+char NV_3_planes_VU[] = \"precision highp float;\\n\"\n+                \"#endif\\n\"\n+                \"varying vec2 textureOut;\\n\"\n+                \"uniform sampler2D tex_y;\\n\"\n+                \"uniform sampler2D tex_u;\\n\"\n+                \"uniform sampler2D tex_v;\\n\"\n+                \"void main(void)\\n\"\n+                \"{\\n\"\n+                \"vec3 yuv;\\n\"\n+                \"vec3 rgb;\\n\"\n+                \"yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\\n\"\n+                \"yuv.y = texture2D(tex_u, textureOut).g - 0.5;\\n\"\n+                \"yuv.z = texture2D(tex_v, textureOut).r - 0.5;\\n\"\n+                \"rgb = mat3( 1,       1,         1,\\n\"\n+                \"            0,       -0.39465,  2.03211,\\n\"\n+                \"            1.13983, -0.58060,  0) * yuv;\\n\"\n+                \"gl_FragColor = vec4(rgb, 1);\\n\"\n+                \"}\\n\";\n+#endif // __FSHADER_H__\ndiff --git a/src/qcam/main.cpp b/src/qcam/main.cpp\nindex b3468cb..a3ea471 100644\n--- a/src/qcam/main.cpp\n+++ b/src/qcam/main.cpp\n@@ -35,6 +35,8 @@ OptionsParser::Options parseOptions(int argc, char *argv[])\n \t\t\t \"help\");\n \tparser.addOption(OptStream, &streamKeyValue,\n \t\t\t \"Set configuration of a camera stream\", \"stream\", true);\n+\tparser.addOption(OptOpenGL, OptionNone, \"Render YUV formats frame via OpenGL shader\",\n+\t\t\t \"opengl\");\n \n \tOptionsParser::Options options = parser.parse(argc, argv);\n \tif (options.isSet(OptHelp))\ndiff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\nindex 7de0895..a49a15a 100644\n--- a/src/qcam/main_window.cpp\n+++ b/src/qcam/main_window.cpp\n@@ -28,6 +28,9 @@\n #include <libcamera/version.h>\n \n #include \"dng_writer.h\"\n+#include \"main_window.h\"\n+#include \"viewfinder.h\"\n+#include \"viewfinderGL.h\"\n \n using namespace libcamera;\n \n@@ -65,10 +68,18 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n \tsetWindowTitle(title_);\n \tconnect(&titleTimer_, SIGNAL(timeout()), this, SLOT(updateTitle()));\n \n-\tviewfinder_ = new ViewFinder(this);\n-\tconnect(viewfinder_, &ViewFinder::renderComplete,\n-\t\tthis, &MainWindow::queueRequest);\n-\tsetCentralWidget(viewfinder_);\n+\tif (options_.isSet(OptOpenGL)) {\n+\t\tviewfinder_ = new ViewFinderGL(this);\n+\t\tconnect(dynamic_cast<ViewFinderGL *>(viewfinder_), &ViewFinderGL::renderComplete,\n+\t\t\t\tthis, &MainWindow::queueRequest);\n+\t\tsetCentralWidget(dynamic_cast<ViewFinderGL *>(viewfinder_));\n+\t} else {\n+\t\tviewfinder_ = new ViewFinder(this);\n+\t\tconnect(dynamic_cast<ViewFinder *>(viewfinder_), &ViewFinder::renderComplete,\n+\t\t\t\tthis, &MainWindow::queueRequest);\n+\t\tsetCentralWidget(dynamic_cast<ViewFinder *>(viewfinder_));\n+\t}\n+\n \tadjustSize();\n \n \t/* Open the camera and start capture. */\ndiff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\nindex 59fa2d9..2c9713c 100644\n--- a/src/qcam/main_window.h\n+++ b/src/qcam/main_window.h\n@@ -36,6 +36,7 @@ enum {\n \tOptCamera = 'c',\n \tOptHelp = 'h',\n \tOptStream = 's',\n+\tOptOpenGL = 'o',\n };\n \n class CaptureRequest\n@@ -96,7 +97,7 @@ private:\n \tQAction *startStopAction_;\n \tQComboBox *cameraCombo_;\n \tQAction *saveRaw_;\n-\tViewFinder *viewfinder_;\n+\tViewFinderHandler *viewfinder_;\n \n \tQIcon iconPlay_;\n \tQIcon iconStop_;\ndiff --git a/src/qcam/meson.build b/src/qcam/meson.build\nindex 045db52..6a58947 100644\n--- a/src/qcam/meson.build\n+++ b/src/qcam/meson.build\n@@ -7,11 +7,13 @@ qcam_sources = files([\n     'main.cpp',\n     'main_window.cpp',\n     'viewfinder.cpp',\n+    'viewfinderGL.cpp'\n ])\n \n qcam_moc_headers = files([\n     'main_window.h',\n     'viewfinder.h',\n+    'viewfinderGL.h'\n ])\n \n qcam_resources = files([\ndiff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp\nindex 0d68f62..2079e7e 100644\n--- a/src/qcam/viewfinder.cpp\n+++ b/src/qcam/viewfinder.cpp\n@@ -31,22 +31,30 @@ static const QMap<libcamera::PixelFormat, QImage::Format> nativeFormats\n \t{ libcamera::PixelFormat{ DRM_FORMAT_RGB888 }, QImage::Format_RGB888 },\n };\n \n-ViewFinder::ViewFinder(QWidget *parent)\n-\t: QWidget(parent), buffer_(nullptr)\n+ViewFinderHandler::ViewFinderHandler()\n {\n-\ticon_ = QIcon(\":camera-off.svg\");\n }\n \n-ViewFinder::~ViewFinder()\n+ViewFinderHandler::~ViewFinderHandler()\n {\n }\n \n-const QList<libcamera::PixelFormat> &ViewFinder::nativeFormats() const\n+const QList<libcamera::PixelFormat> &ViewFinderHandler::nativeFormats() const\n {\n \tstatic const QList<libcamera::PixelFormat> formats = ::nativeFormats.keys();\n \treturn formats;\n }\n \n+ViewFinder::ViewFinder(QWidget *parent)\n+\t: QWidget(parent), buffer_(nullptr)\n+{\n+\ticon_ = QIcon(\":camera-off.svg\");\n+}\n+\n+ViewFinder::~ViewFinder()\n+{\n+}\n+\n int ViewFinder::setFormat(const libcamera::PixelFormat &format,\n \t\t\t  const QSize &size)\n {\ndiff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h\nindex b3f1d25..3d0d725 100644\n--- a/src/qcam/viewfinder.h\n+++ b/src/qcam/viewfinder.h\n@@ -13,6 +13,8 @@\n #include <QList>\n #include <QImage>\n #include <QMutex>\n+#include <QOpenGLWidget>\n+#include <QOpenGLFunctions>\n #include <QSize>\n #include <QWidget>\n \n@@ -28,7 +30,23 @@ struct MappedBuffer {\n \tsize_t size;\n };\n \n-class ViewFinder : public QWidget\n+class ViewFinderHandler\n+{\n+public:\n+\tViewFinderHandler();\n+\tvirtual ~ViewFinderHandler();\n+\n+\tconst QList<libcamera::PixelFormat> &nativeFormats() const;\n+\n+\tvirtual int setFormat(const libcamera::PixelFormat &format, const QSize &size) =0;\n+\tvirtual void render(libcamera::FrameBuffer *buffer, MappedBuffer *map) =0;\n+\tvirtual void stop() =0;\n+\n+\tvirtual QImage getCurrentImage() =0;\n+\n+};\n+\n+class ViewFinder : public QWidget, public ViewFinderHandler\n {\n \tQ_OBJECT\n \n@@ -36,8 +54,6 @@ public:\n \tViewFinder(QWidget *parent);\n \t~ViewFinder();\n \n-\tconst QList<libcamera::PixelFormat> &nativeFormats() const;\n-\n \tint setFormat(const libcamera::PixelFormat &format, const QSize &size);\n \tvoid render(libcamera::FrameBuffer *buffer, MappedBuffer *map);\n \tvoid stop();\n@@ -67,5 +83,4 @@ private:\n \tQImage image_;\n \tQMutex mutex_; /* Prevent concurrent access to image_ */\n };\n-\n #endif /* __QCAM_VIEWFINDER__ */\ndiff --git a/src/qcam/viewfinderGL.cpp b/src/qcam/viewfinderGL.cpp\nnew file mode 100644\nindex 0000000..c24ccfb\n--- /dev/null\n+++ b/src/qcam/viewfinderGL.cpp\n@@ -0,0 +1,346 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Linaro\n+ *\n+ * viewfinderGL.cpp - Render YUV format frame by OpenGL shader\n+ */\n+#include <QImage>\n+\n+#include \"fshader.h\"\n+#include \"viewfinderGL.h\"\n+\n+\n+#define ATTRIB_VERTEX 0\n+#define ATTRIB_TEXTURE 1\n+\n+ViewFinderGL::ViewFinderGL(QWidget *parent)\n+\t: QOpenGLWidget(parent),\n+\tglBuffer(QOpenGLBuffer::VertexBuffer),\n+\tpFShader(nullptr),\n+\tpVShader(nullptr),\n+\ttextureU(QOpenGLTexture::Target2D),\n+\ttextureV(QOpenGLTexture::Target2D),\n+\ttextureY(QOpenGLTexture::Target2D),\n+\tyuvDataPtr(nullptr)\n+\n+{\n+}\n+\n+ViewFinderGL::~ViewFinderGL()\n+{\n+\tremoveShader();\n+\n+\tif(textureY.isCreated())\n+\t\ttextureY.destroy();\n+\n+\tif(textureU.isCreated())\n+\t\ttextureU.destroy();\n+\n+\tif(textureV.isCreated())\n+\t\ttextureV.destroy();\n+\n+\tglBuffer.destroy();\n+}\n+\n+int ViewFinderGL::setFormat(const libcamera::PixelFormat &format,\n+\t\t\t    const QSize &size)\n+{\n+\tformat_ = format;\n+\tsize_ = size;\n+\n+\tupdateGeometry();\n+\treturn 0;\n+}\n+\n+void ViewFinderGL::stop()\n+{\n+\tif (buffer_) {\n+\t\trenderComplete(buffer_);\n+\t\tbuffer_ = nullptr;\n+\t}\n+}\n+\n+QImage ViewFinderGL::getCurrentImage()\n+{\n+\tQMutexLocker locker(&mutex_);\n+\n+\treturn(grabFramebuffer());\n+}\n+\n+void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer *map)\n+{\n+\tif (buffer->planes().size() != 1) {\n+\t\tqWarning() << \"Multi-planar buffers are not supported\";\n+\t\treturn;\n+\t}\n+\n+\tunsigned char *memory = static_cast<unsigned char *>(map->memory);\n+\tif(memory) {\n+\t\tyuvDataPtr = memory;\n+\t\tupdate();\n+\t\tbuffer_ = buffer;\n+\t}\n+\n+\tif (buffer)\n+\t\trenderComplete(buffer);\n+}\n+\n+void ViewFinderGL::updateFrame(unsigned char *buffer)\n+{\n+\tif(buffer) {\n+\t\tyuvDataPtr = buffer;\n+\t\tupdate();\n+\t}\n+}\n+\n+void ViewFinderGL::setFrameSize(int width, int height)\n+{\n+\tif(width > 0 && height > 0) {\n+\t\twidth_ = width;\n+\t\theight_ = height;\n+\t}\n+}\n+\n+char *ViewFinderGL::selectFragmentShader(unsigned int format)\n+{\n+\tchar *fsrc = nullptr;\n+\n+\tswitch(format) {\n+\tcase DRM_FORMAT_NV12:\n+\t\thorzSubSample_ = 2;\n+\t\tvertSubSample_ = 2;\n+\t\tfsrc = NV_2_planes_UV;\n+\t\tbreak;\n+\tcase DRM_FORMAT_NV21:\n+\t\thorzSubSample_ = 2;\n+\t\tvertSubSample_ = 2;\n+\t\tfsrc = NV_2_planes_VU;\n+\t\tbreak;\n+\tcase DRM_FORMAT_NV16:\n+\t\thorzSubSample_ = 2;\n+\t\tvertSubSample_ = 1;\n+\t\tfsrc = NV_2_planes_UV;\n+\t\tbreak;\n+\tcase DRM_FORMAT_NV61:\n+\t\thorzSubSample_ = 2;\n+\t\tvertSubSample_ = 1;\n+\t\tfsrc = NV_2_planes_VU;\n+\t\tbreak;\n+\tcase DRM_FORMAT_NV24:\n+\t\thorzSubSample_ = 1;\n+\t\tvertSubSample_ = 1;\n+\t\tfsrc = NV_2_planes_UV;\n+\t\tbreak;\n+\tcase DRM_FORMAT_NV42:\n+\t\thorzSubSample_ = 1;\n+\t\tvertSubSample_ = 1;\n+\t\tfsrc = NV_2_planes_VU;\n+\t\tbreak;\n+\tcase DRM_FORMAT_YUV420:\n+\t\thorzSubSample_ = 2;\n+\t\tvertSubSample_ = 2;\n+\t\tfsrc = NV_3_planes_UV;\n+\t\tbreak;\n+\tcase DRM_FORMAT_YVU420:\n+\t\thorzSubSample_ = 2;\n+\t\tvertSubSample_ = 2;\n+\t\tfsrc =  NV_3_planes_VU;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t};\n+\n+\tif(fsrc == nullptr) {\n+\t\tqDebug() << __func__ << \"[ERROR]:\" <<\" No suitable fragment shader can be select.\";\n+\t}\n+\treturn fsrc;\n+}\n+\n+void ViewFinderGL::createFragmentShader()\n+{\n+\tbool bCompile;\n+\n+\tpFShader = new QOpenGLShader(QOpenGLShader::Fragment, this);\n+\tchar *fsrc = selectFragmentShader(format_);\n+\n+\tbCompile = pFShader->compileSourceCode(fsrc);\n+\tif(!bCompile)\n+\t{\n+\t\tqDebug() << __func__ << \":\" << pFShader->log();\n+\t}\n+\n+\tshaderProgram.addShader(pFShader);\n+\n+\t// Link shader pipeline\n+\tif (!shaderProgram.link()) {\n+\t\tqDebug() << __func__ << \": shader program link failed.\\n\" << shaderProgram.log();\n+\t\tclose();\n+\t}\n+\n+\t// Bind shader pipeline for use\n+\tif (!shaderProgram.bind()) {\n+\t\tqDebug() << __func__ << \": shader program binding failed.\\n\" << shaderProgram.log();\n+\t\tclose();\n+\t}\n+\n+\tshaderProgram.enableAttributeArray(ATTRIB_VERTEX);\n+\tshaderProgram.enableAttributeArray(ATTRIB_TEXTURE);\n+\n+\tshaderProgram.setAttributeBuffer(ATTRIB_VERTEX,GL_FLOAT,0,2,2*sizeof(GLfloat));\n+\tshaderProgram.setAttributeBuffer(ATTRIB_TEXTURE,GL_FLOAT,8*sizeof(GLfloat),2,2*sizeof(GLfloat));\n+\n+\ttextureUniformY = shaderProgram.uniformLocation(\"tex_y\");\n+\ttextureUniformU = shaderProgram.uniformLocation(\"tex_u\");\n+\ttextureUniformV = shaderProgram.uniformLocation(\"tex_v\");\n+\n+\tif(!textureY.isCreated())\n+\t\ttextureY.create();\n+\n+\tif(!textureU.isCreated())\n+\t\ttextureU.create();\n+\n+\tif(!textureV.isCreated())\n+\t\ttextureV.create();\n+\n+\tid_y = textureY.textureId();\n+\tid_u = textureU.textureId();\n+\tid_v = textureV.textureId();\n+}\n+\n+void ViewFinderGL::configureTexture(unsigned int id)\n+{\n+\tglBindTexture(GL_TEXTURE_2D, id);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n+}\n+\n+void ViewFinderGL::removeShader()\n+{\n+\tif (shaderProgram.isLinked()) {\n+\t\tshaderProgram.release();\n+\t\tshaderProgram.removeAllShaders();\n+\t}\n+\n+\tif(pFShader)\n+\t\tdelete pFShader;\n+\n+\tif(pVShader)\n+\t\tdelete pVShader;\n+}\n+\n+void ViewFinderGL::initializeGL()\n+{\n+\tbool bCompile;\n+\n+\tinitializeOpenGLFunctions();\n+\tglEnable(GL_TEXTURE_2D);\n+\tglDisable(GL_DEPTH_TEST);\n+\n+\tstatic const GLfloat vertices[] {\n+\t\t-1.0f,-1.0f,\n+\t\t-1.0f,+1.0f,\n+\t\t+1.0f,+1.0f,\n+\t\t+1.0f,-1.0f,\n+\t\t0.0f,1.0f,\n+\t\t0.0f,0.0f,\n+\t\t1.0f,0.0f,\n+\t\t1.0f,1.0f,\n+\t};\n+\n+\tglBuffer.create();\n+\tglBuffer.bind();\n+\tglBuffer.allocate(vertices,sizeof(vertices));\n+\n+\t/* Create Vertex Shader */\n+\tpVShader = new QOpenGLShader(QOpenGLShader::Vertex, this);\n+\tconst char *vsrc =  \"attribute vec4 vertexIn;\\n\"\n+                        \"attribute vec2 textureIn;\\n\"\n+                        \"varying vec2 textureOut;\\n\"\n+                        \"void main(void)\\n\"\n+                        \"{\\n\"\n+                        \"gl_Position = vertexIn;\\n\"\n+                        \"textureOut = textureIn;\\n\"\n+                        \"}\\n\";\n+\n+\tbCompile = pVShader->compileSourceCode(vsrc);\n+\tif(!bCompile) {\n+\t\tqDebug() << __func__<< \":\" << pVShader->log();\n+\t}\n+\n+\tshaderProgram.addShader(pVShader);\n+\n+\tglClearColor(1.0f, 1.0f, 1.0f, 0.0f);\n+}\n+\n+void ViewFinderGL::render()\n+{\n+\tswitch(format_) {\n+\tcase DRM_FORMAT_NV12:\n+\tcase DRM_FORMAT_NV21:\n+\tcase DRM_FORMAT_NV16:\n+\tcase DRM_FORMAT_NV61:\n+\tcase DRM_FORMAT_NV24:\n+\tcase DRM_FORMAT_NV42:\n+\t\t/* activate texture 0 */\n+\t\tglActiveTexture(GL_TEXTURE0);\n+\t\tconfigureTexture(id_y);\n+\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size_.width(), size_.height(), 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);\n+\t\tglUniform1i(textureUniformY, 0);\n+\n+\t\t/* activate texture 1 */\n+\t\tglActiveTexture(GL_TEXTURE1);\n+\t\tconfigureTexture(id_u);\n+\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+size_.width()*size_.height());\n+\t\tglUniform1i(textureUniformU, 1);\n+\t\tbreak;\n+\tcase DRM_FORMAT_YUV420:\n+\tcase DRM_FORMAT_YVU420:\n+\t\t/* activate texture 0 */\n+\t\tglActiveTexture(GL_TEXTURE0);\n+\t\tconfigureTexture(id_y);\n+\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size_.width(), size_.height(), 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);\n+\t\tglUniform1i(textureUniformY, 0);\n+\n+\t\t/* activate texture 1 */\n+\t\tglActiveTexture(GL_TEXTURE1);\n+\t\tconfigureTexture(id_u);\n+\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+size_.width()*size_.height());\n+\t\tglUniform1i(textureUniformU, 1);\n+\n+\t\t/* activate texture 2 */\n+\t\tglActiveTexture(GL_TEXTURE2);\n+\t\tconfigureTexture(id_v);\n+\t\tglTexImage2D(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);\n+\t\tglUniform1i(textureUniformV, 2);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t};\n+}\n+\n+void ViewFinderGL::paintGL()\n+{\n+\tif(pFShader == nullptr)\n+\t\tcreateFragmentShader();\n+\n+\tif(yuvDataPtr)\n+\t{\n+\t\tglClearColor(0.0, 0.0, 0.0, 1.0);\n+\t\tglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );\n+\n+\t\trender();\n+\t\tglDrawArrays(GL_TRIANGLE_FAN, 0, 4);\n+\t}\n+}\n+\n+void ViewFinderGL::resizeGL(int w, int h)\n+{\n+\tglViewport(0,0,w,h);\n+}\n+\n+QSize ViewFinderGL::sizeHint() const\n+{\n+\treturn size_.isValid() ? size_ : QSize(640, 480);\n+}\ndiff --git a/src/qcam/viewfinderGL.h b/src/qcam/viewfinderGL.h\nnew file mode 100644\nindex 0000000..5366358\n--- /dev/null\n+++ b/src/qcam/viewfinderGL.h\n@@ -0,0 +1,102 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Linaro\n+ *\n+ * viewfinderGL.h - Render YUV format frame by OpenGL shader\n+ */\n+#ifndef __VIEWFINDERGL_H__\n+#define __VIEWFINDERGL_H__\n+\n+#include <fcntl.h>\n+#include <linux/drm_fourcc.h>\n+#include <string.h>\n+#include <unistd.h>\n+\n+#include <iomanip>\n+#include <iostream>\n+#include <sstream>\n+\n+#include <QImage>\n+#include <QOpenGLBuffer>\n+#include <QOpenGLFunctions>\n+#include <QOpenGLShader>\n+#include <QOpenGLShaderProgram>\n+#include <QSize>\n+#include <QOpenGLTexture>\n+#include <QOpenGLWidget>\n+\n+#include <libcamera/buffer.h>\n+#include <libcamera/pixelformats.h>\n+#include \"viewfinder.h\"\n+\n+class ViewFinderGL : public QOpenGLWidget,\n+\t\t\t\t\t public ViewFinderHandler,\n+\t\t\t\t\t protected QOpenGLFunctions\n+{\n+\tQ_OBJECT\n+\n+public:\n+\tViewFinderGL(QWidget *parent = 0);\n+\t~ViewFinderGL();\n+\n+\tint setFormat(const libcamera::PixelFormat &format, const QSize &size);\n+\tvoid render(libcamera::FrameBuffer *buffer, MappedBuffer *map);\n+\tvoid stop();\n+\n+\tQImage getCurrentImage();\n+\n+\tvoid setFrameSize(int width, int height);\n+\tvoid updateFrame(unsigned char  *buffer);\n+\n+\tchar *selectFragmentShader(unsigned int format);\n+\tvoid createFragmentShader();\n+\tvoid render();\n+\n+Q_SIGNALS:\n+\tvoid renderComplete(libcamera::FrameBuffer *buffer);\n+\n+protected:\n+\tvoid initializeGL() Q_DECL_OVERRIDE;\n+\tvoid paintGL() Q_DECL_OVERRIDE;\n+\tvoid resizeGL(int w, int h) Q_DECL_OVERRIDE;\n+\tQSize sizeHint() const override;\n+\n+private:\n+\n+\tvoid configureTexture(unsigned int id);\n+\tvoid removeShader();\n+\n+\t/* Captured image size, format and buffer */\n+\tlibcamera::FrameBuffer *buffer_;\n+\tlibcamera::PixelFormat format_;\n+\tQOpenGLBuffer glBuffer;\n+\tQSize size_;\n+\n+\tGLuint id_u;\n+\tGLuint id_v;\n+\tGLuint id_y;\n+\n+\tQMutex mutex_; /* Prevent concurrent access to image_ */\n+\n+\tQOpenGLShader *pFShader;\n+\tQOpenGLShader *pVShader;\n+\tQOpenGLShaderProgram shaderProgram;\n+\n+\tGLuint textureUniformU;\n+\tGLuint textureUniformV;\n+\tGLuint textureUniformY;\n+\n+\tQOpenGLTexture textureU;\n+\tQOpenGLTexture textureV;\n+\tQOpenGLTexture textureY;\n+\n+\tunsigned int width_;\n+\tunsigned int height_;\n+\n+\tunsigned char* yuvDataPtr;\n+\n+\t/* NV parameters */\n+\tunsigned int horzSubSample_ ;\n+\tunsigned int vertSubSample_;\n+};\n+#endif // __VIEWFINDERGL_H__\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "v2",
        "1/1"
    ]
}