summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testyuv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testyuv.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testyuv.c690
1 files changed, 690 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testyuv.c b/src/contrib/SDL-3.2.20/test/testyuv.c
new file mode 100644
index 0000000..8dda4c5
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testyuv.c
@@ -0,0 +1,690 @@
1/*
2 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <SDL3/SDL.h>
13#include <SDL3/SDL_main.h>
14#include <SDL3/SDL_test.h>
15#include "testyuv_cvt.h"
16#include "testutils.h"
17
18/* 422 (YUY2, etc) and P010 formats are the largest */
19#define MAX_YUV_SURFACE_SIZE(W, H, P) ((H + 1) * ((W + 1) + P) * 4)
20
21/* Return true if the YUV format is packed pixels */
22static bool is_packed_yuv_format(Uint32 format)
23{
24 return format == SDL_PIXELFORMAT_YUY2 || format == SDL_PIXELFORMAT_UYVY || format == SDL_PIXELFORMAT_YVYU;
25}
26
27/* Create a surface with a good pattern for verifying YUV conversion */
28static SDL_Surface *generate_test_pattern(int pattern_size)
29{
30 SDL_Surface *pattern = SDL_CreateSurface(pattern_size, pattern_size, SDL_PIXELFORMAT_RGB24);
31
32 if (pattern) {
33 int i, x, y;
34 Uint8 *p, c;
35 const int thickness = 2; /* Important so 2x2 blocks of color are the same, to avoid Cr/Cb interpolation over pixels */
36
37 /* R, G, B in alternating horizontal bands */
38 for (y = 0; y < pattern->h - (thickness - 1); y += thickness) {
39 for (i = 0; i < thickness; ++i) {
40 p = (Uint8 *)pattern->pixels + (y + i) * pattern->pitch + ((y / thickness) % 3);
41 for (x = 0; x < pattern->w; ++x) {
42 *p = 0xFF;
43 p += 3;
44 }
45 }
46 }
47
48 /* Black and white in alternating vertical bands */
49 c = 0xFF;
50 for (x = 1 * thickness; x < pattern->w; x += 2 * thickness) {
51 for (i = 0; i < thickness; ++i) {
52 p = (Uint8 *)pattern->pixels + (x + i) * 3;
53 for (y = 0; y < pattern->h; ++y) {
54 SDL_memset(p, c, 3);
55 p += pattern->pitch;
56 }
57 }
58 if (c) {
59 c = 0x00;
60 } else {
61 c = 0xFF;
62 }
63 }
64 }
65 return pattern;
66}
67
68static bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface, int tolerance)
69{
70 const int size = (surface->h * surface->pitch);
71 Uint8 *rgb;
72 bool result = false;
73
74 rgb = (Uint8 *)SDL_malloc(size);
75 if (!rgb) {
76 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory");
77 return false;
78 }
79
80 if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, 0, yuv, yuv_pitch, surface->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch)) {
81 int x, y;
82 result = true;
83 for (y = 0; y < surface->h; ++y) {
84 const Uint8 *actual = rgb + y * surface->pitch;
85 const Uint8 *expected = (const Uint8 *)surface->pixels + y * surface->pitch;
86 for (x = 0; x < surface->w; ++x) {
87 int deltaR = (int)actual[0] - expected[0];
88 int deltaG = (int)actual[1] - expected[1];
89 int deltaB = (int)actual[2] - expected[2];
90 int distance = (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
91 if (distance > tolerance) {
92 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", x, y, actual[0], actual[1], actual[2], expected[0], expected[1], expected[2], distance);
93 result = false;
94 }
95 actual += 3;
96 expected += 3;
97 }
98 }
99 } else {
100 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(format), SDL_GetPixelFormatName(surface->format), SDL_GetError());
101 }
102 SDL_free(rgb);
103
104 return result;
105}
106
107static bool run_automated_tests(int pattern_size, int extra_pitch)
108{
109 const Uint32 formats[] = {
110 SDL_PIXELFORMAT_YV12,
111 SDL_PIXELFORMAT_IYUV,
112 SDL_PIXELFORMAT_NV12,
113 SDL_PIXELFORMAT_NV21,
114 SDL_PIXELFORMAT_YUY2,
115 SDL_PIXELFORMAT_UYVY,
116 SDL_PIXELFORMAT_YVYU
117 };
118 int i, j;
119 SDL_Surface *pattern = generate_test_pattern(pattern_size);
120 const int yuv_len = MAX_YUV_SURFACE_SIZE(pattern->w, pattern->h, extra_pitch);
121 Uint8 *yuv1 = (Uint8 *)SDL_malloc(yuv_len);
122 Uint8 *yuv2 = (Uint8 *)SDL_malloc(yuv_len);
123 int yuv1_pitch, yuv2_pitch;
124 YUV_CONVERSION_MODE mode;
125 SDL_Colorspace colorspace;
126 const int tight_tolerance = 20;
127 const int loose_tolerance = 333;
128 bool result = false;
129
130 if (!pattern || !yuv1 || !yuv2) {
131 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate test surfaces");
132 goto done;
133 }
134
135 mode = GetYUVConversionModeForResolution(pattern->w, pattern->h);
136 colorspace = GetColorspaceForYUVConversionMode(mode);
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, mode, 0, 100)) {
141 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s", SDL_GetPixelFormatName(formats[i]));
142 goto done;
143 }
144 yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w);
145 if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) {
146 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB", 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_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
155 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
156 goto done;
157 }
158 if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) {
159 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s", 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_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
170 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
171 goto done;
172 }
173 if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, 0, yuv1, yuv1_pitch, formats[j], colorspace, 0, yuv2, yuv2_pitch)) {
174 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError());
175 goto done;
176 }
177 if (!verify_yuv_data(formats[j], colorspace, yuv2, yuv2_pitch, pattern, tight_tolerance)) {
178 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s", 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_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
195 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError());
196 goto done;
197 }
198 if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, 0, yuv1, yuv1_pitch, formats[j], colorspace, 0, yuv1, yuv2_pitch)) {
199 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError());
200 goto done;
201 }
202 if (!verify_yuv_data(formats[j], colorspace, yuv1, yuv2_pitch, pattern, tight_tolerance)) {
203 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]));
204 goto done;
205 }
206 }
207 }
208
209 /* Verify round trip through BT.2020 */
210 colorspace = SDL_COLORSPACE_BT2020_FULL;
211 if (!ConvertRGBtoYUV(SDL_PIXELFORMAT_P010, pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, YUV_CONVERSION_BT2020, 0, 100)) {
212 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010));
213 goto done;
214 }
215 yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w);
216 if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) {
217 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010));
218 goto done;
219 }
220
221 /* The pitch needs to be Uint16 aligned for P010 pixels */
222 yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w) + ((extra_pitch + 1) & ~1);
223 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)) {
224 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s", SDL_GetPixelFormatName(pattern->format), SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010), SDL_GetError());
225 goto done;
226 }
227 /* Going through XRGB2101010 format during P010 conversion is slightly lossy, so use looser tolerance here */
228 if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, yuv1, yuv1_pitch, pattern, loose_tolerance)) {
229 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010));
230 goto done;
231 }
232
233 result = true;
234
235done:
236 SDL_free(yuv1);
237 SDL_free(yuv2);
238 SDL_DestroySurface(pattern);
239 return result;
240}
241
242static bool run_colorspace_test(void)
243{
244 bool result = false;
245 SDL_Window *window;
246 SDL_Renderer *renderer;
247 struct {
248 const char *name;
249 SDL_Colorspace colorspace;
250 } colorspaces[] = {
251#define COLORSPACE(X) { #X, X }
252 COLORSPACE(SDL_COLORSPACE_JPEG),
253#if 0 /* We don't support converting color primaries here */
254 COLORSPACE(SDL_COLORSPACE_BT601_LIMITED),
255 COLORSPACE(SDL_COLORSPACE_BT601_FULL),
256#endif
257 COLORSPACE(SDL_COLORSPACE_BT709_LIMITED),
258 COLORSPACE(SDL_COLORSPACE_BT709_FULL)
259#undef COLORSPACE
260 };
261 SDL_Surface *rgb = NULL;
262 SDL_Surface *yuv = NULL;
263 SDL_Texture *texture = NULL;
264 int allowed_error = 2;
265 int i;
266
267 if (!SDL_CreateWindowAndRenderer("testyuv", 320, 240, 0, &window, &renderer)) {
268 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s", SDL_GetError());
269 goto done;
270 }
271
272 rgb = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_XRGB8888);
273 if (!rgb) {
274 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create RGB surface: %s", SDL_GetError());
275 goto done;
276 }
277 SDL_FillSurfaceRect(rgb, NULL, SDL_MapSurfaceRGB(rgb, 255, 0, 0));
278
279 for (i = 0; i < SDL_arraysize(colorspaces); ++i) {
280 bool next = false;
281 Uint8 r, g, b, a;
282
283 SDL_Log("Checking colorspace %s", colorspaces[i].name);
284
285 yuv = SDL_ConvertSurfaceAndColorspace(rgb, SDL_PIXELFORMAT_NV12, NULL, colorspaces[i].colorspace, 0);
286 if (!yuv) {
287 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV surface: %s", SDL_GetError());
288 goto done;
289 }
290
291 if (!SDL_ReadSurfacePixel(yuv, 0, 0, &r, &g, &b, &a)) {
292 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't read YUV surface: %s", SDL_GetError());
293 goto done;
294 }
295
296 if (SDL_abs((int)r - 255) > allowed_error ||
297 SDL_abs((int)g - 0) > allowed_error ||
298 SDL_abs((int)b - 0) > allowed_error) {
299 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed color conversion, expected 255,0,0, got %d,%d,%d", r, g, b);
300 }
301
302 texture = SDL_CreateTextureFromSurface(renderer, yuv);
303 if (!texture) {
304 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV texture: %s", SDL_GetError());
305 goto done;
306 }
307
308 SDL_DestroySurface(yuv);
309 yuv = NULL;
310
311 SDL_RenderTexture(renderer, texture, NULL, NULL);
312 yuv = SDL_RenderReadPixels(renderer, NULL);
313
314 if (!SDL_ReadSurfacePixel(yuv, 0, 0, &r, &g, &b, &a)) {
315 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't read YUV surface: %s", SDL_GetError());
316 goto done;
317 }
318
319 if (SDL_abs((int)r - 255) > allowed_error ||
320 SDL_abs((int)g - 0) > allowed_error ||
321 SDL_abs((int)b - 0) > allowed_error) {
322 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed renderer color conversion, expected 255,0,0, got %d,%d,%d", r, g, b);
323 }
324
325 while (!next) {
326 SDL_Event event;
327 while (SDL_PollEvent(&event)) {
328 switch (event.type) {
329 case SDL_EVENT_KEY_DOWN:
330 if (event.key.key == SDLK_ESCAPE) {
331 result = true;
332 goto done;
333 }
334 if (event.key.key == SDLK_SPACE) {
335 next = true;
336 }
337 break;
338 case SDL_EVENT_QUIT:
339 result = true;
340 goto done;
341 }
342 }
343
344 SDL_RenderTexture(renderer, texture, NULL, NULL);
345 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
346 SDL_RenderDebugText(renderer, 4, 4, colorspaces[i].name);
347 SDL_RenderPresent(renderer);
348 SDL_Delay(10);
349 }
350
351 SDL_DestroyTexture(texture);
352 texture = NULL;
353 }
354
355 result = true;
356
357done:
358 SDL_DestroySurface(rgb);
359 SDL_DestroySurface(yuv);
360 SDL_DestroyTexture(texture);
361 SDL_Quit();
362 return result;
363}
364
365int main(int argc, char **argv)
366{
367 struct
368 {
369 bool enable_intrinsics;
370 int pattern_size;
371 int extra_pitch;
372 } automated_test_params[] = {
373 /* Test: single pixel */
374 { false, 1, 0 },
375 /* Test: even width and height */
376 { false, 2, 0 },
377 { false, 4, 0 },
378 /* Test: odd width and height */
379 { false, 1, 0 },
380 { false, 3, 0 },
381 /* Test: even width and height, extra pitch */
382 { false, 2, 3 },
383 { false, 4, 3 },
384 /* Test: odd width and height, extra pitch */
385 { false, 1, 3 },
386 { false, 3, 3 },
387 /* Test: even width and height with intrinsics */
388 { true, 32, 0 },
389 /* Test: odd width and height with intrinsics */
390 { true, 33, 0 },
391 { true, 37, 0 },
392 /* Test: even width and height with intrinsics, extra pitch */
393 { true, 32, 3 },
394 /* Test: odd width and height with intrinsics, extra pitch */
395 { true, 33, 3 },
396 { true, 37, 3 },
397 };
398 char *filename = NULL;
399 SDL_Surface *original;
400 SDL_Surface *converted;
401 SDL_Surface *bmp;
402 SDL_Window *window;
403 SDL_Renderer *renderer;
404 SDL_Texture *output[3];
405 const char *titles[3] = { "ORIGINAL", "SOFTWARE", "HARDWARE" };
406 char title[128];
407 YUV_CONVERSION_MODE yuv_mode;
408 const char *yuv_mode_name;
409 Uint32 yuv_format = SDL_PIXELFORMAT_YV12;
410 const char *yuv_format_name;
411 SDL_Colorspace yuv_colorspace;
412 Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888;
413 SDL_Colorspace rgb_colorspace = SDL_COLORSPACE_SRGB;
414 SDL_PropertiesID props;
415 bool monochrome = false;
416 int luminance = 100;
417 int current = 0;
418 int pitch;
419 Uint8 *raw_yuv;
420 Uint64 then, now;
421 int i, iterations = 100;
422 bool should_run_automated_tests = false;
423 bool should_run_colorspace_test = false;
424 SDLTest_CommonState *state;
425
426 /* Initialize test framework */
427 state = SDLTest_CommonCreateState(argv, 0);
428 if (!state) {
429 return 1;
430 }
431
432 /* Parse commandline */
433 for (i = 1; i < argc;) {
434 int consumed;
435
436 consumed = SDLTest_CommonArg(state, i);
437 if (!consumed) {
438 if (SDL_strcmp(argv[i], "--jpeg") == 0) {
439 SetYUVConversionMode(YUV_CONVERSION_JPEG);
440 consumed = 1;
441 } else if (SDL_strcmp(argv[i], "--bt601") == 0) {
442 SetYUVConversionMode(YUV_CONVERSION_BT601);
443 consumed = 1;
444 } else if (SDL_strcmp(argv[i], "--bt709") == 0) {
445 SetYUVConversionMode(YUV_CONVERSION_BT709);
446 consumed = 1;
447 } else if (SDL_strcmp(argv[i], "--bt2020") == 0) {
448 SetYUVConversionMode(YUV_CONVERSION_BT2020);
449 consumed = 1;
450 } else if (SDL_strcmp(argv[i], "--auto") == 0) {
451 SetYUVConversionMode(YUV_CONVERSION_AUTOMATIC);
452 consumed = 1;
453 } else if (SDL_strcmp(argv[i], "--yv12") == 0) {
454 yuv_format = SDL_PIXELFORMAT_YV12;
455 consumed = 1;
456 } else if (SDL_strcmp(argv[i], "--iyuv") == 0) {
457 yuv_format = SDL_PIXELFORMAT_IYUV;
458 consumed = 1;
459 } else if (SDL_strcmp(argv[i], "--yuy2") == 0) {
460 yuv_format = SDL_PIXELFORMAT_YUY2;
461 consumed = 1;
462 } else if (SDL_strcmp(argv[i], "--uyvy") == 0) {
463 yuv_format = SDL_PIXELFORMAT_UYVY;
464 consumed = 1;
465 } else if (SDL_strcmp(argv[i], "--yvyu") == 0) {
466 yuv_format = SDL_PIXELFORMAT_YVYU;
467 consumed = 1;
468 } else if (SDL_strcmp(argv[i], "--nv12") == 0) {
469 yuv_format = SDL_PIXELFORMAT_NV12;
470 consumed = 1;
471 } else if (SDL_strcmp(argv[i], "--nv21") == 0) {
472 yuv_format = SDL_PIXELFORMAT_NV21;
473 consumed = 1;
474 } else if (SDL_strcmp(argv[i], "--rgb555") == 0) {
475 rgb_format = SDL_PIXELFORMAT_XRGB1555;
476 consumed = 1;
477 } else if (SDL_strcmp(argv[i], "--rgb565") == 0) {
478 rgb_format = SDL_PIXELFORMAT_RGB565;
479 consumed = 1;
480 } else if (SDL_strcmp(argv[i], "--rgb24") == 0) {
481 rgb_format = SDL_PIXELFORMAT_RGB24;
482 consumed = 1;
483 } else if (SDL_strcmp(argv[i], "--argb") == 0) {
484 rgb_format = SDL_PIXELFORMAT_ARGB8888;
485 consumed = 1;
486 } else if (SDL_strcmp(argv[i], "--abgr") == 0) {
487 rgb_format = SDL_PIXELFORMAT_ABGR8888;
488 consumed = 1;
489 } else if (SDL_strcmp(argv[i], "--rgba") == 0) {
490 rgb_format = SDL_PIXELFORMAT_RGBA8888;
491 consumed = 1;
492 } else if (SDL_strcmp(argv[i], "--bgra") == 0) {
493 rgb_format = SDL_PIXELFORMAT_BGRA8888;
494 consumed = 1;
495 } else if (SDL_strcmp(argv[i], "--monochrome") == 0) {
496 monochrome = true;
497 consumed = 1;
498 } else if (SDL_strcmp(argv[i], "--luminance") == 0 && argv[i+1]) {
499 luminance = SDL_atoi(argv[i+1]);
500 consumed = 2;
501 } else if (SDL_strcmp(argv[i], "--automated") == 0) {
502 should_run_automated_tests = true;
503 consumed = 1;
504 } else if (SDL_strcmp(argv[i], "--colorspace-test") == 0) {
505 should_run_colorspace_test = true;
506 consumed = 1;
507 } else if (!filename) {
508 filename = argv[i];
509 consumed = 1;
510 }
511 }
512 if (consumed <= 0) {
513 static const char *options[] = {
514 "[--jpeg|--bt601|--bt709|--bt2020|--auto]",
515 "[--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21]",
516 "[--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra]",
517 "[--monochrome] [--luminance N%]",
518 "[--automated] [--colorspace-test]",
519 "[sample.bmp]",
520 NULL,
521 };
522 SDLTest_CommonLogUsage(state, argv[0], options);
523 SDL_Quit();
524 SDLTest_CommonDestroyState(state);
525 return 1;
526 }
527 i += consumed;
528 }
529
530 /* Run automated tests */
531 if (should_run_automated_tests) {
532 for (i = 0; i < (int)SDL_arraysize(automated_test_params); ++i) {
533 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Running automated test, pattern size %d, extra pitch %d, intrinsics %s",
534 automated_test_params[i].pattern_size,
535 automated_test_params[i].extra_pitch,
536 automated_test_params[i].enable_intrinsics ? "enabled" : "disabled");
537 if (!run_automated_tests(automated_test_params[i].pattern_size, automated_test_params[i].extra_pitch)) {
538 return 2;
539 }
540 }
541 return 0;
542 }
543
544 if (should_run_colorspace_test) {
545 if (!run_colorspace_test()) {
546 return 2;
547 }
548 return 0;
549 }
550
551 filename = GetResourceFilename(filename, "testyuv.bmp");
552 bmp = SDL_LoadBMP(filename);
553 original = SDL_ConvertSurface(bmp, SDL_PIXELFORMAT_RGB24);
554 SDL_DestroySurface(bmp);
555 if (!original) {
556 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", filename, SDL_GetError());
557 return 3;
558 }
559
560 yuv_mode = GetYUVConversionModeForResolution(original->w, original->h);
561 switch (yuv_mode) {
562 case YUV_CONVERSION_JPEG:
563 yuv_mode_name = "JPEG";
564 break;
565 case YUV_CONVERSION_BT601:
566 yuv_mode_name = "BT.601";
567 break;
568 case YUV_CONVERSION_BT709:
569 yuv_mode_name = "BT.709";
570 break;
571 case YUV_CONVERSION_BT2020:
572 yuv_mode_name = "BT.2020";
573 yuv_format = SDL_PIXELFORMAT_P010;
574 rgb_format = SDL_PIXELFORMAT_XBGR2101010;
575 rgb_colorspace = SDL_COLORSPACE_HDR10;
576 break;
577 default:
578 yuv_mode_name = "UNKNOWN";
579 break;
580 }
581 yuv_colorspace = GetColorspaceForYUVConversionMode(yuv_mode);
582
583 raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0));
584 ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, monochrome, luminance);
585 pitch = CalculateYUVPitch(yuv_format, original->w);
586
587 converted = SDL_CreateSurface(original->w, original->h, rgb_format);
588 if (!converted) {
589 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create converted surface: %s", SDL_GetError());
590 return 3;
591 }
592
593 then = SDL_GetTicks();
594 for (i = 0; i < iterations; ++i) {
595 SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, yuv_colorspace, 0, raw_yuv, pitch, rgb_format, rgb_colorspace, 0, converted->pixels, converted->pitch);
596 }
597 now = SDL_GetTicks();
598 SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %" SDL_PRIu64 " ms, %.2fms each", iterations, (now - then), (float)(now - then) / iterations);
599
600 window = SDL_CreateWindow("YUV test", original->w, original->h, 0);
601 if (!window) {
602 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s", SDL_GetError());
603 return 4;
604 }
605
606 renderer = SDL_CreateRenderer(window, NULL);
607 if (!renderer) {
608 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s", SDL_GetError());
609 return 4;
610 }
611
612 output[0] = SDL_CreateTextureFromSurface(renderer, original);
613 output[1] = SDL_CreateTextureFromSurface(renderer, converted);
614 props = SDL_CreateProperties();
615 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, yuv_colorspace);
616 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, yuv_format);
617 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING);
618 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, original->w);
619 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, original->h);
620 output[2] = SDL_CreateTextureWithProperties(renderer, props);
621 SDL_DestroyProperties(props);
622 if (!output[0] || !output[1] || !output[2]) {
623 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s", SDL_GetError());
624 return 5;
625 }
626 SDL_UpdateTexture(output[2], NULL, raw_yuv, pitch);
627
628 yuv_format_name = SDL_GetPixelFormatName(yuv_format);
629 if (SDL_strncmp(yuv_format_name, "SDL_PIXELFORMAT_", 16) == 0) {
630 yuv_format_name += 16;
631 }
632
633 {
634 int done = 0;
635 while (!done) {
636 SDL_Event event;
637 while (SDL_PollEvent(&event) > 0) {
638 if (event.type == SDL_EVENT_QUIT) {
639 done = 1;
640 }
641 if (event.type == SDL_EVENT_KEY_DOWN) {
642 if (event.key.key == SDLK_ESCAPE) {
643 done = 1;
644 } else if (event.key.key == SDLK_LEFT) {
645 --current;
646 } else if (event.key.key == SDLK_RIGHT) {
647 ++current;
648 }
649 }
650 if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
651 if (event.button.x < (original->w / 2)) {
652 --current;
653 } else {
654 ++current;
655 }
656 }
657 }
658
659 /* Handle wrapping */
660 if (current < 0) {
661 current += SDL_arraysize(output);
662 }
663 if (current >= SDL_arraysize(output)) {
664 current -= SDL_arraysize(output);
665 }
666
667 SDL_RenderClear(renderer);
668 SDL_RenderTexture(renderer, output[current], NULL, NULL);
669 SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
670 if (current == 0) {
671 SDLTest_DrawString(renderer, 4, 4, titles[current]);
672 } else {
673 (void)SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_format_name, yuv_mode_name);
674 SDLTest_DrawString(renderer, 4, 4, title);
675 }
676 SDL_RenderPresent(renderer);
677 SDL_Delay(10);
678 }
679 }
680 SDL_free(raw_yuv);
681 SDL_free(filename);
682 SDL_DestroySurface(original);
683 SDL_DestroySurface(converted);
684 SDLTest_CleanupTextDrawing();
685 SDL_DestroyRenderer(renderer);
686 SDL_DestroyWindow(window);
687 SDL_Quit();
688 SDLTest_CommonDestroyState(state);
689 return 0;
690}