mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-25 17:45:06 +03:00
qcam: viewfinder_gl: Add shader to render packed YUV formats
The shader supports all 4 packed 8-bit YUV variants. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
parent
ae9e7fbf3a
commit
dcc47ff715
4 changed files with 140 additions and 0 deletions
82
src/qcam/assets/shader/YUV_packed.frag
Normal file
82
src/qcam/assets/shader/YUV_packed.frag
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||||
|
*
|
||||||
|
* YUV_packed.frag - Fragment shader code for YUYV packed formats
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
varying vec2 textureOut;
|
||||||
|
|
||||||
|
uniform sampler2D tex_y;
|
||||||
|
uniform float tex_stepx;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
mat3 yuv2rgb_bt601_mat = mat3(
|
||||||
|
vec3(1.164, 1.164, 1.164),
|
||||||
|
vec3(0.000, -0.392, 2.017),
|
||||||
|
vec3(1.596, -0.813, 0.000)
|
||||||
|
);
|
||||||
|
vec3 yuv2rgb_bt601_offset = vec3(0.063, 0.500, 0.500);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The sampler won't interpolate the texture correctly along the X axis,
|
||||||
|
* as each RGBA pixel effectively stores two pixels. We thus need to
|
||||||
|
* interpolate manually.
|
||||||
|
*
|
||||||
|
* In integer texture coordinates, the Y values are layed out in the
|
||||||
|
* texture memory as follows:
|
||||||
|
*
|
||||||
|
* ...| Y U Y V | Y U Y V | Y U Y V |...
|
||||||
|
* ...| R G B A | R G B A | R G B A |...
|
||||||
|
* ^ ^ ^ ^ ^ ^
|
||||||
|
* | | | | | |
|
||||||
|
* n-1 n-0.5 n n+0.5 n+1 n+1.5
|
||||||
|
*
|
||||||
|
* For a texture location x in the interval [n, n+1[, sample the left
|
||||||
|
* and right pixels at n and n+1, and interpolate them with
|
||||||
|
*
|
||||||
|
* left.r * (1 - a) + left.b * a if fract(x) < 0.5
|
||||||
|
* left.b * (1 - a) + right.r * a if fract(x) >= 0.5
|
||||||
|
*
|
||||||
|
* with a = fract(x * 2) which can also be written
|
||||||
|
*
|
||||||
|
* a = fract(x) * 2 if fract(x) < 0.5
|
||||||
|
* a = fract(x) * 2 - 1 if fract(x) >= 0.5
|
||||||
|
*/
|
||||||
|
vec2 pos = textureOut;
|
||||||
|
float f_x = fract(pos.x / tex_stepx);
|
||||||
|
|
||||||
|
vec4 left = texture2D(tex_y, vec2(pos.x - f_x * tex_stepx, pos.y));
|
||||||
|
vec4 right = texture2D(tex_y, vec2(pos.x + (1.0 - f_x) * tex_stepx , pos.y));
|
||||||
|
|
||||||
|
#if defined(YUV_PATTERN_UYVY)
|
||||||
|
float y_left = mix(left.g, left.a, f_x * 2.0);
|
||||||
|
float y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);
|
||||||
|
vec2 uv = mix(left.rb, right.rb, f_x);
|
||||||
|
#elif defined(YUV_PATTERN_VYUY)
|
||||||
|
float y_left = mix(left.g, left.a, f_x * 2.0);
|
||||||
|
float y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);
|
||||||
|
vec2 uv = mix(left.br, right.br, f_x);
|
||||||
|
#elif defined(YUV_PATTERN_YUYV)
|
||||||
|
float y_left = mix(left.r, left.b, f_x * 2.0);
|
||||||
|
float y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);
|
||||||
|
vec2 uv = mix(left.ga, right.ga, f_x);
|
||||||
|
#elif defined(YUV_PATTERN_YVYU)
|
||||||
|
float y_left = mix(left.r, left.b, f_x * 2.0);
|
||||||
|
float y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);
|
||||||
|
vec2 uv = mix(left.ag, right.ag, f_x);
|
||||||
|
#else
|
||||||
|
#error Invalid pattern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float y = mix(y_left, y_right, step(0.5, f_x));
|
||||||
|
|
||||||
|
vec3 rgb = yuv2rgb_bt601_mat * (vec3(y, uv) - yuv2rgb_bt601_offset);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(rgb, 1.0);
|
||||||
|
}
|
|
@ -4,5 +4,6 @@
|
||||||
<file>YUV.vert</file>
|
<file>YUV.vert</file>
|
||||||
<file>YUV_2_planes.frag</file>
|
<file>YUV_2_planes.frag</file>
|
||||||
<file>YUV_3_planes.frag</file>
|
<file>YUV_3_planes.frag</file>
|
||||||
|
<file>YUV_packed.frag</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -14,12 +14,19 @@
|
||||||
#include <libcamera/formats.h>
|
#include <libcamera/formats.h>
|
||||||
|
|
||||||
static const QList<libcamera::PixelFormat> supportedFormats{
|
static const QList<libcamera::PixelFormat> supportedFormats{
|
||||||
|
/* Packed (single plane) */
|
||||||
|
libcamera::formats::UYVY,
|
||||||
|
libcamera::formats::VYUY,
|
||||||
|
libcamera::formats::YUYV,
|
||||||
|
libcamera::formats::YVYU,
|
||||||
|
/* Semi planar (two planes) */
|
||||||
libcamera::formats::NV12,
|
libcamera::formats::NV12,
|
||||||
libcamera::formats::NV21,
|
libcamera::formats::NV21,
|
||||||
libcamera::formats::NV16,
|
libcamera::formats::NV16,
|
||||||
libcamera::formats::NV61,
|
libcamera::formats::NV61,
|
||||||
libcamera::formats::NV24,
|
libcamera::formats::NV24,
|
||||||
libcamera::formats::NV42,
|
libcamera::formats::NV42,
|
||||||
|
/* Fully planar (three planes) */
|
||||||
libcamera::formats::YUV420,
|
libcamera::formats::YUV420,
|
||||||
libcamera::formats::YVU420,
|
libcamera::formats::YVU420,
|
||||||
};
|
};
|
||||||
|
@ -149,6 +156,22 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)
|
||||||
vertSubSample_ = 2;
|
vertSubSample_ = 2;
|
||||||
fragmentShaderFile_ = ":YUV_3_planes.frag";
|
fragmentShaderFile_ = ":YUV_3_planes.frag";
|
||||||
break;
|
break;
|
||||||
|
case libcamera::formats::UYVY:
|
||||||
|
fragmentShaderDefines_.append("#define YUV_PATTERN_UYVY");
|
||||||
|
fragmentShaderFile_ = ":YUV_packed.frag";
|
||||||
|
break;
|
||||||
|
case libcamera::formats::VYUY:
|
||||||
|
fragmentShaderDefines_.append("#define YUV_PATTERN_VYUY");
|
||||||
|
fragmentShaderFile_ = ":YUV_packed.frag";
|
||||||
|
break;
|
||||||
|
case libcamera::formats::YUYV:
|
||||||
|
fragmentShaderDefines_.append("#define YUV_PATTERN_YUYV");
|
||||||
|
fragmentShaderFile_ = ":YUV_packed.frag";
|
||||||
|
break;
|
||||||
|
case libcamera::formats::YVYU:
|
||||||
|
fragmentShaderDefines_.append("#define YUV_PATTERN_YVYU");
|
||||||
|
fragmentShaderFile_ = ":YUV_packed.frag";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ret = false;
|
ret = false;
|
||||||
qWarning() << "[ViewFinderGL]:"
|
qWarning() << "[ViewFinderGL]:"
|
||||||
|
@ -235,6 +258,7 @@ bool ViewFinderGL::createFragmentShader()
|
||||||
textureUniformY_ = shaderProgram_.uniformLocation("tex_y");
|
textureUniformY_ = shaderProgram_.uniformLocation("tex_y");
|
||||||
textureUniformU_ = shaderProgram_.uniformLocation("tex_u");
|
textureUniformU_ = shaderProgram_.uniformLocation("tex_u");
|
||||||
textureUniformV_ = shaderProgram_.uniformLocation("tex_v");
|
textureUniformV_ = shaderProgram_.uniformLocation("tex_v");
|
||||||
|
textureUniformStepX_ = shaderProgram_.uniformLocation("tex_stepx");
|
||||||
|
|
||||||
if (!textureY_.isCreated())
|
if (!textureY_.isCreated())
|
||||||
textureY_.create();
|
textureY_.create();
|
||||||
|
@ -431,6 +455,38 @@ void ViewFinderGL::doRender()
|
||||||
shaderProgram_.setUniformValue(textureUniformU_, 1);
|
shaderProgram_.setUniformValue(textureUniformU_, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case libcamera::formats::UYVY:
|
||||||
|
case libcamera::formats::VYUY:
|
||||||
|
case libcamera::formats::YUYV:
|
||||||
|
case libcamera::formats::YVYU:
|
||||||
|
/*
|
||||||
|
* Packed YUV formats are stored in a RGBA texture to match the
|
||||||
|
* OpenGL texel size with the 4 bytes repeating pattern in YUV.
|
||||||
|
* The texture width is thus half of the image with.
|
||||||
|
*/
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
configureTexture(textureY_);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_RGBA,
|
||||||
|
size_.width() / 2,
|
||||||
|
size_.height(),
|
||||||
|
0,
|
||||||
|
GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
yuvData_);
|
||||||
|
shaderProgram_.setUniformValue(textureUniformY_, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The shader needs the step between two texture pixels in the
|
||||||
|
* horizontal direction, expressed in texture coordinate units
|
||||||
|
* ([0, 1]). There are exactly width - 1 steps between the
|
||||||
|
* leftmost and rightmost texels.
|
||||||
|
*/
|
||||||
|
shaderProgram_.setUniformValue(textureUniformStepX_,
|
||||||
|
1.0f / (size_.width() / 2 - 1));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,6 +79,7 @@ private:
|
||||||
GLuint textureUniformU_;
|
GLuint textureUniformU_;
|
||||||
GLuint textureUniformV_;
|
GLuint textureUniformV_;
|
||||||
GLuint textureUniformY_;
|
GLuint textureUniformY_;
|
||||||
|
GLuint textureUniformStepX_;
|
||||||
QOpenGLTexture textureU_;
|
QOpenGLTexture textureU_;
|
||||||
QOpenGLTexture textureV_;
|
QOpenGLTexture textureV_;
|
||||||
QOpenGLTexture textureY_;
|
QOpenGLTexture textureY_;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue