Atlas - testyuv.c

Home / ext / SDL2 / test Lines: 21 | Size: 18775 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12#include <stdlib.h> 13#include <stdio.h> 14#include <string.h> 15 16#include "SDL.h" 17#include "SDL_test_font.h" 18#include "testyuv_cvt.h" 19 20 21/* 422 (YUY2, etc) formats are the largest */ 22#define MAX_YUV_SURFACE_SIZE(W, H, P) (H*4*(W+P+1)/2) 23 24 25/* Return true if the YUV format is packed pixels */ 26static SDL_bool is_packed_yuv_format(Uint32 format) 27{ 28 return (format == SDL_PIXELFORMAT_YUY2 || 29 format == SDL_PIXELFORMAT_UYVY || 30 format == SDL_PIXELFORMAT_YVYU); 31} 32 33/* Create a surface with a good pattern for verifying YUV conversion */ 34static SDL_Surface *generate_test_pattern(int pattern_size) 35{ 36 SDL_Surface *pattern = SDL_CreateRGBSurfaceWithFormat(0, pattern_size, pattern_size, 0, SDL_PIXELFORMAT_RGB24); 37 38 if (pattern) { 39 int i, x, y; 40 Uint8 *p, c; 41 const int thickness = 2; /* Important so 2x2 blocks of color are the same, to avoid Cr/Cb interpolation over pixels */ 42 43 /* R, G, B in alternating horizontal bands */ 44 for (y = 0; y < pattern->h; y += thickness) { 45 for (i = 0; i < thickness; ++i) { 46 p = (Uint8 *)pattern->pixels + (y + i) * pattern->pitch + ((y/thickness) % 3); 47 for (x = 0; x < pattern->w; ++x) { 48 *p = 0xFF; 49 p += 3; 50 } 51 } 52 } 53 54 /* Black and white in alternating vertical bands */ 55 c = 0xFF; 56 for (x = 1*thickness; x < pattern->w; x += 2*thickness) { 57 for (i = 0; i < thickness; ++i) { 58 p = (Uint8 *)pattern->pixels + (x + i)*3; 59 for (y = 0; y < pattern->h; ++y) { 60 SDL_memset(p, c, 3); 61 p += pattern->pitch; 62 } 63 } 64 if (c) { 65 c = 0x00; 66 } else { 67 c = 0xFF; 68 } 69 } 70 } 71 return pattern; 72} 73 74static SDL_bool verify_yuv_data(Uint32 format, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface) 75{ 76 const int tolerance = 20; 77 const int size = (surface->h * surface->pitch); 78 Uint8 *rgb; 79 SDL_bool result = SDL_FALSE; 80 81 rgb = (Uint8 *)SDL_malloc(size); 82 if (!rgb) { 83 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory"); 84 return SDL_FALSE; 85 } 86 87 if (SDL_ConvertPixels(surface->w, surface->h, format, yuv, yuv_pitch, surface->format->format, rgb, surface->pitch) == 0) { 88 int x, y; 89 result = SDL_TRUE; 90 for (y = 0; y < surface->h; ++y) { 91 const Uint8 *actual = rgb + y * surface->pitch; 92 const Uint8 *expected = (const Uint8 *)surface->pixels + y * surface->pitch; 93 for (x = 0; x < surface->w; ++x) { 94 int deltaR = (int)actual[0] - expected[0]; 95 int deltaG = (int)actual[1] - expected[1]; 96 int deltaB = (int)actual[2] - expected[2]; 97 int distance = (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB); 98 if (distance > tolerance) { 99 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); 100 result = SDL_FALSE; 101 } 102 actual += 3; 103 expected += 3; 104 } 105 } 106 } else { 107 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(format), SDL_GetPixelFormatName(surface->format->format), SDL_GetError()); 108 } 109 SDL_free(rgb); 110 111 return result; 112} 113 114static int run_automated_tests(int pattern_size, int extra_pitch) 115{ 116 const Uint32 formats[] = { 117 SDL_PIXELFORMAT_YV12, 118 SDL_PIXELFORMAT_IYUV, 119 SDL_PIXELFORMAT_NV12, 120 SDL_PIXELFORMAT_NV21, 121 SDL_PIXELFORMAT_YUY2, 122 SDL_PIXELFORMAT_UYVY, 123 SDL_PIXELFORMAT_YVYU 124 }; 125 int i, j; 126 SDL_Surface *pattern = generate_test_pattern(pattern_size); 127 const int yuv_len = MAX_YUV_SURFACE_SIZE(pattern->w, pattern->h, extra_pitch); 128 Uint8 *yuv1 = (Uint8 *)SDL_malloc(yuv_len); 129 Uint8 *yuv2 = (Uint8 *)SDL_malloc(yuv_len); 130 int yuv1_pitch, yuv2_pitch; 131 int result = -1; 132 133 if (!pattern || !yuv1 || !yuv2) { 134 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate test surfaces"); 135 goto done; 136 } 137 138 /* Verify conversion from YUV formats */ 139 for (i = 0; i < SDL_arraysize(formats); ++i) { 140 if (!ConvertRGBtoYUV(formats[i], pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, SDL_GetYUVConversionModeForResolution(pattern->w, pattern->h), 0, 100)) { 141 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s\n", SDL_GetPixelFormatName(formats[i])); 142 goto done; 143 } 144 yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w); 145 if (!verify_yuv_data(formats[i], yuv1, yuv1_pitch, pattern)) { 146 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(formats[i])); 147 goto done; 148 } 149 } 150 151 /* Verify conversion to YUV formats */ 152 for (i = 0; i < SDL_arraysize(formats); ++i) { 153 yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; 154 if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) { 155 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); 156 goto done; 157 } 158 if (!verify_yuv_data(formats[i], yuv1, yuv1_pitch, pattern)) { 159 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(formats[i])); 160 goto done; 161 } 162 } 163 164 /* Verify conversion between YUV formats */ 165 for (i = 0; i < SDL_arraysize(formats); ++i) { 166 for (j = 0; j < SDL_arraysize(formats); ++j) { 167 yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; 168 yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch; 169 if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) { 170 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); 171 goto done; 172 } 173 if (SDL_ConvertPixels(pattern->w, pattern->h, formats[i], yuv1, yuv1_pitch, formats[j], yuv2, yuv2_pitch) < 0) { 174 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); 175 goto done; 176 } 177 if (!verify_yuv_data(formats[j], yuv2, yuv2_pitch, pattern)) { 178 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); 179 goto done; 180 } 181 } 182 } 183 184 /* Verify conversion between YUV formats in-place */ 185 for (i = 0; i < SDL_arraysize(formats); ++i) { 186 for (j = 0; j < SDL_arraysize(formats); ++j) { 187 if (is_packed_yuv_format(formats[i]) != is_packed_yuv_format(formats[j])) { 188 /* Can't change plane vs packed pixel layout in-place */ 189 continue; 190 } 191 192 yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; 193 yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch; 194 if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) { 195 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); 196 goto done; 197 } 198 if (SDL_ConvertPixels(pattern->w, pattern->h, formats[i], yuv1, yuv1_pitch, formats[j], yuv1, yuv2_pitch) < 0) { 199 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); 200 goto done; 201 } 202 if (!verify_yuv_data(formats[j], yuv1, yuv2_pitch, pattern)) { 203 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); 204 goto done; 205 } 206 } 207 } 208 209 210 result = 0; 211 212done: 213 SDL_free(yuv1); 214 SDL_free(yuv2); 215 SDL_FreeSurface(pattern); 216 return result; 217} 218 219int 220main(int argc, char **argv) 221{ 222 struct { 223 SDL_bool enable_intrinsics; 224 int pattern_size; 225 int extra_pitch; 226 } automated_test_params[] = { 227 /* Test: even width and height */ 228 { SDL_FALSE, 2, 0 }, 229 { SDL_FALSE, 4, 0 }, 230 /* Test: odd width and height */ 231 { SDL_FALSE, 1, 0 }, 232 { SDL_FALSE, 3, 0 }, 233 /* Test: even width and height, extra pitch */ 234 { SDL_FALSE, 2, 3 }, 235 { SDL_FALSE, 4, 3 }, 236 /* Test: odd width and height, extra pitch */ 237 { SDL_FALSE, 1, 3 }, 238 { SDL_FALSE, 3, 3 }, 239 /* Test: even width and height with intrinsics */ 240 { SDL_TRUE, 32, 0 }, 241 /* Test: odd width and height with intrinsics */ 242 { SDL_TRUE, 33, 0 }, 243 { SDL_TRUE, 37, 0 }, 244 /* Test: even width and height with intrinsics, extra pitch */ 245 { SDL_TRUE, 32, 3 }, 246 /* Test: odd width and height with intrinsics, extra pitch */ 247 { SDL_TRUE, 33, 3 }, 248 { SDL_TRUE, 37, 3 }, 249 }; 250 int arg = 1; 251 const char *filename; 252 SDL_Surface *original; 253 SDL_Surface *converted; 254 SDL_Window *window; 255 SDL_Renderer *renderer; 256 SDL_Texture *output[3]; 257 const char *titles[3] = { "ORIGINAL", "SOFTWARE", "HARDWARE" }; 258 char title[128]; 259 const char *yuv_name; 260 const char *yuv_mode; 261 Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888; 262 Uint32 yuv_format = SDL_PIXELFORMAT_YV12; 263 int current = 0; 264 int pitch; 265 Uint8 *raw_yuv; 266 Uint32 then, now, i, iterations = 100; 267 SDL_bool should_run_automated_tests = SDL_FALSE; 268 269 while (argv[arg] && *argv[arg] == '-') { 270 if (SDL_strcmp(argv[arg], "--jpeg") == 0) { 271 SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_JPEG); 272 } else if (SDL_strcmp(argv[arg], "--bt601") == 0) { 273 SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT601); 274 } else if (SDL_strcmp(argv[arg], "--bt709") == 0) { 275 SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT709); 276 } else if (SDL_strcmp(argv[arg], "--auto") == 0) { 277 SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_AUTOMATIC); 278 } else if (SDL_strcmp(argv[arg], "--yv12") == 0) { 279 yuv_format = SDL_PIXELFORMAT_YV12; 280 } else if (SDL_strcmp(argv[arg], "--iyuv") == 0) { 281 yuv_format = SDL_PIXELFORMAT_IYUV; 282 } else if (SDL_strcmp(argv[arg], "--yuy2") == 0) { 283 yuv_format = SDL_PIXELFORMAT_YUY2; 284 } else if (SDL_strcmp(argv[arg], "--uyvy") == 0) { 285 yuv_format = SDL_PIXELFORMAT_UYVY; 286 } else if (SDL_strcmp(argv[arg], "--yvyu") == 0) { 287 yuv_format = SDL_PIXELFORMAT_YVYU; 288 } else if (SDL_strcmp(argv[arg], "--nv12") == 0) { 289 yuv_format = SDL_PIXELFORMAT_NV12; 290 } else if (SDL_strcmp(argv[arg], "--nv21") == 0) { 291 yuv_format = SDL_PIXELFORMAT_NV21; 292 } else if (SDL_strcmp(argv[arg], "--rgb555") == 0) { 293 rgb_format = SDL_PIXELFORMAT_RGB555; 294 } else if (SDL_strcmp(argv[arg], "--rgb565") == 0) { 295 rgb_format = SDL_PIXELFORMAT_RGB565; 296 } else if (SDL_strcmp(argv[arg], "--rgb24") == 0) { 297 rgb_format = SDL_PIXELFORMAT_RGB24; 298 } else if (SDL_strcmp(argv[arg], "--argb") == 0) { 299 rgb_format = SDL_PIXELFORMAT_ARGB8888; 300 } else if (SDL_strcmp(argv[arg], "--abgr") == 0) { 301 rgb_format = SDL_PIXELFORMAT_ABGR8888; 302 } else if (SDL_strcmp(argv[arg], "--rgba") == 0) { 303 rgb_format = SDL_PIXELFORMAT_RGBA8888; 304 } else if (SDL_strcmp(argv[arg], "--bgra") == 0) { 305 rgb_format = SDL_PIXELFORMAT_BGRA8888; 306 } else if (SDL_strcmp(argv[arg], "--automated") == 0) { 307 should_run_automated_tests = SDL_TRUE; 308 } else { 309 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Usage: %s [--jpeg|--bt601|-bt709|--auto] [--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21] [--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra] [image_filename]\n", argv[0]); 310 return 1; 311 } 312 ++arg; 313 } 314 315 /* Run automated tests */ 316 if (should_run_automated_tests) { 317 for (i = 0; i < SDL_arraysize(automated_test_params); ++i) { 318 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Running automated test, pattern size %d, extra pitch %d, intrinsics %s\n", 319 automated_test_params[i].pattern_size, 320 automated_test_params[i].extra_pitch, 321 automated_test_params[i].enable_intrinsics ? "enabled" : "disabled"); 322 if (run_automated_tests(automated_test_params[i].pattern_size, automated_test_params[i].extra_pitch) < 0) { 323 return 2; 324 } 325 } 326 return 0; 327 } 328 329 if (argv[arg]) { 330 filename = argv[arg]; 331 } else { 332 filename = "testyuv.bmp"; 333 } 334 original = SDL_ConvertSurfaceFormat(SDL_LoadBMP(filename), SDL_PIXELFORMAT_RGB24, 0); 335 if (!original) { 336 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError()); 337 return 3; 338 } 339 340 raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0)); 341 ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, 342 SDL_GetYUVConversionModeForResolution(original->w, original->h), 343 0, 100); 344 pitch = CalculateYUVPitch(yuv_format, original->w); 345 346 converted = SDL_CreateRGBSurfaceWithFormat(0, original->w, original->h, 0, rgb_format); 347 if (!converted) { 348 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create converted surface: %s\n", SDL_GetError()); 349 return 3; 350 } 351 352 then = SDL_GetTicks(); 353 for ( i = 0; i < iterations; ++i ) { 354 SDL_ConvertPixels(original->w, original->h, yuv_format, raw_yuv, pitch, rgb_format, converted->pixels, converted->pitch); 355 } 356 now = SDL_GetTicks(); 357 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %d ms, %.2fms each\n", iterations, (now - then), (float)(now - then)/iterations); 358 359 window = SDL_CreateWindow("YUV test", 360 SDL_WINDOWPOS_UNDEFINED, 361 SDL_WINDOWPOS_UNDEFINED, 362 original->w, original->h, 363 0); 364 if (!window) { 365 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); 366 return 4; 367 } 368 369 renderer = SDL_CreateRenderer(window, -1, 0); 370 if (!renderer) { 371 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); 372 return 4; 373 } 374 375 output[0] = SDL_CreateTextureFromSurface(renderer, original); 376 output[1] = SDL_CreateTextureFromSurface(renderer, converted); 377 output[2] = SDL_CreateTexture(renderer, yuv_format, SDL_TEXTUREACCESS_STREAMING, original->w, original->h); 378 if (!output[0] || !output[1] || !output[2]) { 379 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError()); 380 return 5; 381 } 382 SDL_UpdateTexture(output[2], NULL, raw_yuv, pitch); 383 384 yuv_name = SDL_GetPixelFormatName(yuv_format); 385 if (SDL_strncmp(yuv_name, "SDL_PIXELFORMAT_", 16) == 0) { 386 yuv_name += 16; 387 } 388 389 switch (SDL_GetYUVConversionModeForResolution(original->w, original->h)) { 390 case SDL_YUV_CONVERSION_JPEG: 391 yuv_mode = "JPEG"; 392 break; 393 case SDL_YUV_CONVERSION_BT601: 394 yuv_mode = "BT.601"; 395 break; 396 case SDL_YUV_CONVERSION_BT709: 397 yuv_mode = "BT.709"; 398 break; 399 default: 400 yuv_mode = "UNKNOWN"; 401 break; 402 } 403 404 { int done = 0; 405 while ( !done ) 406 { 407 SDL_Event event; 408 while (SDL_PollEvent(&event) > 0) { 409 if (event.type == SDL_QUIT) { 410 done = 1; 411 } 412 if (event.type == SDL_KEYDOWN) { 413 if (event.key.keysym.sym == SDLK_ESCAPE) { 414 done = 1; 415 } else if (event.key.keysym.sym == SDLK_LEFT) { 416 --current; 417 } else if (event.key.keysym.sym == SDLK_RIGHT) { 418 ++current; 419 } 420 } 421 if (event.type == SDL_MOUSEBUTTONDOWN) { 422 if (event.button.x < (original->w/2)) { 423 --current; 424 } else { 425 ++current; 426 } 427 } 428 } 429 430 /* Handle wrapping */ 431 if (current < 0) { 432 current += SDL_arraysize(output); 433 } 434 if (current >= SDL_arraysize(output)) { 435 current -= SDL_arraysize(output); 436 } 437 438 SDL_RenderClear(renderer); 439 SDL_RenderCopy(renderer, output[current], NULL, NULL); 440 SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); 441 if (current == 0) { 442 SDLTest_DrawString(renderer, 4, 4, titles[current]); 443 } else { 444 SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_name, yuv_mode); 445 SDLTest_DrawString(renderer, 4, 4, title); 446 } 447 SDL_RenderPresent(renderer); 448 SDL_Delay(10); 449 } 450 } 451 SDL_Quit(); 452 return 0; 453} 454 455/* vi: set ts=4 sw=4 expandtab: */ 456
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.