8000 SDL/src/video/SDL_yuv.c at SDL2 · aljen/SDL · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1989 lines (1808 loc) · 66.2 KB

SDL_yuv.c

File metadata and controls

1989 lines (1808 loc) · 66.2 KB
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../SDL_internal.h"
#include "SDL_endian.h"
#include "SDL_video.h"
#include "SDL_pixels_c.h"
#include "SDL_yuv_c.h"
#include "yuv2rgb/yuv_rgb.h"
#define SDL_YUV_SD_THRESHOLD 576
static SDL_YUV_CONVERSION_MODE SDL_YUV_ConversionMode = SDL_YUV_CONVERSION_BT601;
#if SDL_HAVE_YUV
static SDL_bool IsPlanar2x2Format(Uint32 format);
#endif
void SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_MODE mode)
{
SDL_YUV_ConversionMode = mode;
}
SDL_YUV_CONVERSION_MODE SDL_GetYUVConversionMode(void)
{
return SDL_YUV_ConversionMode;
}
SDL_YUV_CONVERSION_MODE SDL_GetYUVConversionModeForResolution(int width, int height)
{
SDL_YUV_CONVERSION_MODE mode = SDL_GetYUVConversionMode();
if (mode == SDL_YUV_CONVERSION_AUTOMATIC) {
if (height <= SDL_YUV_SD_THRESHOLD) {
mode = SDL_YUV_CONVERSION_BT601;
} else {
mode = SDL_YUV_CONVERSION_BT709;
}
}
return mode;
}
/*
* Calculate YUV size and pitch. Check for overflow.
* Output 'pitch' that can be used with SDL_ConvertPixels()
*
* return 0 on success, -1 on error
*/
int SDL_CalculateYUVSize(Uint32 format, int w, int h, size_t *size, int *pitch)
{
#if SDL_HAVE_YUV
int sz_plane = 0, sz_plane_chroma = 0, sz_plane_packed = 0;
if (IsPlanar2x2Format(format) == SDL_TRUE) {
{
/* sz_plane == w * h; */
size_t s1;
if (SDL_size_mul_overflow(w, h, &s1) < 0) {
return -1;
}
sz_plane = (int) s1;
}
{
/* sz_plane_chroma == ((w + 1) / 2) * ((h + 1) / 2); */
size_t s1, s2, s3;
if (SDL_size_add_overflow(w, 1, &s1) < 0) {
return -1;
}
s1 = s1 / 2;
if (SDL_size_add_overflow(h, 1, &s2) < 0) {
return -1;
}
s2 = s2 / 2;
if (SDL_size_mul_overflow(s1, s2, &s3) < 0) {
return -1;
}
sz_plane_chroma = (int) s3;
}
} else {
/* sz_plane_packed == ((w + 1) / 2) * h; */
size_t s1, s2;
if (SDL_size_add_overflow(w, 1, &s1) < 0) {
return -1;
}
s1 = s1 / 2;
if (SDL_size_mul_overflow(s1, h, &s2) < 0) {
return -1;
}
sz_plane_packed = (int) s2;
}
switch (format) {
case SDL_PIXELFORMAT_YV12: /**< Planar mode: Y + V + U (3 planes) */
case SDL_PIXELFORMAT_IYUV: /**< Planar mode: Y + U + V (3 planes) */
if (pitch) {
*pitch = w;
}
if (size) {
/* dst_size == sz_plane + sz_plane_chroma + sz_plane_chroma; */
size_t s1, s2;
if (SDL_size_add_overflow(sz_plane, sz_plane_chroma, &s1) < 0) {
return -1;
}
if (SDL_size_add_overflow(s1, sz_plane_chroma, &s2) < 0) {
return -1;
}
*size = (int)s2;
}
break;
case SDL_PIXELFORMAT_YUY2: /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
case SDL_PIXELFORMAT_UYVY: /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
case SDL_PIXELFORMAT_YVYU: /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
if (pitch) {
/* pitch == ((w + 1) / 2) * 4; */
size_t p1, p2;
if (SDL_size_add_overflow(w, 1, &p1) < 0) {
return -1;
}
p1 = p1 / 2;
if (SDL_size_mul_overflow(p1, 4, &p2) < 0) {
return -1;
}
*pitch = (int) p2;
}
if (size) {
/* dst_size == 4 * sz_plane_packed; */
size_t s1;
if (SDL_size_mul_overflow(sz_plane_packed, 4, &s1) < 0) {
return -1;
}
*size = (int) s1;
}
break;
case SDL_PIXELFORMAT_NV12: /**< Planar mode: Y + U/V interleaved (2 planes) */
case SDL_PIXELFORMAT_NV21: /**< Planar mode: Y + V/U interleaved (2 planes) */
if (pitch) {
*pitch = w;
}
if (size) {
/* dst_size == sz_plane + sz_plane_chroma + sz_plane_chroma; */
size_t s1, s2;
if (SDL_size_add_overflow(sz_plane, sz_plane_chroma, &s1) < 0) {
return -1;
}
if (SDL_size_add_overflow(s1, sz_plane_chroma, &s2) < 0) {
return -1;
}
*size = (int) s2;
}
break;
default:
return -1;
}
return 0;
#else
return -1;
#endif
}
#if SDL_HAVE_YUV
static int GetYUVConversionType(int width, int height, YCbCrType *yuv_type)
{
switch (SDL_GetYUVConversionModeForResolution(width, height)) {
case SDL_YUV_CONVERSION_JPEG:
*yuv_type = YCBCR_JPEG;
break;
case SDL_YUV_CONVERSION_BT601:
*yuv_type = YCBCR_601;
break;
case SDL_YUV_CONVERSION_BT709:
*yuv_type = YCBCR_709;
break;
default:
return SDL_SetError("Unexpected YUV conversion mode");
}
return 0;
}
static SDL_bool IsPlanar2x2Format(Uint32 format)
{
return format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV || format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21;
}
static SDL_bool IsPacked4Format(Uint32 format)
{
return format == SDL_PIXELFORMAT_YUY2 || format == SDL_PIXELFORMAT_UYVY || format == SDL_PIXELFORMAT_YVYU;
}
static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, int yuv_pitch,
const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride)
{
const Uint8 *planes[3] = { NULL, NULL, NULL };
int pitches[3] = { 0, 0, 0 };
switch (format) {
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
pitches[0] = yuv_pitch;
pitches[1] = (pitches[0] + 1) / 2;
pitches[2] = (pitches[0] + 1) / 2;
planes[0] = (const Uint8 *)yuv;
planes[1] = planes[0] + pitches[0] * height;
planes[2] = planes[1] + pitches[1] * ((height + 1) / 2);
break;
case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU:
pitches[0] = yuv_pitch;
planes[0] = (const Uint8 *)yuv;
break;
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
pitches[0] = yuv_pitch;
pitches[1] = 2 * ((pitches[0] + 1) / 2);
planes[0] = (const Uint8 *)yuv;
planes[1] = planes[0] + pitches[0] * height;
break;
default:
return SDL_SetError("GetYUVPlanes(): Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
}
switch (format) {
case SDL_PIXELFORMAT_YV12:
*y = planes[0];
*y_stride = pitches[0];
*v = planes[1];
*u = planes[2];
*uv_stride = pitches[1];
break;
case SDL_PIXELFORMAT_IYUV:
*y = planes[0];
*y_stride = pitches[0];
*v = planes[2];
*u = planes[1];
*uv_stride = pitches[1];
break;
case SDL_PIXELFORMAT_YUY2:
*y = planes[0];
*y_stride = pitches[0];
*v = *y + 3;
*u = *y + 1;
*uv_stride = pitches[0];
break;
case SDL_PIXELFORMAT_UYVY:
*y = planes[0] + 1;
*y_stride = pitches[0];
*v = *y + 1;
*u = *y - 1;
*uv_stride = pitches[0];
break;
case SDL_PIXELFORMAT_YVYU:
*y = planes[0];
*y_stride = pitches[0];
*v = *y + 1;
*u = *y + 3;
*uv_stride = pitches[0];
break;
case SDL_PIXELFORMAT_NV12:
*y = planes[0];
*y_stride = pitches[0];
*u = planes[1];
*v = *u + 1;
*uv_stride = pitches[1];
break;
case SDL_PIXELFORMAT_NV21:
*y = planes[0];
*y_stride = pitches[0];
*v = planes[1];
*u = *v + 1;
*uv_stride = pitches[1];
break;
default:
/* Should have caught this above */
return SDL_SetError("GetYUVPlanes[2]: Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
}
return 0;
}
static SDL_bool yuv_rgb_sse(
Uint32 src_format, Uint32 dst_format,
Uint32 width, Uint32 height,
const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
Uint8 *rgb, Uint32 rgb_stride,
YCbCrType yuv_type)
{
#ifdef __SSE2__
if (!SDL_HasSSE2()) {
return SDL_FALSE;
}
if (src_format == SDL_PIXELFORMAT_YV12 ||
src_format == SDL_PIXELFORMAT_IYUV) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB565:
yuv420_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB24:
yuv420_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuv420_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuv420_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuv420_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuv420_abgr_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
if (src_format == SDL_PIXELFORMAT_YUY2 ||
src_format == SDL_PIXELFORMAT_UYVY ||
src_format == SDL_PIXELFORMAT_YVYU) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB565:
yuv422_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB24:
yuv422_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuv422_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuv422_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuv422_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuv422_abgr_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
if (src_format == SDL_PIXELFORMAT_NV12 ||
src_format == SDL_PIXELFORMAT_NV21) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB565:
yuvnv12_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB24:
yuvnv12_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuvnv12_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuvnv12_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuvnv12_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuvnv12_abgr_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
#endif
return SDL_FALSE;
}
static SDL_bool yuv_rgb_lsx(
Uint32 src_format, Uint32 dst_format,
Uint32 width, Uint32 height,
const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
Uint8 *rgb, Uint32 rgb_stride,
YCbCrType yuv_type)
{
#ifdef __loongarch_sx
if (!SDL_HasLSX()) {
return SDL_FALSE;
}
if (src_format == SDL_PIXELFORMAT_YV12 ||
src_format == SDL_PIXELFORMAT_IYUV) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB24:
yuv420_rgb24_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuv420_rgba_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuv420_bgra_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuv420_argb_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuv420_abgr_lsx(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
#endif
return SDL_FALSE;
}
static SDL_bool yuv_rgb_std(
Uint32 src_format, Uint32 dst_format,
Uint32 width, Uint32 height,
const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride,
Uint8 *rgb, Uint32 rgb_stride,
YCbCrType yuv_type)
{
if (src_format == SDL_PIXELFORMAT_YV12 ||
src_format == SDL_PIXELFORMAT_IYUV) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB565:
yuv420_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB24:
yuv420_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuv420_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuv420_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuv420_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuv420_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
if (src_format == SDL_PIXELFORMAT_YUY2 ||
src_format == SDL_PIXELFORMAT_UYVY ||
src_format == SDL_PIXELFORMAT_YVYU) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB565:
yuv422_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB24:
yuv422_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuv422_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuv422_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuv422_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuv422_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
if (src_format == SDL_PIXELFORMAT_NV12 ||
src_format == SDL_PIXELFORMAT_NV21) {
switch (dst_format) {
case SDL_PIXELFORMAT_RGB565:
yuvnv12_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB24:
yuvnv12_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_RGBA8888:
yuvnv12_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGRX8888:
case SDL_PIXELFORMAT_BGRA8888:
yuvnv12_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_RGB888:
case SDL_PIXELFORMAT_ARGB8888:
yuvnv12_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
case SDL_PIXELFORMAT_BGR888:
case SDL_PIXELFORMAT_ABGR8888:
yuvnv12_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
return SDL_TRUE;
default:
break;
}
}
return SDL_FALSE;
}
int SDL_ConvertPixels_YUV_to_RGB(int width, int height,
Uint32 src_format, const void *src, int src_pitch,
Uint32 dst_format, void *dst, int dst_pitch)
{
const Uint8 *y = NULL;
const Uint8 *u = NULL;
const Uint8 *v = NULL;
Uint32 y_stride = 0;
Uint32 uv_stride = 0;
YCbCrType yuv_type = YCBCR_601;
if (GetYUVPlanes(width, height, src_format, src, src_pitch, &y, &u, &v, &y_stride, &uv_stride) < 0) {
return -1;
}
if (GetYUVConversionType(width, height, &yuv_type) < 0) {
return -1;
}
if (yuv_rgb_sse(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) {
return 0;
}
if (yuv_rgb_lsx(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) {
return 0;
}
if (yuv_rgb_std(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) {
return 0;
}
/* No fast path for the RGB format, instead convert using an intermediate buffer */
if (dst_format != SDL_PIXELFORMAT_ARGB8888) {
int ret;
void *tmp;
int tmp_pitch = (width * sizeof(Uint32));
tmp = SDL_malloc((size_t)tmp_pitch * height);
if (!tmp) {
return SDL_OutOfMemory();
}
/* convert src/src_format to tmp/ARGB8888 */
ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch);
if (ret < 0) {
SDL_free(tmp);
return ret;
}
/* convert tmp/ARGB8888 to dst/RGB */
ret = SDL_ConvertPixels(width, height, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch, dst_format, dst, dst_pitch);
SDL_free(tmp);
return ret;
}
return SDL_SetError("Unsupported YUV conversion");
}
struct RGB2YUVFactors
{
int y_offset;
float y[3]; /* Rfactor, Gfactor, Bfactor */
float u[3]; /* Rfactor, Gfactor, Bfactor */
float v[3]; /* Rfactor, Gfactor, Bfactor */
};
static int SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch)
{
const int src_pitch_x_2 = src_pitch * 2;
const int height_half = height / 2;
const int height_remainder = (height & 0x1);
const int width_half = width / 2;
const int width_remainder = (width & 0x1);
int i, j;
static struct RGB2YUVFactors RGB2YUVFactorTables[SDL_YUV_CONVERSION_BT709 + 1] = {
/* ITU-T T.871 (JPEG) */
{
0,
{ 0.2990f, 0.5870f, 0.1140f },
{ -0.1687f, -0.3313f, 0.5000f },
{ 0.5000f, -0.4187f, -0.0813f },
},
/* ITU-R BT.601-7 */
{
16,
{ 0.2568f, 0.5041f, 0.0979f },
{ -0.1482f, -0.2910f, 0.4392f },
{ 0.4392f, -0.3678f, -0.0714f },
},
/* ITU-R BT.709-6 */
{
16,
{ 0.1826f, 0.6142f, 0.0620f },
{ -0.1006f, -0.3386f, 0.4392f },
{ 0.4392f, -0.3989f, -0.0403f },
},
};
const struct RGB2YUVFactors *cvt = &RGB2YUVFactorTables[SDL_GetYUVConversionModeForResolution(width, height)];
#define MAKE_Y(r, g, b) (Uint8)((int)(cvt->y[0] * (r) + cvt->y[1] * (g) + cvt->y[2] * (b) + 0.5f) + cvt->y_offset)
#define MAKE_U(r, g, b) (Uint8)((int)(cvt->u[0] * (r) + cvt->u[1] * (g) + cvt->u[2] * (b) + 0.5f) + 128)
#define MAKE_V(r, g, b) (Uint8)((int)(cvt->v[0] * (r) + cvt->v[1] * (g) + cvt->v[2] * (b) + 0.5f) + 128)
#define READ_2x2_PIXELS \
const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \
const Uint32 p3 = ((const Uint32 *)next_row)[2 * i]; \
const Uint32 p4 = ((const Uint32 *)next_row)[2 * i + 1]; \
const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000) + (p3 & 0x00ff0000) + (p4 & 0x00ff0000)) >> 18; \
const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00) + (p3 & 0x0000ff00) + (p4 & 0x0000ff00)) >> 10; \
const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff) + (p3 & 0x000000ff) + (p4 & 0x000000ff)) >> 2;
#define READ_2x1_PIXELS \
const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
const Uint32 p2 = ((const Uint32 *)next_row)[2 * i]; \
const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000)) >> 17; \
const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00)) >> 9; \
const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff)) >> 1;
#define READ_1x2_PIXELS \
const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \
const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \
const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000)) >> 17; \
const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00)) >> 9; \
const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff)) >> 1;
#define READ_1x1_PIXEL \
const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \
const Uint32 r = (p & 0x00ff0000) >> 16; \
const Uint32 g = (p & 0x0000ff00) >> 8; \
const Uint32 b = (p & 0x000000ff);
#define READ_TWO_RGB_PIXELS \
const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \
const Uint32 r = (p & 0x00ff0000) >> 16; \
const Uint32 g = (p & 0x0000ff00) >> 8; \
const Uint32 b = (p & 0x000000ff); \
const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i + 1]; \
const Uint32 r1 = (p1 & 0x00ff0000) >> 16; \
const Uint32 g1 = (p1 & 0x0000ff00) >> 8; \
const Uint32 b1 = (p1 & 0x000000ff); \
const Uint32 R = (r + r1) / 2; \
const Uint32 G = (g + g1) / 2; \
const Uint32 B = (b + b1) / 2;
#define READ_ONE_RGB_PIXEL READ_1x1_PIXEL
switch (dst_format) {
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
{
const Uint8 *curr_row, *next_row;
Uint8 *plane_y;
Uint8 *plane_u;
Uint8 *plane_v;
Uint8 *plane_interleaved_uv;
Uint32 y_stride, uv_stride, y_skip, uv_skip;
if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
(const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v,
&y_stride, &uv_stride) != 0) {
return -1;
}
plane_interleaved_uv = (plane_y + height * y_stride);
y_skip = (y_stride - width);
curr_row = (const Uint8 *)src;
/* Write Y plane */
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
const Uint32 p1 = ((const Uint32 *)curr_row)[i];
const Uint32 r = (p1 & 0x00ff0000) >> 16;
const Uint32 g = (p1 & 0x0000ff00) >> 8;
const Uint32 b = (p1 & 0x000000ff);
*plane_y++ = MAKE_Y(r, g, b);
}
plane_y += y_skip;
curr_row += src_pitch;
}
curr_row = (const Uint8 *)src;
next_row = (const Uint8 *)src;
next_row += src_pitch;
if (dst_format == SDL_PIXELFORMAT_YV12 || dst_format == SDL_PIXELFORMAT_IYUV) {
/* Write UV planes, not interleaved */
uv_skip = (uv_stride - (width + 1) / 2);
for (j = 0; j < height_half; j++) {
for (i = 0; i < width_half; i++) {
READ_2x2_PIXELS;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_2x1_PIXELS;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
plane_u += uv_skip;
plane_v += uv_skip;
curr_row += src_pitch_x_2;
next_row += src_pitch_x_2;
}
if (height_remainder) {
for (i = 0; i < width_half; i++) {
READ_1x2_PIXELS;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_1x1_PIXEL;
*plane_u++ = MAKE_U(r, g, b);
*plane_v++ = MAKE_V(r, g, b);
}
plane_u += uv_skip;
plane_v += uv_skip;
}
} else if (dst_format == SDL_PIXELFORMAT_NV12) {
uv_skip = (uv_stride - ((width + 1) / 2) * 2);
for (j = 0; j < height_half; j++) {
for (i = 0; i < width_half; i++) {
READ_2x2_PIXELS;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_2x1_PIXELS;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
plane_interleaved_uv += uv_skip;
curr_row += src_pitch_x_2;
next_row += src_pitch_x_2;
}
if (height_remainder) {
for (i = 0; i < width_half; i++) {
READ_1x2_PIXELS;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
if (width_remainder) {
READ_1x1_PIXEL;
*plane_interleaved_uv++ = MAKE_U(r, g, b);
*plane_interleaved_uv++ = MAKE_V(r, g, b);
}
}
} else /* dst_format == SDL_PIXELFORMAT_NV21 */ {
uv_skip = (uv_stride - ((width + 1) / 2) * 2);
for (j = 0; j < height_half; j++) {
for (i = 0; i < width_half; i++) {
READ_2x2_PIXELS;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
if (width_remainder) {
READ_2x1_PIXELS;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
plane_interleaved_uv += uv_skip;
curr_row += src_pitch_x_2;
next_row += src_pitch_x_2;
}
if (height_remainder) {
for (i = 0; i < width_half; i++) {
READ_1x2_PIXELS;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
if (width_remainder) {
READ_1x1_PIXEL;
*plane_interleaved_uv++ = MAKE_V(r, g, b);
*plane_interleaved_uv++ = MAKE_U(r, g, b);
}
}
}
} break;
case SDL_PIXELFORMAT_YUY2:
case SDL_PIXELFORMAT_UYVY:
case SDL_PIXELFORMAT_YVYU:
{
const Uint8 *curr_row = (const Uint8 *)src;
Uint8 *plane = (Uint8 *)dst;
const int row_size = (4 * ((width + 1) / 2));
int plane_skip;
if (dst_pitch < row_size) {
return SDL_SetError("Destination pitch is too small, expected at least %d\n", row_size);
}
plane_skip = (dst_pitch - row_size);
/* Write YUV plane, packed */
if (dst_format == SDL_PIXELFORMAT_YUY2) {
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_TWO_RGB_PIXELS;
/* Y U Y1 V */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_U(R, G, B);
*plane++ = MAKE_Y(r1, g1, b1);
*plane++ = MAKE_V(R, G, B);
}
if (width_remainder) {
READ_ONE_RGB_PIXEL;
/* Y U Y V */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_U(r, g, b);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(r, g, b);
}
plane += plane_skip;
curr_row += src_pitch;
}
} else if (dst_format == SDL_PIXELFORMAT_UYVY) {
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_TWO_RGB_PIXELS;
/* U Y V Y1 */
*plane++ = MAKE_U(R, G, B);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(R, G, B);
*plane++ = MAKE_Y(r1, g1, b1);
}
if (width_remainder) {
READ_ONE_RGB_PIXEL;
/* U Y V Y */
*plane++ = MAKE_U(r, g, b);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(r, g, b);
*plane++ = MAKE_Y(r, g, b);
}
plane += plane_skip;
curr_row += src_pitch;
}
} else if (dst_format == SDL_PIXELFORMAT_YVYU) {
for (j = 0; j < height; j++) {
for (i = 0; i < width_half; i++) {
READ_TWO_RGB_PIXELS;
/* Y V Y1 U */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(R, G, B);
*plane++ = MAKE_Y(r1, g1, b1);
*plane++ = MAKE_U(R, G, B);
}
if (width_remainder) {
READ_ONE_RGB_PIXEL;
/* Y V Y U */
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_V(r, g, b);
*plane++ = MAKE_Y(r, g, b);
*plane++ = MAKE_U(r, g, b);
}
plane += plane_skip;
curr_row += src_pitch;
}
}
} break;
default:
return SDL_SetError("Unsupported YUV destination format: %s", SDL_GetPixelFormatName(dst_format));
}
#undef MAKE_Y
#undef MAKE_U
#undef MAKE_V
#undef READ_2x2_PIXELS
#undef READ_2x1_PIXELS
#undef READ_1x2_PIXELS
#undef READ_1x1_PIXEL
#undef READ_TWO_RGB_PIXELS
#undef READ_ONE_RGB_PIXEL
return 0;
}
int SDL_ConvertPixels_RGB_to_YUV(int width, int height,
Uint32 src_format, const void *src, int src_pitch,
Uint32 dst_format, void *dst, int dst_pitch)
{
#if 0 /* Doesn't handle odd widths */
/* RGB24 to FOURCC */
if (src_format == SDL_PIXELFORMAT_RGB24) {
Uint8 *y;
Uint8 *u;
Uint8 *v;
Uint32 y_stride;
Uint32 uv_stride;
YCbCrType yuv_type;
if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch, (const Uint8 **)&y, (const Uint8 **)&u, (const Uint8 **)&v, &y_stride, &uv_stride) < 0) {
return -1;
}
if (GetYUVConversionType(width, height, &yuv_type) < 0) {
return -1;
}
rgb24_yuv420_std(width, height, src, src_pitch, y, u, v, y_stride, uv_stride, yuv_type);
return 0;
}
#endif
/* ARGB8888 to FOURCC */
if (src_format == SDL_PIXELFORMAT_ARGB8888) {
return SDL_ConvertPixels_ARGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst, dst_pitch);
}
/* not ARGB8888 to FOURCC : need an intermediate conversion */
{
int ret;
void *tmp;
int tmp_pitch = (width * sizeof(Uint32));
tmp = SDL_malloc((size_t)tmp_pitch * height);
if (!tmp) {
return SDL_OutOfMemory();
}
/* convert src/src_format to tmp/ARGB8888 */
ret = SDL_ConvertPixels(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch);
if (ret == -1) {
SDL_free(tmp);
return ret;
}
/* convert tmp/ARGB8888 to dst/FOURCC */
ret = SDL_ConvertPixels_ARGB8888_to_YUV(width, height, tmp, tmp_pitch, dst_format, dst, dst_pitch);
SDL_free(tmp);
return ret;
}
}
static int SDL_ConvertPixels_YUV_to_YUV_Copy(int width, int height, Uint32 format,
const void *src, int src_pitch, void *dst, int dst_pitch)
0