summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/SDL_yuv.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/SDL_yuv.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/SDL_yuv.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/SDL_yuv.c2592
1 files changed, 2592 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/SDL_yuv.c b/contrib/SDL-3.2.8/src/video/SDL_yuv.c
new file mode 100644
index 0000000..ccacfcf
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/SDL_yuv.c
@@ -0,0 +1,2592 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include "SDL_pixels_c.h"
24#include "SDL_yuv_c.h"
25
26#include "yuv2rgb/yuv_rgb.h"
27
28
29#ifdef SDL_HAVE_YUV
30static bool IsPlanar2x2Format(SDL_PixelFormat format);
31#endif
32
33/*
34 * Calculate YUV size and pitch. Check for overflow.
35 * Output 'pitch' that can be used with SDL_ConvertPixels()
36 */
37bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, size_t *pitch)
38{
39#ifdef SDL_HAVE_YUV
40 int sz_plane = 0, sz_plane_chroma = 0, sz_plane_packed = 0;
41
42 if (IsPlanar2x2Format(format) == true) {
43 {
44 /* sz_plane == w * h; */
45 size_t s1;
46 if (!SDL_size_mul_check_overflow(w, h, &s1)) {
47 return SDL_SetError("width * height would overflow");
48 }
49 sz_plane = (int) s1;
50 }
51
52 {
53 /* sz_plane_chroma == ((w + 1) / 2) * ((h + 1) / 2); */
54 size_t s1, s2, s3;
55 if (!SDL_size_add_check_overflow(w, 1, &s1)) {
56 return SDL_SetError("width + 1 would overflow");
57 }
58 s1 = s1 / 2;
59 if (!SDL_size_add_check_overflow(h, 1, &s2)) {
60 return SDL_SetError("height + 1 would overflow");
61 }
62 s2 = s2 / 2;
63 if (!SDL_size_mul_check_overflow(s1, s2, &s3)) {
64 return SDL_SetError("width * height would overflow");
65 }
66 sz_plane_chroma = (int) s3;
67 }
68 } else {
69 /* sz_plane_packed == ((w + 1) / 2) * h; */
70 size_t s1, s2;
71 if (!SDL_size_add_check_overflow(w, 1, &s1)) {
72 return SDL_SetError("width + 1 would overflow");
73 }
74 s1 = s1 / 2;
75 if (!SDL_size_mul_check_overflow(s1, h, &s2)) {
76 return SDL_SetError("width * height would overflow");
77 }
78 sz_plane_packed = (int) s2;
79 }
80
81 switch (format) {
82 case SDL_PIXELFORMAT_YV12: /**< Planar mode: Y + V + U (3 planes) */
83 case SDL_PIXELFORMAT_IYUV: /**< Planar mode: Y + U + V (3 planes) */
84
85 if (pitch) {
86 *pitch = w;
87 }
88
89 if (size) {
90 // dst_size == sz_plane + sz_plane_chroma + sz_plane_chroma;
91 size_t s1, s2;
92 if (!SDL_size_add_check_overflow(sz_plane, sz_plane_chroma, &s1)) {
93 return SDL_SetError("Y + U would overflow");
94 }
95 if (!SDL_size_add_check_overflow(s1, sz_plane_chroma, &s2)) {
96 return SDL_SetError("Y + U + V would overflow");
97 }
98 *size = (int)s2;
99 }
100 break;
101
102 case SDL_PIXELFORMAT_YUY2: /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
103 case SDL_PIXELFORMAT_UYVY: /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
104 case SDL_PIXELFORMAT_YVYU: /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
105
106 if (pitch) {
107 /* pitch == ((w + 1) / 2) * 4; */
108 size_t p1, p2;
109 if (!SDL_size_add_check_overflow(w, 1, &p1)) {
110 return SDL_SetError("width + 1 would overflow");
111 }
112 p1 = p1 / 2;
113 if (!SDL_size_mul_check_overflow(p1, 4, &p2)) {
114 return SDL_SetError("width * 4 would overflow");
115 }
116 *pitch = p2;
117 }
118
119 if (size) {
120 /* dst_size == 4 * sz_plane_packed; */
121 size_t s1;
122 if (!SDL_size_mul_check_overflow(sz_plane_packed, 4, &s1)) {
123 return SDL_SetError("plane * 4 would overflow");
124 }
125 *size = (int) s1;
126 }
127 break;
128
129 case SDL_PIXELFORMAT_NV12: /**< Planar mode: Y + U/V interleaved (2 planes) */
130 case SDL_PIXELFORMAT_NV21: /**< Planar mode: Y + V/U interleaved (2 planes) */
131 if (pitch) {
132 *pitch = w;
133 }
134
135 if (size) {
136 // dst_size == sz_plane + sz_plane_chroma + sz_plane_chroma;
137 size_t s1, s2;
138 if (!SDL_size_add_check_overflow(sz_plane, sz_plane_chroma, &s1)) {
139 return SDL_SetError("Y + U would overflow");
140 }
141 if (!SDL_size_add_check_overflow(s1, sz_plane_chroma, &s2)) {
142 return SDL_SetError("Y + U + V would overflow");
143 }
144 *size = (int) s2;
145 }
146 break;
147
148 default:
149 return SDL_Unsupported();
150 }
151
152 return true;
153#else
154 return SDL_Unsupported();
155#endif
156}
157
158#ifdef SDL_HAVE_YUV
159
160static bool GetYUVConversionType(SDL_Colorspace colorspace, YCbCrType *yuv_type)
161{
162 if (SDL_ISCOLORSPACE_MATRIX_BT601(colorspace)) {
163 if (SDL_ISCOLORSPACE_LIMITED_RANGE(colorspace)) {
164 *yuv_type = YCBCR_601_LIMITED;
165 } else {
166 *yuv_type = YCBCR_601_FULL;
167 }
168 return true;
169 }
170
171 if (SDL_ISCOLORSPACE_MATRIX_BT709(colorspace)) {
172 if (SDL_ISCOLORSPACE_LIMITED_RANGE(colorspace)) {
173 *yuv_type = YCBCR_709_LIMITED;
174 } else {
175 *yuv_type = YCBCR_709_FULL;
176 }
177 return true;
178 }
179
180 if (SDL_ISCOLORSPACE_MATRIX_BT2020_NCL(colorspace)) {
181 if (SDL_ISCOLORSPACE_FULL_RANGE(colorspace)) {
182 *yuv_type = YCBCR_2020_NCL_FULL;
183 return true;
184 }
185 }
186
187 return SDL_SetError("Unsupported YUV colorspace");
188}
189
190static bool IsPlanar2x2Format(SDL_PixelFormat format)
191{
192 return format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV || format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21 || format == SDL_PIXELFORMAT_P010;
193}
194
195static bool IsPacked4Format(Uint32 format)
196{
197 return format == SDL_PIXELFORMAT_YUY2 || format == SDL_PIXELFORMAT_UYVY || format == SDL_PIXELFORMAT_YVYU;
198}
199
200static bool GetYUVPlanes(int width, int height, SDL_PixelFormat format, const void *yuv, int yuv_pitch,
201 const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride)
202{
203 const Uint8 *planes[3] = { NULL, NULL, NULL };
204 int pitches[3] = { 0, 0, 0 };
205 int uv_width;
206
207 switch (format) {
208 case SDL_PIXELFORMAT_YV12:
209 case SDL_PIXELFORMAT_IYUV:
210 pitches[0] = yuv_pitch;
211 pitches[1] = (pitches[0] + 1) / 2;
212 pitches[2] = (pitches[0] + 1) / 2;
213 planes[0] = (const Uint8 *)yuv;
214 planes[1] = planes[0] + pitches[0] * height;
215 planes[2] = planes[1] + pitches[1] * ((height + 1) / 2);
216 break;
217 case SDL_PIXELFORMAT_YUY2:
218 case SDL_PIXELFORMAT_UYVY:
219 case SDL_PIXELFORMAT_YVYU:
220 pitches[0] = yuv_pitch;
221 planes[0] = (const Uint8 *)yuv;
222 break;
223 case SDL_PIXELFORMAT_NV12:
224 case SDL_PIXELFORMAT_NV21:
225 pitches[0] = yuv_pitch;
226 pitches[1] = 2 * ((pitches[0] + 1) / 2);
227 planes[0] = (const Uint8 *)yuv;
228 planes[1] = planes[0] + pitches[0] * height;
229 break;
230 case SDL_PIXELFORMAT_P010:
231 pitches[0] = yuv_pitch;
232 uv_width = ((width + 1) / 2) * 2;
233 pitches[1] = SDL_max(pitches[0], (int)(uv_width * sizeof(Uint16)));
234 planes[0] = (const Uint8 *)yuv;
235 planes[1] = planes[0] + pitches[0] * height;
236 break;
237 default:
238 return SDL_SetError("GetYUVPlanes(): Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
239 }
240
241 switch (format) {
242 case SDL_PIXELFORMAT_YV12:
243 *y = planes[0];
244 *y_stride = pitches[0];
245 *v = planes[1];
246 *u = planes[2];
247 *uv_stride = pitches[1];
248 break;
249 case SDL_PIXELFORMAT_IYUV:
250 *y = planes[0];
251 *y_stride = pitches[0];
252 *v = planes[2];
253 *u = planes[1];
254 *uv_stride = pitches[1];
255 break;
256 case SDL_PIXELFORMAT_YUY2:
257 *y = planes[0];
258 *y_stride = pitches[0];
259 *v = *y + 3;
260 *u = *y + 1;
261 *uv_stride = pitches[0];
262 break;
263 case SDL_PIXELFORMAT_UYVY:
264 *y = planes[0] + 1;
265 *y_stride = pitches[0];
266 *v = *y + 1;
267 *u = *y - 1;
268 *uv_stride = pitches[0];
269 break;
270 case SDL_PIXELFORMAT_YVYU:
271 *y = planes[0];
272 *y_stride = pitches[0];
273 *v = *y + 1;
274 *u = *y + 3;
275 *uv_stride = pitches[0];
276 break;
277 case SDL_PIXELFORMAT_NV12:
278 *y = planes[0];
279 *y_stride = pitches[0];
280 *u = planes[1];
281 *v = *u + 1;
282 *uv_stride = pitches[1];
283 break;
284 case SDL_PIXELFORMAT_NV21:
285 *y = planes[0];
286 *y_stride = pitches[0];
287 *v = planes[1];
288 *u = *v + 1;
289 *uv_stride = pitches[1];
290 break;
291 case SDL_PIXELFORMAT_P010:
292 *y = planes[0];
293 *y_stride = pitches[0];
294 *u = planes[1];
295 *v = *u + sizeof(Uint16);
296 *uv_stride = pitches[1];
297 break;
298 default:
299 // Should have caught this above
300 return SDL_SetError("GetYUVPlanes[2]: Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
301 }
302 return true;
303}
304
305#ifdef SDL_SSE2_INTRINSICS
306static bool SDL_TARGETING("sse2") yuv_rgb_sse(
307 SDL_PixelFormat src_format, SDL_PixelFormat dst_format,
308 Uint32 width, Uint32 height,
309 const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
310 Uint8 *rgb, Uint32 rgb_stride,
311 YCbCrType yuv_type)
312{
313 if (!SDL_HasSSE2()) {
314 return false;
315 }
316
317 if (src_format == SDL_PIXELFORMAT_YV12 ||
318 src_format == SDL_PIXELFORMAT_IYUV) {
319
320 switch (dst_format) {
321 case SDL_PIXELFORMAT_RGB565:
322 yuv420_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
323 return true;
324 case SDL_PIXELFORMAT_RGB24:
325 yuv420_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
326 return true;
327 case SDL_PIXELFORMAT_RGBX8888:
328 case SDL_PIXELFORMAT_RGBA8888:
329 yuv420_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
330 return true;
331 case SDL_PIXELFORMAT_BGRX8888:
332 case SDL_PIXELFORMAT_BGRA8888:
333 yuv420_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
334 return true;
335 case SDL_PIXELFORMAT_XRGB8888:
336 case SDL_PIXELFORMAT_ARGB8888:
337 yuv420_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
338 return true;
339 case SDL_PIXELFORMAT_XBGR8888:
340 case SDL_PIXELFORMAT_ABGR8888:
341 yuv420_abgr_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
342 return true;
343 default:
344 break;
345 }
346 }
347
348 if (src_format == SDL_PIXELFORMAT_YUY2 ||
349 src_format == SDL_PIXELFORMAT_UYVY ||
350 src_format == SDL_PIXELFORMAT_YVYU) {
351
352 switch (dst_format) {
353 case SDL_PIXELFORMAT_RGB565:
354 yuv422_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
355 return true;
356 case SDL_PIXELFORMAT_RGB24:
357 yuv422_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
358 return true;
359 case SDL_PIXELFORMAT_RGBX8888:
360 case SDL_PIXELFORMAT_RGBA8888:
361 yuv422_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
362 return true;
363 case SDL_PIXELFORMAT_BGRX8888:
364 case SDL_PIXELFORMAT_BGRA8888:
365 yuv422_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
366 return true;
367 case SDL_PIXELFORMAT_XRGB8888:
368 case SDL_PIXELFORMAT_ARGB8888:
369 yuv422_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
370 return true;
371 case SDL_PIXELFORMAT_XBGR8888:
372 case SDL_PIXELFORMAT_ABGR8888:
373 yuv422_abgr_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
374 return true;
375 default:
376 break;
377 }
378 }
379
380 if (src_format == SDL_PIXELFORMAT_NV12 ||
381 src_format == SDL_PIXELFORMAT_NV21) {
382
383 switch (dst_format) {
384 case SDL_PIXELFORMAT_RGB565:
385 yuvnv12_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
386 return true;
387 case SDL_PIXELFORMAT_RGB24:
388 yuvnv12_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
389 return true;
390 case SDL_PIXELFORMAT_RGBX8888:
391 case SDL_PIXELFORMAT_RGBA8888:
392 yuvnv12_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
393 return true;
394 case SDL_PIXELFORMAT_BGRX8888:
395 case SDL_PIXELFORMAT_BGRA8888:
396 yuvnv12_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
397 return true;
398 case SDL_PIXELFORMAT_XRGB8888:
399 case SDL_PIXELFORMAT_ARGB8888:
400 yuvnv12_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
401 return true;
402 case SDL_PIXELFORMAT_XBGR8888:
403 case SDL_PIXELFORMAT_ABGR8888:
404 yuvnv12_abgr_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
405 return true;
406 default:
407 break;
408 }
409 }
410 return false;
411}
412#else
413static bool yuv_rgb_sse(
414 SDL_PixelFormat src_format, SDL_PixelFormat dst_format,
415 Uint32 width, Uint32 height,
416 const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
417 Uint8 *rgb, Uint32 rgb_stride,
418 YCbCrType yuv_type)
419{
420 return false;
421}
422#endif
423
424#ifdef SDL_LSX_INTRINSICS
425static bool yuv_rgb_lsx(
426 SDL_PixelFormat src_format, SDL_PixelFormat dst_format,
427 Uint32 width, Uint32 height,
428 const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
429 Uint8 *rgb, Uint32 rgb_stride,
430 YCbCrType yuv_type)
431{
432 if (!SDL_HasLSX()) {
433 return false;
434 }
435 if (src_format == SDL_PIXELFORMAT_YV12 ||
436 src_format == SDL_PIXELFORMAT_IYUV) {
437
438 switch (dst_format) {
439 case SDL_PIXELFORMAT_RGB24:
440 yuv420_rgb24_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
441 return true;
442 case SDL_PIXELFORMAT_RGBX8888:
443 case SDL_PIXELFORMAT_RGBA8888:
444 yuv420_rgba_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
445 return true;
446 case SDL_PIXELFORMAT_BGRX8888:
447 case SDL_PIXELFORMAT_BGRA8888:
448 yuv420_bgra_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
449 return true;
450 case SDL_PIXELFORMAT_XRGB8888:
451 case SDL_PIXELFORMAT_ARGB8888:
452 yuv420_argb_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
453 return true;
454 case SDL_PIXELFORMAT_XBGR8888:
455 case SDL_PIXELFORMAT_ABGR8888:
456 yuv420_abgr_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
457 return true;
458 default:
459 break;
460 }
461 }
462 return false;
463}
464#else
465static bool yuv_rgb_lsx(
466 SDL_PixelFormat src_format, SDL_PixelFormat dst_format,
467 Uint32 width, Uint32 height,
468 const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
469 Uint8 *rgb, Uint32 rgb_stride,
470 YCbCrType yuv_type)
471{
472 return false;
473}
474#endif
475
476static bool yuv_rgb_std(
477 SDL_PixelFormat src_format, SDL_PixelFormat dst_format,
478 Uint32 width, Uint32 height,
479 const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
480 Uint8 *rgb, Uint32 rgb_stride,
481 YCbCrType yuv_type)
482{
483 if (src_format == SDL_PIXELFORMAT_YV12 ||
484 src_format == SDL_PIXELFORMAT_IYUV) {
485
486 switch (dst_format) {
487 case SDL_PIXELFORMAT_RGB565:
488 yuv420_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
489 return true;
490 case SDL_PIXELFORMAT_RGB24:
491 yuv420_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
492 return true;
493 case SDL_PIXELFORMAT_RGBX8888:
494 case SDL_PIXELFORMAT_RGBA8888:
495 yuv420_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
496 return true;
497 case SDL_PIXELFORMAT_BGRX8888:
498 case SDL_PIXELFORMAT_BGRA8888:
499 yuv420_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
500 return true;
501 case SDL_PIXELFORMAT_XRGB8888:
502 case SDL_PIXELFORMAT_ARGB8888:
503 yuv420_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
504 return true;
505 case SDL_PIXELFORMAT_XBGR8888:
506 case SDL_PIXELFORMAT_ABGR8888:
507 yuv420_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
508 return true;
509 default:
510 break;
511 }
512 }
513
514 if (src_format == SDL_PIXELFORMAT_YUY2 ||
515 src_format == SDL_PIXELFORMAT_UYVY ||
516 src_format == SDL_PIXELFORMAT_YVYU) {
517
518 switch (dst_format) {
519 case SDL_PIXELFORMAT_RGB565:
520 yuv422_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
521 return true;
522 case SDL_PIXELFORMAT_RGB24:
523 yuv422_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
524 return true;
525 case SDL_PIXELFORMAT_RGBX8888:
526 case SDL_PIXELFORMAT_RGBA8888:
527 yuv422_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
528 return true;
529 case SDL_PIXELFORMAT_BGRX8888:
530 case SDL_PIXELFORMAT_BGRA8888:
531 yuv422_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
532 return true;
533 case SDL_PIXELFORMAT_XRGB8888:
534 case SDL_PIXELFORMAT_ARGB8888:
535 yuv422_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
536 return true;
537 case SDL_PIXELFORMAT_XBGR8888:
538 case SDL_PIXELFORMAT_ABGR8888:
539 yuv422_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
540 return true;
541 default:
542 break;
543 }
544 }
545
546 if (src_format == SDL_PIXELFORMAT_NV12 ||
547 src_format == SDL_PIXELFORMAT_NV21) {
548
549 switch (dst_format) {
550 case SDL_PIXELFORMAT_RGB565:
551 yuvnv12_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
552 return true;
553 case SDL_PIXELFORMAT_RGB24:
554 yuvnv12_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
555 return true;
556 case SDL_PIXELFORMAT_RGBX8888:
557 case SDL_PIXELFORMAT_RGBA8888:
558 yuvnv12_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
559 return true;
560 case SDL_PIXELFORMAT_BGRX8888:
561 case SDL_PIXELFORMAT_BGRA8888:
562 yuvnv12_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
563 return true;
564 case SDL_PIXELFORMAT_XRGB8888:
565 case SDL_PIXELFORMAT_ARGB8888:
566 yuvnv12_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
567 return true;
568 case SDL_PIXELFORMAT_XBGR8888:
569 case SDL_PIXELFORMAT_ABGR8888:
570 yuvnv12_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
571 return true;
572 default:
573 break;
574 }
575 }
576
577 if (src_format == SDL_PIXELFORMAT_P010) {
578 switch (dst_format) {
579 case SDL_PIXELFORMAT_XBGR2101010:
580 yuvp010_xbgr2101010_std(width, height, (const uint16_t *)y, (const uint16_t *)u, (const uint16_t *)v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
581 return true;
582 default:
583 break;
584 }
585 }
586 return false;
587}
588
589bool SDL_ConvertPixels_YUV_to_RGB(int width, int height,
590 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
591 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
592{
593 const Uint8 *y = NULL;
594 const Uint8 *u = NULL;
595 const Uint8 *v = NULL;
596 Uint32 y_stride = 0;
597 Uint32 uv_stride = 0;
598
599 if (!GetYUVPlanes(width, height, src_format, src, src_pitch, &y, &u, &v, &y_stride, &uv_stride)) {
600 return false;
601 }
602
603 if (SDL_COLORSPACEPRIMARIES(src_colorspace) == SDL_COLORSPACEPRIMARIES(dst_colorspace)) {
604 YCbCrType yuv_type = YCBCR_601_LIMITED;
605
606 if (!GetYUVConversionType(src_colorspace, &yuv_type)) {
607 return false;
608 }
609
610 if (yuv_rgb_sse(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) {
611 return true;
612 }
613
614 if (yuv_rgb_lsx(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) {
615 return true;
616 }
617
618 if (yuv_rgb_std(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) {
619 return true;
620 }
621 }
622
623 // No fast path for the RGB format, instead convert using an intermediate buffer
624 if (src_format == SDL_PIXELFORMAT_P010 && dst_format != SDL_PIXELFORMAT_XBGR2101010) {
625 bool result;
626 void *tmp;
627 int tmp_pitch = (width * sizeof(Uint32));
628
629 tmp = SDL_malloc((size_t)tmp_pitch * height);
630 if (!tmp) {
631 return false;
632 }
633
634 // convert src/src_format to tmp/XBGR2101010
635 result = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_XBGR2101010, src_colorspace, src_properties, tmp, tmp_pitch);
636 if (!result) {
637 SDL_free(tmp);
638 return false;
639 }
640
641 // convert tmp/XBGR2101010 to dst/RGB
642 result = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_XBGR2101010, src_colorspace, src_properties, tmp, tmp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
643 SDL_free(tmp);
644 return result;
645 }
646
647 if (dst_format != SDL_PIXELFORMAT_ARGB8888) {
648 bool result;
649 void *tmp;
650 int tmp_pitch = (width * sizeof(Uint32));
651
652 tmp = SDL_malloc((size_t)tmp_pitch * height);
653 if (!tmp) {
654 return false;
655 }
656
657 // convert src/src_format to tmp/ARGB8888
658 result = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch);
659 if (!result) {
660 SDL_free(tmp);
661 return false;
662 }
663
664 // convert tmp/ARGB8888 to dst/RGB
665 result = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
666 SDL_free(tmp);
667 return result;
668 }
669
670 return SDL_SetError("Unsupported YUV conversion");
671}
672
673struct RGB2YUVFactors
674{
675 int y_offset;
676 float y[3]; // Rfactor, Gfactor, Bfactor
677 float u[3]; // Rfactor, Gfactor, Bfactor
678 float v[3]; // Rfactor, Gfactor, Bfactor
679};
680
681static struct RGB2YUVFactors RGB2YUVFactorTables[] = {
682 // ITU-T T.871 (JPEG)
683 {
684 0,
685 { 0.2990f, 0.5870f, 0.1140f },
686 { -0.1687f, -0.3313f, 0.5000f },
687 { 0.5000f, -0.4187f, -0.0813f },
688 },
689 // ITU-R BT.601-7
690 {
691 16,
692 { 0.2568f, 0.5041f, 0.0979f },
693 { -0.1482f, -0.2910f, 0.4392f },
694 { 0.4392f, -0.3678f, -0.0714f },
695 },
696 // ITU-R BT.709-6 full range
697 {
698 0,
699 { 0.2126f, 0.7152f, 0.0722f },
700 { -0.1141f, -0.3839f, 0.498f },
701 { 0.498f, -0.4524f, -0.0457f },
702 },
703 // ITU-R BT.709-6
704 {
705 16,
706 { 0.1826f, 0.6142f, 0.0620f },
707 { -0.1006f, -0.3386f, 0.4392f },
708 { 0.4392f, -0.3989f, -0.0403f },
709 },
710 // ITU-R BT.2020 10-bit full range
711 {
712 0,
713 { 0.2627f, 0.6780f, 0.0593f },
714 { -0.1395f, -0.3600f, 0.4995f },
715 { 0.4995f, -0.4593f, -0.0402f },
716 },
717};
718
719static bool SDL_ConvertPixels_XRGB8888_to_YUV(int width, int height, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch, YCbCrType yuv_type)
720{
721 const int src_pitch_x_2 = src_pitch * 2;
722 const int height_half = height / 2;
723 const int height_remainder = (height & 0x1);
724 const int width_half = width / 2;
725 const int width_remainder = (width & 0x1);
726 int i, j;
727
728 const struct RGB2YUVFactors *cvt = &RGB2YUVFactorTables[yuv_type];
729
730#define MAKE_Y(r, g, b) (Uint8)SDL_clamp(((int)(cvt->y[0] * (r) + cvt->y[1] * (g) + cvt->y[2] * (b) + 0.5f) + cvt->y_offset), 0, 255)
731#define MAKE_U(r, g, b) (Uint8)SDL_clamp(((int)(cvt->u[0] * (r) + cvt->u[1] * (g) + cvt->u[2] * (b) + 0.5f) + 128), 0, 255)
732#define MAKE_V(r, g, b) (Uint8)SDL_clamp(((int)(cvt->v[0] * (r) + cvt->v[1] * (g) + cvt->v[2] * (b) + 0.5f) + 128), 0, 255)
733
734#define READ_2x2_PIXELS \
735 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
736 const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \
737 const Uint32 p3 = ((const Uint32 *)next_row)[2 * i]; \
738 const Uint32 p4 = ((const Uint32 *)next_row)[2 * i + 1]; \
739 const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000) + (p3 & 0x00ff0000) + (p4 & 0x00ff0000)) >> 18; \
740 const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00) + (p3 & 0x0000ff00) + (p4 & 0x0000ff00)) >> 10; \
741 const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff) + (p3 & 0x000000ff) + (p4 & 0x000000ff)) >> 2;
742
743#define READ_2x1_PIXELS \
744 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
745 const Uint32 p2 = ((const Uint32 *)next_row)[2 * i]; \
746 const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000)) >> 17; \
747 const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00)) >> 9; \
748 const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff)) >> 1;
749
750#define READ_1x2_PIXELS \
751 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
752 const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \
753 const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000)) >> 17; \
754 const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00)) >> 9; \
755 const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff)) >> 1;
756
757#define READ_1x1_PIXEL \
758 const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \
759 const Uint32 r = (p & 0x00ff0000) >> 16; \
760 const Uint32 g = (p & 0x0000ff00) >> 8; \
761 const Uint32 b = (p & 0x000000ff);
762
763#define READ_TWO_RGB_PIXELS \
764 const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \
765 const Uint32 r = (p & 0x00ff0000) >> 16; \
766 const Uint32 g = (p & 0x0000ff00) >> 8; \
767 const Uint32 b = (p & 0x000000ff); \
768 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i + 1]; \
769 const Uint32 r1 = (p1 & 0x00ff0000) >> 16; \
770 const Uint32 g1 = (p1 & 0x0000ff00) >> 8; \
771 const Uint32 b1 = (p1 & 0x000000ff); \
772 const Uint32 R = (r + r1) / 2; \
773 const Uint32 G = (g + g1) / 2; \
774 const Uint32 B = (b + b1) / 2;
775
776#define READ_ONE_RGB_PIXEL READ_1x1_PIXEL
777
778 switch (dst_format) {
779 case SDL_PIXELFORMAT_YV12:
780 case SDL_PIXELFORMAT_IYUV:
781 case SDL_PIXELFORMAT_NV12:
782 case SDL_PIXELFORMAT_NV21:
783 {
784 const Uint8 *curr_row, *next_row;
785
786 Uint8 *plane_y;
787 Uint8 *plane_u;
788 Uint8 *plane_v;
789 Uint8 *plane_interleaved_uv;
790 Uint32 y_stride, uv_stride, y_skip, uv_skip;
791
792 if (!GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
793 (const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v,
794 &y_stride, &uv_stride)) {
795 return false;
796 }
797
798 plane_interleaved_uv = (plane_y + height * y_stride);
799 y_skip = (y_stride - width);
800
801 curr_row = (const Uint8 *)src;
802
803 // Write Y plane
804 for (j = 0; j < height; j++) {
805 for (i = 0; i < width; i++) {
806 const Uint32 p1 = ((const Uint32 *)curr_row)[i];
807 const Uint32 r = (p1 & 0x00ff0000) >> 16;
808 const Uint32 g = (p1 & 0x0000ff00) >> 8;
809 const Uint32 b = (p1 & 0x000000ff);
810 *plane_y++ = MAKE_Y(r, g, b);
811 }
812 plane_y += y_skip;
813 curr_row += src_pitch;
814 }
815
816 curr_row = (const Uint8 *)src;
817 next_row = (const Uint8 *)src;
818 next_row += src_pitch;
819
820 if (dst_format == SDL_PIXELFORMAT_YV12 || dst_format == SDL_PIXELFORMAT_IYUV) {
821 // Write UV planes, not interleaved
822 uv_skip = (uv_stride - (width + 1) / 2);
823 for (j = 0; j < height_half; j++) {
824 for (i = 0; i < width_half; i++) {
825 READ_2x2_PIXELS;
826 *plane_u++ = MAKE_U(r, g, b);
827 *plane_v++ = MAKE_V(r, g, b);
828 }
829 if (width_remainder) {
830 READ_2x1_PIXELS;
831 *plane_u++ = MAKE_U(r, g, b);
832 *plane_v++ = MAKE_V(r, g, b);
833 }
834 plane_u += uv_skip;
835 plane_v += uv_skip;
836 curr_row += src_pitch_x_2;
837 next_row += src_pitch_x_2;
838 }
839 if (height_remainder) {
840 for (i = 0; i < width_half; i++) {
841 READ_1x2_PIXELS;
842 *plane_u++ = MAKE_U(r, g, b);
843 *plane_v++ = MAKE_V(r, g, b);
844 }
845 if (width_remainder) {
846 READ_1x1_PIXEL;
847 *plane_u++ = MAKE_U(r, g, b);
848 *plane_v++ = MAKE_V(r, g, b);
849 }
850 plane_u += uv_skip;
851 plane_v += uv_skip;
852 }
853 } else if (dst_format == SDL_PIXELFORMAT_NV12) {
854 uv_skip = (uv_stride - ((width + 1) / 2) * 2);
855 for (j = 0; j < height_half; j++) {
856 for (i = 0; i < width_half; i++) {
857 READ_2x2_PIXELS;
858 *plane_interleaved_uv++ = MAKE_U(r, g, b);
859 *plane_interleaved_uv++ = MAKE_V(r, g, b);
860 }
861 if (width_remainder) {
862 READ_2x1_PIXELS;
863 *plane_interleaved_uv++ = MAKE_U(r, g, b);
864 *plane_interleaved_uv++ = MAKE_V(r, g, b);
865 }
866 plane_interleaved_uv += uv_skip;
867 curr_row += src_pitch_x_2;
868 next_row += src_pitch_x_2;
869 }
870 if (height_remainder) {
871 for (i = 0; i < width_half; i++) {
872 READ_1x2_PIXELS;
873 *plane_interleaved_uv++ = MAKE_U(r, g, b);
874 *plane_interleaved_uv++ = MAKE_V(r, g, b);
875 }
876 if (width_remainder) {
877 READ_1x1_PIXEL;
878 *plane_interleaved_uv++ = MAKE_U(r, g, b);
879 *plane_interleaved_uv++ = MAKE_V(r, g, b);
880 }
881 }
882 } else /* dst_format == SDL_PIXELFORMAT_NV21 */ {
883 uv_skip = (uv_stride - ((width + 1) / 2) * 2);
884 for (j = 0; j < height_half; j++) {
885 for (i = 0; i < width_half; i++) {
886 READ_2x2_PIXELS;
887 *plane_interleaved_uv++ = MAKE_V(r, g, b);
888 *plane_interleaved_uv++ = MAKE_U(r, g, b);
889 }
890 if (width_remainder) {
891 READ_2x1_PIXELS;
892 *plane_interleaved_uv++ = MAKE_V(r, g, b);
893 *plane_interleaved_uv++ = MAKE_U(r, g, b);
894 }
895 plane_interleaved_uv += uv_skip;
896 curr_row += src_pitch_x_2;
897 next_row += src_pitch_x_2;
898 }
899 if (height_remainder) {
900 for (i = 0; i < width_half; i++) {
901 READ_1x2_PIXELS;
902 *plane_interleaved_uv++ = MAKE_V(r, g, b);
903 *plane_interleaved_uv++ = MAKE_U(r, g, b);
904 }
905 if (width_remainder) {
906 READ_1x1_PIXEL;
907 *plane_interleaved_uv++ = MAKE_V(r, g, b);
908 *plane_interleaved_uv++ = MAKE_U(r, g, b);
909 }
910 }
911 }
912 } break;
913
914 case SDL_PIXELFORMAT_YUY2:
915 case SDL_PIXELFORMAT_UYVY:
916 case SDL_PIXELFORMAT_YVYU:
917 {
918 const Uint8 *curr_row = (const Uint8 *)src;
919 Uint8 *plane = (Uint8 *)dst;
920 const int row_size = (4 * ((width + 1) / 2));
921 int plane_skip;
922
923 if (dst_pitch < row_size) {
924 return SDL_SetError("Destination pitch is too small, expected at least %d", row_size);
925 }
926 plane_skip = (dst_pitch - row_size);
927
928 // Write YUV plane, packed
929 if (dst_format == SDL_PIXELFORMAT_YUY2) {
930 for (j = 0; j < height; j++) {
931 for (i = 0; i < width_half; i++) {
932 READ_TWO_RGB_PIXELS;
933 // Y U Y1 V
934 *plane++ = MAKE_Y(r, g, b);
935 *plane++ = MAKE_U(R, G, B);
936 *plane++ = MAKE_Y(r1, g1, b1);
937 *plane++ = MAKE_V(R, G, B);
938 }
939 if (width_remainder) {
940 READ_ONE_RGB_PIXEL;
941 // Y U Y V
942 *plane++ = MAKE_Y(r, g, b);
943 *plane++ = MAKE_U(r, g, b);
944 *plane++ = MAKE_Y(r, g, b);
945 *plane++ = MAKE_V(r, g, b);
946 }
947 plane += plane_skip;
948 curr_row += src_pitch;
949 }
950 } else if (dst_format == SDL_PIXELFORMAT_UYVY) {
951 for (j = 0; j < height; j++) {
952 for (i = 0; i < width_half; i++) {
953 READ_TWO_RGB_PIXELS;
954 // U Y V Y1
955 *plane++ = MAKE_U(R, G, B);
956 *plane++ = MAKE_Y(r, g, b);
957 *plane++ = MAKE_V(R, G, B);
958 *plane++ = MAKE_Y(r1, g1, b1);
959 }
960 if (width_remainder) {
961 READ_ONE_RGB_PIXEL;
962 // U Y V Y
963 *plane++ = MAKE_U(r, g, b);
964 *plane++ = MAKE_Y(r, g, b);
965 *plane++ = MAKE_V(r, g, b);
966 *plane++ = MAKE_Y(r, g, b);
967 }
968 plane += plane_skip;
969 curr_row += src_pitch;
970 }
971 } else if (dst_format == SDL_PIXELFORMAT_YVYU) {
972 for (j = 0; j < height; j++) {
973 for (i = 0; i < width_half; i++) {
974 READ_TWO_RGB_PIXELS;
975 // Y V Y1 U
976 *plane++ = MAKE_Y(r, g, b);
977 *plane++ = MAKE_V(R, G, B);
978 *plane++ = MAKE_Y(r1, g1, b1);
979 *plane++ = MAKE_U(R, G, B);
980 }
981 if (width_remainder) {
982 READ_ONE_RGB_PIXEL;
983 // Y V Y U
984 *plane++ = MAKE_Y(r, g, b);
985 *plane++ = MAKE_V(r, g, b);
986 *plane++ = MAKE_Y(r, g, b);
987 *plane++ = MAKE_U(r, g, b);
988 }
989 plane += plane_skip;
990 curr_row += src_pitch;
991 }
992 }
993 } break;
994
995 default:
996 return SDL_SetError("Unsupported YUV destination format: %s", SDL_GetPixelFormatName(dst_format));
997 }
998#undef MAKE_Y
999#undef MAKE_U
1000#undef MAKE_V
1001#undef READ_2x2_PIXELS
1002#undef READ_2x1_PIXELS
1003#undef READ_1x2_PIXELS
1004#undef READ_1x1_PIXEL
1005#undef READ_TWO_RGB_PIXELS
1006#undef READ_ONE_RGB_PIXEL
1007 return true;
1008}
1009
1010static bool SDL_ConvertPixels_XBGR2101010_to_P010(int width, int height, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch, YCbCrType yuv_type)
1011{
1012 const int src_pitch_x_2 = src_pitch * 2;
1013 const int height_half = height / 2;
1014 const int height_remainder = (height & 0x1);
1015 const int width_half = width / 2;
1016 const int width_remainder = (width & 0x1);
1017 int i, j;
1018
1019 const struct RGB2YUVFactors *cvt = &RGB2YUVFactorTables[yuv_type];
1020
1021#define MAKE_Y(r, g, b) (Uint16)(((int)(cvt->y[0] * (r) + cvt->y[1] * (g) + cvt->y[2] * (b) + 0.5f) + cvt->y_offset) << 6)
1022#define MAKE_U(r, g, b) (Uint16)(((int)(cvt->u[0] * (r) + cvt->u[1] * (g) + cvt->u[2] * (b) + 0.5f) + 512) << 6)
1023#define MAKE_V(r, g, b) (Uint16)(((int)(cvt->v[0] * (r) + cvt->v[1] * (g) + cvt->v[2] * (b) + 0.5f) + 512) << 6)
1024
1025#define READ_2x2_PIXELS \
1026 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
1027 const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \
1028 const Uint32 p3 = ((const Uint32 *)next_row)[2 * i]; \
1029 const Uint32 p4 = ((const Uint32 *)next_row)[2 * i + 1]; \
1030 const Uint32 r = ((p1 & 0x000003ff) + (p2 & 0x000003ff) + (p3 & 0x000003ff) + (p4 & 0x000003ff)) >> 2; \
1031 const Uint32 g = ((p1 & 0x000ffc00) + (p2 & 0x000ffc00) + (p3 & 0x000ffc00) + (p4 & 0x000ffc00)) >> 12; \
1032 const Uint32 b = ((p1 & 0x3ff00000) + (p2 & 0x3ff00000) + (p3 & 0x3ff00000) + (p4 & 0x3ff00000)) >> 22;
1033
1034#define READ_2x1_PIXELS \
1035 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
1036 const Uint32 p2 = ((const Uint32 *)next_row)[2 * i]; \
1037 const Uint32 r = ((p1 & 0x000003ff) + (p2 & 0x000003ff)) >> 1; \
1038 const Uint32 g = ((p1 & 0x000ffc00) + (p2 & 0x000ffc00)) >> 11; \
1039 const Uint32 b = ((p1 & 0x3ff00000) + (p2 & 0x3ff00000)) >> 21;
1040
1041#define READ_1x2_PIXELS \
1042 const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
1043 const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \
1044 const Uint32 r = ((p1 & 0x000003ff) + (p2 & 0x000003ff)) >> 1; \
1045 const Uint32 g = ((p1 & 0x000ffc00) + (p2 & 0x000ffc00)) >> 11; \
1046 const Uint32 b = ((p1 & 0x3ff00000) + (p2 & 0x3ff00000)) >> 21;
1047
1048#define READ_1x1_PIXEL \
1049 const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \
1050 const Uint32 r = (p & 0x000003ff); \
1051 const Uint32 g = (p & 0x000ffc00) >> 10; \
1052 const Uint32 b = (p & 0x3ff00000) >> 20;
1053
1054 const Uint8 *curr_row, *next_row;
1055
1056 Uint16 *plane_y;
1057 Uint16 *plane_u;
1058 Uint16 *plane_v;
1059 Uint16 *plane_interleaved_uv;
1060 Uint32 y_stride, uv_stride, y_skip, uv_skip;
1061
1062 if (!GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
1063 (const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v,
1064 &y_stride, &uv_stride)) {
1065 return false;
1066 }
1067
1068 y_stride /= sizeof(Uint16);
1069 uv_stride /= sizeof(Uint16);
1070
1071 plane_interleaved_uv = (plane_y + height * y_stride);
1072 y_skip = (y_stride - width);
1073
1074 curr_row = (const Uint8 *)src;
1075
1076 // Write Y plane
1077 for (j = 0; j < height; j++) {
1078 for (i = 0; i < width; i++) {
1079 const Uint32 p1 = ((const Uint32 *)curr_row)[i];
1080 const Uint32 r = (p1 >> 0) & 0x03ff;
1081 const Uint32 g = (p1 >> 10) & 0x03ff;
1082 const Uint32 b = (p1 >> 20) & 0x03ff;
1083 *plane_y++ = MAKE_Y(r, g, b);
1084 }
1085 plane_y += y_skip;
1086 curr_row += src_pitch;
1087 }
1088
1089 curr_row = (const Uint8 *)src;
1090 next_row = (const Uint8 *)src;
1091 next_row += src_pitch;
1092
1093 uv_skip = (uv_stride - ((width + 1) / 2) * 2);
1094 for (j = 0; j < height_half; j++) {
1095 for (i = 0; i < width_half; i++) {
1096 READ_2x2_PIXELS;
1097 *plane_interleaved_uv++ = MAKE_U(r, g, b);
1098 *plane_interleaved_uv++ = MAKE_V(r, g, b);
1099 }
1100 if (width_remainder) {
1101 READ_2x1_PIXELS;
1102 *plane_interleaved_uv++ = MAKE_U(r, g, b);
1103 *plane_interleaved_uv++ = MAKE_V(r, g, b);
1104 }
1105 plane_interleaved_uv += uv_skip;
1106 curr_row += src_pitch_x_2;
1107 next_row += src_pitch_x_2;
1108 }
1109 if (height_remainder) {
1110 for (i = 0; i < width_half; i++) {
1111 READ_1x2_PIXELS;
1112 *plane_interleaved_uv++ = MAKE_U(r, g, b);
1113 *plane_interleaved_uv++ = MAKE_V(r, g, b);
1114 }
1115 if (width_remainder) {
1116 READ_1x1_PIXEL;
1117 *plane_interleaved_uv++ = MAKE_U(r, g, b);
1118 *plane_interleaved_uv++ = MAKE_V(r, g, b);
1119 }
1120 }
1121
1122#undef MAKE_Y
1123#undef MAKE_U
1124#undef MAKE_V
1125#undef READ_2x2_PIXELS
1126#undef READ_2x1_PIXELS
1127#undef READ_1x2_PIXELS
1128#undef READ_1x1_PIXEL
1129 return true;
1130}
1131
1132bool SDL_ConvertPixels_RGB_to_YUV(int width, int height,
1133 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
1134 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
1135{
1136 YCbCrType yuv_type = YCBCR_601_LIMITED;
1137
1138 if (!GetYUVConversionType(dst_colorspace, &yuv_type)) {
1139 return false;
1140 }
1141
1142#if 0 // Doesn't handle odd widths
1143 // RGB24 to FOURCC
1144 if (src_format == SDL_PIXELFORMAT_RGB24) {
1145 Uint8 *y;
1146 Uint8 *u;
1147 Uint8 *v;
1148 Uint32 y_stride;
1149 Uint32 uv_stride;
1150
1151 if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch, (const Uint8 **)&y, (const Uint8 **)&u, (const Uint8 **)&v, &y_stride, &uv_stride) < 0) {
1152 return false;
1153 }
1154
1155 rgb24_yuv420_std(width, height, src, src_pitch, y, u, v, y_stride, uv_stride, yuv_type);
1156 return true;
1157 }
1158#endif
1159
1160 // ARGB8888 to FOURCC
1161 if ((src_format == SDL_PIXELFORMAT_ARGB8888 || src_format == SDL_PIXELFORMAT_XRGB8888) &&
1162 SDL_COLORSPACEPRIMARIES(src_colorspace) == SDL_COLORSPACEPRIMARIES(dst_colorspace)) {
1163 return SDL_ConvertPixels_XRGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst, dst_pitch, yuv_type);
1164 }
1165
1166 if (dst_format == SDL_PIXELFORMAT_P010) {
1167 if (src_format == SDL_PIXELFORMAT_XBGR2101010 &&
1168 SDL_COLORSPACEPRIMARIES(src_colorspace) == SDL_COLORSPACEPRIMARIES(dst_colorspace)) {
1169 return SDL_ConvertPixels_XBGR2101010_to_P010(width, height, src, src_pitch, dst_format, dst, dst_pitch, yuv_type);
1170 }
1171
1172 // We currently only support converting from XBGR2101010 to P010
1173 bool result;
1174 void *tmp;
1175 int tmp_pitch = (width * sizeof(Uint32));
1176
1177 tmp = SDL_malloc((size_t)tmp_pitch * height);
1178 if (!tmp) {
1179 return false;
1180 }
1181
1182 // convert src/src_format to tmp/XBGR2101010
1183 result = SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_XBGR2101010, dst_colorspace, dst_properties, tmp, tmp_pitch);
1184 if (!result) {
1185 SDL_free(tmp);
1186 return false;
1187 }
1188
1189 // convert tmp/XBGR2101010 to dst/P010
1190 result = SDL_ConvertPixels_XBGR2101010_to_P010(width, height, tmp, tmp_pitch, dst_format, dst, dst_pitch, yuv_type);
1191 SDL_free(tmp);
1192 return result;
1193 }
1194
1195 // not ARGB8888 to FOURCC : need an intermediate conversion
1196 {
1197 bool result;
1198 void *tmp;
1199 int tmp_pitch = (width * sizeof(Uint32));
1200
1201 tmp = SDL_malloc((size_t)tmp_pitch * height);
1202 if (!tmp) {
1203 return false;
1204 }
1205
1206 // convert src/src_format to tmp/XRGB8888
1207 result = SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_XRGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch);
1208 if (!result) {
1209 SDL_free(tmp);
1210 return false;
1211 }
1212
1213 // convert tmp/XRGB8888 to dst/FOURCC
1214 result = SDL_ConvertPixels_XRGB8888_to_YUV(width, height, tmp, tmp_pitch, dst_format, dst, dst_pitch, yuv_type);
1215 SDL_free(tmp);
1216 return result;
1217 }
1218}
1219
1220static bool SDL_ConvertPixels_YUV_to_YUV_Copy(int width, int height, SDL_PixelFormat format, const void *src, int src_pitch, void *dst, int dst_pitch)
1221{
1222 int i;
1223
1224 if (IsPlanar2x2Format(format)) {
1225 // Y plane
1226 for (i = height; i--;) {
1227 SDL_memcpy(dst, src, width);
1228 src = (const Uint8 *)src + src_pitch;
1229 dst = (Uint8 *)dst + dst_pitch;
1230 }
1231
1232 if (format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV) {
1233 // U and V planes are a quarter the size of the Y plane, rounded up
1234 width = (width + 1) / 2;
1235 height = (height + 1) / 2;
1236 src_pitch = (src_pitch + 1) / 2;
1237 dst_pitch = (dst_pitch + 1) / 2;
1238 for (i = height * 2; i--;) {
1239 SDL_memcpy(dst, src, width);
1240 src = (const Uint8 *)src + src_pitch;
1241 dst = (Uint8 *)dst + dst_pitch;
1242 }
1243 } else if (format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21) {
1244 // U/V plane is half the height of the Y plane, rounded up
1245 height = (height + 1) / 2;
1246 width = ((width + 1) / 2) * 2;
1247 src_pitch = ((src_pitch + 1) / 2) * 2;
1248 dst_pitch = ((dst_pitch + 1) / 2) * 2;
1249 for (i = height; i--;) {
1250 SDL_memcpy(dst, src, width);
1251 src = (const Uint8 *)src + src_pitch;
1252 dst = (Uint8 *)dst + dst_pitch;
1253 }
1254 } else if (format == SDL_PIXELFORMAT_P010) {
1255 // U/V plane is half the height of the Y plane, rounded up
1256 height = (height + 1) / 2;
1257 width = ((width + 1) / 2) * 2;
1258 src_pitch = ((src_pitch + 1) / 2) * 2;
1259 dst_pitch = ((dst_pitch + 1) / 2) * 2;
1260 for (i = height; i--;) {
1261 SDL_memcpy(dst, src, width * sizeof(Uint16));
1262 src = (const Uint8 *)src + src_pitch;
1263 dst = (Uint8 *)dst + dst_pitch;
1264 }
1265 }
1266 return true;
1267 }
1268
1269 if (IsPacked4Format(format)) {
1270 // Packed planes
1271 width = 4 * ((width + 1) / 2);
1272 for (i = height; i--;) {
1273 SDL_memcpy(dst, src, width);
1274 src = (const Uint8 *)src + src_pitch;
1275 dst = (Uint8 *)dst + dst_pitch;
1276 }
1277 return true;
1278 }
1279
1280 return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV_Copy: Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
1281}
1282
1283static bool SDL_ConvertPixels_SwapUVPlanes(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1284{
1285 int y;
1286 const int UVwidth = (width + 1) / 2;
1287 const int UVheight = (height + 1) / 2;
1288
1289 // Skip the Y plane
1290 src = (const Uint8 *)src + height * src_pitch;
1291 dst = (Uint8 *)dst + height * dst_pitch;
1292
1293 if (src == dst) {
1294 int UVpitch = (dst_pitch + 1) / 2;
1295 Uint8 *tmp;
1296 Uint8 *row1 = (Uint8 *)dst;
1297 Uint8 *row2 = row1 + UVheight * UVpitch;
1298
1299 // Allocate a temporary row for the swap
1300 tmp = (Uint8 *)SDL_malloc(UVwidth);
1301 if (!tmp) {
1302 return false;
1303 }
1304 for (y = 0; y < UVheight; ++y) {
1305 SDL_memcpy(tmp, row1, UVwidth);
1306 SDL_memcpy(row1, row2, UVwidth);
1307 SDL_memcpy(row2, tmp, UVwidth);
1308 row1 += UVpitch;
1309 row2 += UVpitch;
1310 }
1311 SDL_free(tmp);
1312 } else {
1313 const Uint8 *srcUV;
1314 Uint8 *dstUV;
1315 int srcUVPitch = ((src_pitch + 1) / 2);
1316 int dstUVPitch = ((dst_pitch + 1) / 2);
1317
1318 // Copy the first plane
1319 srcUV = (const Uint8 *)src;
1320 dstUV = (Uint8 *)dst + UVheight * dstUVPitch;
1321 for (y = 0; y < UVheight; ++y) {
1322 SDL_memcpy(dstUV, srcUV, UVwidth);
1323 srcUV += srcUVPitch;
1324 dstUV += dstUVPitch;
1325 }
1326
1327 // Copy the second plane
1328 dstUV = (Uint8 *)dst;
1329 for (y = 0; y < UVheight; ++y) {
1330 SDL_memcpy(dstUV, srcUV, UVwidth);
1331 srcUV += srcUVPitch;
1332 dstUV += dstUVPitch;
1333 }
1334 }
1335 return true;
1336}
1337
1338#ifdef SDL_SSE2_INTRINSICS
1339static bool SDL_TARGETING("sse2") SDL_ConvertPixels_PackUVPlanes_to_NV_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1340{
1341 int x, y;
1342 const int UVwidth = (width + 1) / 2;
1343 const int UVheight = (height + 1) / 2;
1344 const int srcUVPitch = ((src_pitch + 1) / 2);
1345 const int srcUVPitchLeft = srcUVPitch - UVwidth;
1346 const int dstUVPitch = ((dst_pitch + 1) / 2) * 2;
1347 const int dstUVPitchLeft = dstUVPitch - UVwidth * 2;
1348 const Uint8 *src1, *src2;
1349 Uint8 *dstUV;
1350 Uint8 *tmp = NULL;
1351
1352 // Skip the Y plane
1353 src = (const Uint8 *)src + height * src_pitch;
1354 dst = (Uint8 *)dst + height * dst_pitch;
1355
1356 if (src == dst) {
1357 // Need to make a copy of the buffer so we don't clobber it while converting
1358 tmp = (Uint8 *)SDL_malloc((size_t)2 * UVheight * srcUVPitch);
1359 if (tmp == NULL) {
1360 return false;
1361 }
1362 SDL_memcpy(tmp, src, (size_t)2 * UVheight * srcUVPitch);
1363 src = tmp;
1364 }
1365
1366 if (reverseUV) {
1367 src2 = (const Uint8 *)src;
1368 src1 = src2 + UVheight * srcUVPitch;
1369 } else {
1370 src1 = (const Uint8 *)src;
1371 src2 = src1 + UVheight * srcUVPitch;
1372 }
1373 dstUV = (Uint8 *)dst;
1374
1375 y = UVheight;
1376 while (y--) {
1377 x = UVwidth;
1378 while (x >= 16) {
1379 __m128i u = _mm_loadu_si128((__m128i *)src1);
1380 __m128i v = _mm_loadu_si128((__m128i *)src2);
1381 __m128i uv1 = _mm_unpacklo_epi8(u, v);
1382 __m128i uv2 = _mm_unpackhi_epi8(u, v);
1383 _mm_storeu_si128((__m128i *)dstUV, uv1);
1384 _mm_storeu_si128((__m128i *)(dstUV + 16), uv2);
1385 src1 += 16;
1386 src2 += 16;
1387 dstUV += 32;
1388 x -= 16;
1389 }
1390 while (x--) {
1391 *dstUV++ = *src1++;
1392 *dstUV++ = *src2++;
1393 }
1394 src1 += srcUVPitchLeft;
1395 src2 += srcUVPitchLeft;
1396 dstUV += dstUVPitchLeft;
1397 }
1398
1399 if (tmp) {
1400 SDL_free(tmp);
1401 }
1402 return true;
1403}
1404
1405static bool SDL_TARGETING("sse2") SDL_ConvertPixels_SplitNV_to_UVPlanes_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1406{
1407 int x, y;
1408 const int UVwidth = (width + 1) / 2;
1409 const int UVheight = (height + 1) / 2;
1410 const int srcUVPitch = ((src_pitch + 1) / 2) * 2;
1411 const int srcUVPitchLeft = srcUVPitch - UVwidth * 2;
1412 const int dstUVPitch = ((dst_pitch + 1) / 2);
1413 const int dstUVPitchLeft = dstUVPitch - UVwidth;
1414 const Uint8 *srcUV;
1415 Uint8 *dst1, *dst2;
1416 Uint8 *tmp = NULL;
1417
1418 // Skip the Y plane
1419 src = (const Uint8 *)src + height * src_pitch;
1420 dst = (Uint8 *)dst + height * dst_pitch;
1421
1422 if (src == dst) {
1423 // Need to make a copy of the buffer so we don't clobber it while converting
1424 tmp = (Uint8 *)SDL_malloc((size_t)UVheight * srcUVPitch);
1425 if (tmp == NULL) {
1426 return false;
1427 }
1428 SDL_memcpy(tmp, src, (size_t)UVheight * srcUVPitch);
1429 src = tmp;
1430 }
1431
1432 if (reverseUV) {
1433 dst2 = (Uint8 *)dst;
1434 dst1 = dst2 + UVheight * dstUVPitch;
1435 } else {
1436 dst1 = (Uint8 *)dst;
1437 dst2 = dst1 + UVheight * dstUVPitch;
1438 }
1439 srcUV = (const Uint8 *)src;
1440
1441 y = UVheight;
1442 while (y--) {
1443 __m128i mask = _mm_set1_epi16(0x00FF);
1444 x = UVwidth;
1445 while (x >= 16) {
1446 __m128i uv1 = _mm_loadu_si128((__m128i *)srcUV);
1447 __m128i uv2 = _mm_loadu_si128((__m128i *)(srcUV + 16));
1448 __m128i u1 = _mm_and_si128(uv1, mask);
1449 __m128i u2 = _mm_and_si128(uv2, mask);
1450 __m128i u = _mm_packus_epi16(u1, u2);
1451 __m128i v1 = _mm_srli_epi16(uv1, 8);
1452 __m128i v2 = _mm_srli_epi16(uv2, 8);
1453 __m128i v = _mm_packus_epi16(v1, v2);
1454 _mm_storeu_si128((__m128i *)dst1, u);
1455 _mm_storeu_si128((__m128i *)dst2, v);
1456 srcUV += 32;
1457 dst1 += 16;
1458 dst2 += 16;
1459 x -= 16;
1460 }
1461 while (x--) {
1462 *dst1++ = *srcUV++;
1463 *dst2++ = *srcUV++;
1464 }
1465 srcUV += srcUVPitchLeft;
1466 dst1 += dstUVPitchLeft;
1467 dst2 += dstUVPitchLeft;
1468 }
1469
1470 if (tmp) {
1471 SDL_free(tmp);
1472 }
1473 return true;
1474}
1475
1476static bool SDL_TARGETING("sse2") SDL_ConvertPixels_SwapNV_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1477{
1478 int x, y;
1479 const int UVwidth = (width + 1) / 2;
1480 const int UVheight = (height + 1) / 2;
1481 const int srcUVPitch = ((src_pitch + 1) / 2) * 2;
1482 const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2) / sizeof(Uint16);
1483 const int dstUVPitch = ((dst_pitch + 1) / 2) * 2;
1484 const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2) / sizeof(Uint16);
1485 const Uint16 *srcUV;
1486 Uint16 *dstUV;
1487
1488 // Skip the Y plane
1489 src = (const Uint8 *)src + height * src_pitch;
1490 dst = (Uint8 *)dst + height * dst_pitch;
1491
1492 srcUV = (const Uint16 *)src;
1493 dstUV = (Uint16 *)dst;
1494 y = UVheight;
1495 while (y--) {
1496 x = UVwidth;
1497 while (x >= 8) {
1498 __m128i uv = _mm_loadu_si128((__m128i *)srcUV);
1499 __m128i v = _mm_slli_epi16(uv, 8);
1500 __m128i u = _mm_srli_epi16(uv, 8);
1501 __m128i vu = _mm_or_si128(v, u);
1502 _mm_storeu_si128((__m128i *)dstUV, vu);
1503 srcUV += 8;
1504 dstUV += 8;
1505 x -= 8;
1506 }
1507 while (x--) {
1508 *dstUV++ = SDL_Swap16(*srcUV++);
1509 }
1510 srcUV += srcUVPitchLeft;
1511 dstUV += dstUVPitchLeft;
1512 }
1513 return true;
1514}
1515#endif
1516
1517static bool SDL_ConvertPixels_PackUVPlanes_to_NV_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1518{
1519 int x, y;
1520 const int UVwidth = (width + 1) / 2;
1521 const int UVheight = (height + 1) / 2;
1522 const int srcUVPitch = ((src_pitch + 1) / 2);
1523 const int srcUVPitchLeft = srcUVPitch - UVwidth;
1524 const int dstUVPitch = ((dst_pitch + 1) / 2) * 2;
1525 const int dstUVPitchLeft = dstUVPitch - UVwidth * 2;
1526 const Uint8 *src1, *src2;
1527 Uint8 *dstUV;
1528 Uint8 *tmp = NULL;
1529
1530 // Skip the Y plane
1531 src = (const Uint8 *)src + height * src_pitch;
1532 dst = (Uint8 *)dst + height * dst_pitch;
1533
1534 if (src == dst) {
1535 // Need to make a copy of the buffer so we don't clobber it while converting
1536 tmp = (Uint8 *)SDL_malloc((size_t)2 * UVheight * srcUVPitch);
1537 if (!tmp) {
1538 return false;
1539 }
1540 SDL_memcpy(tmp, src, (size_t)2 * UVheight * srcUVPitch);
1541 src = tmp;
1542 }
1543
1544 if (reverseUV) {
1545 src2 = (const Uint8 *)src;
1546 src1 = src2 + UVheight * srcUVPitch;
1547 } else {
1548 src1 = (const Uint8 *)src;
1549 src2 = src1 + UVheight * srcUVPitch;
1550 }
1551 dstUV = (Uint8 *)dst;
1552
1553 y = UVheight;
1554 while (y--) {
1555 x = UVwidth;
1556 while (x--) {
1557 *dstUV++ = *src1++;
1558 *dstUV++ = *src2++;
1559 }
1560 src1 += srcUVPitchLeft;
1561 src2 += srcUVPitchLeft;
1562 dstUV += dstUVPitchLeft;
1563 }
1564
1565 if (tmp) {
1566 SDL_free(tmp);
1567 }
1568 return true;
1569}
1570
1571static bool SDL_ConvertPixels_SplitNV_to_UVPlanes_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1572{
1573 int x, y;
1574 const int UVwidth = (width + 1) / 2;
1575 const int UVheight = (height + 1) / 2;
1576 const int srcUVPitch = ((src_pitch + 1) / 2) * 2;
1577 const int srcUVPitchLeft = srcUVPitch - UVwidth * 2;
1578 const int dstUVPitch = ((dst_pitch + 1) / 2);
1579 const int dstUVPitchLeft = dstUVPitch - UVwidth;
1580 const Uint8 *srcUV;
1581 Uint8 *dst1, *dst2;
1582 Uint8 *tmp = NULL;
1583
1584 // Skip the Y plane
1585 src = (const Uint8 *)src + height * src_pitch;
1586 dst = (Uint8 *)dst + height * dst_pitch;
1587
1588 if (src == dst) {
1589 // Need to make a copy of the buffer so we don't clobber it while converting
1590 tmp = (Uint8 *)SDL_malloc((size_t)UVheight * srcUVPitch);
1591 if (!tmp) {
1592 return false;
1593 }
1594 SDL_memcpy(tmp, src, (size_t)UVheight * srcUVPitch);
1595 src = tmp;
1596 }
1597
1598 if (reverseUV) {
1599 dst2 = (Uint8 *)dst;
1600 dst1 = dst2 + UVheight * dstUVPitch;
1601 } else {
1602 dst1 = (Uint8 *)dst;
1603 dst2 = dst1 + UVheight * dstUVPitch;
1604 }
1605 srcUV = (const Uint8 *)src;
1606
1607 y = UVheight;
1608 while (y--) {
1609 x = UVwidth;
1610 while (x--) {
1611 *dst1++ = *srcUV++;
1612 *dst2++ = *srcUV++;
1613 }
1614 srcUV += srcUVPitchLeft;
1615 dst1 += dstUVPitchLeft;
1616 dst2 += dstUVPitchLeft;
1617 }
1618
1619 if (tmp) {
1620 SDL_free(tmp);
1621 }
1622 return true;
1623}
1624
1625static bool SDL_ConvertPixels_SwapNV_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1626{
1627 int x, y;
1628 const int UVwidth = (width + 1) / 2;
1629 const int UVheight = (height + 1) / 2;
1630 const int srcUVPitch = ((src_pitch + 1) / 2) * 2;
1631 const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2) / sizeof(Uint16);
1632 const int dstUVPitch = ((dst_pitch + 1) / 2) * 2;
1633 const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2) / sizeof(Uint16);
1634 const Uint16 *srcUV;
1635 Uint16 *dstUV;
1636
1637 // Skip the Y plane
1638 src = (const Uint8 *)src + height * src_pitch;
1639 dst = (Uint8 *)dst + height * dst_pitch;
1640
1641 srcUV = (const Uint16 *)src;
1642 dstUV = (Uint16 *)dst;
1643 y = UVheight;
1644 while (y--) {
1645 x = UVwidth;
1646 while (x--) {
1647 *dstUV++ = SDL_Swap16(*srcUV++);
1648 }
1649 srcUV += srcUVPitchLeft;
1650 dstUV += dstUVPitchLeft;
1651 }
1652 return true;
1653}
1654
1655static bool SDL_ConvertPixels_PackUVPlanes_to_NV(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1656{
1657#ifdef SDL_SSE2_INTRINSICS
1658 if (SDL_HasSSE2()) {
1659 return SDL_ConvertPixels_PackUVPlanes_to_NV_SSE2(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1660 }
1661#endif
1662 return SDL_ConvertPixels_PackUVPlanes_to_NV_std(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1663}
1664
1665static bool SDL_ConvertPixels_SplitNV_to_UVPlanes(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1666{
1667#ifdef SDL_SSE2_INTRINSICS
1668 if (SDL_HasSSE2()) {
1669 return SDL_ConvertPixels_SplitNV_to_UVPlanes_SSE2(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1670 }
1671#endif
1672 return SDL_ConvertPixels_SplitNV_to_UVPlanes_std(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1673}
1674
1675static bool SDL_ConvertPixels_SwapNV(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1676{
1677#ifdef SDL_SSE2_INTRINSICS
1678 if (SDL_HasSSE2()) {
1679 return SDL_ConvertPixels_SwapNV_SSE2(width, height, src, src_pitch, dst, dst_pitch);
1680 }
1681#endif
1682 return SDL_ConvertPixels_SwapNV_std(width, height, src, src_pitch, dst, dst_pitch);
1683}
1684
1685static bool SDL_ConvertPixels_Planar2x2_to_Planar2x2(int width, int height,
1686 SDL_PixelFormat src_format, const void *src, int src_pitch,
1687 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
1688{
1689 if (src != dst) {
1690 // Copy Y plane
1691 int i;
1692 const Uint8 *srcY = (const Uint8 *)src;
1693 Uint8 *dstY = (Uint8 *)dst;
1694 for (i = height; i--;) {
1695 SDL_memcpy(dstY, srcY, width);
1696 srcY += src_pitch;
1697 dstY += dst_pitch;
1698 }
1699 }
1700
1701 switch (src_format) {
1702 case SDL_PIXELFORMAT_YV12:
1703 switch (dst_format) {
1704 case SDL_PIXELFORMAT_IYUV:
1705 return SDL_ConvertPixels_SwapUVPlanes(width, height, src, src_pitch, dst, dst_pitch);
1706 case SDL_PIXELFORMAT_NV12:
1707 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, true);
1708 case SDL_PIXELFORMAT_NV21:
1709 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, false);
1710 default:
1711 break;
1712 }
1713 break;
1714 case SDL_PIXELFORMAT_IYUV:
1715 switch (dst_format) {
1716 case SDL_PIXELFORMAT_YV12:
1717 return SDL_ConvertPixels_SwapUVPlanes(width, height, src, src_pitch, dst, dst_pitch);
1718 case SDL_PIXELFORMAT_NV12:
1719 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, false);
1720 case SDL_PIXELFORMAT_NV21:
1721 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, true);
1722 default:
1723 break;
1724 }
1725 break;
1726 case SDL_PIXELFORMAT_NV12:
1727 switch (dst_format) {
1728 case SDL_PIXELFORMAT_YV12:
1729 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, true);
1730 case SDL_PIXELFORMAT_IYUV:
1731 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, false);
1732 case SDL_PIXELFORMAT_NV21:
1733 return SDL_ConvertPixels_SwapNV(width, height, src, src_pitch, dst, dst_pitch);
1734 default:
1735 break;
1736 }
1737 break;
1738 case SDL_PIXELFORMAT_NV21:
1739 switch (dst_format) {
1740 case SDL_PIXELFORMAT_YV12:
1741 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, false);
1742 case SDL_PIXELFORMAT_IYUV:
1743 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, true);
1744 case SDL_PIXELFORMAT_NV12:
1745 return SDL_ConvertPixels_SwapNV(width, height, src, src_pitch, dst, dst_pitch);
1746 default:
1747 break;
1748 }
1749 break;
1750 default:
1751 break;
1752 }
1753 return SDL_SetError("SDL_ConvertPixels_Planar2x2_to_Planar2x2: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format),
1754 SDL_GetPixelFormatName(dst_format));
1755}
1756
1757#ifdef SDL_SSE2_INTRINSICS
1758#define PACKED4_TO_PACKED4_ROW_SSE2(shuffle) \
1759 while (x >= 4) { \
1760 __m128i yuv = _mm_loadu_si128((__m128i *)srcYUV); \
1761 __m128i lo = _mm_unpacklo_epi8(yuv, _mm_setzero_si128()); \
1762 __m128i hi = _mm_unpackhi_epi8(yuv, _mm_setzero_si128()); \
1763 lo = _mm_shufflelo_epi16(lo, shuffle); \
1764 lo = _mm_shufflehi_epi16(lo, shuffle); \
1765 hi = _mm_shufflelo_epi16(hi, shuffle); \
1766 hi = _mm_shufflehi_epi16(hi, shuffle); \
1767 yuv = _mm_packus_epi16(lo, hi); \
1768 _mm_storeu_si128((__m128i *)dstYUV, yuv); \
1769 srcYUV += 16; \
1770 dstYUV += 16; \
1771 x -= 4; \
1772 }
1773
1774static bool SDL_TARGETING("sse2") SDL_ConvertPixels_YUY2_to_UYVY_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1775{
1776 int x, y;
1777 const int YUVwidth = (width + 1) / 2;
1778 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1779 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1780 const Uint8 *srcYUV = (const Uint8 *)src;
1781 Uint8 *dstYUV = (Uint8 *)dst;
1782
1783 y = height;
1784 x = YUVwidth;
1785 while (y--) {
1786 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 3, 0, 1));
1787 while (x--) {
1788 Uint8 Y1, U, Y2, V;
1789
1790 Y1 = srcYUV[0];
1791 U = srcYUV[1];
1792 Y2 = srcYUV[2];
1793 V = srcYUV[3];
1794 srcYUV += 4;
1795
1796 dstYUV[0] = U;
1797 dstYUV[1] = Y1;
1798 dstYUV[2] = V;
1799 dstYUV[3] = Y2;
1800 dstYUV += 4;
1801 }
1802 srcYUV += srcYUVPitchLeft;
1803 dstYUV += dstYUVPitchLeft;
1804 x = YUVwidth;
1805 }
1806 return true;
1807}
1808
1809static bool SDL_TARGETING("sse2") SDL_ConvertPixels_YUY2_to_YVYU_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1810{
1811 int x, y;
1812 const int YUVwidth = (width + 1) / 2;
1813 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1814 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1815 const Uint8 *srcYUV = (const Uint8 *)src;
1816 Uint8 *dstYUV = (Uint8 *)dst;
1817
1818 y = height;
1819 x = YUVwidth;
1820 while (y--) {
1821 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(1, 2, 3, 0));
1822 while (x--) {
1823 Uint8 Y1, U, Y2, V;
1824
1825 Y1 = srcYUV[0];
1826 U = srcYUV[1];
1827 Y2 = srcYUV[2];
1828 V = srcYUV[3];
1829 srcYUV += 4;
1830
1831 dstYUV[0] = Y1;
1832 dstYUV[1] = V;
1833 dstYUV[2] = Y2;
1834 dstYUV[3] = U;
1835 dstYUV += 4;
1836 }
1837 srcYUV += srcYUVPitchLeft;
1838 dstYUV += dstYUVPitchLeft;
1839 x = YUVwidth;
1840 }
1841 return true;
1842}
1843
1844static bool SDL_TARGETING("sse2") SDL_ConvertPixels_UYVY_to_YUY2_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1845{
1846 int x, y;
1847 const int YUVwidth = (width + 1) / 2;
1848 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1849 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1850 const Uint8 *srcYUV = (const Uint8 *)src;
1851 Uint8 *dstYUV = (Uint8 *)dst;
1852
1853 y = height;
1854 x = YUVwidth;
1855 while (y--) {
1856 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 3, 0, 1));
1857 while (x--) {
1858 Uint8 Y1, U, Y2, V;
1859
1860 U = srcYUV[0];
1861 Y1 = srcYUV[1];
1862 V = srcYUV[2];
1863 Y2 = srcYUV[3];
1864 srcYUV += 4;
1865
1866 dstYUV[0] = Y1;
1867 dstYUV[1] = U;
1868 dstYUV[2] = Y2;
1869 dstYUV[3] = V;
1870 dstYUV += 4;
1871 }
1872 srcYUV += srcYUVPitchLeft;
1873 dstYUV += dstYUVPitchLeft;
1874 x = YUVwidth;
1875 }
1876 return true;
1877}
1878
1879static bool SDL_TARGETING("sse2") SDL_ConvertPixels_UYVY_to_YVYU_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1880{
1881 int x, y;
1882 const int YUVwidth = (width + 1) / 2;
1883 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1884 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1885 const Uint8 *srcYUV = (const Uint8 *)src;
1886 Uint8 *dstYUV = (Uint8 *)dst;
1887
1888 y = height;
1889 x = YUVwidth;
1890 while (y--) {
1891 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(0, 3, 2, 1));
1892 while (x--) {
1893 Uint8 Y1, U, Y2, V;
1894
1895 U = srcYUV[0];
1896 Y1 = srcYUV[1];
1897 V = srcYUV[2];
1898 Y2 = srcYUV[3];
1899 srcYUV += 4;
1900
1901 dstYUV[0] = Y1;
1902 dstYUV[1] = V;
1903 dstYUV[2] = Y2;
1904 dstYUV[3] = U;
1905 dstYUV += 4;
1906 }
1907 srcYUV += srcYUVPitchLeft;
1908 dstYUV += dstYUVPitchLeft;
1909 x = YUVwidth;
1910 }
1911 return true;
1912}
1913
1914static bool SDL_TARGETING("sse2") SDL_ConvertPixels_YVYU_to_YUY2_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1915{
1916 int x, y;
1917 const int YUVwidth = (width + 1) / 2;
1918 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1919 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1920 const Uint8 *srcYUV = (const Uint8 *)src;
1921 Uint8 *dstYUV = (Uint8 *)dst;
1922
1923 y = height;
1924 x = YUVwidth;
1925 while (y--) {
1926 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(1, 2, 3, 0));
1927 while (x--) {
1928 Uint8 Y1, U, Y2, V;
1929
1930 Y1 = srcYUV[0];
1931 V = srcYUV[1];
1932 Y2 = srcYUV[2];
1933 U = srcYUV[3];
1934 srcYUV += 4;
1935
1936 dstYUV[0] = Y1;
1937 dstYUV[1] = U;
1938 dstYUV[2] = Y2;
1939 dstYUV[3] = V;
1940 dstYUV += 4;
1941 }
1942 srcYUV += srcYUVPitchLeft;
1943 dstYUV += dstYUVPitchLeft;
1944 x = YUVwidth;
1945 }
1946 return true;
1947}
1948
1949static bool SDL_TARGETING("sse2") SDL_ConvertPixels_YVYU_to_UYVY_SSE2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1950{
1951 int x, y;
1952 const int YUVwidth = (width + 1) / 2;
1953 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1954 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1955 const Uint8 *srcYUV = (const Uint8 *)src;
1956 Uint8 *dstYUV = (Uint8 *)dst;
1957
1958 y = height;
1959 x = YUVwidth;
1960 while (y--) {
1961 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 1, 0, 3));
1962 while (x--) {
1963 Uint8 Y1, U, Y2, V;
1964
1965 Y1 = srcYUV[0];
1966 V = srcYUV[1];
1967 Y2 = srcYUV[2];
1968 U = srcYUV[3];
1969 srcYUV += 4;
1970
1971 dstYUV[0] = U;
1972 dstYUV[1] = Y1;
1973 dstYUV[2] = V;
1974 dstYUV[3] = Y2;
1975 dstYUV += 4;
1976 }
1977 srcYUV += srcYUVPitchLeft;
1978 dstYUV += dstYUVPitchLeft;
1979 x = YUVwidth;
1980 }
1981 return true;
1982}
1983#endif
1984
1985static bool SDL_ConvertPixels_YUY2_to_UYVY_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1986{
1987 int x, y;
1988 const int YUVwidth = (width + 1) / 2;
1989 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1990 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1991 const Uint8 *srcYUV = (const Uint8 *)src;
1992 Uint8 *dstYUV = (Uint8 *)dst;
1993
1994 y = height;
1995 while (y--) {
1996 x = YUVwidth;
1997 while (x--) {
1998 Uint8 Y1, U, Y2, V;
1999
2000 Y1 = srcYUV[0];
2001 U = srcYUV[1];
2002 Y2 = srcYUV[2];
2003 V = srcYUV[3];
2004 srcYUV += 4;
2005
2006 dstYUV[0] = U;
2007 dstYUV[1] = Y1;
2008 dstYUV[2] = V;
2009 dstYUV[3] = Y2;
2010 dstYUV += 4;
2011 }
2012 srcYUV += srcYUVPitchLeft;
2013 dstYUV += dstYUVPitchLeft;
2014 }
2015 return true;
2016}
2017
2018static bool SDL_ConvertPixels_YUY2_to_YVYU_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2019{
2020 int x, y;
2021 const int YUVwidth = (width + 1) / 2;
2022 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2023 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2024 const Uint8 *srcYUV = (const Uint8 *)src;
2025 Uint8 *dstYUV = (Uint8 *)dst;
2026
2027 y = height;
2028 while (y--) {
2029 x = YUVwidth;
2030 while (x--) {
2031 Uint8 Y1, U, Y2, V;
2032
2033 Y1 = srcYUV[0];
2034 U = srcYUV[1];
2035 Y2 = srcYUV[2];
2036 V = srcYUV[3];
2037 srcYUV += 4;
2038
2039 dstYUV[0] = Y1;
2040 dstYUV[1] = V;
2041 dstYUV[2] = Y2;
2042 dstYUV[3] = U;
2043 dstYUV += 4;
2044 }
2045 srcYUV += srcYUVPitchLeft;
2046 dstYUV += dstYUVPitchLeft;
2047 }
2048 return true;
2049}
2050
2051static bool SDL_ConvertPixels_UYVY_to_YUY2_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2052{
2053 int x, y;
2054 const int YUVwidth = (width + 1) / 2;
2055 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2056 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2057 const Uint8 *srcYUV = (const Uint8 *)src;
2058 Uint8 *dstYUV = (Uint8 *)dst;
2059
2060 y = height;
2061 while (y--) {
2062 x = YUVwidth;
2063 while (x--) {
2064 Uint8 Y1, U, Y2, V;
2065
2066 U = srcYUV[0];
2067 Y1 = srcYUV[1];
2068 V = srcYUV[2];
2069 Y2 = srcYUV[3];
2070 srcYUV += 4;
2071
2072 dstYUV[0] = Y1;
2073 dstYUV[1] = U;
2074 dstYUV[2] = Y2;
2075 dstYUV[3] = V;
2076 dstYUV += 4;
2077 }
2078 srcYUV += srcYUVPitchLeft;
2079 dstYUV += dstYUVPitchLeft;
2080 }
2081 return true;
2082}
2083
2084static bool SDL_ConvertPixels_UYVY_to_YVYU_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2085{
2086 int x, y;
2087 const int YUVwidth = (width + 1) / 2;
2088 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2089 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2090 const Uint8 *srcYUV = (const Uint8 *)src;
2091 Uint8 *dstYUV = (Uint8 *)dst;
2092
2093 y = height;
2094 while (y--) {
2095 x = YUVwidth;
2096 while (x--) {
2097 Uint8 Y1, U, Y2, V;
2098
2099 U = srcYUV[0];
2100 Y1 = srcYUV[1];
2101 V = srcYUV[2];
2102 Y2 = srcYUV[3];
2103 srcYUV += 4;
2104
2105 dstYUV[0] = Y1;
2106 dstYUV[1] = V;
2107 dstYUV[2] = Y2;
2108 dstYUV[3] = U;
2109 dstYUV += 4;
2110 }
2111 srcYUV += srcYUVPitchLeft;
2112 dstYUV += dstYUVPitchLeft;
2113 }
2114 return true;
2115}
2116
2117static bool SDL_ConvertPixels_YVYU_to_YUY2_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2118{
2119 int x, y;
2120 const int YUVwidth = (width + 1) / 2;
2121 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2122 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2123 const Uint8 *srcYUV = (const Uint8 *)src;
2124 Uint8 *dstYUV = (Uint8 *)dst;
2125
2126 y = height;
2127 while (y--) {
2128 x = YUVwidth;
2129 while (x--) {
2130 Uint8 Y1, U, Y2, V;
2131
2132 Y1 = srcYUV[0];
2133 V = srcYUV[1];
2134 Y2 = srcYUV[2];
2135 U = srcYUV[3];
2136 srcYUV += 4;
2137
2138 dstYUV[0] = Y1;
2139 dstYUV[1] = U;
2140 dstYUV[2] = Y2;
2141 dstYUV[3] = V;
2142 dstYUV += 4;
2143 }
2144 srcYUV += srcYUVPitchLeft;
2145 dstYUV += dstYUVPitchLeft;
2146 }
2147 return true;
2148}
2149
2150static bool SDL_ConvertPixels_YVYU_to_UYVY_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2151{
2152 int x, y;
2153 const int YUVwidth = (width + 1) / 2;
2154 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2155 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2156 const Uint8 *srcYUV = (const Uint8 *)src;
2157 Uint8 *dstYUV = (Uint8 *)dst;
2158
2159 y = height;
2160 while (y--) {
2161 x = YUVwidth;
2162 while (x--) {
2163 Uint8 Y1, U, Y2, V;
2164
2165 Y1 = srcYUV[0];
2166 V = srcYUV[1];
2167 Y2 = srcYUV[2];
2168 U = srcYUV[3];
2169 srcYUV += 4;
2170
2171 dstYUV[0] = U;
2172 dstYUV[1] = Y1;
2173 dstYUV[2] = V;
2174 dstYUV[3] = Y2;
2175 dstYUV += 4;
2176 }
2177 srcYUV += srcYUVPitchLeft;
2178 dstYUV += dstYUVPitchLeft;
2179 }
2180 return true;
2181}
2182
2183static bool SDL_ConvertPixels_YUY2_to_UYVY(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2184{
2185#ifdef SDL_SSE2_INTRINSICS
2186 if (SDL_HasSSE2()) {
2187 return SDL_ConvertPixels_YUY2_to_UYVY_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2188 }
2189#endif
2190 return SDL_ConvertPixels_YUY2_to_UYVY_std(width, height, src, src_pitch, dst, dst_pitch);
2191}
2192
2193static bool SDL_ConvertPixels_YUY2_to_YVYU(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2194{
2195#ifdef SDL_SSE2_INTRINSICS
2196 if (SDL_HasSSE2()) {
2197 return SDL_ConvertPixels_YUY2_to_YVYU_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2198 }
2199#endif
2200 return SDL_ConvertPixels_YUY2_to_YVYU_std(width, height, src, src_pitch, dst, dst_pitch);
2201}
2202
2203static bool SDL_ConvertPixels_UYVY_to_YUY2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2204{
2205#ifdef SDL_SSE2_INTRINSICS
2206 if (SDL_HasSSE2()) {
2207 return SDL_ConvertPixels_UYVY_to_YUY2_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2208 }
2209#endif
2210 return SDL_ConvertPixels_UYVY_to_YUY2_std(width, height, src, src_pitch, dst, dst_pitch);
2211}
2212
2213static bool SDL_ConvertPixels_UYVY_to_YVYU(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2214{
2215#ifdef SDL_SSE2_INTRINSICS
2216 if (SDL_HasSSE2()) {
2217 return SDL_ConvertPixels_UYVY_to_YVYU_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2218 }
2219#endif
2220 return SDL_ConvertPixels_UYVY_to_YVYU_std(width, height, src, src_pitch, dst, dst_pitch);
2221}
2222
2223static bool SDL_ConvertPixels_YVYU_to_YUY2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2224{
2225#ifdef SDL_SSE2_INTRINSICS
2226 if (SDL_HasSSE2()) {
2227 return SDL_ConvertPixels_YVYU_to_YUY2_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2228 }
2229#endif
2230 return SDL_ConvertPixels_YVYU_to_YUY2_std(width, height, src, src_pitch, dst, dst_pitch);
2231}
2232
2233static bool SDL_ConvertPixels_YVYU_to_UYVY(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2234{
2235#ifdef SDL_SSE2_INTRINSICS
2236 if (SDL_HasSSE2()) {
2237 return SDL_ConvertPixels_YVYU_to_UYVY_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2238 }
2239#endif
2240 return SDL_ConvertPixels_YVYU_to_UYVY_std(width, height, src, src_pitch, dst, dst_pitch);
2241}
2242
2243static bool SDL_ConvertPixels_Packed4_to_Packed4(int width, int height,
2244 SDL_PixelFormat src_format, const void *src, int src_pitch,
2245 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2246{
2247 switch (src_format) {
2248 case SDL_PIXELFORMAT_YUY2:
2249 switch (dst_format) {
2250 case SDL_PIXELFORMAT_UYVY:
2251 return SDL_ConvertPixels_YUY2_to_UYVY(width, height, src, src_pitch, dst, dst_pitch);
2252 case SDL_PIXELFORMAT_YVYU:
2253 return SDL_ConvertPixels_YUY2_to_YVYU(width, height, src, src_pitch, dst, dst_pitch);
2254 default:
2255 break;
2256 }
2257 break;
2258 case SDL_PIXELFORMAT_UYVY:
2259 switch (dst_format) {
2260 case SDL_PIXELFORMAT_YUY2:
2261 return SDL_ConvertPixels_UYVY_to_YUY2(width, height, src, src_pitch, dst, dst_pitch);
2262 case SDL_PIXELFORMAT_YVYU:
2263 return SDL_ConvertPixels_UYVY_to_YVYU(width, height, src, src_pitch, dst, dst_pitch);
2264 default:
2265 break;
2266 }
2267 break;
2268 case SDL_PIXELFORMAT_YVYU:
2269 switch (dst_format) {
2270 case SDL_PIXELFORMAT_YUY2:
2271 return SDL_ConvertPixels_YVYU_to_YUY2(width, height, src, src_pitch, dst, dst_pitch);
2272 case SDL_PIXELFORMAT_UYVY:
2273 return SDL_ConvertPixels_YVYU_to_UYVY(width, height, src, src_pitch, dst, dst_pitch);
2274 default:
2275 break;
2276 }
2277 break;
2278 default:
2279 break;
2280 }
2281 return SDL_SetError("SDL_ConvertPixels_Packed4_to_Packed4: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format),
2282 SDL_GetPixelFormatName(dst_format));
2283}
2284
2285static bool SDL_ConvertPixels_Planar2x2_to_Packed4(int width, int height,
2286 SDL_PixelFormat src_format, const void *src, int src_pitch,
2287 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2288{
2289 int x, y;
2290 const Uint8 *srcY1, *srcY2, *srcU, *srcV;
2291 Uint32 srcY_pitch, srcUV_pitch;
2292 Uint32 srcY_pitch_left, srcUV_pitch_left, srcUV_pixel_stride;
2293 Uint8 *dstY1, *dstY2, *dstU1, *dstU2, *dstV1, *dstV2;
2294 Uint32 dstY_pitch, dstUV_pitch;
2295 Uint32 dst_pitch_left;
2296
2297 if (src == dst) {
2298 return SDL_SetError("Can't change YUV plane types in-place");
2299 }
2300
2301 if (!GetYUVPlanes(width, height, src_format, src, src_pitch,
2302 &srcY1, &srcU, &srcV, &srcY_pitch, &srcUV_pitch)) {
2303 return false;
2304 }
2305 srcY2 = srcY1 + srcY_pitch;
2306 srcY_pitch_left = (srcY_pitch - width);
2307
2308 if (src_format == SDL_PIXELFORMAT_NV12 || src_format == SDL_PIXELFORMAT_NV21) {
2309 srcUV_pixel_stride = 2;
2310 srcUV_pitch_left = (srcUV_pitch - 2 * ((width + 1) / 2));
2311 } else {
2312 srcUV_pixel_stride = 1;
2313 srcUV_pitch_left = (srcUV_pitch - ((width + 1) / 2));
2314 }
2315
2316 if (!GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
2317 (const Uint8 **)&dstY1, (const Uint8 **)&dstU1, (const Uint8 **)&dstV1,
2318 &dstY_pitch, &dstUV_pitch)) {
2319 return false;
2320 }
2321 dstY2 = dstY1 + dstY_pitch;
2322 dstU2 = dstU1 + dstUV_pitch;
2323 dstV2 = dstV1 + dstUV_pitch;
2324 dst_pitch_left = (dstY_pitch - 4 * ((width + 1) / 2));
2325
2326 // Copy 2x2 blocks of pixels at a time
2327 for (y = 0; y < (height - 1); y += 2) {
2328 for (x = 0; x < (width - 1); x += 2) {
2329 // Row 1
2330 *dstY1 = *srcY1++;
2331 dstY1 += 2;
2332 *dstY1 = *srcY1++;
2333 dstY1 += 2;
2334 *dstU1 = *srcU;
2335 *dstV1 = *srcV;
2336
2337 // Row 2
2338 *dstY2 = *srcY2++;
2339 dstY2 += 2;
2340 *dstY2 = *srcY2++;
2341 dstY2 += 2;
2342 *dstU2 = *srcU;
2343 *dstV2 = *srcV;
2344
2345 srcU += srcUV_pixel_stride;
2346 srcV += srcUV_pixel_stride;
2347 dstU1 += 4;
2348 dstU2 += 4;
2349 dstV1 += 4;
2350 dstV2 += 4;
2351 }
2352
2353 // Last column
2354 if (x == (width - 1)) {
2355 // Row 1
2356 *dstY1 = *srcY1;
2357 dstY1 += 2;
2358 *dstY1 = *srcY1++;
2359 dstY1 += 2;
2360 *dstU1 = *srcU;
2361 *dstV1 = *srcV;
2362
2363 // Row 2
2364 *dstY2 = *srcY2;
2365 dstY2 += 2;
2366 *dstY2 = *srcY2++;
2367 dstY2 += 2;
2368 *dstU2 = *srcU;
2369 *dstV2 = *srcV;
2370
2371 srcU += srcUV_pixel_stride;
2372 srcV += srcUV_pixel_stride;
2373 dstU1 += 4;
2374 dstU2 += 4;
2375 dstV1 += 4;
2376 dstV2 += 4;
2377 }
2378
2379 srcY1 += srcY_pitch_left + srcY_pitch;
2380 srcY2 += srcY_pitch_left + srcY_pitch;
2381 srcU += srcUV_pitch_left;
2382 srcV += srcUV_pitch_left;
2383 dstY1 += dst_pitch_left + dstY_pitch;
2384 dstY2 += dst_pitch_left + dstY_pitch;
2385 dstU1 += dst_pitch_left + dstUV_pitch;
2386 dstU2 += dst_pitch_left + dstUV_pitch;
2387 dstV1 += dst_pitch_left + dstUV_pitch;
2388 dstV2 += dst_pitch_left + dstUV_pitch;
2389 }
2390
2391 // Last row
2392 if (y == (height - 1)) {
2393 for (x = 0; x < (width - 1); x += 2) {
2394 // Row 1
2395 *dstY1 = *srcY1++;
2396 dstY1 += 2;
2397 *dstY1 = *srcY1++;
2398 dstY1 += 2;
2399 *dstU1 = *srcU;
2400 *dstV1 = *srcV;
2401
2402 srcU += srcUV_pixel_stride;
2403 srcV += srcUV_pixel_stride;
2404 dstU1 += 4;
2405 dstV1 += 4;
2406 }
2407
2408 // Last column
2409 if (x == (width - 1)) {
2410 // Row 1
2411 *dstY1 = *srcY1;
2412 dstY1 += 2;
2413 *dstY1 = *srcY1++;
2414 dstY1 += 2;
2415 *dstU1 = *srcU;
2416 *dstV1 = *srcV;
2417
2418 srcU += srcUV_pixel_stride;
2419 srcV += srcUV_pixel_stride;
2420 dstU1 += 4;
2421 dstV1 += 4;
2422 }
2423 }
2424 return true;
2425}
2426
2427static bool SDL_ConvertPixels_Packed4_to_Planar2x2(int width, int height,
2428 SDL_PixelFormat src_format, const void *src, int src_pitch,
2429 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2430{
2431 int x, y;
2432 const Uint8 *srcY1, *srcY2, *srcU1, *srcU2, *srcV1, *srcV2;
2433 Uint32 srcY_pitch, srcUV_pitch;
2434 Uint32 src_pitch_left;
2435 Uint8 *dstY1, *dstY2, *dstU, *dstV;
2436 Uint32 dstY_pitch, dstUV_pitch;
2437 Uint32 dstY_pitch_left, dstUV_pitch_left, dstUV_pixel_stride;
2438
2439 if (src == dst) {
2440 return SDL_SetError("Can't change YUV plane types in-place");
2441 }
2442
2443 if (!GetYUVPlanes(width, height, src_format, src, src_pitch,
2444 &srcY1, &srcU1, &srcV1, &srcY_pitch, &srcUV_pitch)) {
2445 return false;
2446 }
2447 srcY2 = srcY1 + srcY_pitch;
2448 srcU2 = srcU1 + srcUV_pitch;
2449 srcV2 = srcV1 + srcUV_pitch;
2450 src_pitch_left = (srcY_pitch - 4 * ((width + 1) / 2));
2451
2452 if (!GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
2453 (const Uint8 **)&dstY1, (const Uint8 **)&dstU, (const Uint8 **)&dstV,
2454 &dstY_pitch, &dstUV_pitch)) {
2455 return false;
2456 }
2457 dstY2 = dstY1 + dstY_pitch;
2458 dstY_pitch_left = (dstY_pitch - width);
2459
2460 if (dst_format == SDL_PIXELFORMAT_NV12 || dst_format == SDL_PIXELFORMAT_NV21) {
2461 dstUV_pixel_stride = 2;
2462 dstUV_pitch_left = (dstUV_pitch - 2 * ((width + 1) / 2));
2463 } else {
2464 dstUV_pixel_stride = 1;
2465 dstUV_pitch_left = (dstUV_pitch - ((width + 1) / 2));
2466 }
2467
2468 // Copy 2x2 blocks of pixels at a time
2469 for (y = 0; y < (height - 1); y += 2) {
2470 for (x = 0; x < (width - 1); x += 2) {
2471 // Row 1
2472 *dstY1++ = *srcY1;
2473 srcY1 += 2;
2474 *dstY1++ = *srcY1;
2475 srcY1 += 2;
2476
2477 // Row 2
2478 *dstY2++ = *srcY2;
2479 srcY2 += 2;
2480 *dstY2++ = *srcY2;
2481 srcY2 += 2;
2482
2483 *dstU = (Uint8)(((Uint32)*srcU1 + *srcU2) / 2);
2484 *dstV = (Uint8)(((Uint32)*srcV1 + *srcV2) / 2);
2485
2486 srcU1 += 4;
2487 srcU2 += 4;
2488 srcV1 += 4;
2489 srcV2 += 4;
2490 dstU += dstUV_pixel_stride;
2491 dstV += dstUV_pixel_stride;
2492 }
2493
2494 // Last column
2495 if (x == (width - 1)) {
2496 // Row 1
2497 *dstY1 = *srcY1;
2498 srcY1 += 2;
2499 *dstY1++ = *srcY1;
2500 srcY1 += 2;
2501
2502 // Row 2
2503 *dstY2 = *srcY2;
2504 srcY2 += 2;
2505 *dstY2++ = *srcY2;
2506 srcY2 += 2;
2507
2508 *dstU = (Uint8)(((Uint32)*srcU1 + *srcU2) / 2);
2509 *dstV = (Uint8)(((Uint32)*srcV1 + *srcV2) / 2);
2510
2511 srcU1 += 4;
2512 srcU2 += 4;
2513 srcV1 += 4;
2514 srcV2 += 4;
2515 dstU += dstUV_pixel_stride;
2516 dstV += dstUV_pixel_stride;
2517 }
2518
2519 srcY1 += src_pitch_left + srcY_pitch;
2520 srcY2 += src_pitch_left + srcY_pitch;
2521 srcU1 += src_pitch_left + srcUV_pitch;
2522 srcU2 += src_pitch_left + srcUV_pitch;
2523 srcV1 += src_pitch_left + srcUV_pitch;
2524 srcV2 += src_pitch_left + srcUV_pitch;
2525 dstY1 += dstY_pitch_left + dstY_pitch;
2526 dstY2 += dstY_pitch_left + dstY_pitch;
2527 dstU += dstUV_pitch_left;
2528 dstV += dstUV_pitch_left;
2529 }
2530
2531 // Last row
2532 if (y == (height - 1)) {
2533 for (x = 0; x < (width - 1); x += 2) {
2534 *dstY1++ = *srcY1;
2535 srcY1 += 2;
2536 *dstY1++ = *srcY1;
2537 srcY1 += 2;
2538
2539 *dstU = *srcU1;
2540 *dstV = *srcV1;
2541
2542 srcU1 += 4;
2543 srcV1 += 4;
2544 dstU += dstUV_pixel_stride;
2545 dstV += dstUV_pixel_stride;
2546 }
2547
2548 // Last column
2549 if (x == (width - 1)) {
2550 *dstY1 = *srcY1;
2551 *dstU = *srcU1;
2552 *dstV = *srcV1;
2553 }
2554 }
2555 return true;
2556}
2557
2558#endif // SDL_HAVE_YUV
2559
2560bool SDL_ConvertPixels_YUV_to_YUV(int width, int height,
2561 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
2562 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
2563{
2564#ifdef SDL_HAVE_YUV
2565 if (src_colorspace != dst_colorspace) {
2566 return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV: colorspace conversion not supported");
2567 }
2568
2569 if (src_format == dst_format) {
2570 if (src == dst) {
2571 // Nothing to do
2572 return true;
2573 }
2574 return SDL_ConvertPixels_YUV_to_YUV_Copy(width, height, src_format, src, src_pitch, dst, dst_pitch);
2575 }
2576
2577 if (IsPlanar2x2Format(src_format) && IsPlanar2x2Format(dst_format)) {
2578 return SDL_ConvertPixels_Planar2x2_to_Planar2x2(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2579 } else if (IsPacked4Format(src_format) && IsPacked4Format(dst_format)) {
2580 return SDL_ConvertPixels_Packed4_to_Packed4(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2581 } else if (IsPlanar2x2Format(src_format) && IsPacked4Format(dst_format)) {
2582 return SDL_ConvertPixels_Planar2x2_to_Packed4(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2583 } else if (IsPacked4Format(src_format) && IsPlanar2x2Format(dst_format)) {
2584 return SDL_ConvertPixels_Packed4_to_Planar2x2(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2585 } else {
2586 return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format),
2587 SDL_GetPixelFormatName(dst_format));
2588 }
2589#else
2590 return SDL_SetError("SDL not built with YUV support");
2591#endif
2592}