summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testyuv_cvt.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
committer3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
commit6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch)
tree34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testyuv_cvt.c
parent8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff)
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testyuv_cvt.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testyuv_cvt.c556
1 files changed, 556 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testyuv_cvt.c b/src/contrib/SDL-3.2.20/test/testyuv_cvt.c
new file mode 100644
index 0000000..ef44932
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testyuv_cvt.c
@@ -0,0 +1,556 @@
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
13#include <SDL3/SDL.h>
14
15#include "testyuv_cvt.h"
16
17#define YUV_SD_THRESHOLD 576
18
19static YUV_CONVERSION_MODE YUV_ConversionMode = YUV_CONVERSION_BT601;
20
21void SetYUVConversionMode(YUV_CONVERSION_MODE mode)
22{
23 YUV_ConversionMode = mode;
24}
25
26YUV_CONVERSION_MODE GetYUVConversionMode(void)
27{
28 return YUV_ConversionMode;
29}
30
31YUV_CONVERSION_MODE GetYUVConversionModeForResolution(int width, int height)
32{
33 YUV_CONVERSION_MODE mode = GetYUVConversionMode();
34 if (mode == YUV_CONVERSION_AUTOMATIC) {
35 if (height <= YUV_SD_THRESHOLD) {
36 mode = YUV_CONVERSION_BT601;
37 } else {
38 mode = YUV_CONVERSION_BT709;
39 }
40 }
41 return mode;
42}
43
44SDL_Colorspace GetColorspaceForYUVConversionMode(YUV_CONVERSION_MODE mode)
45{
46 SDL_Colorspace colorspace;
47
48 switch (mode) {
49 case YUV_CONVERSION_JPEG:
50 colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR,
51 SDL_COLOR_RANGE_FULL,
52 SDL_COLOR_PRIMARIES_BT709,
53 SDL_TRANSFER_CHARACTERISTICS_BT601,
54 SDL_MATRIX_COEFFICIENTS_BT601,
55 SDL_CHROMA_LOCATION_CENTER);
56 break;
57 case YUV_CONVERSION_BT601:
58 colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR,
59 SDL_COLOR_RANGE_LIMITED,
60 SDL_COLOR_PRIMARIES_BT709,
61 SDL_TRANSFER_CHARACTERISTICS_BT601,
62 SDL_MATRIX_COEFFICIENTS_BT601,
63 SDL_CHROMA_LOCATION_CENTER);
64 break;
65 case YUV_CONVERSION_BT709:
66 colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR,
67 SDL_COLOR_RANGE_LIMITED,
68 SDL_COLOR_PRIMARIES_BT709,
69 SDL_TRANSFER_CHARACTERISTICS_BT709,
70 SDL_MATRIX_COEFFICIENTS_BT709,
71 SDL_CHROMA_LOCATION_CENTER);
72 break;
73 case YUV_CONVERSION_BT2020:
74 colorspace = SDL_COLORSPACE_BT2020_FULL;
75 break;
76 default:
77 colorspace = SDL_COLORSPACE_UNKNOWN;
78 break;
79 }
80 return colorspace;
81}
82
83static float clip3(float x, float y, float z)
84{
85 return (z < x) ? x : ((z > y) ? y : z);
86}
87
88static float sRGBtoNits(float v)
89{
90 /* Normalize from 0..255 */
91 v /= 255.0f;
92
93 /* Convert from sRGB */
94 v = v <= 0.04045f ? (v / 12.92f) : SDL_powf(((v + 0.055f) / 1.055f), 2.4f);
95
96 /* Convert to nits, using a default SDR whitepoint of 203 */
97 v *= 203.0f;
98
99 return v;
100}
101
102static float PQfromNits(float v)
103{
104 const float c1 = 0.8359375f;
105 const float c2 = 18.8515625f;
106 const float c3 = 18.6875f;
107 const float m1 = 0.1593017578125f;
108 const float m2 = 78.84375f;
109
110 float y = SDL_clamp(v / 10000.0f, 0.0f, 1.0f);
111 float num = c1 + c2 * SDL_powf(y, m1);
112 float den = 1.0f + c3 * SDL_powf(y, m1);
113 return SDL_powf(num / den, m2);
114}
115
116void ConvertRec709toRec2020(float *fR, float *fG, float *fB)
117{
118 static const float mat709to2020[] = {
119 0.627404f, 0.329283f, 0.043313f,
120 0.069097f, 0.919541f, 0.011362f,
121 0.016391f, 0.088013f, 0.895595f,
122 };
123 const float *matrix = mat709to2020;
124 float v[3];
125
126 v[0] = *fR;
127 v[1] = *fG;
128 v[2] = *fB;
129
130 *fR = matrix[0 * 3 + 0] * v[0] + matrix[0 * 3 + 1] * v[1] + matrix[0 * 3 + 2] * v[2];
131 *fG = matrix[1 * 3 + 0] * v[0] + matrix[1 * 3 + 1] * v[1] + matrix[1 * 3 + 2] * v[2];
132 *fB = matrix[2 * 3 + 0] * v[0] + matrix[2 * 3 + 1] * v[1] + matrix[2 * 3 + 2] * v[2];
133}
134
135static void RGBtoYUV(const Uint8 *rgb, int rgb_bits, int *yuv, int yuv_bits, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
136{
137 /**
138 * This formula is from Microsoft's documentation:
139 * https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx
140 * L = Kr * R + Kb * B + (1 - Kr - Kb) * G
141 * Y = floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5);
142 * U = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5));
143 * V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5));
144 */
145 bool studio_RGB = false;
146 bool full_range_YUV = false;
147 float N, M, S, Z, R, G, B, L, Kr, Kb, Y, U, V;
148
149 N = (float)rgb_bits;
150 M = (float)yuv_bits;
151 switch (mode) {
152 case YUV_CONVERSION_JPEG:
153 case YUV_CONVERSION_BT601:
154 /* BT.601 */
155 Kr = 0.299f;
156 Kb = 0.114f;
157 break;
158 case YUV_CONVERSION_BT709:
159 /* BT.709 */
160 Kr = 0.2126f;
161 Kb = 0.0722f;
162 break;
163 case YUV_CONVERSION_BT2020:
164 /* BT.2020 */
165 Kr = 0.2627f;
166 Kb = 0.0593f;
167 break;
168 default:
169 /* Invalid */
170 Kr = 1.0f;
171 Kb = 1.0f;
172 break;
173 }
174
175 R = rgb[0];
176 G = rgb[1];
177 B = rgb[2];
178
179 if (mode == YUV_CONVERSION_JPEG || mode == YUV_CONVERSION_BT2020) {
180 full_range_YUV = true;
181 }
182
183 if (mode == YUV_CONVERSION_BT2020) {
184 /* Input is sRGB, need to convert to BT.2020 PQ YUV */
185 R = sRGBtoNits(R);
186 G = sRGBtoNits(G);
187 B = sRGBtoNits(B);
188 ConvertRec709toRec2020(&R, &G, &B);
189 R = PQfromNits(R);
190 G = PQfromNits(G);
191 B = PQfromNits(B);
192 S = 1.0f;
193 Z = 0.0f;
194 } else if (studio_RGB) {
195 S = 219.0f * SDL_powf(2.0f, N - 8);
196 Z = 16.0f * SDL_powf(2.0f, N - 8);
197 } else {
198 S = 255.0f;
199 Z = 0.0f;
200 }
201 L = Kr * R + Kb * B + (1 - Kr - Kb) * G;
202 if (monochrome) {
203 R = L;
204 B = L;
205 }
206 if (full_range_YUV) {
207 Y = SDL_floorf((SDL_powf(2.0f, M) - 1) * ((L - Z) / S) + 0.5f);
208 U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf((SDL_powf(2.0f, M) / 2 - 1) * ((B - L) / ((1.0f - Kb) * S)) + SDL_powf(2.0f, M) / 2 + 0.5f));
209 V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf((SDL_powf(2.0f, M) / 2 - 1) * ((R - L) / ((1.0f - Kr) * S)) + SDL_powf(2.0f, M) / 2 + 0.5f));
210 } else {
211 Y = SDL_floorf(SDL_powf(2.0f, (M - 8)) * (219.0f * (L - Z) / S + 16) + 0.5f);
212 U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (B - L) / ((1.0f - Kb) * S) + 128) + 0.5f));
213 V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (R - L) / ((1.0f - Kr) * S) + 128) + 0.5f));
214 }
215
216 yuv[0] = (int)Y;
217 yuv[1] = (int)U;
218 yuv[2] = (int)V;
219
220 if (luminance != 100) {
221 yuv[0] = (int)clip3(0, SDL_powf(2.0f, M) - 1, SDL_roundf(yuv[0] * (luminance / 100.0f)));
222 }
223}
224
225static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
226{
227 int x, y;
228 int yuv[4][3];
229 Uint8 *Y1, *Y2, *U, *V;
230 Uint8 *rgb1, *rgb2;
231 int rgb_row_advance = (pitch - w * 3) + pitch;
232 int UV_advance;
233
234 rgb1 = src;
235 rgb2 = src + pitch;
236
237 Y1 = out;
238 Y2 = Y1 + w;
239 switch (format) {
240 case SDL_PIXELFORMAT_YV12:
241 V = (Y1 + h * w);
242 U = V + ((h + 1) / 2) * ((w + 1) / 2);
243 UV_advance = 1;
244 break;
245 case SDL_PIXELFORMAT_IYUV:
246 U = (Y1 + h * w);
247 V = U + ((h + 1) / 2) * ((w + 1) / 2);
248 UV_advance = 1;
249 break;
250 case SDL_PIXELFORMAT_NV12:
251 U = (Y1 + h * w);
252 V = U + 1;
253 UV_advance = 2;
254 break;
255 case SDL_PIXELFORMAT_NV21:
256 V = (Y1 + h * w);
257 U = V + 1;
258 UV_advance = 2;
259 break;
260 default:
261 SDL_assert(!"Unsupported planar YUV format");
262 return;
263 }
264
265 for (y = 0; y < (h - 1); y += 2) {
266 for (x = 0; x < (w - 1); x += 2) {
267 RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance);
268 rgb1 += 3;
269 *Y1++ = (Uint8)yuv[0][0];
270
271 RGBtoYUV(rgb1, 8, yuv[1], 8, mode, monochrome, luminance);
272 rgb1 += 3;
273 *Y1++ = (Uint8)yuv[1][0];
274
275 RGBtoYUV(rgb2, 8, yuv[2], 8, mode, monochrome, luminance);
276 rgb2 += 3;
277 *Y2++ = (Uint8)yuv[2][0];
278
279 RGBtoYUV(rgb2, 8, yuv[3], 8, mode, monochrome, luminance);
280 rgb2 += 3;
281 *Y2++ = (Uint8)yuv[3][0];
282
283 *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1]) / 4.0f + 0.5f);
284 U += UV_advance;
285
286 *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2]) / 4.0f + 0.5f);
287 V += UV_advance;
288 }
289 /* Last column */
290 if (x == (w - 1)) {
291 RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance);
292 rgb1 += 3;
293 *Y1++ = (Uint8)yuv[0][0];
294
295 RGBtoYUV(rgb2, 8, yuv[2], 8, mode, monochrome, luminance);
296 rgb2 += 3;
297 *Y2++ = (Uint8)yuv[2][0];
298
299 *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[2][1]) / 2.0f + 0.5f);
300 U += UV_advance;
301
302 *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[2][2]) / 2.0f + 0.5f);
303 V += UV_advance;
304 }
305 Y1 += w;
306 Y2 += w;
307 rgb1 += rgb_row_advance;
308 rgb2 += rgb_row_advance;
309 }
310 /* Last row */
311 if (y == (h - 1)) {
312 for (x = 0; x < (w - 1); x += 2) {
313 RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance);
314 rgb1 += 3;
315 *Y1++ = (Uint8)yuv[0][0];
316
317 RGBtoYUV(rgb1, 8, yuv[1], 8, mode, monochrome, luminance);
318 rgb1 += 3;
319 *Y1++ = (Uint8)yuv[1][0];
320
321 *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1]) / 2.0f + 0.5f);
322 U += UV_advance;
323
324 *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2]) / 2.0f + 0.5f);
325 V += UV_advance;
326 }
327 /* Last column */
328 if (x == (w - 1)) {
329 RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance);
330 *Y1++ = (Uint8)yuv[0][0];
331
332 *U = (Uint8)yuv[0][1];
333 U += UV_advance;
334
335 *V = (Uint8)yuv[0][2];
336 V += UV_advance;
337 }
338 }
339}
340
341static Uint16 Pack10to16(int v)
342{
343 return (Uint16)(v << 6);
344}
345
346static void ConvertRGBtoPlanar2x2_P010(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
347{
348 int x, y;
349 int yuv[4][3];
350 Uint16 *Y1, *Y2, *U, *V;
351 Uint8 *rgb1, *rgb2;
352 int rgb_row_advance = (pitch - w * 3) + pitch;
353 int UV_advance;
354
355 rgb1 = src;
356 rgb2 = src + pitch;
357
358 Y1 = (Uint16 *)out;
359 Y2 = Y1 + w;
360 switch (format) {
361 case SDL_PIXELFORMAT_P010:
362 U = (Y1 + h * w);
363 V = U + 1;
364 UV_advance = 2;
365 break;
366 default:
367 SDL_assert(!"Unsupported planar YUV format");
368 return;
369 }
370
371 for (y = 0; y < (h - 1); y += 2) {
372 for (x = 0; x < (w - 1); x += 2) {
373 RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance);
374 rgb1 += 3;
375 *Y1++ = Pack10to16(yuv[0][0]);
376
377 RGBtoYUV(rgb1, 8, yuv[1], 10, mode, monochrome, luminance);
378 rgb1 += 3;
379 *Y1++ = Pack10to16(yuv[1][0]);
380
381 RGBtoYUV(rgb2, 8, yuv[2], 10, mode, monochrome, luminance);
382 rgb2 += 3;
383 *Y2++ = Pack10to16(yuv[2][0]);
384
385 RGBtoYUV(rgb2, 8, yuv[3], 10, mode, monochrome, luminance);
386 rgb2 += 3;
387 *Y2++ = Pack10to16(yuv[3][0]);
388
389 *U = Pack10to16((int)SDL_floorf((yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1]) / 4.0f + 0.5f));
390 U += UV_advance;
391
392 *V = Pack10to16((int)SDL_floorf((yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2]) / 4.0f + 0.5f));
393 V += UV_advance;
394 }
395 /* Last column */
396 if (x == (w - 1)) {
397 RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance);
398 rgb1 += 3;
399 *Y1++ = Pack10to16(yuv[0][0]);
400
401 RGBtoYUV(rgb2, 8, yuv[2], 10, mode, monochrome, luminance);
402 rgb2 += 3;
403 *Y2++ = Pack10to16(yuv[2][0]);
404
405 *U = Pack10to16((int)SDL_floorf((yuv[0][1] + yuv[2][1]) / 2.0f + 0.5f));
406 U += UV_advance;
407
408 *V = Pack10to16((int)SDL_floorf((yuv[0][2] + yuv[2][2]) / 2.0f + 0.5f));
409 V += UV_advance;
410 }
411 Y1 += w;
412 Y2 += w;
413 rgb1 += rgb_row_advance;
414 rgb2 += rgb_row_advance;
415 }
416 /* Last row */
417 if (y == (h - 1)) {
418 for (x = 0; x < (w - 1); x += 2) {
419 RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance);
420 rgb1 += 3;
421 *Y1++ = Pack10to16(yuv[0][0]);
422
423 RGBtoYUV(rgb1, 8, yuv[1], 10, mode, monochrome, luminance);
424 rgb1 += 3;
425 *Y1++ = Pack10to16(yuv[1][0]);
426
427 *U = Pack10to16((int)SDL_floorf((yuv[0][1] + yuv[1][1]) / 2.0f + 0.5f));
428 U += UV_advance;
429
430 *V = Pack10to16((int)SDL_floorf((yuv[0][2] + yuv[1][2]) / 2.0f + 0.5f));
431 V += UV_advance;
432 }
433 /* Last column */
434 if (x == (w - 1)) {
435 RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance);
436 *Y1++ = Pack10to16(yuv[0][0]);
437
438 *U = Pack10to16(yuv[0][1]);
439 U += UV_advance;
440
441 *V = Pack10to16(yuv[0][2]);
442 V += UV_advance;
443 }
444 }
445}
446
447static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
448{
449 int x, y;
450 int yuv[2][3];
451 Uint8 *Y1, *Y2, *U, *V;
452 Uint8 *rgb;
453 int rgb_row_advance = (pitch - w * 3);
454
455 rgb = src;
456
457 switch (format) {
458 case SDL_PIXELFORMAT_YUY2:
459 Y1 = out;
460 U = out + 1;
461 Y2 = out + 2;
462 V = out + 3;
463 break;
464 case SDL_PIXELFORMAT_UYVY:
465 U = out;
466 Y1 = out + 1;
467 V = out + 2;
468 Y2 = out + 3;
469 break;
470 case SDL_PIXELFORMAT_YVYU:
471 Y1 = out;
472 V = out + 1;
473 Y2 = out + 2;
474 U = out + 3;
475 break;
476 default:
477 SDL_assert(!"Unsupported packed YUV format");
478 return;
479 }
480
481 for (y = 0; y < h; ++y) {
482 for (x = 0; x < (w - 1); x += 2) {
483 RGBtoYUV(rgb, 8, yuv[0], 8, mode, monochrome, luminance);
484 rgb += 3;
485 *Y1 = (Uint8)yuv[0][0];
486 Y1 += 4;
487
488 RGBtoYUV(rgb, 8, yuv[1], 8, mode, monochrome, luminance);
489 rgb += 3;
490 *Y2 = (Uint8)yuv[1][0];
491 Y2 += 4;
492
493 *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1]) / 2.0f + 0.5f);
494 U += 4;
495
496 *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2]) / 2.0f + 0.5f);
497 V += 4;
498 }
499 /* Last column */
500 if (x == (w - 1)) {
501 RGBtoYUV(rgb, 8, yuv[0], 8, mode, monochrome, luminance);
502 rgb += 3;
503 *Y2 = *Y1 = (Uint8)yuv[0][0];
504 Y1 += 4;
505 Y2 += 4;
506
507 *U = (Uint8)yuv[0][1];
508 U += 4;
509
510 *V = (Uint8)yuv[0][2];
511 V += 4;
512 }
513 rgb += rgb_row_advance;
514 }
515}
516
517bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
518{
519 switch (format) {
520 case SDL_PIXELFORMAT_P010:
521 ConvertRGBtoPlanar2x2_P010(format, src, pitch, out, w, h, mode, monochrome, luminance);
522 return true;
523 case SDL_PIXELFORMAT_YV12:
524 case SDL_PIXELFORMAT_IYUV:
525 case SDL_PIXELFORMAT_NV12:
526 case SDL_PIXELFORMAT_NV21:
527 ConvertRGBtoPlanar2x2(format, src, pitch, out, w, h, mode, monochrome, luminance);
528 return true;
529 case SDL_PIXELFORMAT_YUY2:
530 case SDL_PIXELFORMAT_UYVY:
531 case SDL_PIXELFORMAT_YVYU:
532 ConvertRGBtoPacked4(format, src, pitch, out, w, h, mode, monochrome, luminance);
533 return true;
534 default:
535 return false;
536 }
537}
538
539int CalculateYUVPitch(Uint32 format, int width)
540{
541 switch (format) {
542 case SDL_PIXELFORMAT_P010:
543 return width * 2;
544 case SDL_PIXELFORMAT_YV12:
545 case SDL_PIXELFORMAT_IYUV:
546 case SDL_PIXELFORMAT_NV12:
547 case SDL_PIXELFORMAT_NV21:
548 return width;
549 case SDL_PIXELFORMAT_YUY2:
550 case SDL_PIXELFORMAT_UYVY:
551 case SDL_PIXELFORMAT_YVYU:
552 return 4 * ((width + 1) / 2);
553 default:
554 return 0;
555 }
556}