From 1ccbb7551d5e42252950ee5916a69d8c80bf1b1d Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Thu, 12 Aug 2010 22:49:36 -0700 Subject: [PATCH] Cleanup existing native color space conversions. This change clarifies that the existing conversions are according to the ITU 601 standard and scaled to and from full gamut RGB. Also, adjust 2 coefficients according to Charles Poynton's matrices. This does not yet attempt to make any substantial improvements. Finally, it replaces the verbose logic and redundancy in the image conversion routine with a concise function dispatch table. --- src/framework/mlt_frame.h | 27 +- src/modules/core/filter_imageconvert.c | 332 +++++++++---------------- src/modules/core/producer_colour.c | 2 +- src/modules/vmfx/filter_chroma.c | 2 +- src/modules/vmfx/filter_chroma_hold.c | 2 +- 5 files changed, 125 insertions(+), 240 deletions(-) diff --git a/src/framework/mlt_frame.h b/src/framework/mlt_frame.h index db668e97..a03bdd6c 100644 --- a/src/framework/mlt_frame.h +++ b/src/framework/mlt_frame.h @@ -131,31 +131,10 @@ extern int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t p extern const char * mlt_image_format_name( mlt_image_format format ); extern const char * mlt_audio_format_name( mlt_audio_format format ); -/** This macro scales rgb into the yuv gamut - y is scaled by 219/255 and uv by 224/255. */ -#define RGB2YUV(r, g, b, y, u, v)\ +/** This macro scales RGB into the YUV gamut - y is scaled by 219/255 and uv by 224/255. */ +#define RGB2YUV_601_SCALED(r, g, b, y, u, v)\ y = ((263*r + 516*g + 100*b) >> 10) + 16;\ - u = ((-152*r - 298*g + 450*b) >> 10) + 128;\ + u = ((-152*r - 300*g + 450*b) >> 10) + 128;\ v = ((450*r - 377*g - 73*b) >> 10) + 128; -/** This macro assumes the user has already scaled their rgb down into the broadcast limits. **/ -#define RGB2YUV_UNSCALED(r, g, b, y, u, v)\ - y = (299*r + 587*g + 114*b) >> 10;\ - u = ((-169*r - 331*g + 500*b) >> 10) + 128;\ - v = ((500*r - 419*g - 81*b) >> 10) + 128;\ - y = y < 16 ? 16 : y;\ - u = u < 16 ? 16 : u;\ - v = v < 16 ? 16 : v;\ - y = y > 235 ? 235 : y;\ - u = u > 240 ? 240 : u;\ - v = v > 240 ? 240 : v - -/** This macro converts a YUV value to the RGB color space. */ -#define YUV2RGB( y, u, v, r, g, b ) \ - r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \ - g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 400 * ( u - 128 ) ) >> 10 ); \ - b = ((1192 * ( y - 16 ) + 2066 * ( u - 128 ) ) >> 10 ); \ - r = r < 0 ? 0 : r > 255 ? 255 : r; \ - g = g < 0 ? 0 : g > 255 ? 255 : g; \ - b = b < 0 ? 0 : b > 255 ? 255 : b; - #endif diff --git a/src/modules/core/filter_imageconvert.c b/src/modules/core/filter_imageconvert.c index dc07db72..d6cb3dc1 100644 --- a/src/modules/core/filter_imageconvert.c +++ b/src/modules/core/filter_imageconvert.c @@ -25,25 +25,56 @@ #include +/** This macro converts a YUV value to the RGB color space. */ +#define RGB2YUV_601_UNSCALED(r, g, b, y, u, v)\ + y = (299*r + 587*g + 114*b) >> 10;\ + u = ((-169*r - 331*g + 500*b) >> 10) + 128;\ + v = ((500*r - 419*g - 81*b) >> 10) + 128;\ + y = y < 16 ? 16 : y;\ + u = u < 16 ? 16 : u;\ + v = v < 16 ? 16 : v;\ + y = y > 235 ? 235 : y;\ + u = u > 240 ? 240 : u;\ + v = v > 240 ? 240 : v -static int convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, unsigned int total ) +/** This macro scales YUV up into the full gamut of the RGB color space. */ +#define YUV2RGB_601_SCALED( y, u, v, r, g, b ) \ + r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \ + g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 401 * ( u - 128 ) ) >> 10 ); \ + b = ((1192 * ( y - 16 ) + 2066 * ( u - 128 ) ) >> 10 ); \ + r = r < 0 ? 0 : r > 255 ? 255 : r; \ + g = g < 0 ? 0 : g > 255 ? 255 : g; \ + b = b < 0 ? 0 : b > 255 ? 255 : b; + +/** This macro converts a YUV value to the RGB color space. */ +#define YUV2RGB_601_UNSCALED( y, u, v, r, g, b ) \ + r = ((1024 * y + 1404 * ( v - 128 ) ) >> 10 ); \ + g = ((1024 * y - 715 * ( v - 128 ) - 345 * ( u - 128 ) ) >> 10 ); \ + b = ((1024 * y + 1774 * ( u - 128 ) ) >> 10 ); \ + r = r < 0 ? 0 : r > 255 ? 255 : r; \ + g = g < 0 ? 0 : g > 255 ? 255 : g; \ + b = b < 0 ? 0 : b > 255 ? 255 : b; + + +static int convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, uint8_t *alpha, int width, int height ) { int ret = 0; int yy, uu, vv; int r,g,b; - total /= 2; - while (total--) + int total = width * height / 2 + 1; + + while ( --total ) { yy = yuv[0]; uu = yuv[1]; vv = yuv[3]; - YUV2RGB(yy, uu, vv, r, g, b); + YUV2RGB_601_SCALED (yy, uu, vv, r, g, b); rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = 255; yy = yuv[2]; - YUV2RGB(yy, uu, vv, r, g, b); + YUV2RGB_601_SCALED (yy, uu, vv, r, g, b); rgba[4] = r; rgba[5] = g; rgba[6] = b; @@ -54,23 +85,24 @@ static int convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, unsigned int t return ret; } -static int convert_yuv422_to_rgb24( uint8_t *yuv, uint8_t *rgb, unsigned int total ) +static int convert_yuv422_to_rgb24( uint8_t *yuv, uint8_t *rgb, uint8_t *alpha, int width, int height ) { int ret = 0; int yy, uu, vv; int r,g,b; - total /= 2; - while (total--) + int total = width * height / 2 + 1; + + while ( --total ) { yy = yuv[0]; uu = yuv[1]; vv = yuv[3]; - YUV2RGB(yy, uu, vv, r, g, b); + YUV2RGB_601_SCALED (yy, uu, vv, r, g, b); rgb[0] = r; rgb[1] = g; rgb[2] = b; yy = yuv[2]; - YUV2RGB(yy, uu, vv, r, g, b); + YUV2RGB_601_SCALED (yy, uu, vv, r, g, b); rgb[3] = r; rgb[4] = g; rgb[5] = b; @@ -80,30 +112,32 @@ static int convert_yuv422_to_rgb24( uint8_t *yuv, uint8_t *rgb, unsigned int tot return ret; } -static int convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha ) +static int convert_rgb24a_to_yuv422( uint8_t *rgba, uint8_t *yuv, uint8_t *alpha, int width, int height ) { int ret = 0; - register int y0, y1, u0, u1, v0, v1; - register int r, g, b; - register uint8_t *d = yuv; - register int i, j; + int stride = width * 4; + int y0, y1, u0, u1, v0, v1; + int r, g, b; + uint8_t *s, *d = yuv; + int i, j, n = width / 2 + 1; if ( alpha ) for ( i = 0; i < height; i++ ) { - register uint8_t *s = rgba + ( stride * i ); - for ( j = 0; j < ( width / 2 ); j++ ) + s = rgba + ( stride * i ); + j = n; + while ( --j ) { r = *s++; g = *s++; b = *s++; *alpha++ = *s++; - RGB2YUV (r, g, b, y0, u0 , v0); + RGB2YUV_601_SCALED (r, g, b, y0, u0 , v0); r = *s++; g = *s++; b = *s++; *alpha++ = *s++; - RGB2YUV (r, g, b, y1, u1 , v1); + RGB2YUV_601_SCALED (r, g, b, y1, u1 , v1); *d++ = y0; *d++ = (u0+u1) >> 1; *d++ = y1; @@ -115,7 +149,7 @@ static int convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int s g = *s++; b = *s++; *alpha++ = *s++; - RGB2YUV (r, g, b, y0, u0 , v0); + RGB2YUV_601_SCALED (r, g, b, y0, u0 , v0); *d++ = y0; *d++ = u0; } @@ -123,19 +157,20 @@ static int convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int s else for ( i = 0; i < height; i++ ) { - register uint8_t *s = rgba + ( stride * i ); - for ( j = 0; j < ( width / 2 ); j++ ) + s = rgba + ( stride * i ); + j = n; + while ( --j ) { r = *s++; g = *s++; b = *s++; s++; - RGB2YUV (r, g, b, y0, u0 , v0); + RGB2YUV_601_SCALED (r, g, b, y0, u0 , v0); r = *s++; g = *s++; b = *s++; s++; - RGB2YUV (r, g, b, y1, u1 , v1); + RGB2YUV_601_SCALED (r, g, b, y1, u1 , v1); *d++ = y0; *d++ = (u0+u1) >> 1; *d++ = y1; @@ -147,7 +182,7 @@ static int convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int s g = *s++; b = *s++; s++; - RGB2YUV (r, g, b, y0, u0 , v0); + RGB2YUV_601_SCALED (r, g, b, y0, u0 , v0); *d++ = y0; *d++ = u0; } @@ -156,27 +191,29 @@ static int convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int s return ret; } -static int convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv ) +static int convert_rgb24_to_yuv422( uint8_t *rgb, uint8_t *yuv, uint8_t *alpha, int width, int height ) { int ret = 0; - register int y0, y1, u0, u1, v0, v1; - register int r, g, b; - register uint8_t *d = yuv; - register int i, j; + int stride = width * 3; + int y0, y1, u0, u1, v0, v1; + int r, g, b; + uint8_t *s, *d = yuv; + int i, j, n = width / 2 + 1; for ( i = 0; i < height; i++ ) { - register uint8_t *s = rgb + ( stride * i ); - for ( j = 0; j < ( width / 2 ); j++ ) + s = rgb + ( stride * i ); + j = n; + while ( --j ) { r = *s++; g = *s++; b = *s++; - RGB2YUV (r, g, b, y0, u0 , v0); + RGB2YUV_601_SCALED (r, g, b, y0, u0 , v0); r = *s++; g = *s++; b = *s++; - RGB2YUV (r, g, b, y1, u1 , v1); + RGB2YUV_601_SCALED (r, g, b, y1, u1 , v1); *d++ = y0; *d++ = (u0+u1) >> 1; *d++ = y1; @@ -187,7 +224,7 @@ static int convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int str r = *s++; g = *s++; b = *s++; - RGB2YUV (r, g, b, y0, u0 , v0); + RGB2YUV_601_SCALED (r, g, b, y0, u0 , v0); *d++ = y0; *d++ = u0; } @@ -195,25 +232,23 @@ static int convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int str return ret; } -static int convert_yuv420p_to_yuv422( uint8_t *yuv420p, int width, int height, uint8_t *yuv ) +static int convert_yuv420p_to_yuv422( uint8_t *yuv420p, uint8_t *yuv, uint8_t *alpha, int width, int height ) { int ret = 0; - register int i, j; - + int i, j; int half = width >> 1; - uint8_t *Y = yuv420p; uint8_t *U = Y + width * height; uint8_t *V = U + width * height / 4; - - register uint8_t *d = yuv; + uint8_t *d = yuv; for ( i = 0; i < height; i++ ) { - register uint8_t *u = U + ( i / 2 ) * ( half ); - register uint8_t *v = V + ( i / 2 ) * ( half ); + uint8_t *u = U + ( i / 2 ) * ( half ); + uint8_t *v = V + ( i / 2 ) * ( half ); - for ( j = 0; j < half; j++ ) + j = half + 1; + while ( --j ) { *d ++ = *Y ++; *d ++ = *u ++; @@ -224,11 +259,13 @@ static int convert_yuv420p_to_yuv422( uint8_t *yuv420p, int width, int height, u return ret; } -static int convert_rgb24_to_rgb24a( uint8_t *rgb, uint8_t *rgba, int total ) +static int convert_rgb24_to_rgb24a( uint8_t *rgb, uint8_t *rgba, uint8_t *alpha, int width, int height ) { uint8_t *s = rgb; uint8_t *d = rgba; - while ( total-- ) + int total = width * height + 1; + + while ( --total ) { *d++ = s[0]; *d++ = s[1]; @@ -239,11 +276,13 @@ static int convert_rgb24_to_rgb24a( uint8_t *rgb, uint8_t *rgba, int total ) return 0; } -static int convert_rgb24a_to_rgb24( uint8_t *rgba, uint8_t *rgb, int total, uint8_t *alpha ) +static int convert_rgb24a_to_rgb24( uint8_t *rgba, uint8_t *rgb, uint8_t *alpha, int width, int height ) { uint8_t *s = rgba; uint8_t *d = rgb; - while ( total-- ) + int total = width * height + 1; + + while ( --total ) { *d++ = s[0]; *d++ = s[1]; @@ -254,6 +293,18 @@ static int convert_rgb24a_to_rgb24( uint8_t *rgba, uint8_t *rgb, int total, uint return 0; } +typedef int ( *conversion_function )( uint8_t *yuv, uint8_t *rgba, uint8_t *alpha, int width, int height ); + +static conversion_function conversion_matrix[5][5] = { + { NULL, convert_rgb24_to_rgb24a, convert_rgb24_to_yuv422, NULL, convert_rgb24_to_rgb24a }, + { convert_rgb24a_to_rgb24, NULL, convert_rgb24a_to_yuv422, NULL, NULL }, + { convert_yuv422_to_rgb24, convert_yuv422_to_rgb24a, NULL, NULL, convert_yuv422_to_rgb24a }, + { NULL, NULL, convert_yuv420p_to_yuv422, NULL, NULL }, + { convert_rgb24a_to_rgb24, NULL, convert_rgb24a_to_yuv422, NULL, NULL }, +}; + +static uint8_t bpp_table[4] = { 3, 4, 2, 0 }; + static int convert_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, mlt_image_format requested_format ) { int error = 0; @@ -263,180 +314,35 @@ static int convert_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *f if ( *format != requested_format ) { + conversion_function converter = conversion_matrix[ *format - 1 ][ requested_format - 1 ]; + mlt_log_debug( NULL, "[filter imageconvert] %s -> %s\n", mlt_image_format_name( *format ), mlt_image_format_name( requested_format ) ); - switch ( *format ) + if ( converter ) { - case mlt_image_yuv422: - switch ( requested_format ) - { - case mlt_image_rgb24: - { - uint8_t *rgb = mlt_pool_alloc( width * height * 3 ); - error = convert_yuv422_to_rgb24( *buffer, rgb, width * height ); - if ( error ) - { - mlt_pool_release( rgb ); - } - else - { - mlt_properties_set_data( properties, "image", rgb, width * height * 3, mlt_pool_release, NULL ); - *buffer = rgb; - *format = mlt_image_rgb24; - } - break; - } - case mlt_image_rgb24a: - case mlt_image_opengl: - { - uint8_t *rgba = mlt_pool_alloc( width * height * 4 ); - error = convert_yuv422_to_rgb24a( *buffer, rgba, width * height ); - if ( error ) - { - mlt_pool_release( rgba ); - } - else - { - mlt_properties_set_data( properties, "image", rgba, width * height * 4, mlt_pool_release, NULL ); - *buffer = rgba; - *format = requested_format; - } - break; - } - default: - error = 1; - } - break; - case mlt_image_rgb24: - switch ( requested_format ) - { - case mlt_image_yuv422: - { - uint8_t *yuv = mlt_pool_alloc( width * height * 2 ); - error = convert_rgb24_to_yuv422( *buffer, width, height, width * 3, yuv ); - if ( error ) - { - mlt_pool_release( yuv ); - } - else - { - mlt_properties_set_data( properties, "image", yuv, width * height * 2, mlt_pool_release, NULL ); - *buffer = yuv; - *format = mlt_image_yuv422; - } - break; - } - case mlt_image_rgb24a: - case mlt_image_opengl: - { - uint8_t *rgba = mlt_pool_alloc( width * height * 4 ); - error = convert_rgb24_to_rgb24a( *buffer, rgba, width * height ); - if ( error ) - { - mlt_pool_release( rgba ); - } - else - { - mlt_properties_set_data( properties, "image", rgba, width * height * 4, mlt_pool_release, NULL ); - *buffer = rgba; - *format = requested_format; - } - break; - } - default: - error = 1; - } - break; - case mlt_image_rgb24a: - case mlt_image_opengl: - switch ( requested_format ) - { - case mlt_image_yuv422: - { - uint8_t *yuv = mlt_pool_alloc( width * height * 2 ); - uint8_t *alpha = mlt_pool_alloc( width * height ); - error = convert_rgb24a_to_yuv422( *buffer, width, height, width * 4, yuv, alpha ); - if ( error ) - { - mlt_pool_release( yuv ); - mlt_pool_release( alpha ); - } - else - { - mlt_properties_set_data( properties, "image", yuv, width * height * 2, mlt_pool_release, NULL ); - mlt_properties_set_data( properties, "alpha", alpha, width * height, mlt_pool_release, NULL ); - *buffer = yuv; - *format = mlt_image_yuv422; - } - break; - } - case mlt_image_rgb24: + int size = width * height * bpp_table[ requested_format - 1 ]; + uint8_t *image = mlt_pool_alloc( size ); + uint8_t *alpha = ( *format == mlt_image_rgb24a || + *format == mlt_image_opengl ) + ? mlt_pool_alloc( width * height ) : NULL; + + if ( !( error = converter( *buffer, image, alpha, width, height ) ) ) { - uint8_t *rgb = mlt_pool_alloc( width * height * 3 ); - uint8_t *alpha = mlt_pool_alloc( width * height ); - error = convert_rgb24a_to_rgb24( *buffer, rgb, width * height, alpha ); - if ( error ) - { - mlt_pool_release( rgb ); - mlt_pool_release( alpha ); - } - else - { - mlt_properties_set_data( properties, "image", rgb, width * height * 3, mlt_pool_release, NULL ); + mlt_properties_set_data( properties, "image", image, size, mlt_pool_release, NULL ); + if ( alpha ) mlt_properties_set_data( properties, "alpha", alpha, width * height, mlt_pool_release, NULL ); - *buffer = rgb; - *format = mlt_image_rgb24; - } - break; - } - case mlt_image_rgb24a: - case mlt_image_opengl: + *buffer = image; *format = requested_format; - break; - default: - error = 1; } - break; - case mlt_image_yuv420p: - switch ( requested_format ) - { - case mlt_image_rgb24: - case mlt_image_rgb24a: - case mlt_image_opengl: - case mlt_image_yuv422: + else { - uint8_t *yuv = mlt_pool_alloc( width * height * 2 ); - int error = convert_yuv420p_to_yuv422( *buffer, width, height, yuv ); - if ( error ) - { - mlt_pool_release( yuv ); - } - else - { - mlt_properties_set_data( properties, "image", yuv, width * height * 2, mlt_pool_release, NULL ); - *buffer = yuv; - *format = mlt_image_yuv422; - } - switch ( requested_format ) - { - case mlt_image_yuv422: - break; - case mlt_image_rgb24: - case mlt_image_rgb24a: - case mlt_image_opengl: - error = convert_image( frame, buffer, format, requested_format ); - break; - default: - error = 1; - break; - } - break; - } - default: - error = 1; + mlt_pool_release( image ); + if ( alpha ) + mlt_pool_release( alpha ); } - break; - default: + } + else + { error = 1; } } diff --git a/src/modules/core/producer_colour.c b/src/modules/core/producer_colour.c index 742348c2..a43adeda 100644 --- a/src/modules/core/producer_colour.c +++ b/src/modules/core/producer_colour.c @@ -162,7 +162,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form int count = ( *width - uneven ) / 2 + 1; uint8_t y, u, v; - RGB2YUV( color.r, color.g, color.b, y, u, v ); + RGB2YUV_601_SCALED( color.r, color.g, color.b, y, u, v ); i = *height + 1; while ( --i ) { diff --git a/src/modules/vmfx/filter_chroma.c b/src/modules/vmfx/filter_chroma.c index df8c4bfb..87d23fbc 100644 --- a/src/modules/vmfx/filter_chroma.c +++ b/src/modules/vmfx/filter_chroma.c @@ -51,7 +51,7 @@ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format uint8_t b = ( key_val >> 8 ) & 0xff; uint8_t y, u, v; - RGB2YUV( r, g, b, y, u, v ); + RGB2YUV_601_SCALED( r, g, b, y, u, v ); *format = mlt_image_yuv422; if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 ) diff --git a/src/modules/vmfx/filter_chroma_hold.c b/src/modules/vmfx/filter_chroma_hold.c index d375faa5..47b5ea03 100644 --- a/src/modules/vmfx/filter_chroma_hold.c +++ b/src/modules/vmfx/filter_chroma_hold.c @@ -51,7 +51,7 @@ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format uint8_t b = ( key_val >> 8 ) & 0xff; uint8_t y, u, v; - RGB2YUV( r, g, b, y, u, v ); + RGB2YUV_601_SCALED( r, g, b, y, u, v ); *format = mlt_image_yuv422; if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 ) -- 2.39.2