diff --git a/src/gallium/auxiliary/vl/vl_csc.c b/src/gallium/auxiliary/vl/vl_csc.c index d1a88b36d55..67357945785 100644 --- a/src/gallium/auxiliary/vl/vl_csc.c +++ b/src/gallium/auxiliary/vl/vl_csc.c @@ -242,3 +242,144 @@ void vl_csc_get_matrix(enum VL_CSC_COLOR_STANDARD cs, (*cstd)[2][1] * (x * cbbias + y * crbias) + (*cstd)[2][2] * (x * crbias - y * cbbias); } + +static unsigned format_bpc(enum pipe_format format) +{ + const enum pipe_format plane_format = util_format_get_plane_format(format, 0); + const struct util_format_description *desc = util_format_description(plane_format); + + for (unsigned i = 0; i < desc->nr_channels; i++) { + if (desc->channel[i].type != UTIL_FORMAT_TYPE_VOID) + return desc->channel[i].size; + } + UNREACHABLE("invalid format description"); +} + +void vl_csc_get_rgbyuv_matrix(enum pipe_video_vpp_matrix_coefficients coefficients, + enum pipe_format in_format, enum pipe_format out_format, + enum pipe_video_vpp_color_range in_color_range, + enum pipe_video_vpp_color_range out_color_range, + vl_csc_matrix *matrix) +{ + const bool in_yuv = util_format_is_yuv(in_format); + const bool out_yuv = util_format_is_yuv(out_format); + const unsigned bpc = format_bpc(in_format); + const float scale = (1 << bpc) / ((1 << bpc) - 1.0); + const float r_min = 16.0 / 256.0 * scale; + const float r_max = 235.0 / 256.0 * scale; + const float c_mid = 128.0 / 256.0 * scale; + const float c_max = 240.0 / 256.0 * scale; + float r_scale, g_scale, r_bias, g_bias; + + memcpy(matrix, &identity, sizeof(vl_csc_matrix)); + + if (in_yuv != out_yuv && coefficients == PIPE_VIDEO_VPP_MCF_RGB) + return; + + /* Convert input to full range, chroma to [-0.5,0.5]. */ + if (in_color_range == PIPE_VIDEO_VPP_CHROMA_COLOR_RANGE_REDUCED) { + r_scale = 1.0 / (r_max - r_min); + g_scale = in_yuv ? 0.5 / (c_max - c_mid) : r_scale; + r_bias = -r_min; + g_bias = in_yuv ? -c_mid : r_bias; + } else if (in_yuv) { + r_scale = 1.0; + g_scale = 0.5 / (1.0 - c_mid); + r_bias = 0.0; + g_bias = -c_mid; + } else { + r_scale = g_scale = 1.0; + r_bias = g_bias = 0.0; + } + + for (unsigned i = 0; i < 3; i++) { + (*matrix)[i][i] = i == 0 ? r_scale : g_scale; + (*matrix)[i][3] = (i == 0 ? r_bias : g_bias) * (*matrix)[i][i]; + } + + if (in_yuv != out_yuv) { + float cr, cg, cb; + + switch (coefficients) { + case PIPE_VIDEO_VPP_MCF_BT470BG: + case PIPE_VIDEO_VPP_MCF_SMPTE170M: + cr = 0.299; + cb = 0.114; + break; + case PIPE_VIDEO_VPP_MCF_SMPTE240M: + cr = 0.212; + cb = 0.087; + break; + case PIPE_VIDEO_VPP_MCF_BT2020_NCL: + cr = 0.2627; + cb = 0.0593; + break; + case PIPE_VIDEO_VPP_MCF_BT709: + default: + cr = 0.2126; + cb = 0.0722; + break; + } + + cg = 1.0 - cb - cr; + + if (in_yuv) { + /* YUV to RGB */ + (*matrix)[0][0] = 1.0; + (*matrix)[0][1] = 0.0; + (*matrix)[0][2] = 2.0 - 2.0 * cr; + (*matrix)[0][3] = 0.0; + (*matrix)[1][0] = 1.0; + (*matrix)[1][1] = (-cb / cg) * (2.0 - 2.0 * cb); + (*matrix)[1][2] = (-cr / cg) * (2.0 - 2.0 * cr); + (*matrix)[1][3] = 0.0; + (*matrix)[2][0] = 1.0; + (*matrix)[2][1] = 2.0 - 2.0 * cb; + (*matrix)[2][2] = 0.0; + (*matrix)[2][3] = 0.0; + } else { + /* RGB to YUV */ + (*matrix)[0][0] = cr; + (*matrix)[0][1] = cg; + (*matrix)[0][2] = cb; + (*matrix)[0][3] = 0.0; + (*matrix)[1][0] = (0.5 / (cb - 1.0)) * cr; + (*matrix)[1][1] = (0.5 / (cb - 1.0)) * cg; + (*matrix)[1][2] = 0.5; + (*matrix)[1][3] = 0.0; + (*matrix)[2][0] = 0.5; + (*matrix)[2][1] = (0.5 / (cr - 1.0)) * cg; + (*matrix)[2][2] = (0.5 / (cr - 1.0)) * cb; + (*matrix)[2][3] = 0.0; + } + + for (unsigned i = 0; i < 3; i++) { + for (unsigned j = 0; j < 3; j++) { + (*matrix)[i][j] *= j == 0 ? r_scale : g_scale; + (*matrix)[i][3] += (*matrix)[i][j] * (j == 0 ? r_bias : g_bias); + } + } + } + + /* Convert output to reduced range, chroma to [0.0,1.0]. */ + if (out_color_range == PIPE_VIDEO_VPP_CHROMA_COLOR_RANGE_REDUCED) { + r_scale = (r_max - r_min) / 1.0; + g_scale = in_yuv ? r_scale : (c_max - c_mid) / 0.5; + r_bias = r_min; + g_bias = in_yuv ? r_bias : c_mid; + } else if (out_yuv) { + r_scale = 1.0; + g_scale = (1.0 - c_mid) / 0.5; + r_bias = 0.0; + g_bias = c_mid; + } else { + r_scale = g_scale = 1.0; + r_bias = g_bias = 0.0; + } + + for (unsigned i = 0; i < 3; i++) { + for (unsigned j = 0; j < 4; j++) + (*matrix)[i][j] *= i == 0 ? r_scale : g_scale; + (*matrix)[i][3] += i == 0 ? r_bias : g_bias; + } +} diff --git a/src/gallium/auxiliary/vl/vl_csc.h b/src/gallium/auxiliary/vl/vl_csc.h index 43de802c11a..b33d5721835 100644 --- a/src/gallium/auxiliary/vl/vl_csc.h +++ b/src/gallium/auxiliary/vl/vl_csc.h @@ -29,6 +29,8 @@ #define vl_csc_h #include "util/compiler.h" +#include "util/format/u_format.h" +#include "pipe/p_video_enums.h" typedef float vl_csc_matrix[3][4]; @@ -57,4 +59,10 @@ void vl_csc_get_matrix(enum VL_CSC_COLOR_STANDARD cs, bool full_range, vl_csc_matrix *matrix); +void vl_csc_get_rgbyuv_matrix(enum pipe_video_vpp_matrix_coefficients coefficients, + enum pipe_format in_format, enum pipe_format out_format, + enum pipe_video_vpp_color_range in_color_range, + enum pipe_video_vpp_color_range out_color_range, + vl_csc_matrix *matrix); + #endif /* vl_csc_h */