2017-11-13 14:51:12 +08:00
/*
2024-01-02 05:15:26 +08:00
Copyright ( C ) 1997 - 2024 Sam Lantinga < slouken @ libsdl . org >
2017-11-13 14:51:12 +08:00
This software is provided ' as - is ' , without any express or implied
warranty . In no event will the authors be held liable for any damages
arising from the use of this software .
Permission is granted to anyone to use this software for any purpose ,
including commercial applications , and to alter it and redistribute it
freely .
*/
2022-11-27 12:43:38 +08:00
# include <SDL3/SDL.h>
2022-12-15 12:58:20 +08:00
# include <SDL3/SDL_main.h>
2023-03-17 07:25:39 +08:00
# include <SDL3/SDL_test.h>
2017-11-13 14:51:12 +08:00
# include "testyuv_cvt.h"
2023-03-17 07:25:39 +08:00
# include "testutils.h"
2017-11-13 14:51:12 +08:00
2024-03-02 07:35:25 +08:00
/* 422 (YUY2, etc) and P010 formats are the largest */
# define MAX_YUV_SURFACE_SIZE(W, H, P) ((H + 1) * ((W + 1) + P) * 4)
2017-11-13 14:51:12 +08:00
/* Return true if the YUV format is packed pixels */
2024-09-18 22:52:28 +08:00
static bool is_packed_yuv_format ( Uint32 format )
2017-11-13 14:51:12 +08:00
{
2022-11-28 00:38:43 +08:00
return format = = SDL_PIXELFORMAT_YUY2 | | format = = SDL_PIXELFORMAT_UYVY | | format = = SDL_PIXELFORMAT_YVYU ;
2017-11-13 14:51:12 +08:00
}
/* Create a surface with a good pattern for verifying YUV conversion */
static SDL_Surface * generate_test_pattern ( int pattern_size )
{
2022-12-02 00:04:02 +08:00
SDL_Surface * pattern = SDL_CreateSurface ( pattern_size , pattern_size , SDL_PIXELFORMAT_RGB24 ) ;
2017-11-13 14:51:12 +08:00
if ( pattern ) {
int i , x , y ;
Uint8 * p , c ;
2022-12-01 04:51:59 +08:00
const int thickness = 2 ; /* Important so 2x2 blocks of color are the same, to avoid Cr/Cb interpolation over pixels */
2017-11-13 14:51:12 +08:00
/* R, G, B in alternating horizontal bands */
2023-03-17 09:24:15 +08:00
for ( y = 0 ; y < pattern - > h - ( thickness - 1 ) ; y + = thickness ) {
2017-11-13 14:51:12 +08:00
for ( i = 0 ; i < thickness ; + + i ) {
2022-12-01 04:51:59 +08:00
p = ( Uint8 * ) pattern - > pixels + ( y + i ) * pattern - > pitch + ( ( y / thickness ) % 3 ) ;
2017-11-13 14:51:12 +08:00
for ( x = 0 ; x < pattern - > w ; + + x ) {
* p = 0xFF ;
p + = 3 ;
}
}
}
/* Black and white in alternating vertical bands */
c = 0xFF ;
2022-12-01 04:51:59 +08:00
for ( x = 1 * thickness ; x < pattern - > w ; x + = 2 * thickness ) {
2017-11-13 14:51:12 +08:00
for ( i = 0 ; i < thickness ; + + i ) {
2022-12-01 04:51:59 +08:00
p = ( Uint8 * ) pattern - > pixels + ( x + i ) * 3 ;
2017-11-13 14:51:12 +08:00
for ( y = 0 ; y < pattern - > h ; + + y ) {
SDL_memset ( p , c , 3 ) ;
p + = pattern - > pitch ;
}
}
if ( c ) {
c = 0x00 ;
} else {
c = 0xFF ;
}
}
}
return pattern ;
}
2024-09-18 22:52:28 +08:00
static bool verify_yuv_data ( Uint32 format , SDL_Colorspace colorspace , const Uint8 * yuv , int yuv_pitch , SDL_Surface * surface , int tolerance )
2017-11-13 14:51:12 +08:00
{
const int size = ( surface - > h * surface - > pitch ) ;
Uint8 * rgb ;
2024-09-18 22:52:28 +08:00
bool result = false ;
2017-11-13 14:51:12 +08:00
rgb = ( Uint8 * ) SDL_malloc ( size ) ;
2023-11-10 05:29:15 +08:00
if ( ! rgb ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Out of memory " ) ;
2024-09-18 22:52:28 +08:00
return false ;
2017-11-13 14:51:12 +08:00
}
2024-08-23 08:33:49 +08:00
if ( SDL_ConvertPixelsAndColorspace ( surface - > w , surface - > h , format , colorspace , 0 , yuv , yuv_pitch , surface - > format , SDL_COLORSPACE_SRGB , 0 , rgb , surface - > pitch ) ) {
2017-11-13 14:51:12 +08:00
int x , y ;
2024-09-18 22:52:28 +08:00
result = true ;
2017-11-13 14:51:12 +08:00
for ( y = 0 ; y < surface - > h ; + + y ) {
const Uint8 * actual = rgb + y * surface - > pitch ;
const Uint8 * expected = ( const Uint8 * ) surface - > pixels + y * surface - > pitch ;
for ( x = 0 ; x < surface - > w ; + + x ) {
int deltaR = ( int ) actual [ 0 ] - expected [ 0 ] ;
int deltaG = ( int ) actual [ 1 ] - expected [ 1 ] ;
int deltaB = ( int ) actual [ 2 ] - expected [ 2 ] ;
int distance = ( deltaR * deltaR + deltaG * deltaG + deltaB * deltaB ) ;
if ( distance > tolerance ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Pixel at %d,%d was 0x%.2x,0x%.2x,0x%.2x, expected 0x%.2x,0x%.2x,0x%.2x, distance = %d \n " , x , y , actual [ 0 ] , actual [ 1 ] , actual [ 2 ] , expected [ 0 ] , expected [ 1 ] , expected [ 2 ] , distance ) ;
2024-09-18 22:52:28 +08:00
result = false ;
2017-11-13 14:51:12 +08:00
}
actual + = 3 ;
expected + = 3 ;
}
}
} else {
2024-07-09 05:59:18 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( format ) , SDL_GetPixelFormatName ( surface - > format ) , SDL_GetError ( ) ) ;
2017-11-13 14:51:12 +08:00
}
SDL_free ( rgb ) ;
return result ;
}
2024-10-15 06:19:59 +08:00
static bool run_automated_tests ( int pattern_size , int extra_pitch )
2017-11-13 14:51:12 +08:00
{
const Uint32 formats [ ] = {
SDL_PIXELFORMAT_YV12 ,
SDL_PIXELFORMAT_IYUV ,
SDL_PIXELFORMAT_NV12 ,
SDL_PIXELFORMAT_NV21 ,
SDL_PIXELFORMAT_YUY2 ,
SDL_PIXELFORMAT_UYVY ,
SDL_PIXELFORMAT_YVYU
} ;
int i , j ;
SDL_Surface * pattern = generate_test_pattern ( pattern_size ) ;
const int yuv_len = MAX_YUV_SURFACE_SIZE ( pattern - > w , pattern - > h , extra_pitch ) ;
Uint8 * yuv1 = ( Uint8 * ) SDL_malloc ( yuv_len ) ;
Uint8 * yuv2 = ( Uint8 * ) SDL_malloc ( yuv_len ) ;
int yuv1_pitch , yuv2_pitch ;
2024-02-03 23:05:32 +08:00
YUV_CONVERSION_MODE mode ;
SDL_Colorspace colorspace ;
2024-03-02 07:35:25 +08:00
const int tight_tolerance = 20 ;
const int loose_tolerance = 333 ;
2024-10-15 06:19:59 +08:00
bool result = false ;
2022-12-01 04:51:59 +08:00
2023-11-10 05:29:15 +08:00
if ( ! pattern | | ! yuv1 | | ! yuv2 ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't allocate test surfaces " ) ;
goto done ;
}
2024-02-03 23:05:32 +08:00
mode = GetYUVConversionModeForResolution ( pattern - > w , pattern - > h ) ;
2024-03-01 18:02:53 +08:00
colorspace = GetColorspaceForYUVConversionMode ( mode ) ;
2024-02-03 23:05:32 +08:00
2017-11-13 14:51:12 +08:00
/* Verify conversion from YUV formats */
for ( i = 0 ; i < SDL_arraysize ( formats ) ; + + i ) {
2024-02-03 23:05:32 +08:00
if ( ! ConvertRGBtoYUV ( formats [ i ] , pattern - > pixels , pattern - > pitch , yuv1 , pattern - > w , pattern - > h , mode , 0 , 100 ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " ConvertRGBtoYUV() doesn't support converting to %s \n " , SDL_GetPixelFormatName ( formats [ i ] ) ) ;
goto done ;
}
yuv1_pitch = CalculateYUVPitch ( formats [ i ] , pattern - > w ) ;
2024-03-03 07:05:52 +08:00
if ( ! verify_yuv_data ( formats [ i ] , colorspace , yuv1 , yuv1_pitch , pattern , tight_tolerance ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed conversion from %s to RGB \n " , SDL_GetPixelFormatName ( formats [ i ] ) ) ;
goto done ;
}
}
/* Verify conversion to YUV formats */
for ( i = 0 ; i < SDL_arraysize ( formats ) ; + + i ) {
yuv1_pitch = CalculateYUVPitch ( formats [ i ] , pattern - > w ) + extra_pitch ;
2024-08-23 08:33:49 +08:00
if ( ! SDL_ConvertPixelsAndColorspace ( pattern - > w , pattern - > h , pattern - > format , SDL_COLORSPACE_SRGB , 0 , pattern - > pixels , pattern - > pitch , formats [ i ] , colorspace , 0 , yuv1 , yuv1_pitch ) ) {
2024-07-09 05:59:18 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( pattern - > format ) , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetError ( ) ) ;
2017-11-13 14:51:12 +08:00
goto done ;
}
2024-03-03 07:05:52 +08:00
if ( ! verify_yuv_data ( formats [ i ] , colorspace , yuv1 , yuv1_pitch , pattern , tight_tolerance ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed conversion from RGB to %s \n " , SDL_GetPixelFormatName ( formats [ i ] ) ) ;
goto done ;
}
}
/* Verify conversion between YUV formats */
for ( i = 0 ; i < SDL_arraysize ( formats ) ; + + i ) {
for ( j = 0 ; j < SDL_arraysize ( formats ) ; + + j ) {
yuv1_pitch = CalculateYUVPitch ( formats [ i ] , pattern - > w ) + extra_pitch ;
yuv2_pitch = CalculateYUVPitch ( formats [ j ] , pattern - > w ) + extra_pitch ;
2024-08-23 08:33:49 +08:00
if ( ! SDL_ConvertPixelsAndColorspace ( pattern - > w , pattern - > h , pattern - > format , SDL_COLORSPACE_SRGB , 0 , pattern - > pixels , pattern - > pitch , formats [ i ] , colorspace , 0 , yuv1 , yuv1_pitch ) ) {
2024-07-09 05:59:18 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( pattern - > format ) , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetError ( ) ) ;
2017-11-13 14:51:12 +08:00
goto done ;
}
2024-08-23 08:33:49 +08:00
if ( ! SDL_ConvertPixelsAndColorspace ( pattern - > w , pattern - > h , formats [ i ] , colorspace , 0 , yuv1 , yuv1_pitch , formats [ j ] , colorspace , 0 , yuv2 , yuv2_pitch ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetPixelFormatName ( formats [ j ] ) , SDL_GetError ( ) ) ;
goto done ;
}
2024-03-03 07:05:52 +08:00
if ( ! verify_yuv_data ( formats [ j ] , colorspace , yuv2 , yuv2_pitch , pattern , tight_tolerance ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed conversion from %s to %s \n " , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetPixelFormatName ( formats [ j ] ) ) ;
goto done ;
}
}
}
/* Verify conversion between YUV formats in-place */
for ( i = 0 ; i < SDL_arraysize ( formats ) ; + + i ) {
for ( j = 0 ; j < SDL_arraysize ( formats ) ; + + j ) {
if ( is_packed_yuv_format ( formats [ i ] ) ! = is_packed_yuv_format ( formats [ j ] ) ) {
/* Can't change plane vs packed pixel layout in-place */
continue ;
}
yuv1_pitch = CalculateYUVPitch ( formats [ i ] , pattern - > w ) + extra_pitch ;
yuv2_pitch = CalculateYUVPitch ( formats [ j ] , pattern - > w ) + extra_pitch ;
2024-08-23 08:33:49 +08:00
if ( ! SDL_ConvertPixelsAndColorspace ( pattern - > w , pattern - > h , pattern - > format , SDL_COLORSPACE_SRGB , 0 , pattern - > pixels , pattern - > pitch , formats [ i ] , colorspace , 0 , yuv1 , yuv1_pitch ) ) {
2024-07-09 05:59:18 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( pattern - > format ) , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetError ( ) ) ;
2017-11-13 14:51:12 +08:00
goto done ;
}
2024-08-23 08:33:49 +08:00
if ( ! SDL_ConvertPixelsAndColorspace ( pattern - > w , pattern - > h , formats [ i ] , colorspace , 0 , yuv1 , yuv1_pitch , formats [ j ] , colorspace , 0 , yuv1 , yuv2_pitch ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetPixelFormatName ( formats [ j ] ) , SDL_GetError ( ) ) ;
goto done ;
}
2024-03-03 07:05:52 +08:00
if ( ! verify_yuv_data ( formats [ j ] , colorspace , yuv1 , yuv2_pitch , pattern , tight_tolerance ) ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed conversion from %s to %s \n " , SDL_GetPixelFormatName ( formats [ i ] ) , SDL_GetPixelFormatName ( formats [ j ] ) ) ;
goto done ;
}
}
}
2024-03-02 07:35:25 +08:00
/* Verify round trip through BT.2020 */
colorspace = SDL_COLORSPACE_BT2020_FULL ;
if ( ! ConvertRGBtoYUV ( SDL_PIXELFORMAT_P010 , pattern - > pixels , pattern - > pitch , yuv1 , pattern - > w , pattern - > h , YUV_CONVERSION_BT2020 , 0 , 100 ) ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " ConvertRGBtoYUV() doesn't support converting to %s \n " , SDL_GetPixelFormatName ( SDL_PIXELFORMAT_P010 ) ) ;
goto done ;
}
yuv1_pitch = CalculateYUVPitch ( SDL_PIXELFORMAT_P010 , pattern - > w ) ;
2024-03-03 07:05:52 +08:00
if ( ! verify_yuv_data ( SDL_PIXELFORMAT_P010 , colorspace , yuv1 , yuv1_pitch , pattern , tight_tolerance ) ) {
2024-03-02 07:35:25 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed conversion from %s to RGB \n " , SDL_GetPixelFormatName ( SDL_PIXELFORMAT_P010 ) ) ;
goto done ;
}
/* The pitch needs to be Uint16 aligned for P010 pixels */
yuv1_pitch = CalculateYUVPitch ( SDL_PIXELFORMAT_P010 , pattern - > w ) + ( ( extra_pitch + 1 ) & ~ 1 ) ;
2024-08-23 08:33:49 +08:00
if ( ! SDL_ConvertPixelsAndColorspace ( pattern - > w , pattern - > h , pattern - > format , SDL_COLORSPACE_SRGB , 0 , pattern - > pixels , pattern - > pitch , SDL_PIXELFORMAT_P010 , colorspace , 0 , yuv1 , yuv1_pitch ) ) {
2024-07-09 05:59:18 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't convert %s to %s: %s \n " , SDL_GetPixelFormatName ( pattern - > format ) , SDL_GetPixelFormatName ( SDL_PIXELFORMAT_P010 ) , SDL_GetError ( ) ) ;
2024-03-02 07:35:25 +08:00
goto done ;
}
/* Going through XRGB2101010 format during P010 conversion is slightly lossy, so use looser tolerance here */
2024-03-03 07:05:52 +08:00
if ( ! verify_yuv_data ( SDL_PIXELFORMAT_P010 , colorspace , yuv1 , yuv1_pitch , pattern , loose_tolerance ) ) {
2024-03-02 07:35:25 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed conversion from RGB to %s \n " , SDL_GetPixelFormatName ( SDL_PIXELFORMAT_P010 ) ) ;
goto done ;
}
2024-10-15 06:19:59 +08:00
result = true ;
2017-11-13 14:51:12 +08:00
done :
SDL_free ( yuv1 ) ;
SDL_free ( yuv2 ) ;
2022-12-27 22:36:39 +08:00
SDL_DestroySurface ( pattern ) ;
2017-11-13 14:51:12 +08:00
return result ;
}
2024-10-17 00:57:47 +08:00
static bool run_colorspace_test ( void )
2024-10-15 06:19:59 +08:00
{
bool result = false ;
SDL_Window * window ;
SDL_Renderer * renderer ;
struct {
const char * name ;
SDL_Colorspace colorspace ;
} colorspaces [ ] = {
# define COLORSPACE(X) { #X, X }
COLORSPACE ( SDL_COLORSPACE_JPEG ) ,
#if 0 /* We don't support converting color primaries here */
COLORSPACE ( SDL_COLORSPACE_BT601_LIMITED ) ,
COLORSPACE ( SDL_COLORSPACE_BT601_FULL ) ,
# endif
COLORSPACE ( SDL_COLORSPACE_BT709_LIMITED ) ,
COLORSPACE ( SDL_COLORSPACE_BT709_FULL )
# undef COLORSPACE
} ;
SDL_Surface * rgb = NULL ;
SDL_Surface * yuv = NULL ;
SDL_Texture * texture = NULL ;
int allowed_error = 2 ;
int i ;
if ( ! SDL_CreateWindowAndRenderer ( " testyuv " , 320 , 240 , 0 , & window , & renderer ) ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create window and renderer: %s \n " , SDL_GetError ( ) ) ;
goto done ;
}
rgb = SDL_CreateSurface ( 32 , 32 , SDL_PIXELFORMAT_XRGB8888 ) ;
if ( ! rgb ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create RGB surface: %s \n " , SDL_GetError ( ) ) ;
goto done ;
}
SDL_FillSurfaceRect ( rgb , NULL , SDL_MapSurfaceRGB ( rgb , 255 , 0 , 0 ) ) ;
for ( i = 0 ; i < SDL_arraysize ( colorspaces ) ; + + i ) {
bool next = false ;
Uint8 r , g , b , a ;
SDL_Log ( " Checking colorspace %s \n " , colorspaces [ i ] . name ) ;
yuv = SDL_ConvertSurfaceAndColorspace ( rgb , SDL_PIXELFORMAT_NV12 , NULL , colorspaces [ i ] . colorspace , 0 ) ;
if ( ! yuv ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create YUV surface: %s \n " , SDL_GetError ( ) ) ;
goto done ;
}
if ( ! SDL_ReadSurfacePixel ( yuv , 0 , 0 , & r , & g , & b , & a ) ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't read YUV surface: %s \n " , SDL_GetError ( ) ) ;
goto done ;
}
if ( SDL_abs ( ( int ) r - 255 ) > allowed_error | |
SDL_abs ( ( int ) g - 0 ) > allowed_error | |
SDL_abs ( ( int ) b - 0 ) > allowed_error ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed color conversion, expected 255,0,0, got %d,%d,%d \n " , r , g , b ) ;
}
texture = SDL_CreateTextureFromSurface ( renderer , yuv ) ;
if ( ! texture ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create YUV texture: %s \n " , SDL_GetError ( ) ) ;
goto done ;
}
SDL_DestroySurface ( yuv ) ;
yuv = NULL ;
SDL_RenderTexture ( renderer , texture , NULL , NULL ) ;
yuv = SDL_RenderReadPixels ( renderer , NULL ) ;
if ( ! SDL_ReadSurfacePixel ( yuv , 0 , 0 , & r , & g , & b , & a ) ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't read YUV surface: %s \n " , SDL_GetError ( ) ) ;
goto done ;
}
if ( SDL_abs ( ( int ) r - 255 ) > allowed_error | |
SDL_abs ( ( int ) g - 0 ) > allowed_error | |
SDL_abs ( ( int ) b - 0 ) > allowed_error ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Failed renderer color conversion, expected 255,0,0, got %d,%d,%d \n " , r , g , b ) ;
}
while ( ! next ) {
SDL_Event event ;
while ( SDL_PollEvent ( & event ) ) {
switch ( event . type ) {
case SDL_EVENT_KEY_DOWN :
if ( event . key . key = = SDLK_ESCAPE ) {
result = true ;
goto done ;
}
if ( event . key . key = = SDLK_SPACE ) {
next = true ;
}
break ;
case SDL_EVENT_QUIT :
result = true ;
goto done ;
}
}
SDL_RenderTexture ( renderer , texture , NULL , NULL ) ;
SDL_SetRenderDrawColor ( renderer , 255 , 255 , 255 , 255 ) ;
SDL_RenderDebugText ( renderer , 4 , 4 , colorspaces [ i ] . name ) ;
SDL_RenderPresent ( renderer ) ;
SDL_Delay ( 10 ) ;
}
SDL_DestroyTexture ( texture ) ;
texture = NULL ;
}
result = true ;
done :
SDL_DestroySurface ( rgb ) ;
SDL_DestroySurface ( yuv ) ;
SDL_DestroyTexture ( texture ) ;
SDL_Quit ( ) ;
return result ;
}
2022-12-01 04:51:59 +08:00
int main ( int argc , char * * argv )
2017-11-13 14:51:12 +08:00
{
2022-12-01 04:51:59 +08:00
struct
{
2024-09-18 22:52:28 +08:00
bool enable_intrinsics ;
2017-11-13 14:51:12 +08:00
int pattern_size ;
int extra_pitch ;
} automated_test_params [ ] = {
2024-03-02 07:35:25 +08:00
/* Test: single pixel */
2024-09-18 22:52:28 +08:00
{ false , 1 , 0 } ,
2017-11-13 14:51:12 +08:00
/* Test: even width and height */
2024-09-18 22:52:28 +08:00
{ false , 2 , 0 } ,
{ false , 4 , 0 } ,
2017-11-13 14:51:12 +08:00
/* Test: odd width and height */
2024-09-18 22:52:28 +08:00
{ false , 1 , 0 } ,
{ false , 3 , 0 } ,
2017-11-13 14:51:12 +08:00
/* Test: even width and height, extra pitch */
2024-09-18 22:52:28 +08:00
{ false , 2 , 3 } ,
{ false , 4 , 3 } ,
2017-11-13 14:51:12 +08:00
/* Test: odd width and height, extra pitch */
2024-09-18 22:52:28 +08:00
{ false , 1 , 3 } ,
{ false , 3 , 3 } ,
2017-11-13 14:51:12 +08:00
/* Test: even width and height with intrinsics */
2024-09-18 22:52:28 +08:00
{ true , 32 , 0 } ,
2017-11-13 14:51:12 +08:00
/* Test: odd width and height with intrinsics */
2024-09-18 22:52:28 +08:00
{ true , 33 , 0 } ,
{ true , 37 , 0 } ,
2017-11-13 14:51:12 +08:00
/* Test: even width and height with intrinsics, extra pitch */
2024-09-18 22:52:28 +08:00
{ true , 32 , 3 } ,
2017-11-13 14:51:12 +08:00
/* Test: odd width and height with intrinsics, extra pitch */
2024-09-18 22:52:28 +08:00
{ true , 33 , 3 } ,
{ true , 37 , 3 } ,
2017-11-13 14:51:12 +08:00
} ;
2023-03-17 07:25:39 +08:00
char * filename = NULL ;
2017-11-13 14:51:12 +08:00
SDL_Surface * original ;
SDL_Surface * converted ;
2023-08-23 00:44:28 +08:00
SDL_Surface * bmp ;
2017-11-13 14:51:12 +08:00
SDL_Window * window ;
SDL_Renderer * renderer ;
SDL_Texture * output [ 3 ] ;
const char * titles [ 3 ] = { " ORIGINAL " , " SOFTWARE " , " HARDWARE " } ;
char title [ 128 ] ;
2024-02-29 02:03:18 +08:00
YUV_CONVERSION_MODE yuv_mode ;
const char * yuv_mode_name ;
2017-11-13 14:51:12 +08:00
Uint32 yuv_format = SDL_PIXELFORMAT_YV12 ;
2024-02-29 02:03:18 +08:00
const char * yuv_format_name ;
2024-03-02 07:35:25 +08:00
SDL_Colorspace yuv_colorspace ;
2024-02-29 02:03:18 +08:00
Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888 ;
2024-03-02 07:35:25 +08:00
SDL_Colorspace rgb_colorspace = SDL_COLORSPACE_SRGB ;
2024-02-29 02:03:18 +08:00
SDL_PropertiesID props ;
2024-09-18 22:52:28 +08:00
bool monochrome = false ;
2024-03-01 18:42:18 +08:00
int luminance = 100 ;
2017-11-13 14:51:12 +08:00
int current = 0 ;
int pitch ;
Uint8 * raw_yuv ;
2022-12-02 17:17:17 +08:00
Uint64 then , now ;
2023-03-17 07:25:39 +08:00
int i , iterations = 100 ;
2024-09-18 22:52:28 +08:00
bool should_run_automated_tests = false ;
2024-10-15 06:19:59 +08:00
bool should_run_colorspace_test = false ;
2023-03-17 07:25:39 +08:00
SDLTest_CommonState * state ;
2017-11-13 14:51:12 +08:00
2023-03-17 07:25:39 +08:00
/* Initialize test framework */
state = SDLTest_CommonCreateState ( argv , 0 ) ;
2023-11-10 05:29:15 +08:00
if ( ! state ) {
2023-03-17 07:25:39 +08:00
return 1 ;
}
/* Parse commandline */
for ( i = 1 ; i < argc ; ) {
int consumed ;
consumed = SDLTest_CommonArg ( state , i ) ;
if ( ! consumed ) {
if ( SDL_strcmp ( argv [ i ] , " --jpeg " ) = = 0 ) {
2024-02-03 23:05:32 +08:00
SetYUVConversionMode ( YUV_CONVERSION_JPEG ) ;
2023-03-17 07:25:39 +08:00
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --bt601 " ) = = 0 ) {
2024-02-03 23:05:32 +08:00
SetYUVConversionMode ( YUV_CONVERSION_BT601 ) ;
2023-03-17 07:25:39 +08:00
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --bt709 " ) = = 0 ) {
2024-02-03 23:05:32 +08:00
SetYUVConversionMode ( YUV_CONVERSION_BT709 ) ;
2023-03-17 07:25:39 +08:00
consumed = 1 ;
2024-03-02 07:35:25 +08:00
} else if ( SDL_strcmp ( argv [ i ] , " --bt2020 " ) = = 0 ) {
SetYUVConversionMode ( YUV_CONVERSION_BT2020 ) ;
consumed = 1 ;
2023-03-17 07:25:39 +08:00
} else if ( SDL_strcmp ( argv [ i ] , " --auto " ) = = 0 ) {
2024-02-03 23:05:32 +08:00
SetYUVConversionMode ( YUV_CONVERSION_AUTOMATIC ) ;
2023-03-17 07:25:39 +08:00
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --yv12 " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_YV12 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --iyuv " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_IYUV ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --yuy2 " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_YUY2 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --uyvy " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_UYVY ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --yvyu " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_YVYU ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --nv12 " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_NV12 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --nv21 " ) = = 0 ) {
yuv_format = SDL_PIXELFORMAT_NV21 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --rgb555 " ) = = 0 ) {
2024-05-27 21:51:39 +08:00
rgb_format = SDL_PIXELFORMAT_XRGB1555 ;
2023-03-17 07:25:39 +08:00
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --rgb565 " ) = = 0 ) {
rgb_format = SDL_PIXELFORMAT_RGB565 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --rgb24 " ) = = 0 ) {
rgb_format = SDL_PIXELFORMAT_RGB24 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --argb " ) = = 0 ) {
rgb_format = SDL_PIXELFORMAT_ARGB8888 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --abgr " ) = = 0 ) {
rgb_format = SDL_PIXELFORMAT_ABGR8888 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --rgba " ) = = 0 ) {
rgb_format = SDL_PIXELFORMAT_RGBA8888 ;
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --bgra " ) = = 0 ) {
rgb_format = SDL_PIXELFORMAT_BGRA8888 ;
consumed = 1 ;
2024-03-01 18:42:18 +08:00
} else if ( SDL_strcmp ( argv [ i ] , " --monochrome " ) = = 0 ) {
2024-09-18 22:52:28 +08:00
monochrome = true ;
2024-03-01 18:42:18 +08:00
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --luminance " ) = = 0 & & argv [ i + 1 ] ) {
luminance = SDL_atoi ( argv [ i + 1 ] ) ;
consumed = 2 ;
2023-03-17 07:25:39 +08:00
} else if ( SDL_strcmp ( argv [ i ] , " --automated " ) = = 0 ) {
2024-09-18 22:52:28 +08:00
should_run_automated_tests = true ;
2023-03-17 07:25:39 +08:00
consumed = 1 ;
2024-10-15 06:19:59 +08:00
} else if ( SDL_strcmp ( argv [ i ] , " --colorspace-test " ) = = 0 ) {
should_run_colorspace_test = true ;
consumed = 1 ;
2023-03-17 07:25:39 +08:00
} else if ( ! filename ) {
filename = argv [ i ] ;
consumed = 1 ;
}
}
if ( consumed < = 0 ) {
static const char * options [ ] = {
2024-03-02 07:35:25 +08:00
" [--jpeg|--bt601|--bt709|--bt2020|--auto] " ,
2023-03-17 07:25:39 +08:00
" [--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21] " ,
" [--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra] " ,
2024-03-01 18:42:18 +08:00
" [--monochrome] [--luminance N%] " ,
2024-10-15 06:19:59 +08:00
" [--automated] [--colorspace-test] " ,
2023-03-17 07:25:39 +08:00
" [sample.bmp] " ,
NULL ,
} ;
SDLTest_CommonLogUsage ( state , argv [ 0 ] , options ) ;
2024-07-28 22:46:44 +08:00
SDL_Quit ( ) ;
2023-03-17 07:25:39 +08:00
SDLTest_CommonDestroyState ( state ) ;
2017-11-13 14:51:12 +08:00
return 1 ;
}
2023-03-17 07:25:39 +08:00
i + = consumed ;
2017-11-13 14:51:12 +08:00
}
/* Run automated tests */
if ( should_run_automated_tests ) {
2023-03-17 07:25:39 +08:00
for ( i = 0 ; i < ( int ) SDL_arraysize ( automated_test_params ) ; + + i ) {
2022-12-01 04:51:59 +08:00
SDL_LogInfo ( SDL_LOG_CATEGORY_APPLICATION , " Running automated test, pattern size %d, extra pitch %d, intrinsics %s \n " ,
automated_test_params [ i ] . pattern_size ,
automated_test_params [ i ] . extra_pitch ,
automated_test_params [ i ] . enable_intrinsics ? " enabled " : " disabled " ) ;
2024-10-15 06:19:59 +08:00
if ( ! run_automated_tests ( automated_test_params [ i ] . pattern_size , automated_test_params [ i ] . extra_pitch ) ) {
2017-11-13 14:51:12 +08:00
return 2 ;
}
}
return 0 ;
}
2024-10-15 06:19:59 +08:00
if ( should_run_colorspace_test ) {
if ( ! run_colorspace_test ( ) ) {
return 2 ;
}
return 0 ;
}
2023-03-17 07:25:39 +08:00
filename = GetResourceFilename ( filename , " testyuv.bmp " ) ;
2023-08-23 00:44:28 +08:00
bmp = SDL_LoadBMP ( filename ) ;
2024-07-09 05:59:18 +08:00
original = SDL_ConvertSurface ( bmp , SDL_PIXELFORMAT_RGB24 ) ;
2023-08-23 00:44:28 +08:00
SDL_DestroySurface ( bmp ) ;
2023-11-10 05:29:15 +08:00
if ( ! original ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't load %s: %s \n " , filename , SDL_GetError ( ) ) ;
return 3 ;
}
2024-02-29 02:03:18 +08:00
yuv_mode = GetYUVConversionModeForResolution ( original - > w , original - > h ) ;
switch ( yuv_mode ) {
case YUV_CONVERSION_JPEG :
yuv_mode_name = " JPEG " ;
break ;
case YUV_CONVERSION_BT601 :
yuv_mode_name = " BT.601 " ;
break ;
case YUV_CONVERSION_BT709 :
yuv_mode_name = " BT.709 " ;
break ;
2024-03-02 07:35:25 +08:00
case YUV_CONVERSION_BT2020 :
yuv_mode_name = " BT.2020 " ;
yuv_format = SDL_PIXELFORMAT_P010 ;
rgb_format = SDL_PIXELFORMAT_XBGR2101010 ;
rgb_colorspace = SDL_COLORSPACE_HDR10 ;
break ;
2024-02-29 02:03:18 +08:00
default :
yuv_mode_name = " UNKNOWN " ;
break ;
}
2024-03-02 07:35:25 +08:00
yuv_colorspace = GetColorspaceForYUVConversionMode ( yuv_mode ) ;
2024-02-29 02:03:18 +08:00
2017-11-13 14:51:12 +08:00
raw_yuv = SDL_calloc ( 1 , MAX_YUV_SURFACE_SIZE ( original - > w , original - > h , 0 ) ) ;
2024-03-01 18:42:18 +08:00
ConvertRGBtoYUV ( yuv_format , original - > pixels , original - > pitch , raw_yuv , original - > w , original - > h , yuv_mode , monochrome , luminance ) ;
2017-11-13 14:51:12 +08:00
pitch = CalculateYUVPitch ( yuv_format , original - > w ) ;
2022-12-02 00:04:02 +08:00
converted = SDL_CreateSurface ( original - > w , original - > h , rgb_format ) ;
2023-11-10 05:29:15 +08:00
if ( ! converted ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create converted surface: %s \n " , SDL_GetError ( ) ) ;
return 3 ;
}
then = SDL_GetTicks ( ) ;
2022-12-01 04:51:59 +08:00
for ( i = 0 ; i < iterations ; + + i ) {
2024-03-03 07:05:52 +08:00
SDL_ConvertPixelsAndColorspace ( original - > w , original - > h , yuv_format , yuv_colorspace , 0 , raw_yuv , pitch , rgb_format , rgb_colorspace , 0 , converted - > pixels , converted - > pitch ) ;
2017-11-13 14:51:12 +08:00
}
now = SDL_GetTicks ( ) ;
2023-03-17 07:25:39 +08:00
SDL_LogInfo ( SDL_LOG_CATEGORY_APPLICATION , " %d iterations in % " SDL_PRIu64 " ms, %.2fms each \n " , iterations , ( now - then ) , ( float ) ( now - then ) / iterations ) ;
2017-11-13 14:51:12 +08:00
2023-03-06 06:44:38 +08:00
window = SDL_CreateWindow ( " YUV test " , original - > w , original - > h , 0 ) ;
2023-11-10 05:29:15 +08:00
if ( ! window ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create window: %s \n " , SDL_GetError ( ) ) ;
return 4 ;
}
2024-05-14 01:31:55 +08:00
renderer = SDL_CreateRenderer ( window , NULL ) ;
2023-11-10 05:29:15 +08:00
if ( ! renderer ) {
2017-11-13 14:51:12 +08:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create renderer: %s \n " , SDL_GetError ( ) ) ;
return 4 ;
}
output [ 0 ] = SDL_CreateTextureFromSurface ( renderer , original ) ;
output [ 1 ] = SDL_CreateTextureFromSurface ( renderer , converted ) ;
2024-02-29 02:03:18 +08:00
props = SDL_CreateProperties ( ) ;
2024-03-02 07:35:25 +08:00
SDL_SetNumberProperty ( props , SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER , yuv_colorspace ) ;
2024-02-29 02:03:18 +08:00
SDL_SetNumberProperty ( props , SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER , yuv_format ) ;
SDL_SetNumberProperty ( props , SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER , SDL_TEXTUREACCESS_STREAMING ) ;
SDL_SetNumberProperty ( props , SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER , original - > w ) ;
SDL_SetNumberProperty ( props , SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER , original - > h ) ;
output [ 2 ] = SDL_CreateTextureWithProperties ( renderer , props ) ;
SDL_DestroyProperties ( props ) ;
2017-11-13 14:51:12 +08:00
if ( ! output [ 0 ] | | ! output [ 1 ] | | ! output [ 2 ] ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't set create texture: %s \n " , SDL_GetError ( ) ) ;
return 5 ;
}
SDL_UpdateTexture ( output [ 2 ] , NULL , raw_yuv , pitch ) ;
2022-12-01 04:51:59 +08:00
2024-02-29 02:03:18 +08:00
yuv_format_name = SDL_GetPixelFormatName ( yuv_format ) ;
if ( SDL_strncmp ( yuv_format_name , " SDL_PIXELFORMAT_ " , 16 ) = = 0 ) {
yuv_format_name + = 16 ;
2017-11-13 14:51:12 +08:00
}
2022-12-01 04:51:59 +08:00
{
int done = 0 ;
while ( ! done ) {
2017-11-13 14:51:12 +08:00
SDL_Event event ;
while ( SDL_PollEvent ( & event ) > 0 ) {
2023-01-24 09:54:09 +08:00
if ( event . type = = SDL_EVENT_QUIT ) {
2017-11-13 14:51:12 +08:00
done = 1 ;
}
2023-01-24 09:54:09 +08:00
if ( event . type = = SDL_EVENT_KEY_DOWN ) {
2024-06-22 10:50:10 +08:00
if ( event . key . key = = SDLK_ESCAPE ) {
2017-11-13 14:51:12 +08:00
done = 1 ;
2024-06-22 10:50:10 +08:00
} else if ( event . key . key = = SDLK_LEFT ) {
2017-11-13 14:51:12 +08:00
- - current ;
2024-06-22 10:50:10 +08:00
} else if ( event . key . key = = SDLK_RIGHT ) {
2017-11-13 14:51:12 +08:00
+ + current ;
}
}
2023-01-30 11:06:08 +08:00
if ( event . type = = SDL_EVENT_MOUSE_BUTTON_DOWN ) {
2022-12-01 04:51:59 +08:00
if ( event . button . x < ( original - > w / 2 ) ) {
2017-11-13 14:51:12 +08:00
- - current ;
} else {
+ + current ;
}
}
}
/* Handle wrapping */
if ( current < 0 ) {
current + = SDL_arraysize ( output ) ;
}
if ( current > = SDL_arraysize ( output ) ) {
current - = SDL_arraysize ( output ) ;
}
SDL_RenderClear ( renderer ) ;
2022-12-27 22:21:13 +08:00
SDL_RenderTexture ( renderer , output [ current ] , NULL , NULL ) ;
2017-11-13 14:51:12 +08:00
SDL_SetRenderDrawColor ( renderer , 0xFF , 0xFF , 0xFF , 0xFF ) ;
if ( current = = 0 ) {
SDLTest_DrawString ( renderer , 4 , 4 , titles [ current ] ) ;
} else {
2024-02-29 02:03:18 +08:00
( void ) SDL_snprintf ( title , sizeof ( title ) , " %s %s %s " , titles [ current ] , yuv_format_name , yuv_mode_name ) ;
2017-11-13 14:51:12 +08:00
SDLTest_DrawString ( renderer , 4 , 4 , title ) ;
}
SDL_RenderPresent ( renderer ) ;
SDL_Delay ( 10 ) ;
}
}
2023-08-23 00:44:28 +08:00
SDL_free ( raw_yuv ) ;
2023-03-17 07:25:39 +08:00
SDL_free ( filename ) ;
2023-08-23 00:44:28 +08:00
SDL_DestroySurface ( original ) ;
SDL_DestroySurface ( converted ) ;
SDLTest_CleanupTextDrawing ( ) ;
SDL_DestroyRenderer ( renderer ) ;
SDL_DestroyWindow ( window ) ;
2017-11-13 14:51:12 +08:00
SDL_Quit ( ) ;
2023-03-17 07:25:39 +08:00
SDLTest_CommonDestroyState ( state ) ;
2017-11-13 14:51:12 +08:00
return 0 ;
}