239 lines
8 KiB
GLSL
239 lines
8 KiB
GLSL
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include shared
|
|
|
|
#define YUV_FORMAT_NV12 0
|
|
#define YUV_FORMAT_P010 1
|
|
#define YUV_FORMAT_NV16 2
|
|
#define YUV_FORMAT_PLANAR 3
|
|
#define YUV_FORMAT_INTERLEAVED 4
|
|
|
|
//#define YUV_PRECISION mediump
|
|
#define YUV_PRECISION highp
|
|
|
|
#ifdef WR_VERTEX_SHADER
|
|
|
|
#ifdef WR_FEATURE_TEXTURE_RECT
|
|
#define TEX_SIZE_YUV(sampler) vec2(1.0)
|
|
#else
|
|
#define TEX_SIZE_YUV(sampler) vec2(TEX_SIZE(sampler).xy)
|
|
#endif
|
|
|
|
// `YuvRangedColorSpace`
|
|
#define YUV_COLOR_SPACE_REC601_NARROW 0
|
|
#define YUV_COLOR_SPACE_REC601_FULL 1
|
|
#define YUV_COLOR_SPACE_REC709_NARROW 2
|
|
#define YUV_COLOR_SPACE_REC709_FULL 3
|
|
#define YUV_COLOR_SPACE_REC2020_NARROW 4
|
|
#define YUV_COLOR_SPACE_REC2020_FULL 5
|
|
#define YUV_COLOR_SPACE_GBR_IDENTITY 6
|
|
|
|
// The constants added to the Y, U and V components are applied in the fragment shader.
|
|
|
|
// `rgbFromYuv` from https://jdashg.github.io/misc/colors/from-coeffs.html
|
|
// The matrix is stored in column-major.
|
|
const mat3 RgbFromYuv_Rec601 = mat3(
|
|
1.00000, 1.00000, 1.00000,
|
|
0.00000,-0.17207, 0.88600,
|
|
0.70100,-0.35707, 0.00000
|
|
);
|
|
const mat3 RgbFromYuv_Rec709 = mat3(
|
|
1.00000, 1.00000, 1.00000,
|
|
0.00000,-0.09366, 0.92780,
|
|
0.78740,-0.23406, 0.00000
|
|
);
|
|
const mat3 RgbFromYuv_Rec2020 = mat3(
|
|
1.00000, 1.00000, 1.00000,
|
|
0.00000,-0.08228, 0.94070,
|
|
0.73730,-0.28568, 0.00000
|
|
);
|
|
|
|
// The matrix is stored in column-major.
|
|
// Identity is stored as GBR
|
|
const mat3 RgbFromYuv_GbrIdentity = mat3(
|
|
0.0 , 1.0, 0.0,
|
|
0.0 , 0.0, 1.0,
|
|
1.0 , 0.0, 0.0
|
|
);
|
|
|
|
// -
|
|
|
|
struct YuvPrimitive {
|
|
int channel_bit_depth;
|
|
int color_space;
|
|
int yuv_format;
|
|
};
|
|
|
|
struct YuvColorSamplingInfo {
|
|
mat3 rgb_from_yuv;
|
|
vec4 packed_zero_one_vals;
|
|
};
|
|
|
|
struct YuvColorMatrixInfo {
|
|
vec3 ycbcr_bias;
|
|
mat3 rgb_from_debiased_ycbrc;
|
|
};
|
|
|
|
// -
|
|
|
|
vec4 yuv_channel_zero_one_identity(int bit_depth, float channel_max) {
|
|
float all_ones_normalized = float((1 << bit_depth) - 1) / channel_max;
|
|
return vec4(0.0, 0.0, all_ones_normalized, all_ones_normalized);
|
|
}
|
|
|
|
vec4 yuv_channel_zero_one_narrow_range(int bit_depth, float channel_max) {
|
|
// Note: 512/1023 != 128/255
|
|
ivec4 zero_one_ints = ivec4(16, 128, 235, 240) << (bit_depth - 8);
|
|
return vec4(zero_one_ints) / channel_max;
|
|
}
|
|
|
|
vec4 yuv_channel_zero_one_full_range(int bit_depth, float channel_max) {
|
|
vec4 narrow = yuv_channel_zero_one_narrow_range(bit_depth, channel_max);
|
|
vec4 identity = yuv_channel_zero_one_identity(bit_depth, channel_max);
|
|
return vec4(0.0, narrow.y, identity.z, identity.w);
|
|
}
|
|
|
|
YuvColorSamplingInfo get_yuv_color_info(YuvPrimitive prim) {
|
|
float channel_max = 255.0;
|
|
if (prim.channel_bit_depth > 8) {
|
|
if (prim.yuv_format == YUV_FORMAT_P010) {
|
|
// This is an MSB format.
|
|
channel_max = float((1 << prim.channel_bit_depth) - 1);
|
|
} else {
|
|
// For >8bpc, we get the low bits, not the high bits:
|
|
// 10bpc(1.0): 0b0000_0011_1111_1111
|
|
channel_max = 65535.0;
|
|
}
|
|
}
|
|
if (prim.color_space == YUV_COLOR_SPACE_REC601_NARROW) {
|
|
return YuvColorSamplingInfo(RgbFromYuv_Rec601,
|
|
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max));
|
|
} else if (prim.color_space == YUV_COLOR_SPACE_REC601_FULL) {
|
|
return YuvColorSamplingInfo(RgbFromYuv_Rec601,
|
|
yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max));
|
|
|
|
} else if (prim.color_space == YUV_COLOR_SPACE_REC709_NARROW) {
|
|
return YuvColorSamplingInfo(RgbFromYuv_Rec709,
|
|
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max));
|
|
} else if (prim.color_space == YUV_COLOR_SPACE_REC709_FULL) {
|
|
return YuvColorSamplingInfo(RgbFromYuv_Rec709,
|
|
yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max));
|
|
|
|
} else if (prim.color_space == YUV_COLOR_SPACE_REC2020_NARROW) {
|
|
return YuvColorSamplingInfo(RgbFromYuv_Rec2020,
|
|
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max));
|
|
} else if (prim.color_space == YUV_COLOR_SPACE_REC2020_FULL) {
|
|
return YuvColorSamplingInfo(RgbFromYuv_Rec2020,
|
|
yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max));
|
|
|
|
} else {
|
|
// Identity
|
|
return YuvColorSamplingInfo(RgbFromYuv_GbrIdentity,
|
|
yuv_channel_zero_one_identity(prim.channel_bit_depth, channel_max));
|
|
}
|
|
}
|
|
|
|
YuvColorMatrixInfo get_rgb_from_ycbcr_info(YuvPrimitive prim) {
|
|
YuvColorSamplingInfo info = get_yuv_color_info(prim);
|
|
|
|
vec2 zero = info.packed_zero_one_vals.xy;
|
|
vec2 one = info.packed_zero_one_vals.zw;
|
|
// Such that yuv_value = (ycbcr_sample - zero) / (one - zero)
|
|
vec2 scale = 1.0 / (one - zero);
|
|
|
|
YuvColorMatrixInfo mat_info;
|
|
mat_info.ycbcr_bias = zero.xyy;
|
|
mat3 yuv_from_debiased_ycbcr = mat3(scale.x, 0.0, 0.0,
|
|
0.0, scale.y, 0.0,
|
|
0.0, 0.0, scale.y);
|
|
mat_info.rgb_from_debiased_ycbrc = info.rgb_from_yuv * yuv_from_debiased_ycbcr;
|
|
return mat_info;
|
|
}
|
|
|
|
void write_uv_rect(
|
|
vec2 uv0,
|
|
vec2 uv1,
|
|
vec2 f,
|
|
vec2 texture_size,
|
|
out vec2 uv,
|
|
out vec4 uv_bounds
|
|
) {
|
|
uv = mix(uv0, uv1, f);
|
|
|
|
uv_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5));
|
|
|
|
#ifndef WR_FEATURE_TEXTURE_RECT
|
|
uv /= texture_size;
|
|
uv_bounds /= texture_size.xyxy;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef WR_FRAGMENT_SHADER
|
|
|
|
vec4 sample_yuv(
|
|
int format,
|
|
YUV_PRECISION vec3 ycbcr_bias,
|
|
YUV_PRECISION mat3 rgb_from_debiased_ycbrc,
|
|
vec2 in_uv_y,
|
|
vec2 in_uv_u,
|
|
vec2 in_uv_v,
|
|
vec4 uv_bounds_y,
|
|
vec4 uv_bounds_u,
|
|
vec4 uv_bounds_v
|
|
) {
|
|
YUV_PRECISION vec3 ycbcr_sample;
|
|
|
|
switch (format) {
|
|
case YUV_FORMAT_PLANAR:
|
|
{
|
|
// The yuv_planar format should have this third texture coordinate.
|
|
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
|
|
vec2 uv_u = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
|
|
vec2 uv_v = clamp(in_uv_v, uv_bounds_v.xy, uv_bounds_v.zw);
|
|
ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r;
|
|
ycbcr_sample.y = TEX_SAMPLE(sColor1, uv_u).r;
|
|
ycbcr_sample.z = TEX_SAMPLE(sColor2, uv_v).r;
|
|
}
|
|
break;
|
|
|
|
case YUV_FORMAT_NV12:
|
|
case YUV_FORMAT_P010:
|
|
case YUV_FORMAT_NV16:
|
|
{
|
|
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
|
|
vec2 uv_uv = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
|
|
ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r;
|
|
ycbcr_sample.yz = TEX_SAMPLE(sColor1, uv_uv).rg;
|
|
}
|
|
break;
|
|
|
|
case YUV_FORMAT_INTERLEAVED:
|
|
{
|
|
// "The Y, Cb and Cr color channels within the 422 data are mapped into
|
|
// the existing green, blue and red color channels."
|
|
// https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
|
|
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
|
|
ycbcr_sample = TEX_SAMPLE(sColor0, uv_y).gbr;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ycbcr_sample = vec3(0.0);
|
|
break;
|
|
}
|
|
//if (true) return vec4(ycbcr_sample, 1.0);
|
|
|
|
// See the YuvColorMatrix definition for an explanation of where the constants come from.
|
|
YUV_PRECISION vec3 rgb = rgb_from_debiased_ycbrc * (ycbcr_sample - ycbcr_bias);
|
|
|
|
#if defined(WR_FEATURE_ALPHA_PASS) && defined(SWGL_CLIP_MASK)
|
|
// Avoid out-of-range RGB values that can mess with blending. These occur due to invalid
|
|
// YUV values outside the mappable space that never the less can be generated.
|
|
rgb = clamp(rgb, 0.0, 1.0);
|
|
#endif
|
|
return vec4(rgb, 1.0);
|
|
}
|
|
#endif
|