diff options
author | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
commit | 6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch) | |
tree | 34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testyuv.c | |
parent | 8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff) |
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testyuv.c')
-rw-r--r-- | src/contrib/SDL-3.2.20/test/testyuv.c | 690 |
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 */ | ||
22 | static 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 */ | ||
28 | static 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 | |||
68 | static 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 | |||
107 | static 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 | |||
235 | done: | ||
236 | SDL_free(yuv1); | ||
237 | SDL_free(yuv2); | ||
238 | SDL_DestroySurface(pattern); | ||
239 | return result; | ||
240 | } | ||
241 | |||
242 | static 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 | |||
357 | done: | ||
358 | SDL_DestroySurface(rgb); | ||
359 | SDL_DestroySurface(yuv); | ||
360 | SDL_DestroyTexture(texture); | ||
361 | SDL_Quit(); | ||
362 | return result; | ||
363 | } | ||
364 | |||
365 | int 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 | } | ||