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 dstUVPitch = ((dst_pitch + 1) / 2) * 2;
1632
1633 // Skip the Y plane
1634 src = (const Uint8 *)src + height * src_pitch;
1635 dst = (Uint8 *)dst + height * dst_pitch;
1636
1637 bool aligned = (((uintptr_t)src | (uintptr_t)dst) & 1) == 0;
1638 if (aligned) {
1639 const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2) / sizeof(Uint16);
1640 const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2) / sizeof(Uint16);
1641 const Uint16 *srcUV = (const Uint16 *)src;
1642 Uint16 *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 } else {
1653 const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2);
1654 const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2);
1655 const Uint8 *srcUV = (const Uint8 *)src;
1656 Uint8 *dstUV = (Uint8 *)dst;
1657 y = UVheight;
1658 while (y--) {
1659 x = UVwidth;
1660 while (x--) {
1661 Uint8 u = *srcUV++;
1662 Uint8 v = *srcUV++;
1663 *dstUV++ = v;
1664 *dstUV++ = u;
1665 }
1666 srcUV += srcUVPitchLeft;
1667 dstUV += dstUVPitchLeft;
1668 }
1669 }
1670 return true;
1671}
1672
1673static bool SDL_ConvertPixels_PackUVPlanes_to_NV(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1674{
1675#ifdef SDL_SSE2_INTRINSICS
1676 if (SDL_HasSSE2()) {
1677 return SDL_ConvertPixels_PackUVPlanes_to_NV_SSE2(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1678 }
1679#endif
1680 return SDL_ConvertPixels_PackUVPlanes_to_NV_std(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1681}
1682
1683static bool SDL_ConvertPixels_SplitNV_to_UVPlanes(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, bool reverseUV)
1684{
1685#ifdef SDL_SSE2_INTRINSICS
1686 if (SDL_HasSSE2()) {
1687 return SDL_ConvertPixels_SplitNV_to_UVPlanes_SSE2(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1688 }
1689#endif
1690 return SDL_ConvertPixels_SplitNV_to_UVPlanes_std(width, height, src, src_pitch, dst, dst_pitch, reverseUV);
1691}
1692
1693static bool SDL_ConvertPixels_SwapNV(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
1694{
1695#ifdef SDL_SSE2_INTRINSICS
1696 if (SDL_HasSSE2()) {
1697 return SDL_ConvertPixels_SwapNV_SSE2(width, height, src, src_pitch, dst, dst_pitch);
1698 }
1699#endif
1700 return SDL_ConvertPixels_SwapNV_std(width, height, src, src_pitch, dst, dst_pitch);
1701}
1702
1703static bool SDL_ConvertPixels_Planar2x2_to_Planar2x2(int width, int height,
1704 SDL_PixelFormat src_format, const void *src, int src_pitch,
1705 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
1706{
1707 if (src != dst) {
1708 // Copy Y plane
1709 int i;
1710 const Uint8 *srcY = (const Uint8 *)src;
1711 Uint8 *dstY = (Uint8 *)dst;
1712 for (i = height; i--;) {
1713 SDL_memcpy(dstY, srcY, width);
1714 srcY += src_pitch;
1715 dstY += dst_pitch;
1716 }
1717 }
1718
1719 switch (src_format) {
1720 case SDL_PIXELFORMAT_YV12:
1721 switch (dst_format) {
1722 case SDL_PIXELFORMAT_IYUV:
1723 return SDL_ConvertPixels_SwapUVPlanes(width, height, src, src_pitch, dst, dst_pitch);
1724 case SDL_PIXELFORMAT_NV12:
1725 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, true);
1726 case SDL_PIXELFORMAT_NV21:
1727 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, false);
1728 default:
1729 break;
1730 }
1731 break;
1732 case SDL_PIXELFORMAT_IYUV:
1733 switch (dst_format) {
1734 case SDL_PIXELFORMAT_YV12:
1735 return SDL_ConvertPixels_SwapUVPlanes(width, height, src, src_pitch, dst, dst_pitch);
1736 case SDL_PIXELFORMAT_NV12:
1737 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, false);
1738 case SDL_PIXELFORMAT_NV21:
1739 return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, true);
1740 default:
1741 break;
1742 }
1743 break;
1744 case SDL_PIXELFORMAT_NV12:
1745 switch (dst_format) {
1746 case SDL_PIXELFORMAT_YV12:
1747 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, true);
1748 case SDL_PIXELFORMAT_IYUV:
1749 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, false);
1750 case SDL_PIXELFORMAT_NV21:
1751 return SDL_ConvertPixels_SwapNV(width, height, src, src_pitch, dst, dst_pitch);
1752 default:
1753 break;
1754 }
1755 break;
1756 case SDL_PIXELFORMAT_NV21:
1757 switch (dst_format) {
1758 case SDL_PIXELFORMAT_YV12:
1759 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, false);
1760 case SDL_PIXELFORMAT_IYUV:
1761 return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, true);
1762 case SDL_PIXELFORMAT_NV12:
1763 return SDL_ConvertPixels_SwapNV(width, height, src, src_pitch, dst, dst_pitch);
1764 default:
1765 break;
1766 }
1767 break;
1768 default:
1769 break;
1770 }
1771 return SDL_SetError("SDL_ConvertPixels_Planar2x2_to_Planar2x2: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format),
1772 SDL_GetPixelFormatName(dst_format));
1773}
1774
1775#ifdef SDL_SSE2_INTRINSICS
1776#define PACKED4_TO_PACKED4_ROW_SSE2(shuffle) \
1777 while (x >= 4) { \
1778 __m128i yuv = _mm_loadu_si128((__m128i *)srcYUV); \
1779 __m128i lo = _mm_unpacklo_epi8(yuv, _mm_setzero_si128()); \
1780 __m128i hi = _mm_unpackhi_epi8(yuv, _mm_setzero_si128()); \
1781 lo = _mm_shufflelo_epi16(lo, shuffle); \
1782 lo = _mm_shufflehi_epi16(lo, shuffle); \
1783 hi = _mm_shufflelo_epi16(hi, shuffle); \
1784 hi = _mm_shufflehi_epi16(hi, shuffle); \
1785 yuv = _mm_packus_epi16(lo, hi); \
1786 _mm_storeu_si128((__m128i *)dstYUV, yuv); \
1787 srcYUV += 16; \
1788 dstYUV += 16; \
1789 x -= 4; \
1790 }
1791
1792static 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)
1793{
1794 int x, y;
1795 const int YUVwidth = (width + 1) / 2;
1796 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1797 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1798 const Uint8 *srcYUV = (const Uint8 *)src;
1799 Uint8 *dstYUV = (Uint8 *)dst;
1800
1801 y = height;
1802 x = YUVwidth;
1803 while (y--) {
1804 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 3, 0, 1));
1805 while (x--) {
1806 Uint8 Y1, U, Y2, V;
1807
1808 Y1 = srcYUV[0];
1809 U = srcYUV[1];
1810 Y2 = srcYUV[2];
1811 V = srcYUV[3];
1812 srcYUV += 4;
1813
1814 dstYUV[0] = U;
1815 dstYUV[1] = Y1;
1816 dstYUV[2] = V;
1817 dstYUV[3] = Y2;
1818 dstYUV += 4;
1819 }
1820 srcYUV += srcYUVPitchLeft;
1821 dstYUV += dstYUVPitchLeft;
1822 x = YUVwidth;
1823 }
1824 return true;
1825}
1826
1827static 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)
1828{
1829 int x, y;
1830 const int YUVwidth = (width + 1) / 2;
1831 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1832 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1833 const Uint8 *srcYUV = (const Uint8 *)src;
1834 Uint8 *dstYUV = (Uint8 *)dst;
1835
1836 y = height;
1837 x = YUVwidth;
1838 while (y--) {
1839 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(1, 2, 3, 0));
1840 while (x--) {
1841 Uint8 Y1, U, Y2, V;
1842
1843 Y1 = srcYUV[0];
1844 U = srcYUV[1];
1845 Y2 = srcYUV[2];
1846 V = srcYUV[3];
1847 srcYUV += 4;
1848
1849 dstYUV[0] = Y1;
1850 dstYUV[1] = V;
1851 dstYUV[2] = Y2;
1852 dstYUV[3] = U;
1853 dstYUV += 4;
1854 }
1855 srcYUV += srcYUVPitchLeft;
1856 dstYUV += dstYUVPitchLeft;
1857 x = YUVwidth;
1858 }
1859 return true;
1860}
1861
1862static 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)
1863{
1864 int x, y;
1865 const int YUVwidth = (width + 1) / 2;
1866 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1867 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1868 const Uint8 *srcYUV = (const Uint8 *)src;
1869 Uint8 *dstYUV = (Uint8 *)dst;
1870
1871 y = height;
1872 x = YUVwidth;
1873 while (y--) {
1874 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 3, 0, 1));
1875 while (x--) {
1876 Uint8 Y1, U, Y2, V;
1877
1878 U = srcYUV[0];
1879 Y1 = srcYUV[1];
1880 V = srcYUV[2];
1881 Y2 = srcYUV[3];
1882 srcYUV += 4;
1883
1884 dstYUV[0] = Y1;
1885 dstYUV[1] = U;
1886 dstYUV[2] = Y2;
1887 dstYUV[3] = V;
1888 dstYUV += 4;
1889 }
1890 srcYUV += srcYUVPitchLeft;
1891 dstYUV += dstYUVPitchLeft;
1892 x = YUVwidth;
1893 }
1894 return true;
1895}
1896
1897static 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)
1898{
1899 int x, y;
1900 const int YUVwidth = (width + 1) / 2;
1901 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1902 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1903 const Uint8 *srcYUV = (const Uint8 *)src;
1904 Uint8 *dstYUV = (Uint8 *)dst;
1905
1906 y = height;
1907 x = YUVwidth;
1908 while (y--) {
1909 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(0, 3, 2, 1));
1910 while (x--) {
1911 Uint8 Y1, U, Y2, V;
1912
1913 U = srcYUV[0];
1914 Y1 = srcYUV[1];
1915 V = srcYUV[2];
1916 Y2 = srcYUV[3];
1917 srcYUV += 4;
1918
1919 dstYUV[0] = Y1;
1920 dstYUV[1] = V;
1921 dstYUV[2] = Y2;
1922 dstYUV[3] = U;
1923 dstYUV += 4;
1924 }
1925 srcYUV += srcYUVPitchLeft;
1926 dstYUV += dstYUVPitchLeft;
1927 x = YUVwidth;
1928 }
1929 return true;
1930}
1931
1932static 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)
1933{
1934 int x, y;
1935 const int YUVwidth = (width + 1) / 2;
1936 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1937 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1938 const Uint8 *srcYUV = (const Uint8 *)src;
1939 Uint8 *dstYUV = (Uint8 *)dst;
1940
1941 y = height;
1942 x = YUVwidth;
1943 while (y--) {
1944 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(1, 2, 3, 0));
1945 while (x--) {
1946 Uint8 Y1, U, Y2, V;
1947
1948 Y1 = srcYUV[0];
1949 V = srcYUV[1];
1950 Y2 = srcYUV[2];
1951 U = srcYUV[3];
1952 srcYUV += 4;
1953
1954 dstYUV[0] = Y1;
1955 dstYUV[1] = U;
1956 dstYUV[2] = Y2;
1957 dstYUV[3] = V;
1958 dstYUV += 4;
1959 }
1960 srcYUV += srcYUVPitchLeft;
1961 dstYUV += dstYUVPitchLeft;
1962 x = YUVwidth;
1963 }
1964 return true;
1965}
1966
1967static 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)
1968{
1969 int x, y;
1970 const int YUVwidth = (width + 1) / 2;
1971 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
1972 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
1973 const Uint8 *srcYUV = (const Uint8 *)src;
1974 Uint8 *dstYUV = (Uint8 *)dst;
1975
1976 y = height;
1977 x = YUVwidth;
1978 while (y--) {
1979 PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 1, 0, 3));
1980 while (x--) {
1981 Uint8 Y1, U, Y2, V;
1982
1983 Y1 = srcYUV[0];
1984 V = srcYUV[1];
1985 Y2 = srcYUV[2];
1986 U = srcYUV[3];
1987 srcYUV += 4;
1988
1989 dstYUV[0] = U;
1990 dstYUV[1] = Y1;
1991 dstYUV[2] = V;
1992 dstYUV[3] = Y2;
1993 dstYUV += 4;
1994 }
1995 srcYUV += srcYUVPitchLeft;
1996 dstYUV += dstYUVPitchLeft;
1997 x = YUVwidth;
1998 }
1999 return true;
2000}
2001#endif
2002
2003static bool SDL_ConvertPixels_YUY2_to_UYVY_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2004{
2005 int x, y;
2006 const int YUVwidth = (width + 1) / 2;
2007 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2008 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2009 const Uint8 *srcYUV = (const Uint8 *)src;
2010 Uint8 *dstYUV = (Uint8 *)dst;
2011
2012 y = height;
2013 while (y--) {
2014 x = YUVwidth;
2015 while (x--) {
2016 Uint8 Y1, U, Y2, V;
2017
2018 Y1 = srcYUV[0];
2019 U = srcYUV[1];
2020 Y2 = srcYUV[2];
2021 V = srcYUV[3];
2022 srcYUV += 4;
2023
2024 dstYUV[0] = U;
2025 dstYUV[1] = Y1;
2026 dstYUV[2] = V;
2027 dstYUV[3] = Y2;
2028 dstYUV += 4;
2029 }
2030 srcYUV += srcYUVPitchLeft;
2031 dstYUV += dstYUVPitchLeft;
2032 }
2033 return true;
2034}
2035
2036static bool SDL_ConvertPixels_YUY2_to_YVYU_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2037{
2038 int x, y;
2039 const int YUVwidth = (width + 1) / 2;
2040 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2041 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2042 const Uint8 *srcYUV = (const Uint8 *)src;
2043 Uint8 *dstYUV = (Uint8 *)dst;
2044
2045 y = height;
2046 while (y--) {
2047 x = YUVwidth;
2048 while (x--) {
2049 Uint8 Y1, U, Y2, V;
2050
2051 Y1 = srcYUV[0];
2052 U = srcYUV[1];
2053 Y2 = srcYUV[2];
2054 V = srcYUV[3];
2055 srcYUV += 4;
2056
2057 dstYUV[0] = Y1;
2058 dstYUV[1] = V;
2059 dstYUV[2] = Y2;
2060 dstYUV[3] = U;
2061 dstYUV += 4;
2062 }
2063 srcYUV += srcYUVPitchLeft;
2064 dstYUV += dstYUVPitchLeft;
2065 }
2066 return true;
2067}
2068
2069static bool SDL_ConvertPixels_UYVY_to_YUY2_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2070{
2071 int x, y;
2072 const int YUVwidth = (width + 1) / 2;
2073 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2074 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2075 const Uint8 *srcYUV = (const Uint8 *)src;
2076 Uint8 *dstYUV = (Uint8 *)dst;
2077
2078 y = height;
2079 while (y--) {
2080 x = YUVwidth;
2081 while (x--) {
2082 Uint8 Y1, U, Y2, V;
2083
2084 U = srcYUV[0];
2085 Y1 = srcYUV[1];
2086 V = srcYUV[2];
2087 Y2 = srcYUV[3];
2088 srcYUV += 4;
2089
2090 dstYUV[0] = Y1;
2091 dstYUV[1] = U;
2092 dstYUV[2] = Y2;
2093 dstYUV[3] = V;
2094 dstYUV += 4;
2095 }
2096 srcYUV += srcYUVPitchLeft;
2097 dstYUV += dstYUVPitchLeft;
2098 }
2099 return true;
2100}
2101
2102static bool SDL_ConvertPixels_UYVY_to_YVYU_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2103{
2104 int x, y;
2105 const int YUVwidth = (width + 1) / 2;
2106 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2107 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2108 const Uint8 *srcYUV = (const Uint8 *)src;
2109 Uint8 *dstYUV = (Uint8 *)dst;
2110
2111 y = height;
2112 while (y--) {
2113 x = YUVwidth;
2114 while (x--) {
2115 Uint8 Y1, U, Y2, V;
2116
2117 U = srcYUV[0];
2118 Y1 = srcYUV[1];
2119 V = srcYUV[2];
2120 Y2 = srcYUV[3];
2121 srcYUV += 4;
2122
2123 dstYUV[0] = Y1;
2124 dstYUV[1] = V;
2125 dstYUV[2] = Y2;
2126 dstYUV[3] = U;
2127 dstYUV += 4;
2128 }
2129 srcYUV += srcYUVPitchLeft;
2130 dstYUV += dstYUVPitchLeft;
2131 }
2132 return true;
2133}
2134
2135static bool SDL_ConvertPixels_YVYU_to_YUY2_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2136{
2137 int x, y;
2138 const int YUVwidth = (width + 1) / 2;
2139 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2140 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2141 const Uint8 *srcYUV = (const Uint8 *)src;
2142 Uint8 *dstYUV = (Uint8 *)dst;
2143
2144 y = height;
2145 while (y--) {
2146 x = YUVwidth;
2147 while (x--) {
2148 Uint8 Y1, U, Y2, V;
2149
2150 Y1 = srcYUV[0];
2151 V = srcYUV[1];
2152 Y2 = srcYUV[2];
2153 U = srcYUV[3];
2154 srcYUV += 4;
2155
2156 dstYUV[0] = Y1;
2157 dstYUV[1] = U;
2158 dstYUV[2] = Y2;
2159 dstYUV[3] = V;
2160 dstYUV += 4;
2161 }
2162 srcYUV += srcYUVPitchLeft;
2163 dstYUV += dstYUVPitchLeft;
2164 }
2165 return true;
2166}
2167
2168static bool SDL_ConvertPixels_YVYU_to_UYVY_std(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2169{
2170 int x, y;
2171 const int YUVwidth = (width + 1) / 2;
2172 const int srcYUVPitchLeft = (src_pitch - YUVwidth * 4);
2173 const int dstYUVPitchLeft = (dst_pitch - YUVwidth * 4);
2174 const Uint8 *srcYUV = (const Uint8 *)src;
2175 Uint8 *dstYUV = (Uint8 *)dst;
2176
2177 y = height;
2178 while (y--) {
2179 x = YUVwidth;
2180 while (x--) {
2181 Uint8 Y1, U, Y2, V;
2182
2183 Y1 = srcYUV[0];
2184 V = srcYUV[1];
2185 Y2 = srcYUV[2];
2186 U = srcYUV[3];
2187 srcYUV += 4;
2188
2189 dstYUV[0] = U;
2190 dstYUV[1] = Y1;
2191 dstYUV[2] = V;
2192 dstYUV[3] = Y2;
2193 dstYUV += 4;
2194 }
2195 srcYUV += srcYUVPitchLeft;
2196 dstYUV += dstYUVPitchLeft;
2197 }
2198 return true;
2199}
2200
2201static bool SDL_ConvertPixels_YUY2_to_UYVY(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2202{
2203#ifdef SDL_SSE2_INTRINSICS
2204 if (SDL_HasSSE2()) {
2205 return SDL_ConvertPixels_YUY2_to_UYVY_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2206 }
2207#endif
2208 return SDL_ConvertPixels_YUY2_to_UYVY_std(width, height, src, src_pitch, dst, dst_pitch);
2209}
2210
2211static bool SDL_ConvertPixels_YUY2_to_YVYU(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2212{
2213#ifdef SDL_SSE2_INTRINSICS
2214 if (SDL_HasSSE2()) {
2215 return SDL_ConvertPixels_YUY2_to_YVYU_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2216 }
2217#endif
2218 return SDL_ConvertPixels_YUY2_to_YVYU_std(width, height, src, src_pitch, dst, dst_pitch);
2219}
2220
2221static bool SDL_ConvertPixels_UYVY_to_YUY2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2222{
2223#ifdef SDL_SSE2_INTRINSICS
2224 if (SDL_HasSSE2()) {
2225 return SDL_ConvertPixels_UYVY_to_YUY2_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2226 }
2227#endif
2228 return SDL_ConvertPixels_UYVY_to_YUY2_std(width, height, src, src_pitch, dst, dst_pitch);
2229}
2230
2231static bool SDL_ConvertPixels_UYVY_to_YVYU(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2232{
2233#ifdef SDL_SSE2_INTRINSICS
2234 if (SDL_HasSSE2()) {
2235 return SDL_ConvertPixels_UYVY_to_YVYU_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2236 }
2237#endif
2238 return SDL_ConvertPixels_UYVY_to_YVYU_std(width, height, src, src_pitch, dst, dst_pitch);
2239}
2240
2241static bool SDL_ConvertPixels_YVYU_to_YUY2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2242{
2243#ifdef SDL_SSE2_INTRINSICS
2244 if (SDL_HasSSE2()) {
2245 return SDL_ConvertPixels_YVYU_to_YUY2_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2246 }
2247#endif
2248 return SDL_ConvertPixels_YVYU_to_YUY2_std(width, height, src, src_pitch, dst, dst_pitch);
2249}
2250
2251static bool SDL_ConvertPixels_YVYU_to_UYVY(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2252{
2253#ifdef SDL_SSE2_INTRINSICS
2254 if (SDL_HasSSE2()) {
2255 return SDL_ConvertPixels_YVYU_to_UYVY_SSE2(width, height, src, src_pitch, dst, dst_pitch);
2256 }
2257#endif
2258 return SDL_ConvertPixels_YVYU_to_UYVY_std(width, height, src, src_pitch, dst, dst_pitch);
2259}
2260
2261static bool SDL_ConvertPixels_Packed4_to_Packed4(int width, int height,
2262 SDL_PixelFormat src_format, const void *src, int src_pitch,
2263 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2264{
2265 switch (src_format) {
2266 case SDL_PIXELFORMAT_YUY2:
2267 switch (dst_format) {
2268 case SDL_PIXELFORMAT_UYVY:
2269 return SDL_ConvertPixels_YUY2_to_UYVY(width, height, src, src_pitch, dst, dst_pitch);
2270 case SDL_PIXELFORMAT_YVYU:
2271 return SDL_ConvertPixels_YUY2_to_YVYU(width, height, src, src_pitch, dst, dst_pitch);
2272 default:
2273 break;
2274 }
2275 break;
2276 case SDL_PIXELFORMAT_UYVY:
2277 switch (dst_format) {
2278 case SDL_PIXELFORMAT_YUY2:
2279 return SDL_ConvertPixels_UYVY_to_YUY2(width, height, src, src_pitch, dst, dst_pitch);
2280 case SDL_PIXELFORMAT_YVYU:
2281 return SDL_ConvertPixels_UYVY_to_YVYU(width, height, src, src_pitch, dst, dst_pitch);
2282 default:
2283 break;
2284 }
2285 break;
2286 case SDL_PIXELFORMAT_YVYU:
2287 switch (dst_format) {
2288 case SDL_PIXELFORMAT_YUY2:
2289 return SDL_ConvertPixels_YVYU_to_YUY2(width, height, src, src_pitch, dst, dst_pitch);
2290 case SDL_PIXELFORMAT_UYVY:
2291 return SDL_ConvertPixels_YVYU_to_UYVY(width, height, src, src_pitch, dst, dst_pitch);
2292 default:
2293 break;
2294 }
2295 break;
2296 default:
2297 break;
2298 }
2299 return SDL_SetError("SDL_ConvertPixels_Packed4_to_Packed4: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format),
2300 SDL_GetPixelFormatName(dst_format));
2301}
2302
2303static bool SDL_ConvertPixels_Planar2x2_to_Packed4(int width, int height,
2304 SDL_PixelFormat src_format, const void *src, int src_pitch,
2305 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2306{
2307 int x, y;
2308 const Uint8 *srcY1, *srcY2, *srcU, *srcV;
2309 Uint32 srcY_pitch, srcUV_pitch;
2310 Uint32 srcY_pitch_left, srcUV_pitch_left, srcUV_pixel_stride;
2311 Uint8 *dstY1, *dstY2, *dstU1, *dstU2, *dstV1, *dstV2;
2312 Uint32 dstY_pitch, dstUV_pitch;
2313 Uint32 dst_pitch_left;
2314
2315 if (src == dst) {
2316 return SDL_SetError("Can't change YUV plane types in-place");
2317 }
2318
2319 if (!GetYUVPlanes(width, height, src_format, src, src_pitch,
2320 &srcY1, &srcU, &srcV, &srcY_pitch, &srcUV_pitch)) {
2321 return false;
2322 }
2323 srcY2 = srcY1 + srcY_pitch;
2324 srcY_pitch_left = (srcY_pitch - width);
2325
2326 if (src_format == SDL_PIXELFORMAT_NV12 || src_format == SDL_PIXELFORMAT_NV21) {
2327 srcUV_pixel_stride = 2;
2328 srcUV_pitch_left = (srcUV_pitch - 2 * ((width + 1) / 2));
2329 } else {
2330 srcUV_pixel_stride = 1;
2331 srcUV_pitch_left = (srcUV_pitch - ((width + 1) / 2));
2332 }
2333
2334 if (!GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
2335 (const Uint8 **)&dstY1, (const Uint8 **)&dstU1, (const Uint8 **)&dstV1,
2336 &dstY_pitch, &dstUV_pitch)) {
2337 return false;
2338 }
2339 dstY2 = dstY1 + dstY_pitch;
2340 dstU2 = dstU1 + dstUV_pitch;
2341 dstV2 = dstV1 + dstUV_pitch;
2342 dst_pitch_left = (dstY_pitch - 4 * ((width + 1) / 2));
2343
2344 // Copy 2x2 blocks of pixels at a time
2345 for (y = 0; y < (height - 1); y += 2) {
2346 for (x = 0; x < (width - 1); x += 2) {
2347 // Row 1
2348 *dstY1 = *srcY1++;
2349 dstY1 += 2;
2350 *dstY1 = *srcY1++;
2351 dstY1 += 2;
2352 *dstU1 = *srcU;
2353 *dstV1 = *srcV;
2354
2355 // Row 2
2356 *dstY2 = *srcY2++;
2357 dstY2 += 2;
2358 *dstY2 = *srcY2++;
2359 dstY2 += 2;
2360 *dstU2 = *srcU;
2361 *dstV2 = *srcV;
2362
2363 srcU += srcUV_pixel_stride;
2364 srcV += srcUV_pixel_stride;
2365 dstU1 += 4;
2366 dstU2 += 4;
2367 dstV1 += 4;
2368 dstV2 += 4;
2369 }
2370
2371 // Last column
2372 if (x == (width - 1)) {
2373 // Row 1
2374 *dstY1 = *srcY1;
2375 dstY1 += 2;
2376 *dstY1 = *srcY1++;
2377 dstY1 += 2;
2378 *dstU1 = *srcU;
2379 *dstV1 = *srcV;
2380
2381 // Row 2
2382 *dstY2 = *srcY2;
2383 dstY2 += 2;
2384 *dstY2 = *srcY2++;
2385 dstY2 += 2;
2386 *dstU2 = *srcU;
2387 *dstV2 = *srcV;
2388
2389 srcU += srcUV_pixel_stride;
2390 srcV += srcUV_pixel_stride;
2391 dstU1 += 4;
2392 dstU2 += 4;
2393 dstV1 += 4;
2394 dstV2 += 4;
2395 }
2396
2397 srcY1 += srcY_pitch_left + srcY_pitch;
2398 srcY2 += srcY_pitch_left + srcY_pitch;
2399 srcU += srcUV_pitch_left;
2400 srcV += srcUV_pitch_left;
2401 dstY1 += dst_pitch_left + dstY_pitch;
2402 dstY2 += dst_pitch_left + dstY_pitch;
2403 dstU1 += dst_pitch_left + dstUV_pitch;
2404 dstU2 += dst_pitch_left + dstUV_pitch;
2405 dstV1 += dst_pitch_left + dstUV_pitch;
2406 dstV2 += dst_pitch_left + dstUV_pitch;
2407 }
2408
2409 // Last row
2410 if (y == (height - 1)) {
2411 for (x = 0; x < (width - 1); x += 2) {
2412 // Row 1
2413 *dstY1 = *srcY1++;
2414 dstY1 += 2;
2415 *dstY1 = *srcY1++;
2416 dstY1 += 2;
2417 *dstU1 = *srcU;
2418 *dstV1 = *srcV;
2419
2420 srcU += srcUV_pixel_stride;
2421 srcV += srcUV_pixel_stride;
2422 dstU1 += 4;
2423 dstV1 += 4;
2424 }
2425
2426 // Last column
2427 if (x == (width - 1)) {
2428 // Row 1
2429 *dstY1 = *srcY1;
2430 dstY1 += 2;
2431 *dstY1 = *srcY1++;
2432 dstY1 += 2;
2433 *dstU1 = *srcU;
2434 *dstV1 = *srcV;
2435
2436 srcU += srcUV_pixel_stride;
2437 srcV += srcUV_pixel_stride;
2438 dstU1 += 4;
2439 dstV1 += 4;
2440 }
2441 }
2442 return true;
2443}
2444
2445static bool SDL_ConvertPixels_Packed4_to_Planar2x2(int width, int height,
2446 SDL_PixelFormat src_format, const void *src, int src_pitch,
2447 SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2448{
2449 int x, y;
2450 const Uint8 *srcY1, *srcY2, *srcU1, *srcU2, *srcV1, *srcV2;
2451 Uint32 srcY_pitch, srcUV_pitch;
2452 Uint32 src_pitch_left;
2453 Uint8 *dstY1, *dstY2, *dstU, *dstV;
2454 Uint32 dstY_pitch, dstUV_pitch;
2455 Uint32 dstY_pitch_left, dstUV_pitch_left, dstUV_pixel_stride;
2456
2457 if (src == dst) {
2458 return SDL_SetError("Can't change YUV plane types in-place");
2459 }
2460
2461 if (!GetYUVPlanes(width, height, src_format, src, src_pitch,
2462 &srcY1, &srcU1, &srcV1, &srcY_pitch, &srcUV_pitch)) {
2463 return false;
2464 }
2465 srcY2 = srcY1 + srcY_pitch;
2466 srcU2 = srcU1 + srcUV_pitch;
2467 srcV2 = srcV1 + srcUV_pitch;
2468 src_pitch_left = (srcY_pitch - 4 * ((width + 1) / 2));
2469
2470 if (!GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
2471 (const Uint8 **)&dstY1, (const Uint8 **)&dstU, (const Uint8 **)&dstV,
2472 &dstY_pitch, &dstUV_pitch)) {
2473 return false;
2474 }
2475 dstY2 = dstY1 + dstY_pitch;
2476 dstY_pitch_left = (dstY_pitch - width);
2477
2478 if (dst_format == SDL_PIXELFORMAT_NV12 || dst_format == SDL_PIXELFORMAT_NV21) {
2479 dstUV_pixel_stride = 2;
2480 dstUV_pitch_left = (dstUV_pitch - 2 * ((width + 1) / 2));
2481 } else {
2482 dstUV_pixel_stride = 1;
2483 dstUV_pitch_left = (dstUV_pitch - ((width + 1) / 2));
2484 }
2485
2486 // Copy 2x2 blocks of pixels at a time
2487 for (y = 0; y < (height - 1); y += 2) {
2488 for (x = 0; x < (width - 1); x += 2) {
2489 // Row 1
2490 *dstY1++ = *srcY1;
2491 srcY1 += 2;
2492 *dstY1++ = *srcY1;
2493 srcY1 += 2;
2494
2495 // Row 2
2496 *dstY2++ = *srcY2;
2497 srcY2 += 2;
2498 *dstY2++ = *srcY2;
2499 srcY2 += 2;
2500
2501 *dstU = (Uint8)(((Uint32)*srcU1 + *srcU2) / 2);
2502 *dstV = (Uint8)(((Uint32)*srcV1 + *srcV2) / 2);
2503
2504 srcU1 += 4;
2505 srcU2 += 4;
2506 srcV1 += 4;
2507 srcV2 += 4;
2508 dstU += dstUV_pixel_stride;
2509 dstV += dstUV_pixel_stride;
2510 }
2511
2512 // Last column
2513 if (x == (width - 1)) {
2514 // Row 1
2515 *dstY1 = *srcY1;
2516 srcY1 += 2;
2517 *dstY1++ = *srcY1;
2518 srcY1 += 2;
2519
2520 // Row 2
2521 *dstY2 = *srcY2;
2522 srcY2 += 2;
2523 *dstY2++ = *srcY2;
2524 srcY2 += 2;
2525
2526 *dstU = (Uint8)(((Uint32)*srcU1 + *srcU2) / 2);
2527 *dstV = (Uint8)(((Uint32)*srcV1 + *srcV2) / 2);
2528
2529 srcU1 += 4;
2530 srcU2 += 4;
2531 srcV1 += 4;
2532 srcV2 += 4;
2533 dstU += dstUV_pixel_stride;
2534 dstV += dstUV_pixel_stride;
2535 }
2536
2537 srcY1 += src_pitch_left + srcY_pitch;
2538 srcY2 += src_pitch_left + srcY_pitch;
2539 srcU1 += src_pitch_left + srcUV_pitch;
2540 srcU2 += src_pitch_left + srcUV_pitch;
2541 srcV1 += src_pitch_left + srcUV_pitch;
2542 srcV2 += src_pitch_left + srcUV_pitch;
2543 dstY1 += dstY_pitch_left + dstY_pitch;
2544 dstY2 += dstY_pitch_left + dstY_pitch;
2545 dstU += dstUV_pitch_left;
2546 dstV += dstUV_pitch_left;
2547 }
2548
2549 // Last row
2550 if (y == (height - 1)) {
2551 for (x = 0; x < (width - 1); x += 2) {
2552 *dstY1++ = *srcY1;
2553 srcY1 += 2;
2554 *dstY1++ = *srcY1;
2555 srcY1 += 2;
2556
2557 *dstU = *srcU1;
2558 *dstV = *srcV1;
2559
2560 srcU1 += 4;
2561 srcV1 += 4;
2562 dstU += dstUV_pixel_stride;
2563 dstV += dstUV_pixel_stride;
2564 }
2565
2566 // Last column
2567 if (x == (width - 1)) {
2568 *dstY1 = *srcY1;
2569 *dstU = *srcU1;
2570 *dstV = *srcV1;
2571 }
2572 }
2573 return true;
2574}
2575
2576#endif // SDL_HAVE_YUV
2577
2578bool SDL_ConvertPixels_YUV_to_YUV(int width, int height,
2579 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
2580 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
2581{
2582#ifdef SDL_HAVE_YUV
2583 if (src_colorspace != dst_colorspace) {
2584 return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV: colorspace conversion not supported");
2585 }
2586
2587 if (src_format == dst_format) {
2588 if (src == dst) {
2589 // Nothing to do
2590 return true;
2591 }
2592 return SDL_ConvertPixels_YUV_to_YUV_Copy(width, height, src_format, src, src_pitch, dst, dst_pitch);
2593 }
2594
2595 if (IsPlanar2x2Format(src_format) && IsPlanar2x2Format(dst_format)) {
2596 return SDL_ConvertPixels_Planar2x2_to_Planar2x2(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2597 } else if (IsPacked4Format(src_format) && IsPacked4Format(dst_format)) {
2598 return SDL_ConvertPixels_Packed4_to_Packed4(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2599 } else if (IsPlanar2x2Format(src_format) && IsPacked4Format(dst_format)) {
2600 return SDL_ConvertPixels_Planar2x2_to_Packed4(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2601 } else if (IsPacked4Format(src_format) && IsPlanar2x2Format(dst_format)) {
2602 return SDL_ConvertPixels_Packed4_to_Planar2x2(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
2603 } else {
2604 return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format),
2605 SDL_GetPixelFormatName(dst_format));
2606 }
2607#else
2608 return SDL_SetError("SDL not built with YUV support");
2609#endif
2610}
2611