aboutsummaryrefslogtreecommitdiff
path: root/src/color_conversion.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/color_conversion.c')
-rw-r--r--src/color_conversion.c89
1 files changed, 72 insertions, 17 deletions
diff --git a/src/color_conversion.c b/src/color_conversion.c
index ebf802b..2bfb9c2 100644
--- a/src/color_conversion.c
+++ b/src/color_conversion.c
@@ -20,11 +20,59 @@ static float abs_f(float v) {
" 0.0, 0.0, 0.0, 1.0);\n" \
"}\n"
-/* BT709 limited */
-#define RGB_TO_YUV "const mat4 RGBtoYUV = mat4(0.1826, -0.1006, 0.4392, 0.0,\n" \
- " 0.6142, -0.3386, -0.3989, 0.0,\n" \
- " 0.0620, 0.4392, -0.0403, 0.0,\n" \
- " 0.0625, 0.5000, 0.5000, 1.0);"
+/* https://en.wikipedia.org/wiki/YCbCr, see study/color_space_transform_matrix.png */
+
+/* ITU-R BT2020, full */
+/* https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-2-201510-I!!PDF-E.pdf */
+#define RGB_TO_P010_FULL "const mat4 RGBtoYUV = mat4(0.262700, -0.139630, 0.500000, 0.000000,\n" \
+ " 0.678000, -0.360370, -0.459786, 0.000000,\n" \
+ " 0.059300, 0.500000, -0.040214, 0.000000,\n" \
+ " 0.000000, 0.500000, 0.500000, 1.000000);"
+
+/* ITU-R BT2020, limited (full multiplied by (235-16)/255, adding 16/255 to luma) */
+#define RGB_TO_P010_LIMITED "const mat4 RGBtoYUV = mat4(0.225613, -0.119918, 0.429412, 0.000000,\n" \
+ " 0.582282, -0.309494, -0.394875, 0.000000,\n" \
+ " 0.050928, 0.429412, -0.034537, 0.000000,\n" \
+ " 0.062745, 0.500000, 0.500000, 1.000000)";
+
+/* ITU-R BT709, full */
+/* https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf */
+#define RGB_TO_NV12_FULL "const mat4 RGBtoYUV = mat4(0.212600, -0.114572, 0.500000, 0.000000,\n" \
+ " 0.715200, -0.385428, -0.454153, 0.000000,\n" \
+ " 0.072200, 0.500000, -0.045847, 0.000000,\n" \
+ " 0.000000, 0.500000, 0.500000, 1.000000);"
+
+/* ITU-R BT709, limited (full multiplied by (235-16)/255, adding 16/255 to luma) */
+#define RGB_TO_NV12_LIMITED "const mat4 RGBtoYUV = mat4(0.182586, -0.098397, 0.429412, 0.000000,\n" \
+ " 0.614231, -0.331015, -0.390037, 0.000000,\n" \
+ " 0.062007, 0.429412, -0.039375, 0.000000,\n" \
+ " 0.062745, 0.500000, 0.500000, 1.000000);"
+
+static const char* color_format_range_get_transform_matrix(gsr_destination_color color_format, gsr_color_range color_range) {
+ switch(color_format) {
+ case GSR_DESTINATION_COLOR_NV12: {
+ switch(color_range) {
+ case GSR_COLOR_RANGE_LIMITED:
+ return RGB_TO_NV12_LIMITED;
+ case GSR_COLOR_RANGE_FULL:
+ return RGB_TO_NV12_FULL;
+ }
+ break;
+ }
+ case GSR_DESTINATION_COLOR_P010: {
+ switch(color_range) {
+ case GSR_COLOR_RANGE_LIMITED:
+ return RGB_TO_P010_LIMITED;
+ case GSR_COLOR_RANGE_FULL:
+ return RGB_TO_P010_FULL;
+ }
+ break;
+ }
+ default:
+ return NULL;
+ }
+ return NULL;
+}
static int load_shader_bgr(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform) {
char vertex_shader[2048];
@@ -98,7 +146,9 @@ static int load_shader_bgr_external_texture(gsr_shader *shader, gsr_egl *egl, in
return 0;
}
-static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform) {
+static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform, gsr_destination_color color_format, gsr_color_range color_range) {
+ const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
+
char vertex_shader[2048];
snprintf(vertex_shader, sizeof(vertex_shader),
"#version 300 es \n"
@@ -113,19 +163,20 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform
" gl_Position = vec4(pos.x, pos.y, 0.0, 1.0) * rotate_z(rotation); \n"
"} \n");
- char fragment_shader[] =
+ char fragment_shader[2048];
+ snprintf(fragment_shader, sizeof(fragment_shader),
"#version 300 es \n"
"precision mediump float; \n"
"in vec2 texcoords_out; \n"
"uniform sampler2D tex1; \n"
"out vec4 FragColor; \n"
- RGB_TO_YUV
+ "%s"
"void main() \n"
"{ \n"
" vec4 pixel = texture(tex1, texcoords_out); \n"
" FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n"
" FragColor.w = pixel.a; \n"
- "} \n";
+ "} \n", color_transform_matrix);
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
return -1;
@@ -136,7 +187,9 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform
return 0;
}
-static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform) {
+static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform, gsr_destination_color color_format, gsr_color_range color_range) {
+ const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
+
char vertex_shader[2048];
snprintf(vertex_shader, sizeof(vertex_shader),
"#version 300 es \n"
@@ -151,19 +204,20 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, int *rotati
" gl_Position = vec4(pos.x, pos.y, 0.0, 1.0) * rotate_z(rotation) * vec4(0.5, 0.5, 1.0, 1.0) - vec4(0.5, 0.5, 0.0, 0.0); \n"
"} \n");
- char fragment_shader[] =
+ char fragment_shader[2048];
+ snprintf(fragment_shader, sizeof(fragment_shader),
"#version 300 es \n"
"precision mediump float; \n"
"in vec2 texcoords_out; \n"
"uniform sampler2D tex1; \n"
"out vec4 FragColor; \n"
- RGB_TO_YUV
+ "%s"
"void main() \n"
"{ \n"
" vec4 pixel = texture(tex1, texcoords_out); \n"
" FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n"
" FragColor.w = pixel.a; \n"
- "} \n";
+ "} \n", color_transform_matrix);
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
return -1;
@@ -248,18 +302,19 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver
}
break;
}
- case GSR_DESTINATION_COLOR_NV12: {
+ case GSR_DESTINATION_COLOR_NV12:
+ case GSR_DESTINATION_COLOR_P010: {
if(self->params.num_destination_textures != 2) {
- fprintf(stderr, "gsr error: gsr_color_conversion_init: expected 2 destination textures for destination color NV12, got %d destination texture(s)\n", self->params.num_destination_textures);
+ fprintf(stderr, "gsr error: gsr_color_conversion_init: expected 2 destination textures for destination color NV12/P010, got %d destination texture(s)\n", self->params.num_destination_textures);
return -1;
}
- if(load_shader_y(&self->shaders[0], self->params.egl, &self->rotation_uniforms[0]) != 0) {
+ if(load_shader_y(&self->shaders[0], self->params.egl, &self->rotation_uniforms[0], params->destination_color, params->color_range) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n");
goto err;
}
- if(load_shader_uv(&self->shaders[1], self->params.egl, &self->rotation_uniforms[1]) != 0) {
+ if(load_shader_uv(&self->shaders[1], self->params.egl, &self->rotation_uniforms[1], params->destination_color, params->color_range) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n");
goto err;
}