[{"id":12554,"web_url":"https://patchwork.libcamera.org/comment/12554/","msgid":"<20200916155520.GK1850958@oden.dyn.berto.se>","date":"2020-09-16T15:55:20","subject":"Re: [libcamera-devel] [PATCH 7/7] qcam: viewfinder_gl: Add shader\n\tto render packed YUV formats","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your work.\n\nOn 2020-09-16 17:52:54 +0300, Laurent Pinchart wrote:\n> The shader supports all 4 packed 8-bit YUV variants.\n\nOpenGL is not my strong suite so I can't really give it a proper review.  \nFrom what I do understand this change looks good but I'm unable to give \na R-b in good concious with my limited understanding. I do however think \nthe change is neatly modularized and properly tested with good results \nso I think merging it adds value without any risk for regression or \nlater problems.\n\nAcked-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\nPlease see bellow for a small typo tho.\n\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/qcam/assets/shader/YUV_packed.frag | 82 ++++++++++++++++++++++++++\n>  src/qcam/assets/shader/shaders.qrc     |  1 +\n>  src/qcam/viewfinder_gl.cpp             | 56 ++++++++++++++++++\n>  src/qcam/viewfinder_gl.h               |  1 +\n>  4 files changed, 140 insertions(+)\n>  create mode 100644 src/qcam/assets/shader/YUV_packed.frag\n> \n> diff --git a/src/qcam/assets/shader/YUV_packed.frag b/src/qcam/assets/shader/YUV_packed.frag\n> new file mode 100644\n> index 000000000000..224dfafe383e\n> --- /dev/null\n> +++ b/src/qcam/assets/shader/YUV_packed.frag\n> @@ -0,0 +1,82 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> + *\n> + * YUV_packed.frag - Fragment shader code for YUYV packed formats\n> + */\n> +\n> +#ifdef GL_ES\n> +precision mediump float;\n> +#endif\n> +\n> +varying vec2 textureOut;\n> +\n> +uniform sampler2D tex_y;\n> +uniform float tex_stepx;\n> +\n> +void main(void)\n> +{\n> +\tmat3 yuv2rgb_bt601_mat = mat3(\n> +\t\tvec3(1.164,  1.164, 1.164),\n> +\t\tvec3(0.000, -0.392, 2.017),\n> +\t\tvec3(1.596, -0.813, 0.000)\n> +\t);\n> +\tvec3 yuv2rgb_bt601_offset = vec3(0.063, 0.500, 0.500);\n> +\n> +\t/*\n> +\t * The sampler won't interpolate the texture correctly along the X axis,\n> +\t * as each RGBA pixel effectively stores two pixels. We thus need to\n> +\t * interpolate manually.\n> +\t *\n> +\t * In integer texture coordinates, the Y values are layed out in the\n> +\t * texture memory as follows:\n> +\t *\n> +\t * ...| Y  U  Y  V | Y  U  Y  V | Y  U  Y  V |...\n> +\t * ...| R  G  B  A | R  G  B  A | R  G  B  A |...\n> +\t *      ^     ^      ^     ^      ^     ^\n> +\t *      |     |      |     |      |     |\n> +\t *     n-1  n-0.5    n   n+0.5   n+1  n+1.5\n> +\t *\n> +\t * For a texture location x in the interval [n, n+1[, sample the left\n\ns/[n, n+1[/[n, n+1]/\n\n> +\t * and right pixels at n and n+1, and interpolate them with\n> +\t *\n> +\t * left.r * (1 - a) + left.b * a\tif fract(x) < 0.5\n> +\t * left.b * (1 - a) + right.r * a\tif fract(x) >= 0.5\n> +\t *\n> +\t * with a = fract(x * 2) which can also be written\n> +\t *\n> +\t * a = fract(x) * 2\t\t\tif fract(x) < 0.5\n> +\t * a = fract(x) * 2 - 1\t\t\tif fract(x) >= 0.5\n> +\t */\n> +\tvec2 pos = textureOut;\n> +\tfloat f_x = fract(pos.x / tex_stepx);\n> +\n> +\tvec4 left = texture2D(tex_y, vec2(pos.x - f_x * tex_stepx, pos.y));\n> +\tvec4 right = texture2D(tex_y, vec2(pos.x + (1.0 - f_x) * tex_stepx , pos.y));\n> +\n> +#if defined(YUV_PATTERN_UYVY)\n> +\tfloat y_left = mix(left.g, left.a, f_x * 2.0);\n> +\tfloat y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);\n> +\tvec2 uv = mix(left.rb, right.rb, f_x);\n> +#elif defined(YUV_PATTERN_VYUY)\n> +\tfloat y_left = mix(left.g, left.a, f_x * 2.0);\n> +\tfloat y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);\n> +\tvec2 uv = mix(left.br, right.br, f_x);\n> +#elif defined(YUV_PATTERN_YUYV)\n> +\tfloat y_left = mix(left.r, left.b, f_x * 2.0);\n> +\tfloat y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);\n> +\tvec2 uv = mix(left.ga, right.ga, f_x);\n> +#elif defined(YUV_PATTERN_YVYU)\n> +\tfloat y_left = mix(left.r, left.b, f_x * 2.0);\n> +\tfloat y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);\n> +\tvec2 uv = mix(left.ag, right.ag, f_x);\n> +#else\n> +#error Invalid pattern\n> +#endif\n> +\n> +\tfloat y = mix(y_left, y_right, step(0.5, f_x));\n> +\n> +\tvec3 rgb = yuv2rgb_bt601_mat * (vec3(y, uv) - yuv2rgb_bt601_offset);\n> +\n> +\tgl_FragColor = vec4(rgb, 1.0);\n> +}\n> diff --git a/src/qcam/assets/shader/shaders.qrc b/src/qcam/assets/shader/shaders.qrc\n> index 7010d8433c9b..857ed9fd5cde 100644\n> --- a/src/qcam/assets/shader/shaders.qrc\n> +++ b/src/qcam/assets/shader/shaders.qrc\n> @@ -4,5 +4,6 @@\n>  \t<file>YUV.vert</file>\n>  \t<file>YUV_2_planes.frag</file>\n>  \t<file>YUV_3_planes.frag</file>\n> +\t<file>YUV_packed.frag</file>\n>  </qresource>\n>  </RCC>\n> diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> index b8a4827267c3..0b5c942658cd 100644\n> --- a/src/qcam/viewfinder_gl.cpp\n> +++ b/src/qcam/viewfinder_gl.cpp\n> @@ -14,12 +14,19 @@\n>  #include <libcamera/formats.h>\n>  \n>  static const QList<libcamera::PixelFormat> supportedFormats{\n> +\t/* Packed (single plane) */\n> +\tlibcamera::formats::UYVY,\n> +\tlibcamera::formats::VYUY,\n> +\tlibcamera::formats::YUYV,\n> +\tlibcamera::formats::YVYU,\n> +\t/* Semi planar (two planes) */\n>  \tlibcamera::formats::NV12,\n>  \tlibcamera::formats::NV21,\n>  \tlibcamera::formats::NV16,\n>  \tlibcamera::formats::NV61,\n>  \tlibcamera::formats::NV24,\n>  \tlibcamera::formats::NV42,\n> +\t/* Fully planar (three planes) */\n>  \tlibcamera::formats::YUV420,\n>  \tlibcamera::formats::YVU420,\n>  };\n> @@ -149,6 +156,22 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)\n>  \t\tvertSubSample_ = 2;\n>  \t\tfragmentShaderFile_ = \":YUV_3_planes.frag\";\n>  \t\tbreak;\n> +\tcase libcamera::formats::UYVY:\n> +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_UYVY\");\n> +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> +\t\tbreak;\n> +\tcase libcamera::formats::VYUY:\n> +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_VYUY\");\n> +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> +\t\tbreak;\n> +\tcase libcamera::formats::YUYV:\n> +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_YUYV\");\n> +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> +\t\tbreak;\n> +\tcase libcamera::formats::YVYU:\n> +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_YVYU\");\n> +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> +\t\tbreak;\n>  \tdefault:\n>  \t\tret = false;\n>  \t\tqWarning() << \"[ViewFinderGL]:\"\n> @@ -235,6 +258,7 @@ bool ViewFinderGL::createFragmentShader()\n>  \ttextureUniformY_ = shaderProgram_.uniformLocation(\"tex_y\");\n>  \ttextureUniformU_ = shaderProgram_.uniformLocation(\"tex_u\");\n>  \ttextureUniformV_ = shaderProgram_.uniformLocation(\"tex_v\");\n> +\ttextureUniformStepX_ = shaderProgram_.uniformLocation(\"tex_stepx\");\n>  \n>  \tif (!textureY_.isCreated())\n>  \t\ttextureY_.create();\n> @@ -431,6 +455,38 @@ void ViewFinderGL::doRender()\n>  \t\tshaderProgram_.setUniformValue(textureUniformU_, 1);\n>  \t\tbreak;\n>  \n> +\tcase libcamera::formats::UYVY:\n> +\tcase libcamera::formats::VYUY:\n> +\tcase libcamera::formats::YUYV:\n> +\tcase libcamera::formats::YVYU:\n> +\t\t/*\n> +\t\t * Packed YUV formats are stored in a RGBA texture to match the\n> +\t\t * OpenGL texel size with the 4 bytes repeating pattern in YUV.\n> +\t\t * The texture width is thus half of the image with.\n> +\t\t */\n> +\t\tglActiveTexture(GL_TEXTURE0);\n> +\t\tconfigureTexture(textureY_);\n> +\t\tglTexImage2D(GL_TEXTURE_2D,\n> +\t\t\t     0,\n> +\t\t\t     GL_RGBA,\n> +\t\t\t     size_.width() / 2,\n> +\t\t\t     size_.height(),\n> +\t\t\t     0,\n> +\t\t\t     GL_RGBA,\n> +\t\t\t     GL_UNSIGNED_BYTE,\n> +\t\t\t     yuvData_);\n> +\t\tshaderProgram_.setUniformValue(textureUniformY_, 0);\n> +\n> +\t\t/*\n> +\t\t * The shader needs the step between two texture pixels in the\n> +\t\t * horizontal direction, expressed in texture coordinate units\n> +\t\t * ([0, 1]). There are exactly width - 1 steps between the\n> +\t\t * leftmost and rightmost texels.\n> +\t\t */\n> +\t\tshaderProgram_.setUniformValue(textureUniformStepX_,\n> +\t\t\t\t\t       1.0f / (size_.width() / 2 - 1));\n> +\t\tbreak;\n> +\n>  \tdefault:\n>  \t\tbreak;\n>  \t};\n> diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h\n> index 53424dc10bc5..ad1e195e45c7 100644\n> --- a/src/qcam/viewfinder_gl.h\n> +++ b/src/qcam/viewfinder_gl.h\n> @@ -79,6 +79,7 @@ private:\n>  \tGLuint textureUniformU_;\n>  \tGLuint textureUniformV_;\n>  \tGLuint textureUniformY_;\n> +\tGLuint textureUniformStepX_;\n>  \tQOpenGLTexture textureU_;\n>  \tQOpenGLTexture textureV_;\n>  \tQOpenGLTexture textureY_;\n> -- \n> Regards,\n> \n> Laurent Pinchart\n> \n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B33BCC3B5B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 16 Sep 2020 15:55:23 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3586B62F00;\n\tWed, 16 Sep 2020 17:55:23 +0200 (CEST)","from mail-lf1-x143.google.com (mail-lf1-x143.google.com\n\t[IPv6:2a00:1450:4864:20::143])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B77E361E2B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 16 Sep 2020 17:55:21 +0200 (CEST)","by mail-lf1-x143.google.com with SMTP id m5so7536251lfp.7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 16 Sep 2020 08:55:21 -0700 (PDT)","from localhost (h-209-203.A463.priv.bahnhof.se. [155.4.209.203])\n\tby smtp.gmail.com with ESMTPSA id\n\tm206sm4678792lfa.301.2020.09.16.08.55.20\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 16 Sep 2020 08:55:20 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=ragnatech-se.20150623.gappssmtp.com\n\theader.i=@ragnatech-se.20150623.gappssmtp.com\n\theader.b=\"1dwxt0N2\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to;\n\tbh=f8lva1V6QZk3GHRkelWIQD+NFJOuLbSKqq2PMklRRNY=;\n\tb=1dwxt0N2ED4Kz2nnNwq8ZepL6vf3NAMIMT0p69J0SkbpgT4hEloDrPOEvpvPuTITA4\n\tGRwT5GuYRGGzhE19DsezTMEtYkVL9AHQYc/SpkemaiwaijFN70RMNrf4A3/2nbIV9llS\n\t4Th+XepiVSDqqax1P8K+N8JWsfc505vgufeyB4madeYDT7y6EJRgGUcF1g5aFs9O1ihE\n\t4l7KGAQn68CwE8wDXWQnfymO5mjQQUBSWAjeUVyatfey88ndBt5JFEKfQHTVmo4hlNEc\n\tYxrZSzylh1uSqx2LOdPSre4hJfOtm76hxTlC76VFPUvLl3YJFELEcokXrShRXctdhc/r\n\tnJPQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to;\n\tbh=f8lva1V6QZk3GHRkelWIQD+NFJOuLbSKqq2PMklRRNY=;\n\tb=fd0RbbLIrfwu1P5kRQGFH/9ztqn1OoqIXcSunvvlfFoMKxAQ7akhVwPNY9KkRjoFMM\n\tb064hFIvBxdSNO2/ehz69y8HWV8kG3a8BKFOrze7KYjdy9/3EynjhkSOu0Ln4rXuxc7A\n\tablUnRMPCQnUjkpQ0sKrlTPP37RItFJB8jL1hYLT56v2WormQhpsQq9Tif8L7Zt1PJAK\n\t1pvvH3HubVMuXR1J1/9SV3vhSnTcipqv1QQsF5ZptjuXbJdrujZn3sjfzr/It5s2hZqS\n\tp9QIBSp/Kqz9cwxf+a1hmFm2qCgUieL4t/zeABRhl+u/rmjNLJSDNMEJiAMuX0tEaV5j\n\tx4xQ==","X-Gm-Message-State":"AOAM5325eMNuQwh0rE0pLD6mtNJOuwSQsGg7xmBduKKN/MmWZlqzly1K\n\tdsq7TaefEg1qhdvF+cosFIcs/ebhCCa6GQ==","X-Google-Smtp-Source":"ABdhPJxyUETMLUTU8XDBft5VOA54A8+DV1vyyzezU4NOxWgYruDbNGpVq246p3/6/25NgVj+wwi16w==","X-Received":"by 2002:a05:6512:3f3:: with SMTP id\n\tn19mr7259664lfq.531.1600271720947; \n\tWed, 16 Sep 2020 08:55:20 -0700 (PDT)","Date":"Wed, 16 Sep 2020 17:55:20 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20200916155520.GK1850958@oden.dyn.berto.se>","References":"<20200916145254.1644-1-laurent.pinchart@ideasonboard.com>\n\t<20200916145254.1644-8-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200916145254.1644-8-laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH 7/7] qcam: viewfinder_gl: Add shader\n\tto render packed YUV formats","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"iso-8859-1\"","Content-Transfer-Encoding":"quoted-printable","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":12555,"web_url":"https://patchwork.libcamera.org/comment/12555/","msgid":"<20200916155655.GH3853@pendragon.ideasonboard.com>","date":"2020-09-16T15:56:55","subject":"Re: [libcamera-devel] [PATCH 7/7] qcam: viewfinder_gl: Add shader\n\tto render packed YUV formats","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nOn Wed, Sep 16, 2020 at 05:55:20PM +0200, Niklas Söderlund wrote:\n> Hi Laurent,\n> \n> Thanks for your work.\n> \n> On 2020-09-16 17:52:54 +0300, Laurent Pinchart wrote:\n> > The shader supports all 4 packed 8-bit YUV variants.\n> \n> OpenGL is not my strong suite so I can't really give it a proper review.  \n> From what I do understand this change looks good but I'm unable to give \n> a R-b in good concious with my limited understanding. I do however think \n> the change is neatly modularized and properly tested with good results \n> so I think merging it adds value without any risk for regression or \n> later problems.\n> \n> Acked-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> \n> Please see bellow for a small typo tho.\n> \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  src/qcam/assets/shader/YUV_packed.frag | 82 ++++++++++++++++++++++++++\n> >  src/qcam/assets/shader/shaders.qrc     |  1 +\n> >  src/qcam/viewfinder_gl.cpp             | 56 ++++++++++++++++++\n> >  src/qcam/viewfinder_gl.h               |  1 +\n> >  4 files changed, 140 insertions(+)\n> >  create mode 100644 src/qcam/assets/shader/YUV_packed.frag\n> > \n> > diff --git a/src/qcam/assets/shader/YUV_packed.frag b/src/qcam/assets/shader/YUV_packed.frag\n> > new file mode 100644\n> > index 000000000000..224dfafe383e\n> > --- /dev/null\n> > +++ b/src/qcam/assets/shader/YUV_packed.frag\n> > @@ -0,0 +1,82 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > + *\n> > + * YUV_packed.frag - Fragment shader code for YUYV packed formats\n> > + */\n> > +\n> > +#ifdef GL_ES\n> > +precision mediump float;\n> > +#endif\n> > +\n> > +varying vec2 textureOut;\n> > +\n> > +uniform sampler2D tex_y;\n> > +uniform float tex_stepx;\n> > +\n> > +void main(void)\n> > +{\n> > +\tmat3 yuv2rgb_bt601_mat = mat3(\n> > +\t\tvec3(1.164,  1.164, 1.164),\n> > +\t\tvec3(0.000, -0.392, 2.017),\n> > +\t\tvec3(1.596, -0.813, 0.000)\n> > +\t);\n> > +\tvec3 yuv2rgb_bt601_offset = vec3(0.063, 0.500, 0.500);\n> > +\n> > +\t/*\n> > +\t * The sampler won't interpolate the texture correctly along the X axis,\n> > +\t * as each RGBA pixel effectively stores two pixels. We thus need to\n> > +\t * interpolate manually.\n> > +\t *\n> > +\t * In integer texture coordinates, the Y values are layed out in the\n> > +\t * texture memory as follows:\n> > +\t *\n> > +\t * ...| Y  U  Y  V | Y  U  Y  V | Y  U  Y  V |...\n> > +\t * ...| R  G  B  A | R  G  B  A | R  G  B  A |...\n> > +\t *      ^     ^      ^     ^      ^     ^\n> > +\t *      |     |      |     |      |     |\n> > +\t *     n-1  n-0.5    n   n+0.5   n+1  n+1.5\n> > +\t *\n> > +\t * For a texture location x in the interval [n, n+1[, sample the left\n> \n> s/[n, n+1[/[n, n+1]/\n\nThis was on purpose, it means an interval from n to n+1 that includes n\nbut doesn't include n+1. If x was equal to n+1, it would be in the\ninterval [n', n'+1[ where n' = n+1 :-)\n\n> > +\t * and right pixels at n and n+1, and interpolate them with\n> > +\t *\n> > +\t * left.r * (1 - a) + left.b * a\tif fract(x) < 0.5\n> > +\t * left.b * (1 - a) + right.r * a\tif fract(x) >= 0.5\n> > +\t *\n> > +\t * with a = fract(x * 2) which can also be written\n> > +\t *\n> > +\t * a = fract(x) * 2\t\t\tif fract(x) < 0.5\n> > +\t * a = fract(x) * 2 - 1\t\t\tif fract(x) >= 0.5\n> > +\t */\n> > +\tvec2 pos = textureOut;\n> > +\tfloat f_x = fract(pos.x / tex_stepx);\n> > +\n> > +\tvec4 left = texture2D(tex_y, vec2(pos.x - f_x * tex_stepx, pos.y));\n> > +\tvec4 right = texture2D(tex_y, vec2(pos.x + (1.0 - f_x) * tex_stepx , pos.y));\n> > +\n> > +#if defined(YUV_PATTERN_UYVY)\n> > +\tfloat y_left = mix(left.g, left.a, f_x * 2.0);\n> > +\tfloat y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);\n> > +\tvec2 uv = mix(left.rb, right.rb, f_x);\n> > +#elif defined(YUV_PATTERN_VYUY)\n> > +\tfloat y_left = mix(left.g, left.a, f_x * 2.0);\n> > +\tfloat y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);\n> > +\tvec2 uv = mix(left.br, right.br, f_x);\n> > +#elif defined(YUV_PATTERN_YUYV)\n> > +\tfloat y_left = mix(left.r, left.b, f_x * 2.0);\n> > +\tfloat y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);\n> > +\tvec2 uv = mix(left.ga, right.ga, f_x);\n> > +#elif defined(YUV_PATTERN_YVYU)\n> > +\tfloat y_left = mix(left.r, left.b, f_x * 2.0);\n> > +\tfloat y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);\n> > +\tvec2 uv = mix(left.ag, right.ag, f_x);\n> > +#else\n> > +#error Invalid pattern\n> > +#endif\n> > +\n> > +\tfloat y = mix(y_left, y_right, step(0.5, f_x));\n> > +\n> > +\tvec3 rgb = yuv2rgb_bt601_mat * (vec3(y, uv) - yuv2rgb_bt601_offset);\n> > +\n> > +\tgl_FragColor = vec4(rgb, 1.0);\n> > +}\n> > diff --git a/src/qcam/assets/shader/shaders.qrc b/src/qcam/assets/shader/shaders.qrc\n> > index 7010d8433c9b..857ed9fd5cde 100644\n> > --- a/src/qcam/assets/shader/shaders.qrc\n> > +++ b/src/qcam/assets/shader/shaders.qrc\n> > @@ -4,5 +4,6 @@\n> >  \t<file>YUV.vert</file>\n> >  \t<file>YUV_2_planes.frag</file>\n> >  \t<file>YUV_3_planes.frag</file>\n> > +\t<file>YUV_packed.frag</file>\n> >  </qresource>\n> >  </RCC>\n> > diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> > index b8a4827267c3..0b5c942658cd 100644\n> > --- a/src/qcam/viewfinder_gl.cpp\n> > +++ b/src/qcam/viewfinder_gl.cpp\n> > @@ -14,12 +14,19 @@\n> >  #include <libcamera/formats.h>\n> >  \n> >  static const QList<libcamera::PixelFormat> supportedFormats{\n> > +\t/* Packed (single plane) */\n> > +\tlibcamera::formats::UYVY,\n> > +\tlibcamera::formats::VYUY,\n> > +\tlibcamera::formats::YUYV,\n> > +\tlibcamera::formats::YVYU,\n> > +\t/* Semi planar (two planes) */\n> >  \tlibcamera::formats::NV12,\n> >  \tlibcamera::formats::NV21,\n> >  \tlibcamera::formats::NV16,\n> >  \tlibcamera::formats::NV61,\n> >  \tlibcamera::formats::NV24,\n> >  \tlibcamera::formats::NV42,\n> > +\t/* Fully planar (three planes) */\n> >  \tlibcamera::formats::YUV420,\n> >  \tlibcamera::formats::YVU420,\n> >  };\n> > @@ -149,6 +156,22 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)\n> >  \t\tvertSubSample_ = 2;\n> >  \t\tfragmentShaderFile_ = \":YUV_3_planes.frag\";\n> >  \t\tbreak;\n> > +\tcase libcamera::formats::UYVY:\n> > +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_UYVY\");\n> > +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> > +\t\tbreak;\n> > +\tcase libcamera::formats::VYUY:\n> > +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_VYUY\");\n> > +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> > +\t\tbreak;\n> > +\tcase libcamera::formats::YUYV:\n> > +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_YUYV\");\n> > +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> > +\t\tbreak;\n> > +\tcase libcamera::formats::YVYU:\n> > +\t\tfragmentShaderDefines_.append(\"#define YUV_PATTERN_YVYU\");\n> > +\t\tfragmentShaderFile_ = \":YUV_packed.frag\";\n> > +\t\tbreak;\n> >  \tdefault:\n> >  \t\tret = false;\n> >  \t\tqWarning() << \"[ViewFinderGL]:\"\n> > @@ -235,6 +258,7 @@ bool ViewFinderGL::createFragmentShader()\n> >  \ttextureUniformY_ = shaderProgram_.uniformLocation(\"tex_y\");\n> >  \ttextureUniformU_ = shaderProgram_.uniformLocation(\"tex_u\");\n> >  \ttextureUniformV_ = shaderProgram_.uniformLocation(\"tex_v\");\n> > +\ttextureUniformStepX_ = shaderProgram_.uniformLocation(\"tex_stepx\");\n> >  \n> >  \tif (!textureY_.isCreated())\n> >  \t\ttextureY_.create();\n> > @@ -431,6 +455,38 @@ void ViewFinderGL::doRender()\n> >  \t\tshaderProgram_.setUniformValue(textureUniformU_, 1);\n> >  \t\tbreak;\n> >  \n> > +\tcase libcamera::formats::UYVY:\n> > +\tcase libcamera::formats::VYUY:\n> > +\tcase libcamera::formats::YUYV:\n> > +\tcase libcamera::formats::YVYU:\n> > +\t\t/*\n> > +\t\t * Packed YUV formats are stored in a RGBA texture to match the\n> > +\t\t * OpenGL texel size with the 4 bytes repeating pattern in YUV.\n> > +\t\t * The texture width is thus half of the image with.\n> > +\t\t */\n> > +\t\tglActiveTexture(GL_TEXTURE0);\n> > +\t\tconfigureTexture(textureY_);\n> > +\t\tglTexImage2D(GL_TEXTURE_2D,\n> > +\t\t\t     0,\n> > +\t\t\t     GL_RGBA,\n> > +\t\t\t     size_.width() / 2,\n> > +\t\t\t     size_.height(),\n> > +\t\t\t     0,\n> > +\t\t\t     GL_RGBA,\n> > +\t\t\t     GL_UNSIGNED_BYTE,\n> > +\t\t\t     yuvData_);\n> > +\t\tshaderProgram_.setUniformValue(textureUniformY_, 0);\n> > +\n> > +\t\t/*\n> > +\t\t * The shader needs the step between two texture pixels in the\n> > +\t\t * horizontal direction, expressed in texture coordinate units\n> > +\t\t * ([0, 1]). There are exactly width - 1 steps between the\n> > +\t\t * leftmost and rightmost texels.\n> > +\t\t */\n> > +\t\tshaderProgram_.setUniformValue(textureUniformStepX_,\n> > +\t\t\t\t\t       1.0f / (size_.width() / 2 - 1));\n> > +\t\tbreak;\n> > +\n> >  \tdefault:\n> >  \t\tbreak;\n> >  \t};\n> > diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h\n> > index 53424dc10bc5..ad1e195e45c7 100644\n> > --- a/src/qcam/viewfinder_gl.h\n> > +++ b/src/qcam/viewfinder_gl.h\n> > @@ -79,6 +79,7 @@ private:\n> >  \tGLuint textureUniformU_;\n> >  \tGLuint textureUniformV_;\n> >  \tGLuint textureUniformY_;\n> > +\tGLuint textureUniformStepX_;\n> >  \tQOpenGLTexture textureU_;\n> >  \tQOpenGLTexture textureV_;\n> >  \tQOpenGLTexture textureY_;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 4B370C3B5B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 16 Sep 2020 15:57:29 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DB50062E75;\n\tWed, 16 Sep 2020 17:57:28 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 202D761E2B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 16 Sep 2020 17:57:27 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id BB1B626B;\n\tWed, 16 Sep 2020 17:57:24 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"IYZMQSSO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1600271844;\n\tbh=Mh9zRMCsd0A8p5ysaEwTBrldZKbZROVMaCVZcKjYn7k=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=IYZMQSSOU8qfQZRuzTbw/nRv4eBs3kGJCuLm/Z4LYSt0cB+L263wHprcxhOt5E0DT\n\tiknhfMDfjgPYbU5VFrf+UAXyCtwHhLEDRrr0olK9rWWBropZLfBl15/y7KSmxvJqJn\n\tLuiX9YtMd7YxfgNR4C5DAXCpR7Ql4u0rD0ueRVSc=","Date":"Wed, 16 Sep 2020 18:56:55 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Message-ID":"<20200916155655.GH3853@pendragon.ideasonboard.com>","References":"<20200916145254.1644-1-laurent.pinchart@ideasonboard.com>\n\t<20200916145254.1644-8-laurent.pinchart@ideasonboard.com>\n\t<20200916155520.GK1850958@oden.dyn.berto.se>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200916155520.GK1850958@oden.dyn.berto.se>","Subject":"Re: [libcamera-devel] [PATCH 7/7] qcam: viewfinder_gl: Add shader\n\tto render packed YUV formats","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]