mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-03-11 17:50:32 +01:00
Merge branch 'GL_EXT_YUV_target' into 'main'
Draft: add support for GL_EXT_YUV_target See merge request mesa/mesa!40191
This commit is contained in:
commit
ebe7eebe18
22 changed files with 1072 additions and 4 deletions
|
|
@ -584,6 +584,7 @@ move_tex_coords(struct move_tex_coords_state *state, nir_function_impl *impl, ni
|
|||
case GLSL_SAMPLER_DIM_3D:
|
||||
case GLSL_SAMPLER_DIM_CUBE:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
break;
|
||||
case GLSL_SAMPLER_DIM_RECT:
|
||||
case GLSL_SAMPLER_DIM_BUF:
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ simple_type("dmat4x3", "GL_DOUBLE_MAT4x3", "GLSL_TYPE_DOUBLE", 3, 4)
|
|||
|
||||
simple_type("atomic_uint", "GL_UNSIGNED_INT_ATOMIC_COUNTER", "GLSL_TYPE_ATOMIC_UINT", 1, 1)
|
||||
|
||||
simple_type("yuvCscStandardEXT", "GL_INVALID_ENUM", "GLSL_TYPE_INT", 1, 1)
|
||||
|
||||
sampler_type("sampler", "GL_SAMPLER_1D", "GLSL_TYPE_SAMPLER", "GLSL_SAMPLER_DIM_1D", 0, 0, "GLSL_TYPE_VOID")
|
||||
sampler_type("sampler1D", "GL_SAMPLER_1D", "GLSL_TYPE_SAMPLER", "GLSL_SAMPLER_DIM_1D", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
sampler_type("sampler2D", "GL_SAMPLER_2D", "GLSL_TYPE_SAMPLER", "GLSL_SAMPLER_DIM_2D", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
|
|
@ -148,6 +150,8 @@ sampler_type("sampler2DRectShadow", "GL_SAMPLER_2D_RECT_SHADOW", "GLSL
|
|||
|
||||
sampler_type("samplerExternalOES", "GL_SAMPLER_EXTERNAL_OES", "GLSL_TYPE_SAMPLER", "GLSL_SAMPLER_DIM_EXTERNAL", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
|
||||
sampler_type("samplerExternal2DY2YEXT", "GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT", "GLSL_TYPE_SAMPLER", "GLSL_SAMPLER_DIM_2D", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
|
||||
sampler_type("texture1D", "GL_SAMPLER_1D", "GLSL_TYPE_TEXTURE", "GLSL_SAMPLER_DIM_1D", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
sampler_type("texture2D", "GL_SAMPLER_2D", "GLSL_TYPE_TEXTURE", "GLSL_SAMPLER_DIM_2D", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
sampler_type("texture3D", "GL_SAMPLER_3D", "GLSL_TYPE_TEXTURE", "GLSL_SAMPLER_DIM_3D", 0, 0, "GLSL_TYPE_FLOAT")
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ enum ast_operators {
|
|||
ast_int64_constant,
|
||||
ast_uint64_constant,
|
||||
|
||||
ast_csc_standard,
|
||||
|
||||
ast_sequence,
|
||||
ast_aggregate
|
||||
|
||||
|
|
@ -266,6 +268,7 @@ public:
|
|||
double double_constant;
|
||||
uint64_t uint64_constant;
|
||||
int64_t int64_constant;
|
||||
enum yuv_csc_standard csc_standard;
|
||||
} primary_expression;
|
||||
|
||||
|
||||
|
|
@ -687,6 +690,9 @@ struct ast_type_qualifier {
|
|||
unsigned task_payload:1;
|
||||
unsigned per_primitive:1;
|
||||
unsigned max_primitives:1;
|
||||
|
||||
/** GL_EXT_YUV_target */
|
||||
unsigned yuv:1;
|
||||
}
|
||||
/** \brief Set of flags, accessed by name. */
|
||||
q;
|
||||
|
|
|
|||
|
|
@ -2156,7 +2156,8 @@ ast_function_expression::handle_method(ir_exec_list *instructions,
|
|||
static inline bool is_valid_constructor(const glsl_type *type,
|
||||
struct _mesa_glsl_parse_state *state)
|
||||
{
|
||||
return glsl_type_is_numeric(type) || glsl_type_is_boolean(type) ||
|
||||
return (glsl_type_is_numeric(type) && type != &glsl_type_builtin_yuvCscStandardEXT) ||
|
||||
glsl_type_is_boolean(type) ||
|
||||
(state->has_bindless() && (glsl_type_is_sampler(type) || glsl_type_is_image(type)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -363,6 +363,12 @@ apply_implicit_conversion(const glsl_type *to, ir_rvalue * &from,
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
is_arithmetic_type(const glsl_type *type)
|
||||
{
|
||||
return glsl_type_is_numeric(type) &&
|
||||
type != &glsl_type_builtin_yuvCscStandardEXT;
|
||||
}
|
||||
|
||||
static const struct glsl_type *
|
||||
arithmetic_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b,
|
||||
|
|
@ -378,7 +384,7 @@ arithmetic_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b,
|
|||
* multiply (*), and divide (/) operate on integer and
|
||||
* floating-point scalars, vectors, and matrices."
|
||||
*/
|
||||
if (!glsl_type_is_numeric(type_a) || !glsl_type_is_numeric(type_b)) {
|
||||
if (!is_arithmetic_type(type_a) || !is_arithmetic_type(type_b)) {
|
||||
_mesa_glsl_error(loc, state,
|
||||
"operands to arithmetic operators must be numeric");
|
||||
return &glsl_type_builtin_error;
|
||||
|
|
@ -517,7 +523,7 @@ unary_arithmetic_result_type(const struct glsl_type *type,
|
|||
* component-wise on their operands. These result with the same type
|
||||
* they operated on."
|
||||
*/
|
||||
if (!glsl_type_is_numeric(type)) {
|
||||
if (!is_arithmetic_type(type)) {
|
||||
_mesa_glsl_error(loc, state,
|
||||
"operands to arithmetic operators must be numeric");
|
||||
return &glsl_type_builtin_error;
|
||||
|
|
@ -1624,6 +1630,10 @@ ast_expression::do_hir(ir_exec_list *instructions,
|
|||
glsl_contains_opaque(op[1]->type))) {
|
||||
_mesa_glsl_error(&loc, state, "opaque type comparisons forbidden");
|
||||
error_emitted = true;
|
||||
} else if (op[0]->type == &glsl_type_builtin_yuvCscStandardEXT ||
|
||||
op[1]->type == &glsl_type_builtin_yuvCscStandardEXT) {
|
||||
_mesa_glsl_error(&loc, state, "yuvCscStandardEXT comparisons forbidden");
|
||||
error_emitted = true;
|
||||
}
|
||||
|
||||
if (error_emitted) {
|
||||
|
|
@ -2197,6 +2207,14 @@ ast_expression::do_hir(ir_exec_list *instructions,
|
|||
result = new(linalloc) ir_constant(this->primary_expression.int64_constant);
|
||||
break;
|
||||
|
||||
case ast_csc_standard: {
|
||||
ir_constant_data data = { { 0 } };
|
||||
data.i[0] = this->primary_expression.csc_standard;
|
||||
result = new(linalloc) ir_constant(&glsl_type_builtin_yuvCscStandardEXT,
|
||||
&data);
|
||||
break;
|
||||
}
|
||||
|
||||
case ast_sequence: {
|
||||
/* It should not be possible to generate a sequence in the AST without
|
||||
* any expressions in it.
|
||||
|
|
@ -2326,6 +2344,7 @@ ast_expression::has_sequence_subexpression() const
|
|||
case ast_double_constant:
|
||||
case ast_int64_constant:
|
||||
case ast_uint64_constant:
|
||||
case ast_csc_standard:
|
||||
return false;
|
||||
|
||||
case ast_aggregate:
|
||||
|
|
@ -2633,6 +2652,13 @@ get_type_name_for_precision_qualifier(const glsl_type *type)
|
|||
};
|
||||
return names[type_idx];
|
||||
}
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y: {
|
||||
assert(glsl_type_is_sampler(type));
|
||||
static const char *const names[4] = {
|
||||
"__samplerExternal2DY2YEXT", NULL, NULL, NULL
|
||||
};
|
||||
return names[type_idx];
|
||||
}
|
||||
default:
|
||||
UNREACHABLE("Unsupported sampler/image dimensionality");
|
||||
} /* sampler/image float dimensionality */
|
||||
|
|
@ -4221,6 +4247,9 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
|
|||
if (qual->flags.q.patch)
|
||||
var->data.patch = 1;
|
||||
|
||||
if (qual->flags.q.yuv)
|
||||
var->data.yuv = 1;
|
||||
|
||||
if (qual->flags.q.attribute && state->stage != MESA_SHADER_VERTEX) {
|
||||
var->type = &glsl_type_builtin_error;
|
||||
_mesa_glsl_error(loc, state,
|
||||
|
|
@ -9455,10 +9484,13 @@ detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
|
|||
{
|
||||
bool gl_FragColor_assigned = false;
|
||||
bool gl_FragData_assigned = false;
|
||||
bool gl_FragDepth_assigned = false;
|
||||
bool gl_FragSecondaryColor_assigned = false;
|
||||
bool gl_FragSecondaryData_assigned = false;
|
||||
bool user_defined_fs_output_assigned = false;
|
||||
ir_variable *user_defined_fs_output = NULL;
|
||||
bool yuv_layout_used = false;
|
||||
int color_outputs = 0;
|
||||
|
||||
/* It would be nice to have proper location information. */
|
||||
YYLTYPE loc;
|
||||
|
|
@ -9467,7 +9499,13 @@ detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
|
|||
ir_foreach_in_list(ir_instruction, node, instructions) {
|
||||
ir_variable *var = node->as_variable();
|
||||
|
||||
if (!var || !var->data.assigned)
|
||||
if (!var)
|
||||
continue;
|
||||
|
||||
if (var->data.yuv)
|
||||
yuv_layout_used = true;
|
||||
|
||||
if (!var->data.assigned)
|
||||
continue;
|
||||
|
||||
if (strcmp(var->name, "gl_FragColor") == 0) {
|
||||
|
|
@ -9485,11 +9523,14 @@ detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
|
|||
gl_FragSecondaryColor_assigned = true;
|
||||
else if (strcmp(var->name, "gl_SecondaryFragDataEXT") == 0)
|
||||
gl_FragSecondaryData_assigned = true;
|
||||
else if (strcmp(var->name, "gl_FragDepth") == 0)
|
||||
gl_FragDepth_assigned = true;
|
||||
else if (!is_gl_identifier(var->name)) {
|
||||
if (state->stage == MESA_SHADER_FRAGMENT &&
|
||||
var->data.mode == ir_var_shader_out) {
|
||||
user_defined_fs_output_assigned = true;
|
||||
user_defined_fs_output = var;
|
||||
color_outputs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9539,6 +9580,28 @@ detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
|
|||
_mesa_glsl_error(&loc, state,
|
||||
"Dual source blending requires EXT_blend_func_extended");
|
||||
}
|
||||
|
||||
if (yuv_layout_used) {
|
||||
/**
|
||||
* From the GL_EXT_YUV_target spec:
|
||||
*
|
||||
* "Additionally if the shader qualifies fragment shader output with
|
||||
* the new yuv qualifier and write depth or multiple color output,
|
||||
* it would cause compilation failure."
|
||||
*
|
||||
* However, since the extension requires GLSL ES 3.00, we don't need to
|
||||
* consider interactions with gl_FragColor and gl_FragData.
|
||||
*/
|
||||
|
||||
if (gl_FragDepth_assigned) {
|
||||
_mesa_glsl_error(&loc, state, "fragment shader uses yuv-layout and "
|
||||
"`gl_FragDepth'");
|
||||
} else if (color_outputs > 1) {
|
||||
_mesa_glsl_error(&loc, state, "fragment shader uses yuv-layout and "
|
||||
"multiple color-outputs");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -546,6 +546,25 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc,
|
|||
}
|
||||
}
|
||||
|
||||
if ((this->flags.q.explicit_location && q.flags.q.yuv) ||
|
||||
(this->flags.q.yuv && q.flags.q.explicit_location)) {
|
||||
/***
|
||||
* The EXT_YUV_target spec says:
|
||||
* The new yuv layout qualifier can't be combined with any other
|
||||
* layout qualifier, <snip>
|
||||
*
|
||||
* However, forbidding *all* layout qualifiers seems like a massive
|
||||
* over-reaction, and limiting potentially useful stuff. So instead,
|
||||
* let's just disallow the location=n layout. This is the only valid
|
||||
* output layout in GLSL ES 3.00, and the extension spec explicitly
|
||||
* says that it's not valid together with multiple color outputs
|
||||
* anyway.
|
||||
*/
|
||||
_mesa_glsl_error(loc, state, "yuv layout can't be combined with any "
|
||||
"other layout qualifier");
|
||||
return false;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@
|
|||
#include <math.h>
|
||||
#include "builtin_functions.h"
|
||||
#include "util/hash_table.h"
|
||||
#include "util/u_ycbcr.h"
|
||||
|
||||
#ifndef M_PIf
|
||||
#define M_PIf ((float) M_PI)
|
||||
|
|
@ -230,6 +231,14 @@ texture_external_es3(const _mesa_glsl_parse_state *state)
|
|||
state->is_version(0, 300);
|
||||
}
|
||||
|
||||
static bool
|
||||
texture_external_2d_y2y(const _mesa_glsl_parse_state *state)
|
||||
{
|
||||
return state->EXT_YUV_target_enable &&
|
||||
state->es_shader &&
|
||||
state->is_version(0, 300);
|
||||
}
|
||||
|
||||
static bool
|
||||
texture_shadow2Dext(const _mesa_glsl_parse_state *state)
|
||||
{
|
||||
|
|
@ -1226,6 +1235,11 @@ private:
|
|||
unsigned num_arguments,
|
||||
unsigned flags);
|
||||
|
||||
ir_variable *get_to_ycbcr_matrix(ir_factory &body, const char *name,
|
||||
const float coeffs[3], bool full_range);
|
||||
ir_variable *get_to_rgb_matrix(ir_factory &body, const char *name,
|
||||
const float coeffs[3], bool full_range);
|
||||
|
||||
/**
|
||||
* Create a new image built-in function for all known image types.
|
||||
* \p flags is a bitfield of \c image_function_flags flags.
|
||||
|
|
@ -1599,6 +1613,9 @@ private:
|
|||
ir_function_signature *_set_mesh_outputs_intrinsic();
|
||||
ir_function_signature *_set_mesh_outputs();
|
||||
|
||||
ir_function_signature *_rgb_2_yuv();
|
||||
ir_function_signature *_yuv_2_rgb();
|
||||
|
||||
#undef B0
|
||||
#undef B1
|
||||
#undef B2
|
||||
|
|
@ -2856,6 +2873,8 @@ builtin_builder::create_builtins()
|
|||
_textureSize(texture_multisample_array, &glsl_type_builtin_ivec3, &glsl_type_builtin_usampler2DMSArray),
|
||||
|
||||
_textureSize(texture_external_es3, &glsl_type_builtin_ivec2, &glsl_type_builtin_samplerExternalOES),
|
||||
|
||||
_textureSize(texture_external_2d_y2y, &glsl_type_builtin_ivec2, &glsl_type_builtin_samplerExternal2DY2YEXT),
|
||||
NULL);
|
||||
|
||||
add_function("textureSize1D",
|
||||
|
|
@ -2964,6 +2983,8 @@ builtin_builder::create_builtins()
|
|||
|
||||
_texture(ir_tex, texture_external_es3, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternalOES, &glsl_type_builtin_vec2),
|
||||
|
||||
_texture(ir_tex, texture_external_2d_y2y, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternal2DY2YEXT, &glsl_type_builtin_vec2),
|
||||
|
||||
_texture(ir_txb, v130_derivatives_only, &glsl_type_builtin_vec4, &glsl_type_builtin_sampler1D, &glsl_type_builtin_float),
|
||||
_texture(ir_txb, v130_derivatives_only, &glsl_type_builtin_ivec4, &glsl_type_builtin_isampler1D, &glsl_type_builtin_float),
|
||||
_texture(ir_txb, v130_derivatives_only, &glsl_type_builtin_uvec4, &glsl_type_builtin_usampler1D, &glsl_type_builtin_float),
|
||||
|
|
@ -3209,6 +3230,8 @@ builtin_builder::create_builtins()
|
|||
_texture(ir_tex, v130, &glsl_type_builtin_ivec4, &glsl_type_builtin_isampler2DRect, &glsl_type_builtin_vec3, TEX_PROJECT),
|
||||
_texture(ir_tex, texture_external_es3, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternalOES, &glsl_type_builtin_vec3, TEX_PROJECT),
|
||||
_texture(ir_tex, texture_external_es3, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternalOES, &glsl_type_builtin_vec4, TEX_PROJECT),
|
||||
_texture(ir_tex, texture_external_2d_y2y, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternal2DY2YEXT, &glsl_type_builtin_vec3, TEX_PROJECT),
|
||||
_texture(ir_tex, texture_external_2d_y2y, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternal2DY2YEXT, &glsl_type_builtin_vec4, TEX_PROJECT),
|
||||
|
||||
_texture(ir_tex, v130, &glsl_type_builtin_uvec4, &glsl_type_builtin_usampler2DRect, &glsl_type_builtin_vec3, TEX_PROJECT),
|
||||
_texture(ir_tex, v130, &glsl_type_builtin_vec4, &glsl_type_builtin_sampler2DRect, &glsl_type_builtin_vec4, TEX_PROJECT),
|
||||
|
|
@ -3278,6 +3301,8 @@ builtin_builder::create_builtins()
|
|||
|
||||
_texelFetch(texture_external_es3, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternalOES, &glsl_type_builtin_ivec2),
|
||||
|
||||
_texelFetch(texture_external_2d_y2y, &glsl_type_builtin_vec4, &glsl_type_builtin_samplerExternal2DY2YEXT, &glsl_type_builtin_ivec2),
|
||||
|
||||
NULL);
|
||||
|
||||
add_function("texelFetch1D",
|
||||
|
|
@ -6044,6 +6069,9 @@ builtin_builder::create_builtins()
|
|||
add_function("EmitMeshTasksEXT", _emit_mesh_tasks(), NULL);
|
||||
add_function("SetMeshOutputsEXT", _set_mesh_outputs(), NULL);
|
||||
|
||||
add_function("rgb_2_yuv", _rgb_2_yuv(), NULL);
|
||||
add_function("yuv_2_rgb", _yuv_2_rgb(), NULL);
|
||||
|
||||
#undef F
|
||||
#undef FI
|
||||
#undef FIUDHF_VEC
|
||||
|
|
@ -9623,6 +9651,158 @@ ir_function_signature *builtin_builder::_set_mesh_outputs()
|
|||
return sig;
|
||||
}
|
||||
|
||||
ir_variable *builtin_builder::get_to_ycbcr_matrix(ir_factory &body,
|
||||
const char *name,
|
||||
const float coeffs[3],
|
||||
bool full_range)
|
||||
{
|
||||
float data[3][4];
|
||||
util_get_rgb_to_ycbcr_matrix(data, coeffs);
|
||||
|
||||
const unsigned bpc[3] = { 8, 8, 8 };
|
||||
float range[3][2];
|
||||
if (full_range)
|
||||
util_get_full_range_coeffs(range, bpc);
|
||||
else
|
||||
util_get_narrow_range_coeffs(range, bpc);
|
||||
|
||||
util_ycbcr_adjust_to_range(data, range);
|
||||
|
||||
ir_variable *var = body.make_temp(&glsl_type_builtin_mat4x3, name);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
ir_constant_data col_data;
|
||||
col_data.f[0] = data[0][i];
|
||||
col_data.f[1] = data[1][i];
|
||||
col_data.f[2] = data[2][i];
|
||||
ir_constant *col = imm(&glsl_type_builtin_vec3, col_data);
|
||||
body.emit(assign(array_ref(var, i), col));
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
ir_variable *builtin_builder::get_to_rgb_matrix(ir_factory &body,
|
||||
const char *name,
|
||||
const float coeffs[3],
|
||||
bool full_range)
|
||||
{
|
||||
float data[3][4];
|
||||
util_get_ycbcr_to_rgb_matrix(data, coeffs);
|
||||
|
||||
const unsigned bpc[3] = { 8, 8, 8 };
|
||||
float range[3][2];
|
||||
if (full_range)
|
||||
util_get_full_range_coeffs(range, bpc);
|
||||
else
|
||||
util_get_narrow_range_coeffs(range, bpc);
|
||||
|
||||
util_ycbcr_adjust_from_range(data, range);
|
||||
|
||||
ir_variable *var = body.make_temp(&glsl_type_builtin_mat4x3, name);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
ir_constant_data col_data;
|
||||
col_data.f[0] = data[0][i];
|
||||
col_data.f[1] = data[1][i];
|
||||
col_data.f[2] = data[2][i];
|
||||
ir_constant *col = imm(&glsl_type_builtin_vec3, col_data);
|
||||
body.emit(assign(array_ref(var, i), col));
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
ir_function_signature *
|
||||
builtin_builder::_rgb_2_yuv()
|
||||
{
|
||||
ir_variable *color = in_var(&glsl_type_builtin_vec3, "color");
|
||||
ir_variable *conv_standard = in_var(&glsl_type_builtin_yuvCscStandardEXT, "conv_standard");
|
||||
MAKE_SIG(&glsl_type_builtin_vec3, texture_external_2d_y2y, 2, color, conv_standard);
|
||||
|
||||
ir_swizzle *r = swizzle(color, SWIZZLE_XXXX, 3);
|
||||
ir_swizzle *g = swizzle(color, SWIZZLE_YYYY, 3);
|
||||
ir_swizzle *b = swizzle(color, SWIZZLE_ZZZZ, 3);
|
||||
|
||||
ir_constant_data data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.i[0] = YUV_CSC_STANDARD_601;
|
||||
ir_constant *itu_601 = imm(&glsl_type_builtin_yuvCscStandardEXT, data);
|
||||
data.i[0] = YUV_CSC_STANDARD_601_FULL_RANGE;
|
||||
ir_constant *itu_601_full_range = imm(&glsl_type_builtin_yuvCscStandardEXT, data);
|
||||
data.i[0] = YUV_CSC_STANDARD_709;
|
||||
ir_constant *itu_709 = imm(&glsl_type_builtin_yuvCscStandardEXT, data);
|
||||
|
||||
ir_variable *m = body.make_temp(&glsl_type_builtin_mat4x3, "m");
|
||||
body.emit(
|
||||
if_tree(
|
||||
equal(conv_standard, itu_601),
|
||||
assign(m, get_to_ycbcr_matrix(body, "m_601", util_ycbcr_bt601_coeffs, false)),
|
||||
if_tree(
|
||||
equal(conv_standard, itu_601_full_range),
|
||||
assign(m, get_to_ycbcr_matrix(body, "m_601f", util_ycbcr_bt601_coeffs, true)),
|
||||
if_tree(
|
||||
equal(conv_standard, itu_709),
|
||||
assign(m, get_to_ycbcr_matrix(body, "m_709", util_ycbcr_bt709_coeffs, false))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
body.emit(ret(
|
||||
ir_builder::fma(r, array_ref(m, 0),
|
||||
ir_builder::fma(g, array_ref(m, 1),
|
||||
ir_builder::fma(b, array_ref(m, 2),
|
||||
array_ref(m, 3))))));
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
ir_function_signature *
|
||||
builtin_builder::_yuv_2_rgb()
|
||||
{
|
||||
ir_variable *color = in_var(&glsl_type_builtin_vec3, "color");
|
||||
ir_variable *conv_standard = in_var(&glsl_type_builtin_yuvCscStandardEXT, "conv_standard");
|
||||
MAKE_SIG(&glsl_type_builtin_vec3, texture_external_2d_y2y, 2, color, conv_standard);
|
||||
|
||||
ir_swizzle *y = swizzle(color, SWIZZLE_XXXX, 3);
|
||||
ir_swizzle *u = swizzle(color, SWIZZLE_YYYY, 3);
|
||||
ir_swizzle *v = swizzle(color, SWIZZLE_ZZZZ, 3);
|
||||
|
||||
ir_constant_data data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.i[0] = YUV_CSC_STANDARD_601;
|
||||
ir_constant *itu_601 = imm(&glsl_type_builtin_yuvCscStandardEXT, data);
|
||||
data.i[0] = YUV_CSC_STANDARD_601_FULL_RANGE;
|
||||
ir_constant *itu_601_full_range = imm(&glsl_type_builtin_yuvCscStandardEXT, data);
|
||||
data.i[0] = YUV_CSC_STANDARD_709;
|
||||
ir_constant *itu_709 = imm(&glsl_type_builtin_yuvCscStandardEXT, data);
|
||||
|
||||
ir_variable *m = body.make_temp(&glsl_type_builtin_mat4x3, "m");
|
||||
body.emit(
|
||||
if_tree(
|
||||
equal(conv_standard, itu_601),
|
||||
assign(m, get_to_rgb_matrix(body, "m_601", util_ycbcr_bt601_coeffs, false)),
|
||||
if_tree(
|
||||
equal(conv_standard, itu_601_full_range),
|
||||
assign(m, get_to_rgb_matrix(body, "m_601f", util_ycbcr_bt601_coeffs, true)),
|
||||
if_tree(
|
||||
equal(conv_standard, itu_709),
|
||||
assign(m, get_to_rgb_matrix(body, "m_709", util_ycbcr_bt709_coeffs, false))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
body.emit(ret(
|
||||
ir_builder::fma(y, array_ref(m, 0),
|
||||
ir_builder::fma(u, array_ref(m, 1),
|
||||
ir_builder::fma(v, array_ref(m, 2),
|
||||
array_ref(m, 3))))));
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
|||
|
|
@ -438,6 +438,32 @@ mat4x2 TYPE(120, 300, 120, 300, &glsl_type_builtin_mat4x2);
|
|||
mat4x3 TYPE(120, 300, 120, 300, &glsl_type_builtin_mat4x3);
|
||||
mat4x4 TYPE(120, 300, 120, 300, &glsl_type_builtin_mat4);
|
||||
|
||||
yuvCscStandardEXT TYPE_WITH_ALT(0, 0, 0, 0, yyextra->EXT_YUV_target_enable, &glsl_type_builtin_yuvCscStandardEXT);
|
||||
|
||||
itu_601 {
|
||||
if (!yyextra->EXT_YUV_target_enable)
|
||||
return classify_identifier(yyextra, yytext, yyleng, yylval);
|
||||
|
||||
yylval->csc_standard = YUV_CSC_STANDARD_601;
|
||||
return CSCSTANDARD;
|
||||
}
|
||||
|
||||
itu_601_full_range {
|
||||
if (!yyextra->EXT_YUV_target_enable)
|
||||
return classify_identifier(yyextra, yytext, yyleng, yylval);
|
||||
|
||||
yylval->csc_standard = YUV_CSC_STANDARD_601_FULL_RANGE;
|
||||
return CSCSTANDARD;
|
||||
}
|
||||
|
||||
itu_709 {
|
||||
if (!yyextra->EXT_YUV_target_enable)
|
||||
return classify_identifier(yyextra, yytext, yyleng, yylval);
|
||||
|
||||
yylval->csc_standard = YUV_CSC_STANDARD_709;
|
||||
return CSCSTANDARD;
|
||||
}
|
||||
|
||||
in return IN_TOK;
|
||||
out return OUT_TOK;
|
||||
inout return INOUT_TOK;
|
||||
|
|
@ -502,6 +528,14 @@ samplerExternalOES {
|
|||
return IDENTIFIER;
|
||||
}
|
||||
|
||||
__samplerExternal2DY2YEXT {
|
||||
if (yyextra->EXT_YUV_target_enable) {
|
||||
yylval->type = &glsl_type_builtin_samplerExternal2DY2YEXT;
|
||||
return BASIC_TYPE_TOK;
|
||||
} else
|
||||
return IDENTIFIER;
|
||||
}
|
||||
|
||||
/* keywords available with ARB_gpu_shader5 */
|
||||
precise KEYWORD_WITH_ALT(400, 310, 400, 320, yyextra->ARB_gpu_shader5_enable || yyextra->EXT_gpu_shader5_enable || yyextra->OES_gpu_shader5_enable, PRECISE);
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ static bool match_layout_qualifier(const char *s1, const char *s2,
|
|||
float real;
|
||||
double dreal;
|
||||
const char *identifier;
|
||||
enum yuv_csc_standard csc_standard;
|
||||
|
||||
struct ast_type_qualifier type_qualifier;
|
||||
|
||||
|
|
@ -159,6 +160,7 @@ static bool match_layout_qualifier(const char *s1, const char *s2,
|
|||
%token <n> INTCONSTANT UINTCONSTANT BOOLCONSTANT
|
||||
%token <n64> INT64CONSTANT UINT64CONSTANT
|
||||
%token <identifier> FIELD_SELECTION
|
||||
%token <csc_standard> CSCSTANDARD
|
||||
%token LEFT_OP RIGHT_OP
|
||||
%token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
|
||||
%token AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN
|
||||
|
|
@ -311,6 +313,7 @@ translation_unit:
|
|||
}
|
||||
state->symbols->add_default_precision_qualifier("sampler2D", ast_precision_low);
|
||||
state->symbols->add_default_precision_qualifier("samplerExternalOES", ast_precision_low);
|
||||
state->symbols->add_default_precision_qualifier("__samplerExternal2DY2YEXT", ast_precision_low);
|
||||
state->symbols->add_default_precision_qualifier("samplerCube", ast_precision_low);
|
||||
state->symbols->add_default_precision_qualifier("atomic_uint", ast_precision_high);
|
||||
}
|
||||
|
|
@ -493,6 +496,13 @@ primary_expression:
|
|||
$$->set_location(@1);
|
||||
$$->primary_expression.bool_constant = $1;
|
||||
}
|
||||
| CSCSTANDARD
|
||||
{
|
||||
linear_ctx *ctx = state->linalloc;
|
||||
$$ = new(ctx) ast_expression(ast_csc_standard, NULL, NULL, NULL);
|
||||
$$->set_location(@1);
|
||||
$$->primary_expression.csc_standard = $1;
|
||||
}
|
||||
| '(' expression ')'
|
||||
{
|
||||
$$ = $2;
|
||||
|
|
@ -1743,6 +1753,24 @@ layout_qualifier_id:
|
|||
}
|
||||
}
|
||||
|
||||
/* Layout qualifier for EXT_YUV_target. */
|
||||
if (match_layout_qualifier($1, "yuv", state) == 0) {
|
||||
if (state->stage != MESA_SHADER_FRAGMENT) {
|
||||
_mesa_glsl_error(& @1, state,
|
||||
"yuv layout qualifier only valid in fragment "
|
||||
"shaders");
|
||||
}
|
||||
|
||||
if (state->EXT_YUV_target_enable) {
|
||||
$$.flags.q.yuv = 1;
|
||||
} else {
|
||||
_mesa_glsl_error(& @1, state,
|
||||
"yuv layout qualifier present, but the "
|
||||
"EXT_YUV_target_enable extension is not "
|
||||
"enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$$.flags.i) {
|
||||
_mesa_glsl_error(& @1, state, "unrecognized layout identifier "
|
||||
"`%s'", $1);
|
||||
|
|
|
|||
|
|
@ -853,6 +853,7 @@ static const _mesa_glsl_extension _mesa_glsl_supported_extensions[] = {
|
|||
EXT_AEP(EXT_texture_cube_map_array),
|
||||
EXT(EXT_texture_query_lod),
|
||||
EXT(EXT_texture_shadow_lod),
|
||||
EXT(EXT_YUV_target),
|
||||
EXT(INTEL_conservative_rasterization),
|
||||
EXT(INTEL_shader_atomic_float_minmax),
|
||||
EXT(INTEL_shader_integer_functions2),
|
||||
|
|
@ -1355,6 +1356,8 @@ _mesa_ast_type_qualifier_print(const struct ast_type_qualifier *q)
|
|||
printf("noperspective ");
|
||||
if (q->flags.q.per_primitive)
|
||||
printf("per_primitive ");
|
||||
if (q->flags.q.yuv)
|
||||
printf("yuv ");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1537,6 +1540,17 @@ ast_expression::print(void) const
|
|||
break;
|
||||
}
|
||||
|
||||
case ast_csc_standard:
|
||||
switch (primary_expression.csc_standard) {
|
||||
case YUV_CSC_STANDARD_601:
|
||||
printf("itu_601 ");
|
||||
case YUV_CSC_STANDARD_601_FULL_RANGE:
|
||||
printf("itu_601_full_range ");
|
||||
case YUV_CSC_STANDARD_709:
|
||||
printf("itu_709 ");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -937,6 +937,8 @@ struct _mesa_glsl_parse_state {
|
|||
bool EXT_texture_query_lod_warn;
|
||||
bool EXT_texture_shadow_lod_enable;
|
||||
bool EXT_texture_shadow_lod_warn;
|
||||
bool EXT_YUV_target_enable;
|
||||
bool EXT_YUV_target_warn;
|
||||
bool INTEL_conservative_rasterization_enable;
|
||||
bool INTEL_conservative_rasterization_warn;
|
||||
bool INTEL_shader_atomic_float_minmax_enable;
|
||||
|
|
|
|||
|
|
@ -475,6 +475,7 @@ nir_visitor::visit(ir_variable *ir)
|
|||
var->data.implicit_sized_array = ir->data.implicit_sized_array;
|
||||
var->data.from_ssbo_unsized_array = ir->data.from_ssbo_unsized_array;
|
||||
var->data.per_primitive = ir->data.per_primitive;
|
||||
var->data.yuv = ir->data.yuv;
|
||||
|
||||
switch(ir->data.mode) {
|
||||
case ir_var_auto:
|
||||
|
|
|
|||
|
|
@ -870,6 +870,11 @@ public:
|
|||
*/
|
||||
unsigned pixel_local_storage:2;
|
||||
|
||||
/**
|
||||
* Non-zero if the fragment shader output produce YUV color output
|
||||
*/
|
||||
unsigned yuv:1;
|
||||
|
||||
/**
|
||||
* Emit a warning if this variable is accessed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -876,6 +876,11 @@ glsl_sampler_type(enum glsl_sampler_dim dim, bool shadow,
|
|||
return &glsl_type_builtin_error;
|
||||
else
|
||||
return &glsl_type_builtin_samplerExternalOES;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
if (shadow || array)
|
||||
return &glsl_type_builtin_error;
|
||||
else
|
||||
return &glsl_type_builtin_samplerExternal2DY2YEXT;
|
||||
case GLSL_SAMPLER_DIM_SUBPASS:
|
||||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
return &glsl_type_builtin_error;
|
||||
|
|
@ -906,6 +911,7 @@ glsl_sampler_type(enum glsl_sampler_dim dim, bool shadow,
|
|||
case GLSL_SAMPLER_DIM_MS:
|
||||
return (array ? &glsl_type_builtin_isampler2DMSArray : &glsl_type_builtin_isampler2DMS);
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
case GLSL_SAMPLER_DIM_SUBPASS:
|
||||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
|
|
@ -937,6 +943,7 @@ glsl_sampler_type(enum glsl_sampler_dim dim, bool shadow,
|
|||
case GLSL_SAMPLER_DIM_MS:
|
||||
return (array ? &glsl_type_builtin_usampler2DMSArray : &glsl_type_builtin_usampler2DMS);
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
case GLSL_SAMPLER_DIM_SUBPASS:
|
||||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
|
|
@ -999,6 +1006,8 @@ glsl_texture_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type typ
|
|||
return &glsl_type_builtin_error;
|
||||
else
|
||||
return &glsl_type_builtin_textureExternalOES;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
case GLSL_TYPE_INT:
|
||||
|
|
@ -1028,6 +1037,7 @@ glsl_texture_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type typ
|
|||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
return &glsl_type_builtin_itextureSubpassInputMS;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1058,6 +1068,7 @@ glsl_texture_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type typ
|
|||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
return &glsl_type_builtin_utextureSubpassInputMS;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1114,6 +1125,7 @@ glsl_image_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type type)
|
|||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
return &glsl_type_builtin_subpassInputMS;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1144,6 +1156,7 @@ glsl_image_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type type)
|
|||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
return &glsl_type_builtin_isubpassInputMS;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1174,6 +1187,7 @@ glsl_image_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type type)
|
|||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
return &glsl_type_builtin_usubpassInputMS;
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1202,6 +1216,7 @@ glsl_image_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type type)
|
|||
case GLSL_SAMPLER_DIM_SUBPASS:
|
||||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1230,6 +1245,7 @@ glsl_image_type(enum glsl_sampler_dim dim, bool array, enum glsl_base_type type)
|
|||
case GLSL_SAMPLER_DIM_SUBPASS:
|
||||
case GLSL_SAMPLER_DIM_SUBPASS_MS:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL:
|
||||
case GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y:
|
||||
return &glsl_type_builtin_error;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -696,6 +696,11 @@ typedef struct nir_variable {
|
|||
*/
|
||||
unsigned depth_layout : 3;
|
||||
|
||||
/**
|
||||
* Whether the variable is a YUV color-output.
|
||||
*/
|
||||
unsigned yuv : 1;
|
||||
|
||||
/**
|
||||
* Vertex stream output identifier.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1641,11 +1641,18 @@ enum glsl_sampler_dim {
|
|||
GLSL_SAMPLER_DIM_RECT,
|
||||
GLSL_SAMPLER_DIM_BUF,
|
||||
GLSL_SAMPLER_DIM_EXTERNAL,
|
||||
GLSL_SAMPLER_DIM_EXTERNAL_2D_Y2Y,
|
||||
GLSL_SAMPLER_DIM_MS,
|
||||
GLSL_SAMPLER_DIM_SUBPASS, /* for vulkan input attachments */
|
||||
GLSL_SAMPLER_DIM_SUBPASS_MS, /* for multisampled vulkan input attachments */
|
||||
};
|
||||
|
||||
enum yuv_csc_standard {
|
||||
YUV_CSC_STANDARD_601,
|
||||
YUV_CSC_STANDARD_601_FULL_RANGE,
|
||||
YUV_CSC_STANDARD_709,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ struct gl_extensions
|
|||
GLboolean EXT_timer_query;
|
||||
GLboolean EXT_vertex_array_bgra;
|
||||
GLboolean EXT_window_rectangles;
|
||||
GLboolean EXT_YUV_target;
|
||||
GLboolean OES_copy_image;
|
||||
GLboolean OES_primitive_bounding_box;
|
||||
GLboolean OES_sample_variables;
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ _mesa_init_extensions(struct gl_extensions *extensions)
|
|||
extensions->EXT_shadow_samplers = GL_TRUE;
|
||||
extensions->EXT_stencil_two_side = GL_TRUE;
|
||||
extensions->EXT_texture_env_dot3 = GL_TRUE;
|
||||
extensions->EXT_YUV_target = GL_TRUE;
|
||||
|
||||
extensions->ATI_fragment_shader = GL_TRUE;
|
||||
extensions->ATI_texture_env_combine3 = GL_TRUE;
|
||||
|
|
|
|||
|
|
@ -365,6 +365,7 @@ EXT(EXT_vertex_array , dummy_true
|
|||
EXT(EXT_vertex_array_bgra , EXT_vertex_array_bgra , GLL, GLC, x , x , 2008)
|
||||
EXT(EXT_vertex_attrib_64bit , ARB_vertex_attrib_64bit , 32, GLC, x , x , 2010)
|
||||
EXT(EXT_window_rectangles , EXT_window_rectangles , GLL, GLC, x , 30, 2016)
|
||||
EXT(EXT_YUV_target , EXT_YUV_target , x , x , x , 30, 2013)
|
||||
|
||||
EXT(GREMEDY_string_marker , GREMEDY_string_marker , GLL, GLC, x , x , 2007)
|
||||
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ if with_tests
|
|||
'tests/u_memstream_test.cpp',
|
||||
'tests/u_printf_test.cpp',
|
||||
'tests/u_qsort_test.cpp',
|
||||
'tests/u_ycbcr_test.cpp',
|
||||
'tests/vector_test.cpp',
|
||||
)
|
||||
|
||||
|
|
|
|||
520
src/util/tests/u_ycbcr_test.cpp
Normal file
520
src/util/tests/u_ycbcr_test.cpp
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
/**
|
||||
* Copyright (c) 2026 Collabora Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "util/u_ycbcr.h"
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
static void
|
||||
test_to_rgb_coeffs(const float coeffs[3])
|
||||
{
|
||||
float mat[3][4];
|
||||
util_get_ycbcr_to_rgb_matrix(mat, coeffs);
|
||||
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2];
|
||||
float d = 2 - 2 * c;
|
||||
float e = 2 - 2 * a;
|
||||
|
||||
const struct {
|
||||
float input[3];
|
||||
float expected[3];
|
||||
} test_data[] = { {
|
||||
{ 1.0f, 0.0f, 0.0f},
|
||||
{ 1.0f, 1.0f, 1.0f },
|
||||
}, {
|
||||
{ 0.0f, 0.0f, 0.0f},
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 0.5f, 0.0f, 0.0f },
|
||||
{ 0.5f, 0.5f, 0.5f },
|
||||
}, {
|
||||
{ a, -a / d, 0.5f },
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ b, -b / d, -b / e },
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
}, {
|
||||
{ c, 0.5f, -c / e },
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
}, {
|
||||
{ 1 - c, -0.5f, c / e },
|
||||
{ 1.0f, 1.0f, 0.0f },
|
||||
}, {
|
||||
{ 1 - a, a / d, (a - 1) / e },
|
||||
{ 0.0f, 1.0f, 1.0f },
|
||||
}, {
|
||||
{ 1 - b, b / d, b / e },
|
||||
{ 1.0f, 0.0f, 1.0f },
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i) {
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
result[c] = test_data[i].input[0] * mat[c][0] +
|
||||
test_data[i].input[1] * mat[c][1] +
|
||||
test_data[i].input[2] * mat[c][2];
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-7);
|
||||
|
||||
/* verify with reference equation */
|
||||
float Y = test_data[i].input[0];
|
||||
float Cb = test_data[i].input[1];
|
||||
float Cr = test_data[i].input[2];
|
||||
|
||||
result[0] = Y + e * Cr;
|
||||
result[1] = Y - (a * e / b) * Cr - (c * d / b) * Cb;
|
||||
result[2] = Y + d * Cb;
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-6);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-6);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, to_rgb)
|
||||
{
|
||||
test_to_rgb_coeffs(util_ycbcr_bt601_coeffs);
|
||||
test_to_rgb_coeffs(util_ycbcr_bt709_coeffs);
|
||||
test_to_rgb_coeffs(util_ycbcr_bt2020_coeffs);
|
||||
}
|
||||
|
||||
static void
|
||||
test_to_ycbcr_coeffs(const float coeffs[3])
|
||||
{
|
||||
float mat[3][4];
|
||||
util_get_rgb_to_ycbcr_matrix(mat, coeffs);
|
||||
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2];
|
||||
float d = 2 - 2 * c;
|
||||
float e = 2 - 2 * a;
|
||||
|
||||
const struct {
|
||||
float input[3];
|
||||
float expected[3];
|
||||
} test_data[] = { {
|
||||
{ 1.0f, 1.0f, 1.0f },
|
||||
{ 1.0f, 0.0f, 0.0f},
|
||||
}, {
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.0f},
|
||||
}, {
|
||||
{ 0.5f, 0.5f, 0.5f },
|
||||
{ 0.5f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
{ a, -a / d, 0.5f },
|
||||
}, {
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
{ b, -b / d, -b / e },
|
||||
}, {
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
{ c, 0.5f, -c / e },
|
||||
}, {
|
||||
{ 1.0f, 1.0f, 0.0f },
|
||||
{ 1 - c, -0.5f, c / e },
|
||||
}, {
|
||||
{ 0.0f, 1.0f, 1.0f },
|
||||
{ 1 - a, a / d, (a - 1) / e },
|
||||
}, {
|
||||
{ 1.0f, 0.0f, 1.0f },
|
||||
{ 1 - b, b / d, b / e },
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i) {
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
result[c] = test_data[i].input[0] * mat[c][0] +
|
||||
test_data[i].input[1] * mat[c][1] +
|
||||
test_data[i].input[2] * mat[c][2];
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-7);
|
||||
|
||||
/* verify with reference equation */
|
||||
float R = test_data[i].input[0];
|
||||
float G = test_data[i].input[1];
|
||||
float B = test_data[i].input[2];
|
||||
|
||||
float Y = a * R + b * G + c * B;;
|
||||
result[0] = Y;
|
||||
result[1] = (B - Y) / d;
|
||||
result[2] = (R - Y) / e;
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-7);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, to_ycbcr)
|
||||
{
|
||||
test_to_ycbcr_coeffs(util_ycbcr_bt601_coeffs);
|
||||
test_to_ycbcr_coeffs(util_ycbcr_bt709_coeffs);
|
||||
test_to_ycbcr_coeffs(util_ycbcr_bt2020_coeffs);
|
||||
}
|
||||
|
||||
static void
|
||||
test_to_ycbcr_and_back_coeffs(const float coeffs[3])
|
||||
{
|
||||
float to_ycbcr[3][4], to_rgb[3][4];
|
||||
util_get_rgb_to_ycbcr_matrix(to_ycbcr, coeffs);
|
||||
util_get_ycbcr_to_rgb_matrix(to_rgb, coeffs);
|
||||
|
||||
float inputs[][3] = {
|
||||
{ 1.0f, 1.0f, 1.0f },
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
{ 0.5f, 0.5f, 0.5f },
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
{ 1.0f, 1.0f, 0.0f },
|
||||
{ 0.0f, 1.0f, 1.0f },
|
||||
{ 1.0f, 0.0f, 1.0f },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(inputs); ++i) {
|
||||
float ycbcr[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
ycbcr[c] = inputs[i][0] * to_ycbcr[c][0] +
|
||||
inputs[i][1] * to_ycbcr[c][1] +
|
||||
inputs[i][2] * to_ycbcr[c][2];
|
||||
|
||||
}
|
||||
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
result[c] = ycbcr[0] * to_rgb[c][0] +
|
||||
ycbcr[1] * to_rgb[c][1] +
|
||||
ycbcr[2] * to_rgb[c][2];
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NEAR(inputs[i][0], result[0], 1e-7);
|
||||
EXPECT_NEAR(inputs[i][1], result[1], 1e-7);
|
||||
EXPECT_NEAR(inputs[i][2], result[2], 1e-7);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, to_ycbcr_and_back)
|
||||
{
|
||||
test_to_ycbcr_and_back_coeffs(util_ycbcr_bt601_coeffs);
|
||||
test_to_ycbcr_and_back_coeffs(util_ycbcr_bt709_coeffs);
|
||||
test_to_ycbcr_and_back_coeffs(util_ycbcr_bt2020_coeffs);
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, full_range)
|
||||
{
|
||||
float range[3][2];
|
||||
unsigned bpc[3] = {8, 8, 8};
|
||||
util_get_full_range_coeffs(range, bpc);
|
||||
|
||||
const struct {
|
||||
uint8_t input[3];
|
||||
float expected[3];
|
||||
} test_data[] = { {
|
||||
{ 0, 128, 128 },
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 255, 128, 128 },
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 0, 0, 255 },
|
||||
{ 0.0f, -128.0f / 255, 127.0f / 255 },
|
||||
}, {
|
||||
{ 255, 255, 0 },
|
||||
{ 1.0f, 127.0f / 255, -128.0f / 255 },
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i) {
|
||||
float input[3] = {
|
||||
test_data[i].input[0] / 255.0f,
|
||||
test_data[i].input[1] / 255.0f,
|
||||
test_data[i].input[2] / 255.0f,
|
||||
};
|
||||
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c)
|
||||
result[c] = input[c] * range[c][0] + range[c][1];
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-7);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, narrow_range)
|
||||
{
|
||||
float range[3][2];
|
||||
unsigned bpc[3] = {8, 8, 8};
|
||||
util_get_narrow_range_coeffs(range, bpc);
|
||||
|
||||
const struct {
|
||||
uint8_t input[3];
|
||||
float expected[3];
|
||||
} test_data[] = { {
|
||||
{ 16, 128, 128 },
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 235, 128, 128 },
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 16, 16, 240 },
|
||||
{ 0.0f, -0.5f, 0.5f },
|
||||
}, {
|
||||
{ 235, 240, 16 },
|
||||
{ 1.0f, 0.5f, -0.5f },
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i) {
|
||||
float input[3] = {
|
||||
test_data[i].input[0] / 255.0f,
|
||||
test_data[i].input[1] / 255.0f,
|
||||
test_data[i].input[2] / 255.0f,
|
||||
};
|
||||
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c)
|
||||
result[c] = input[c] * range[c][0] + range[c][1];
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-7);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-7);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_to_rgb_narrow_range_coeffs(const float coeffs[3])
|
||||
{
|
||||
const unsigned bpc[3] = {8, 8, 10};
|
||||
float range[3][2];
|
||||
util_get_narrow_range_coeffs(range, bpc);
|
||||
|
||||
float mat[3][4];
|
||||
util_get_ycbcr_to_rgb_matrix(mat, coeffs);
|
||||
util_ycbcr_adjust_from_range(mat, range);
|
||||
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2];
|
||||
float d = 2 - 2 * c;
|
||||
float e = 2 - 2 * a;
|
||||
|
||||
const struct {
|
||||
float input[3];
|
||||
float expected[3];
|
||||
} test_data[] = { {
|
||||
{ 1.0f, 0.0f, 0.0f},
|
||||
{ 1.0f, 1.0f, 1.0f },
|
||||
}, {
|
||||
{ 0.0f, 0.0f, 0.0f},
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 0.5f, 0.0f, 0.0f },
|
||||
{ 0.5f, 0.5f, 0.5f },
|
||||
}, {
|
||||
{ a, -a / d, 0.5f },
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ b, -b / d, -b / e },
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
}, {
|
||||
{ c, 0.5f, -c / e },
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
}, {
|
||||
{ 1 - c, -0.5f, c / e },
|
||||
{ 1.0f, 1.0f, 0.0f },
|
||||
}, {
|
||||
{ 1 - a, a / d, (a - 1) / e },
|
||||
{ 0.0f, 1.0f, 1.0f },
|
||||
}, {
|
||||
{ 1 - b, b / d, b / e },
|
||||
{ 1.0f, 0.0f, 1.0f },
|
||||
}
|
||||
};
|
||||
for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i) {
|
||||
float input[3] = {
|
||||
(16 + test_data[i].input[0] * (235 - 16)) / 255.0f,
|
||||
(16 + (test_data[i].input[1] + 0.5f) * (240 - 16)) / 255.0f,
|
||||
(16 + (test_data[i].input[2] + 0.5f) * (240 - 16)) / 255.75f,
|
||||
};
|
||||
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
result[c] = input[0] * mat[c][0] +
|
||||
input[1] * mat[c][1] +
|
||||
input[2] * mat[c][2] +
|
||||
mat[c][3];
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NEAR(test_data[i].expected[0], result[0], 1e-6);
|
||||
EXPECT_NEAR(test_data[i].expected[1], result[1], 1e-6);
|
||||
EXPECT_NEAR(test_data[i].expected[2], result[2], 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, bt601_to_rgb_narrow_range)
|
||||
{
|
||||
test_to_rgb_narrow_range_coeffs(util_ycbcr_bt601_coeffs);
|
||||
test_to_rgb_narrow_range_coeffs(util_ycbcr_bt709_coeffs);
|
||||
test_to_rgb_narrow_range_coeffs(util_ycbcr_bt2020_coeffs);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_to_ycbcr_narrow_range_coeffs(const float coeffs[3])
|
||||
{
|
||||
const unsigned bpc[3] = {8, 8, 10};
|
||||
|
||||
float range[3][2];
|
||||
util_get_narrow_range_coeffs(range, bpc);
|
||||
|
||||
float mat[3][4];
|
||||
util_get_rgb_to_ycbcr_matrix(mat, coeffs);
|
||||
util_ycbcr_adjust_to_range(mat, range);
|
||||
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2];
|
||||
float d = 2 - 2 * c;
|
||||
float e = 2 - 2 * a;
|
||||
|
||||
const struct {
|
||||
float input[3];
|
||||
float expected[3];
|
||||
} test_data[] = { {
|
||||
{ 1.0f, 1.0f, 1.0f },
|
||||
{ 1.0f, 0.0f, 0.0f},
|
||||
}, {
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.0f},
|
||||
}, {
|
||||
{ 0.5f, 0.5f, 0.5f },
|
||||
{ 0.5f, 0.0f, 0.0f },
|
||||
}, {
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
{ a, -a / d, 0.5f },
|
||||
}, {
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
{ b, -b / d, -b / e },
|
||||
}, {
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
{ c, 0.5f, -c / e },
|
||||
}, {
|
||||
{ 1.0f, 1.0f, 0.0f },
|
||||
{ 1 - c, -0.5f, c / e },
|
||||
}, {
|
||||
{ 0.0f, 1.0f, 1.0f },
|
||||
{ 1 - a, a / d, (a - 1) / e },
|
||||
}, {
|
||||
{ 1.0f, 0.0f, 1.0f },
|
||||
{ 1 - b, b / d, b / e },
|
||||
}
|
||||
};
|
||||
for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i) {
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
result[c] = test_data[i].input[0] * mat[c][0] +
|
||||
test_data[i].input[1] * mat[c][1] +
|
||||
test_data[i].input[2] * mat[c][2] +
|
||||
mat[c][3];
|
||||
}
|
||||
|
||||
float expected[3] = {
|
||||
(16 + test_data[i].expected[0] * (235 - 16)) / 255.0f,
|
||||
(16 + (test_data[i].expected[1] + 0.5f) * (240 - 16)) / 255.0f,
|
||||
(16 + (test_data[i].expected[2] + 0.5f) * (240 - 16)) / 255.75f,
|
||||
};
|
||||
|
||||
EXPECT_NEAR(expected[0], result[0], 1e-6);
|
||||
EXPECT_NEAR(expected[1], result[1], 1e-6);
|
||||
EXPECT_NEAR(expected[2], result[2], 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, bt601_to_ycbcr_narrow_range)
|
||||
{
|
||||
test_to_ycbcr_narrow_range_coeffs(util_ycbcr_bt601_coeffs);
|
||||
test_to_ycbcr_narrow_range_coeffs(util_ycbcr_bt709_coeffs);
|
||||
test_to_ycbcr_narrow_range_coeffs(util_ycbcr_bt2020_coeffs);
|
||||
}
|
||||
|
||||
static void
|
||||
test_to_ycbcr_range_and_back_coeffs(const float coeffs[3])
|
||||
{
|
||||
const unsigned bpc[3] = {8, 8, 10};
|
||||
|
||||
float range[3][2];
|
||||
util_get_narrow_range_coeffs(range, bpc);
|
||||
|
||||
float to_ycbcr[3][4];
|
||||
util_get_rgb_to_ycbcr_matrix(to_ycbcr, coeffs);
|
||||
util_ycbcr_adjust_to_range(to_ycbcr, range);
|
||||
|
||||
float to_rgb[3][4];
|
||||
util_get_ycbcr_to_rgb_matrix(to_rgb, coeffs);
|
||||
util_ycbcr_adjust_from_range(to_rgb, range);
|
||||
|
||||
float inputs[][3] = {
|
||||
{ 1.0f, 1.0f, 1.0f },
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
{ 0.5f, 0.5f, 0.5f },
|
||||
{ 1.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 1.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
{ 1.0f, 1.0f, 0.0f },
|
||||
{ 0.0f, 1.0f, 1.0f },
|
||||
{ 1.0f, 0.0f, 1.0f },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(inputs); ++i) {
|
||||
float ycbcr[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
ycbcr[c] = inputs[i][0] * to_ycbcr[c][0] +
|
||||
inputs[i][1] * to_ycbcr[c][1] +
|
||||
inputs[i][2] * to_ycbcr[c][2];
|
||||
|
||||
}
|
||||
|
||||
float result[3];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
result[c] = ycbcr[0] * to_rgb[c][0] +
|
||||
ycbcr[1] * to_rgb[c][1] +
|
||||
ycbcr[2] * to_rgb[c][2];
|
||||
|
||||
}
|
||||
|
||||
EXPECT_NEAR(inputs[i][0], result[0], 1e-6);
|
||||
EXPECT_NEAR(inputs[i][1], result[1], 1e-6);
|
||||
EXPECT_NEAR(inputs[i][2], result[2], 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(u_ycbcr_test, to_ycbcr_range_and_back)
|
||||
{
|
||||
test_to_ycbcr_range_and_back_coeffs(util_ycbcr_bt601_coeffs);
|
||||
test_to_ycbcr_range_and_back_coeffs(util_ycbcr_bt709_coeffs);
|
||||
test_to_ycbcr_range_and_back_coeffs(util_ycbcr_bt2020_coeffs);
|
||||
}
|
||||
158
src/util/u_ycbcr.h
Normal file
158
src/util/u_ycbcr.h
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/**
|
||||
* Copyright (c) 2026 Collabora Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef U_YCBCR_H
|
||||
#define U_YCBCR_H
|
||||
|
||||
/* BT.601 coefficients */
|
||||
static const float util_ycbcr_bt601_coeffs[3] = {
|
||||
0.299f, 0.587f, 0.114f
|
||||
};
|
||||
|
||||
/* BT.701 coefficients */
|
||||
static const float util_ycbcr_bt709_coeffs[3] = {
|
||||
0.2126f, 0.7152f, 0.0722f
|
||||
};
|
||||
|
||||
/* BT.2020 coefficients */
|
||||
static const float util_ycbcr_bt2020_coeffs[3] = {
|
||||
0.2627f, 0.6780f, 0.0593f
|
||||
};
|
||||
|
||||
/* SMPTE 240M coefficients */
|
||||
static const float util_ycbcr_smpte240m_coeffs[3] = {
|
||||
0.2122f, 0.7013f, 0.0865f
|
||||
};
|
||||
|
||||
static inline void
|
||||
util_get_ycbcr_to_rgb_matrix(float m[3][4], const float coeffs[3])
|
||||
{
|
||||
/**
|
||||
* Sets up a 3x4 matrix that computes:
|
||||
*
|
||||
* R = Y + e * Cr
|
||||
* G = Y - (a * e / b) * Cr - (c * d / b) * Cb
|
||||
* B = Y + d * Cb
|
||||
*/
|
||||
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2];
|
||||
float d = 2 - 2 * c;
|
||||
float e = 2 - 2 * a;
|
||||
float f = 1.0f / b;
|
||||
|
||||
m[0][0] = 1; m[0][1] = 0; m[0][2] = e; m[0][3] = 0;
|
||||
m[1][0] = 1; m[1][1] = -c * d * f; m[1][2] = -a * e * f; m[1][3] = 0;
|
||||
m[2][0] = 1; m[2][1] = d; m[2][2] = 0; m[2][3] = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
util_get_rgb_to_ycbcr_matrix(float m[3][4], const float coeffs[3])
|
||||
{
|
||||
/**
|
||||
* Sets up a 3x4 matrix that computes:
|
||||
*
|
||||
* Y = a * R + b * G + c * B
|
||||
* Cb = (B - Y) / d
|
||||
* Cr = (R - Y) / e
|
||||
*/
|
||||
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2];
|
||||
float d = 0.5f / (c - 1);
|
||||
float e = 0.5f / (a - 1);
|
||||
|
||||
m[0][0] = a; m[0][1] = b; m[0][2] = c; m[0][3] = 0;
|
||||
m[1][0] = d * a; m[1][1] = d * b; m[1][2] = 0.5f; m[1][3] = 0;
|
||||
m[2][0] = 0.5f; m[2][1] = e * b; m[2][2] = e * c; m[2][3] = 0;
|
||||
}
|
||||
|
||||
static inline float
|
||||
util_get_full_range_chroma_bias(unsigned bpc)
|
||||
{
|
||||
return -(1 << (bpc - 1)) / ((1 << bpc) - 1.0f);
|
||||
}
|
||||
|
||||
static inline void
|
||||
util_get_full_range_coeffs(float out[3][2], const unsigned bpc[3])
|
||||
{
|
||||
out[0][0] = 1; out[0][1] = 0;
|
||||
out[1][0] = 1; out[1][1] = util_get_full_range_chroma_bias(bpc[1]);
|
||||
out[2][0] = 1; out[2][1] = util_get_full_range_chroma_bias(bpc[2]);
|
||||
}
|
||||
|
||||
static inline float
|
||||
util_get_narrow_range(unsigned bpc)
|
||||
{
|
||||
return 1 - 1.0f / (1 << bpc);
|
||||
}
|
||||
|
||||
static inline float
|
||||
util_get_narrow_range_luma_factor(unsigned bpc)
|
||||
{
|
||||
return util_get_narrow_range(bpc) * (256.0f / 219);
|
||||
}
|
||||
|
||||
static inline float
|
||||
util_get_narrow_range_chroma_factor(unsigned bpc)
|
||||
{
|
||||
return util_get_narrow_range(bpc) * (256.0f / 224);
|
||||
}
|
||||
|
||||
static inline void
|
||||
util_get_narrow_range_coeffs(float out[3][2], const unsigned bpc[3])
|
||||
{
|
||||
float y_factor = util_get_narrow_range_luma_factor(bpc[0]);
|
||||
float cb_factor = util_get_narrow_range_chroma_factor(bpc[1]);
|
||||
float cr_factor = util_get_narrow_range_chroma_factor(bpc[2]);
|
||||
|
||||
float y_bias = -16.0f / 219;
|
||||
float c_bias = -128.0f / 224;
|
||||
|
||||
out[0][0] = y_factor; out[0][1] = y_bias;
|
||||
out[1][0] = cb_factor; out[1][1] = c_bias;
|
||||
out[2][0] = cr_factor; out[2][1] = c_bias;
|
||||
}
|
||||
|
||||
static inline void
|
||||
util_get_identity_range_coeffs(float out[3][2])
|
||||
{
|
||||
out[0][0] = out[1][0] = out[2][0] = 1.0f;
|
||||
out[0][1] = out[1][1] = out[2][1] = 0.0f;
|
||||
}
|
||||
|
||||
static inline void
|
||||
util_ycbcr_adjust_from_range(float mat[3][4],
|
||||
const float range[3][2])
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
mat[i][3] = range[0][1] * mat[i][0] +
|
||||
range[1][1] * mat[i][1] +
|
||||
range[2][1] * mat[i][2] +
|
||||
mat[i][3];
|
||||
|
||||
mat[i][0] = mat[i][0] * range[0][0];
|
||||
mat[i][1] = mat[i][1] * range[1][0];
|
||||
mat[i][2] = mat[i][2] * range[2][0];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
util_ycbcr_adjust_to_range(float mat[3][4],
|
||||
const float range[3][2])
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
float tmp = 1.0f / range[i][0];
|
||||
mat[i][0] = mat[i][0] * tmp;
|
||||
mat[i][1] = mat[i][1] * tmp;
|
||||
mat[i][2] = mat[i][2] * tmp;
|
||||
mat[i][3] -= range[i][1] * tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* U_YCBCR_H */
|
||||
Loading…
Add table
Reference in a new issue