Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef TEXTURECOMPONENT
#define TEXTURECOMPONENT
#include "uniformbuffer.glsl"
#include "qrhitextureformats_p.h"
float convert_RG8_to_R16(vec2 rg)
{
return (rg.r * 255 + rg.g * 255 * 256) / 65535;
}
vec2 convert_RGBA8_to_RG16(vec4 rgba)
{
float r = convert_RG8_to_R16(rgba.rg);
float g = convert_RG8_to_R16(rgba.ba);
return vec2(r, g);
}
vec2 get_RG8_from_packed_RGBA8_texture(sampler2D planeTexture, vec2 texCoord, float frameWidth)
{
// NV12 for GLES2.0 requires interleaved chroma plane to be packed into RGBA texture
//
// | r | g | b | a |
// | U0 | V0 | U1 | V1 | ...
// Original (scaling factor 2): Input texture (scaling factor 4):
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
// | y | y | y | y | y | y | y | y | | a | a | a | a | a | a | a | a |
// ... ...
// |[u v]|[u v]|[u v]|[u v]| |[r g] [b a]|[r g] [b a]|
// | |
//
// 0 1 Column index
// |-|-|-|-|-|-|-|-|
// s s X coordinate to sample from
// 0 1 0 1 X coordinates of 2 blocks, "rg" and "ba" in sampled rgba value
float sampleWidth = 4 / frameWidth; // Distance between input sample x-coords (scaling factor 4)
float subSampleWidth = sampleWidth / 2.; // Distance between output "rg" and "ba" x-coords, or "uv" blocks for nv12
float colIndex = floor(texCoord.x * frameWidth * 0.25); // 0., 0., 0., 0., 1., 1., 1., 1.
float middleSampleX = colIndex * sampleWidth + 0.5 * sampleWidth;
vec4 rgba = texture(planeTexture, vec2(middleSampleX, texCoord.y)).rgba;
// X-coords of output UVs
float x0 = middleSampleX - 0.5 * subSampleWidth;
float x1 = middleSampleX + 0.5 * subSampleWidth;
if (texCoord.x >= x0 && texCoord.x < x1) {
// The target is between "rg" and "ba"
// |-|-|-|-|
// s
// | 0 1 |
// X X
return mix(rgba.rg, rgba.ba, (texCoord.x - x0) / subSampleWidth);
} else if (texCoord.x >= x1) {
// The target is after "ba"
// |-|-|-|-|-|-|-|-|
// s s
// | 0 1 | 0 1 |
// X
float rightSampleX = middleSampleX + sampleWidth;
if (rightSampleX < 1.) {
// Mix with sampled "rg" on the right
vec4 rightSample = texture(planeTexture, vec2(rightSampleX, texCoord.y));
return mix(rgba.ba, rightSample.rg, (texCoord.x - x1) / subSampleWidth);
} else {
return rgba.ba;
}
} else { // texCoord.x < x0
// The target is before "rg"
// |-|-|-|-|-|-|-|-|
// s s
// | 0 1 | 0 1 |
// X
float leftSampleX = middleSampleX - sampleWidth;
if (leftSampleX >= 0.) {
// Mix with sampled UV1 on the left
vec4 leftSample = texture(planeTexture, vec2(leftSampleX, texCoord.y));
return mix(rgba.rg, leftSample.ba, (x0 - texCoord.x) / subSampleWidth);
} else {
return rgba.rg;
}
}
}
float get_R16_from_packed_RGBA8_texture(sampler2D planeTexture, vec2 texCoord, float frameWidth, int rhiScale)
{
// 16-bit formats for GLES2.0 require plane to be packed into RGBA texture
//
// Input texture (scaling by 2):
// | 0 | 1 | 2 | 3 |
// | r g b a | r g b a |
// |[ Y Y ]|[ Y Y ]|
//
float sampleWidth = rhiScale / frameWidth; // Distance between input sample x-coords
float subSampleWidth = sampleWidth / 2.; // Distance between output "rg" and "ba" x-coords, or "uv" blocks for nv12
float colIndex = floor(texCoord.x / sampleWidth); // 0., 0., 1., 1., 2., 2., 3., 3.
float middleSampleX = colIndex * sampleWidth + 0.5 * sampleWidth;
vec4 rgba = texture(planeTexture, vec2(middleSampleX, texCoord.y)).rgba;
// Get raw (not interpolated) float values
float r0 = convert_RG8_to_R16(rgba.rg);
float r1 = convert_RG8_to_R16(rgba.ba);
// X-coords of output UVs
float x0 = middleSampleX - 0.5 * subSampleWidth;
float x1 = middleSampleX + 0.5 * subSampleWidth;
if (texCoord.x >= x0 && texCoord.x < x1) {
// The target is between "x0" and "x1"
// |-|-|-|-|
// s
// | 0 1 |
// X X
return mix(r0, r1, (texCoord.x - x0) / subSampleWidth);
} else if (texCoord.x >= x1) {
// The target is after "x1"
// |-|-|-|-|-|-|-|-|
// s s
// | 0 1 | 0 1 |
// X
float rightSampleX = middleSampleX + sampleWidth;
if (rightSampleX < 1.) {
// Mix with sampled "x0" on the right
vec4 rightSample = texture(planeTexture, vec2(rightSampleX, texCoord.y));
return mix(r1, convert_RG8_to_R16(rightSample.rg), (texCoord.x - x1) / subSampleWidth);
} else {
return r1;
}
} else { // texCoord.x < x0
// The target is before "x0"
// |-|-|-|-|-|-|-|-|
// s s
// | 0 1 | 0 1 |
// X
float leftSampleX = middleSampleX - sampleWidth;
if (leftSampleX >= 0.) {
// Mix with sampled "x1" on the left
vec4 leftSample = texture(planeTexture, vec2(leftSampleX, texCoord.y));
return mix(r0, convert_RG8_to_R16(leftSample.ba), (x0 - texCoord.x) / subSampleWidth);
} else {
return r0;
}
}
}
vec2 getRG8(sampler2D planeTexture, vec2 texCoord, int rhiTextureFormat) {
if (rhiTextureFormat == RhiTextureFormat_RG8)
return texture(planeTexture, texCoord).rg;
if (rhiTextureFormat == RhiTextureFormat_RGBA8)
return get_RG8_from_packed_RGBA8_texture(planeTexture, texCoord, ubuf.width);
return vec2(0, 0);
}
// it assumes that the rhi texture format is R8 or RED_OR_ALPHA
float getR8(sampler2D planeTexture, vec2 texCoord) {
#if defined(GL_ES) && __VERSION__ < 300 // webGL1 does not support non-const indexation of vec4
if (ubuf.redOrAlphaIndex == 0)
return texture(planeTexture, texCoord).r;
return texture(planeTexture, texCoord).a;
#else
return texture(planeTexture, texCoord)[ubuf.redOrAlphaIndex];
#endif
}
float getR16(sampler2D planeTexture, vec2 texCoord, int rhiTextureFormat, int xScale) {
if (rhiTextureFormat == RhiTextureFormat_R16)
return texture(planeTexture, texCoord).r;
if (rhiTextureFormat == RhiTextureFormat_RG8)
return convert_RG8_to_R16(texture(planeTexture, texCoord).rg);
if (rhiTextureFormat == RhiTextureFormat_RGBA8)
return get_R16_from_packed_RGBA8_texture(planeTexture, texCoord, ubuf.width, xScale * 2);
return 0;
}
vec2 getRG16(sampler2D planeTexture, vec2 texCoord, int rhiTextureFormat) {
if (rhiTextureFormat == RhiTextureFormat_RG16)
return texture(planeTexture, texCoord).rg;
if (rhiTextureFormat == RhiTextureFormat_RGBA8)
return convert_RGBA8_to_RG16(texture(planeTexture, texCoord).rgba);
return vec2(0, 0);
}
#endif