|  | /* | 
|  | Simple DirectMedia Layer | 
|  | Copyright (C) 1997-2023 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 "../video/SDL_yuv_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 == NULL) { | 
|  | SDL_OutOfMemory(); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | swdata->format = format; | 
|  | swdata->target_format = SDL_PIXELFORMAT_UNKNOWN; | 
|  | swdata->w = w; | 
|  | swdata->h = h; | 
|  | { | 
|  | size_t dst_size; | 
|  | if (SDL_CalculateYUVSize(format, w, h, &dst_size, NULL) < 0) { | 
|  | SDL_SW_DestroyYUVTexture(swdata); | 
|  | SDL_OutOfMemory(); | 
|  | return NULL; | 
|  | } | 
|  | 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, | 
|  | (size_t)(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 * (((size_t)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, | 
|  | (size_t)(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 * (((size_t)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: */ |