mirror of
https://github.com/Relintai/sdl2_frt.git
synced 2024-12-16 11:06:49 +01:00
453 lines
14 KiB
C
453 lines
14 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2021 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"
|
|
|
|
/* This is the software implementation of the YUV texture support */
|
|
|
|
#if SDL_HAVE_YUV
|
|
|
|
|
|
#include "SDL_yuv_sw_c.h"
|
|
#include "SDL_cpuinfo.h"
|
|
|
|
|
|
SDL_SW_YUVTexture *
|
|
SDL_SW_CreateYUVTexture(Uint32 format, int w, int h)
|
|
{
|
|
SDL_SW_YUVTexture *swdata;
|
|
|
|
switch (format) {
|
|
case SDL_PIXELFORMAT_YV12:
|
|
case SDL_PIXELFORMAT_IYUV:
|
|
case SDL_PIXELFORMAT_YUY2:
|
|
case SDL_PIXELFORMAT_UYVY:
|
|
case SDL_PIXELFORMAT_YVYU:
|
|
case SDL_PIXELFORMAT_NV12:
|
|
case SDL_PIXELFORMAT_NV21:
|
|
break;
|
|
default:
|
|
SDL_SetError("Unsupported YUV format");
|
|
return NULL;
|
|
}
|
|
|
|
swdata = (SDL_SW_YUVTexture *) SDL_calloc(1, sizeof(*swdata));
|
|
if (!swdata) {
|
|
SDL_OutOfMemory();
|
|
return NULL;
|
|
}
|
|
|
|
swdata->format = format;
|
|
swdata->target_format = SDL_PIXELFORMAT_UNKNOWN;
|
|
swdata->w = w;
|
|
swdata->h = h;
|
|
{
|
|
const int sz_plane = w * h;
|
|
const int sz_plane_chroma = ((w + 1) / 2) * ((h + 1) / 2);
|
|
const int sz_plane_packed = ((w + 1) / 2) * h;
|
|
int dst_size = 0;
|
|
switch(format)
|
|
{
|
|
case SDL_PIXELFORMAT_YV12: /**< Planar mode: Y + V + U (3 planes) */
|
|
case SDL_PIXELFORMAT_IYUV: /**< Planar mode: Y + U + V (3 planes) */
|
|
dst_size = sz_plane + sz_plane_chroma + sz_plane_chroma;
|
|
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) */
|
|
dst_size = 4 * sz_plane_packed;
|
|
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) */
|
|
dst_size = sz_plane + sz_plane_chroma + sz_plane_chroma;
|
|
break;
|
|
|
|
default:
|
|
SDL_assert(0 && "We should never get here (caught above)");
|
|
break;
|
|
}
|
|
swdata->pixels = (Uint8 *) SDL_SIMDAlloc(dst_size);
|
|
if (!swdata->pixels) {
|
|
SDL_SW_DestroyYUVTexture(swdata);
|
|
SDL_OutOfMemory();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Find the pitch and offset values for the texture */
|
|
switch (format) {
|
|
case SDL_PIXELFORMAT_YV12:
|
|
case SDL_PIXELFORMAT_IYUV:
|
|
swdata->pitches[0] = w;
|
|
swdata->pitches[1] = (swdata->pitches[0] + 1) / 2;
|
|
swdata->pitches[2] = (swdata->pitches[0] + 1) / 2;
|
|
swdata->planes[0] = swdata->pixels;
|
|
swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
|
|
swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
|
|
break;
|
|
case SDL_PIXELFORMAT_YUY2:
|
|
case SDL_PIXELFORMAT_UYVY:
|
|
case SDL_PIXELFORMAT_YVYU:
|
|
swdata->pitches[0] = ((w + 1) / 2) * 4;
|
|
swdata->planes[0] = swdata->pixels;
|
|
break;
|
|
|
|
case SDL_PIXELFORMAT_NV12:
|
|
case SDL_PIXELFORMAT_NV21:
|
|
swdata->pitches[0] = w;
|
|
swdata->pitches[1] = 2 * ((swdata->pitches[0] + 1) / 2);
|
|
swdata->planes[0] = swdata->pixels;
|
|
swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
|
|
break;
|
|
|
|
default:
|
|
SDL_assert(0 && "We should never get here (caught above)");
|
|
break;
|
|
}
|
|
|
|
/* We're all done.. */
|
|
return (swdata);
|
|
}
|
|
|
|
int
|
|
SDL_SW_QueryYUVTexturePixels(SDL_SW_YUVTexture * swdata, void **pixels,
|
|
int *pitch)
|
|
{
|
|
*pixels = swdata->planes[0];
|
|
*pitch = swdata->pitches[0];
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
|
const void *pixels, int pitch)
|
|
{
|
|
switch (swdata->format) {
|
|
case SDL_PIXELFORMAT_YV12:
|
|
case SDL_PIXELFORMAT_IYUV:
|
|
if (rect->x == 0 && rect->y == 0 &&
|
|
rect->w == swdata->w && rect->h == swdata->h) {
|
|
SDL_memcpy(swdata->pixels, pixels,
|
|
(swdata->h * swdata->w) + 2* ((swdata->h + 1) /2) * ((swdata->w + 1) / 2));
|
|
} else {
|
|
Uint8 *src, *dst;
|
|
int row;
|
|
size_t length;
|
|
|
|
/* Copy the Y plane */
|
|
src = (Uint8 *) pixels;
|
|
dst = swdata->pixels + rect->y * swdata->w + rect->x;
|
|
length = rect->w;
|
|
for (row = 0; row < rect->h; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += pitch;
|
|
dst += swdata->w;
|
|
}
|
|
|
|
/* Copy the next plane */
|
|
src = (Uint8 *) pixels + rect->h * pitch;
|
|
dst = swdata->pixels + swdata->h * swdata->w;
|
|
dst += rect->y/2 * ((swdata->w + 1) / 2) + rect->x/2;
|
|
length = (rect->w + 1) / 2;
|
|
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += (pitch + 1)/2;
|
|
dst += (swdata->w + 1)/2;
|
|
}
|
|
|
|
/* Copy the next plane */
|
|
src = (Uint8 *) pixels + rect->h * pitch + ((rect->h + 1) / 2) * ((pitch + 1) / 2);
|
|
dst = swdata->pixels + swdata->h * swdata->w +
|
|
((swdata->h + 1)/2) * ((swdata->w+1) / 2);
|
|
dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
|
|
length = (rect->w + 1) / 2;
|
|
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += (pitch + 1)/2;
|
|
dst += (swdata->w + 1)/2;
|
|
}
|
|
}
|
|
break;
|
|
case SDL_PIXELFORMAT_YUY2:
|
|
case SDL_PIXELFORMAT_UYVY:
|
|
case SDL_PIXELFORMAT_YVYU:
|
|
{
|
|
Uint8 *src, *dst;
|
|
int row;
|
|
size_t length;
|
|
|
|
src = (Uint8 *) pixels;
|
|
dst =
|
|
swdata->planes[0] + rect->y * swdata->pitches[0] +
|
|
rect->x * 2;
|
|
length = 4 * ((rect->w + 1) / 2);
|
|
for (row = 0; row < rect->h; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += pitch;
|
|
dst += swdata->pitches[0];
|
|
}
|
|
}
|
|
break;
|
|
case SDL_PIXELFORMAT_NV12:
|
|
case SDL_PIXELFORMAT_NV21:
|
|
{
|
|
if (rect->x == 0 && rect->y == 0 && rect->w == swdata->w && rect->h == swdata->h) {
|
|
SDL_memcpy(swdata->pixels, pixels,
|
|
(swdata->h * swdata->w) + 2* ((swdata->h + 1) /2) * ((swdata->w + 1) / 2));
|
|
} else {
|
|
|
|
Uint8 *src, *dst;
|
|
int row;
|
|
size_t length;
|
|
|
|
/* Copy the Y plane */
|
|
src = (Uint8 *) pixels;
|
|
dst = swdata->pixels + rect->y * swdata->w + rect->x;
|
|
length = rect->w;
|
|
for (row = 0; row < rect->h; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += pitch;
|
|
dst += swdata->w;
|
|
}
|
|
|
|
/* Copy the next plane */
|
|
src = (Uint8 *) pixels + rect->h * pitch;
|
|
dst = swdata->pixels + swdata->h * swdata->w;
|
|
dst += 2 * ((rect->y + 1)/2) * ((swdata->w + 1) / 2) + 2 * (rect->x/2);
|
|
length = 2 * ((rect->w + 1) / 2);
|
|
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += 2 * ((pitch + 1)/2);
|
|
dst += 2 * ((swdata->w + 1)/2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
|
const Uint8 *Yplane, int Ypitch,
|
|
const Uint8 *Uplane, int Upitch,
|
|
const Uint8 *Vplane, int Vpitch)
|
|
{
|
|
const Uint8 *src;
|
|
Uint8 *dst;
|
|
int row;
|
|
size_t length;
|
|
|
|
/* Copy the Y plane */
|
|
src = Yplane;
|
|
dst = swdata->pixels + rect->y * swdata->w + rect->x;
|
|
length = rect->w;
|
|
for (row = 0; row < rect->h; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += Ypitch;
|
|
dst += swdata->w;
|
|
}
|
|
|
|
/* Copy the U plane */
|
|
src = Uplane;
|
|
if (swdata->format == SDL_PIXELFORMAT_IYUV) {
|
|
dst = swdata->pixels + swdata->h * swdata->w;
|
|
} else {
|
|
dst = swdata->pixels + swdata->h * swdata->w +
|
|
((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
|
|
}
|
|
dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
|
|
length = (rect->w + 1) / 2;
|
|
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += Upitch;
|
|
dst += (swdata->w + 1)/2;
|
|
}
|
|
|
|
/* Copy the V plane */
|
|
src = Vplane;
|
|
if (swdata->format == SDL_PIXELFORMAT_YV12) {
|
|
dst = swdata->pixels + swdata->h * swdata->w;
|
|
} else {
|
|
dst = swdata->pixels + swdata->h * swdata->w +
|
|
((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
|
|
}
|
|
dst += rect->y/2 * ((swdata->w + 1)/2) + rect->x/2;
|
|
length = (rect->w + 1) / 2;
|
|
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += Vpitch;
|
|
dst += (swdata->w + 1)/2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SDL_SW_UpdateNVTexturePlanar(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
|
const Uint8 *Yplane, int Ypitch,
|
|
const Uint8 *UVplane, int UVpitch)
|
|
{
|
|
const Uint8 *src;
|
|
Uint8 *dst;
|
|
int row;
|
|
size_t length;
|
|
|
|
/* Copy the Y plane */
|
|
src = Yplane;
|
|
dst = swdata->pixels + rect->y * swdata->w + rect->x;
|
|
length = rect->w;
|
|
for (row = 0; row < rect->h; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += Ypitch;
|
|
dst += swdata->w;
|
|
}
|
|
|
|
/* Copy the UV or VU plane */
|
|
src = UVplane;
|
|
dst = swdata->pixels + swdata->h * swdata->w;
|
|
dst += rect->y * ((swdata->w + 1)/2) + rect->x;
|
|
length = (rect->w + 1) / 2;
|
|
length *= 2;
|
|
for (row = 0; row < (rect->h + 1)/2; ++row) {
|
|
SDL_memcpy(dst, src, length);
|
|
src += UVpitch;
|
|
dst += 2 * ((swdata->w + 1)/2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SDL_SW_LockYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect,
|
|
void **pixels, int *pitch)
|
|
{
|
|
switch (swdata->format) {
|
|
case SDL_PIXELFORMAT_YV12:
|
|
case SDL_PIXELFORMAT_IYUV:
|
|
case SDL_PIXELFORMAT_NV12:
|
|
case SDL_PIXELFORMAT_NV21:
|
|
if (rect
|
|
&& (rect->x != 0 || rect->y != 0 || rect->w != swdata->w
|
|
|| rect->h != swdata->h)) {
|
|
return SDL_SetError
|
|
("YV12, IYUV, NV12, NV21 textures only support full surface locks");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (rect) {
|
|
*pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * 2;
|
|
} else {
|
|
*pixels = swdata->planes[0];
|
|
}
|
|
*pitch = swdata->pitches[0];
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
SDL_SW_UnlockYUVTexture(SDL_SW_YUVTexture * swdata)
|
|
{
|
|
}
|
|
|
|
int
|
|
SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata, const SDL_Rect * srcrect,
|
|
Uint32 target_format, int w, int h, void *pixels,
|
|
int pitch)
|
|
{
|
|
int stretch;
|
|
|
|
/* Make sure we're set up to display in the desired format */
|
|
if (target_format != swdata->target_format && swdata->display) {
|
|
SDL_FreeSurface(swdata->display);
|
|
swdata->display = NULL;
|
|
}
|
|
|
|
stretch = 0;
|
|
if (srcrect->x || srcrect->y || srcrect->w < swdata->w || srcrect->h < swdata->h) {
|
|
/* The source rectangle has been clipped.
|
|
Using a scratch surface is easier than adding clipped
|
|
source support to all the blitters, plus that would
|
|
slow them down in the general unclipped case.
|
|
*/
|
|
stretch = 1;
|
|
} else if ((srcrect->w != w) || (srcrect->h != h)) {
|
|
stretch = 1;
|
|
}
|
|
if (stretch) {
|
|
int bpp;
|
|
Uint32 Rmask, Gmask, Bmask, Amask;
|
|
|
|
if (swdata->display) {
|
|
swdata->display->w = w;
|
|
swdata->display->h = h;
|
|
swdata->display->pixels = pixels;
|
|
swdata->display->pitch = pitch;
|
|
} else {
|
|
/* This must have succeeded in SDL_SW_SetupYUVDisplay() earlier */
|
|
SDL_PixelFormatEnumToMasks(target_format, &bpp, &Rmask, &Gmask,
|
|
&Bmask, &Amask);
|
|
swdata->display =
|
|
SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch, Rmask,
|
|
Gmask, Bmask, Amask);
|
|
if (!swdata->display) {
|
|
return (-1);
|
|
}
|
|
}
|
|
if (!swdata->stretch) {
|
|
/* This must have succeeded in SDL_SW_SetupYUVDisplay() earlier */
|
|
SDL_PixelFormatEnumToMasks(target_format, &bpp, &Rmask, &Gmask,
|
|
&Bmask, &Amask);
|
|
swdata->stretch =
|
|
SDL_CreateRGBSurface(0, swdata->w, swdata->h, bpp, Rmask,
|
|
Gmask, Bmask, Amask);
|
|
if (!swdata->stretch) {
|
|
return (-1);
|
|
}
|
|
}
|
|
pixels = swdata->stretch->pixels;
|
|
pitch = swdata->stretch->pitch;
|
|
}
|
|
if (SDL_ConvertPixels(swdata->w, swdata->h, swdata->format,
|
|
swdata->planes[0], swdata->pitches[0],
|
|
target_format, pixels, pitch) < 0) {
|
|
return -1;
|
|
}
|
|
if (stretch) {
|
|
SDL_Rect rect = *srcrect;
|
|
SDL_SoftStretch(swdata->stretch, &rect, swdata->display, NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
SDL_SW_DestroyYUVTexture(SDL_SW_YUVTexture * swdata)
|
|
{
|
|
if (swdata) {
|
|
SDL_SIMDFree(swdata->pixels);
|
|
SDL_FreeSurface(swdata->stretch);
|
|
SDL_FreeSurface(swdata->display);
|
|
SDL_free(swdata);
|
|
}
|
|
}
|
|
|
|
#endif /* SDL_HAVE_YUV */
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|