diff --git a/src/render/SDL_yuv_sw.c b/src/render/SDL_yuv_sw.c index 9bb35eedd0..96ca1bec63 100644 --- a/src/render/SDL_yuv_sw.c +++ b/src/render/SDL_yuv_sw.c @@ -140,6 +140,7 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect, int row; size_t length; const int bpp = SDL_BYTESPERPIXEL(swdata->format); + const int UVpitch = ((pitch / bpp + 1) / 2) * bpp; // Copy the Y plane src = (Uint8 *)pixels; @@ -158,19 +159,19 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect, length = ((rect->w + 1) / 2) * bpp; for (row = 0; row < (rect->h + 1) / 2; ++row) { SDL_memcpy(dst, src, length); - src += (pitch + 1) / 2; + src += UVpitch; dst += swdata->pitches[1]; } // Copy the next plane - src = (Uint8 *)pixels + rect->h * pitch + ((rect->h + 1) / 2) * ((pitch + 1) / 2); + src = (Uint8 *)pixels + rect->h * pitch + ((rect->h + 1) / 2) * UVpitch; 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) * bpp; for (row = 0; row < (rect->h + 1) / 2; ++row) { SDL_memcpy(dst, src, length); - src += (pitch + 1) / 2; + src += UVpitch; dst += swdata->pitches[2]; } } diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index ed5e4130df..929dbdafd6 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -28,6 +28,7 @@ #include "../SDL_d3dmath.h" #include "../../video/windows/SDL_windowsvideo.h" #include "../../video/SDL_pixels_c.h" +#include "../../video/SDL_yuv_c.h" #define D3D_DEBUG_INFO #include @@ -807,8 +808,12 @@ static bool D3D_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (texturedata->yuv) { // It's more efficient to upload directly... if (!texturedata->pixels) { - texturedata->pitch = texture->w; - texturedata->pixels = (Uint8 *)SDL_malloc((texture->h * texturedata->pitch * 3) / 2); + size_t size, calculated_pitch; + if (!SDL_CalculateYUVSize(texture->format, texture->w, texture->h, &size, &calculated_pitch)) { + return false; + } + texturedata->pitch = (int)calculated_pitch; + texturedata->pixels = (Uint8 *)SDL_malloc(size); if (!texturedata->pixels) { return false; } diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 2b6b339286..121c61d3c9 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -28,6 +28,7 @@ #include "../SDL_sysrender.h" #include "../SDL_d3dmath.h" #include "../../video/SDL_pixels_c.h" +#include "../../video/SDL_yuv_c.h" #include #ifdef HAVE_DXGI1_5_H @@ -1608,8 +1609,9 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return false; } } else { - int Ypitch = srcPitch; - int UVpitch = ((Ypitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2); + const int bpp = SDL_BYTESPERPIXEL(texture->format); + const int Ypitch = srcPitch; + const int UVpitch = ((Ypitch / bpp + 1) / 2) * bpp; const Uint8 *plane0 = (const Uint8 *)srcPixels; const Uint8 *plane1 = plane0 + rect->h * Ypitch; const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch; @@ -1795,8 +1797,12 @@ static bool D3D11_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (textureData->yuv || textureData->nv12) { // It's more efficient to upload directly... if (!textureData->pixels) { - textureData->pitch = texture->w; - textureData->pixels = (Uint8 *)SDL_malloc((texture->h * textureData->pitch * 3) / 2); + size_t size, calculated_calculated_pitch; + if (!SDL_CalculateYUVSize(texture->format, texture->w, texture->h, &size, &calculated_calculated_pitch)) { + return false; + } + textureData->pitch = (int)calculated_calculated_pitch; + textureData->pixels = (Uint8 *)SDL_malloc(size); if (!textureData->pixels) { return false; } diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 9e529ce5be..8051b18385 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -46,6 +46,7 @@ extern "C" { // This must be included here as the function definitions in SDL_pixels.c/_c.h are C, not C++ #include "../../video/SDL_pixels_c.h" +#include "../../video/SDL_yuv_c.h" /* !!! FIXME: vertex buffer bandwidth could be lower; only use UV coords when !!! FIXME: textures are needed. */ @@ -2079,16 +2080,17 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } } else { const int bpp = SDL_BYTESPERPIXEL(texture->format); + const int UVpitch = ((srcPitch / bpp + 1) / 2) * bpp; // Skip to the correct offset into the next texture srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); - if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1 * bpp) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) { + if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, UVpitch, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) { return false; } // Skip to the correct offset into the next texture - srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1 * bpp) / 2)); - if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1 * bpp) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) { + srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * UVpitch); + if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, UVpitch, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) { return false; } } @@ -2201,8 +2203,12 @@ static bool D3D12_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (textureData->yuv || textureData->nv12) { // It's more efficient to upload directly... if (!textureData->pixels) { - textureData->pitch = texture->w; - textureData->pixels = (Uint8 *)SDL_malloc((texture->h * textureData->pitch * 3) / 2); + size_t size, calculated_pitch; + if (!SDL_CalculateYUVSize(texture->format, texture->w, texture->h, &size, &calculated_pitch)) { + return false; + } + textureData->pitch = (int)calculated_pitch; + textureData->pixels = (Uint8 *)SDL_malloc(size); if (!textureData->pixels) { return false; } diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index d54243478c..c6e7ff38bc 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -25,6 +25,7 @@ #include "../../events/SDL_windowevents_c.h" #include "../../video/SDL_pixels_c.h" #include "../../video/SDL_sysvideo.h" +#include "../../video/SDL_yuv_c.h" #include "../SDL_d3dmath.h" #include "../SDL_sysrender.h" #include "SDL_gpu_util.h" @@ -321,25 +322,16 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ data->format = format; if (texture->access == SDL_TEXTUREACCESS_STREAMING) { - size_t size; - data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); - size = (size_t)texture->h * data->pitch; - if (texture->format == SDL_PIXELFORMAT_YV12 || - texture->format == SDL_PIXELFORMAT_IYUV || - texture->format == SDL_PIXELFORMAT_I0FL) { - // Need to add size for the U and V planes - size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); - } - if (texture->format == SDL_PIXELFORMAT_I444 || - texture->format == SDL_PIXELFORMAT_I4FL) { - // Need to add size for the U and V planes - size += 2 * texture->h * data->pitch; - } - if (texture->format == SDL_PIXELFORMAT_NV12 || - texture->format == SDL_PIXELFORMAT_NV21 || - texture->format == SDL_PIXELFORMAT_P010) { - // Need to add size for the U/V plane - size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); + size_t size, pitch; + if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { + if (!SDL_CalculateYUVSize(texture->format, texture->w, texture->h, &size, &pitch)) { + SDL_free(data); + return false; + } + data->pitch = (int)pitch; + } else { + data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); + size = (size_t)texture->h * data->pitch; } data->pixels = SDL_calloc(1, size); if (!data->pixels) { @@ -584,8 +576,8 @@ static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x, rect->y, rect->w, rect->h, Uplane, pitch); retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x, rect->y, rect->w, rect->h, Vplane, pitch); } else { - int Ypitch = pitch; - int UVpitch = ((Ypitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2); + const int Ypitch = pitch; + const int UVpitch = ((Ypitch / bpp + 1) / 2) * bpp; const Uint8 *Yplane = (const Uint8 *)pixels; const Uint8 *Uplane = Yplane + rect->h * Ypitch; const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch; diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 39eae3a36d..2e9cc4a491 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -24,6 +24,7 @@ #include "../SDL_sysrender.h" #include "../../video/SDL_pixels_c.h" +#include "../../video/SDL_yuv_c.h" #import #import @@ -1003,13 +1004,14 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, // YV12 stores V before U, so the plane order is swapped for it. id firstplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureV : texturedata.mtltextureU; id secondplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureU : texturedata.mtltextureV; + const int bpp = SDL_BYTESPERPIXEL(texture->format); int UVpitch; SDL_Rect UVrect; if (texture->format == SDL_PIXELFORMAT_I444 || texture->format == SDL_PIXELFORMAT_I4FL) { UVpitch = pitch; UVrect = *rect; } else { - UVpitch = (pitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2; + UVpitch = ((pitch / bpp + 1) / 2) * bpp; UVrect.x = rect->x / 2; UVrect.y = rect->y / 2; UVrect.w = (rect->w + 1) / 2; @@ -1030,8 +1032,9 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } if (texturedata.nv12) { + const int bpp = SDL_BYTESPERPIXEL(texture->format); + const int UVpitch = ((pitch / bpp + 1) / 2) * 2 * bpp; SDL_Rect UVrect = { rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2 }; - int UVpitch = 2 * ((pitch + 1) / 2); // Skip to the correct offset into the next texture pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); @@ -1121,24 +1124,26 @@ static bool METAL_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, @autoreleasepool { SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal; SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal; - int buffersize = 0; id lockedbuffer = nil; + size_t size, calculated_pitch; if (rect->w <= 0 || rect->h <= 0) { return SDL_SetError("Invalid rectangle dimensions for LockTexture."); } - *pitch = SDL_BYTESPERPIXEL(texture->format) * rect->w; #ifdef SDL_HAVE_YUV if (texturedata.yuv || texturedata.nv12) { - buffersize = ((*pitch) * rect->h) + (2 * (*pitch + 1) / 2) * ((rect->h + 1) / 2); + if (!SDL_CalculateYUVSize(texture->format, rect->w, rect->h, &size, &calculated_pitch)) { + return false; + } } else #endif { - buffersize = (*pitch) * rect->h; + calculated_pitch = SDL_BYTESPERPIXEL(texture->format) * rect->w; + size = rect->h * calculated_pitch; } - lockedbuffer = [data.mtldevice newBufferWithLength:buffersize options:MTLResourceStorageModeShared]; + lockedbuffer = [data.mtldevice newBufferWithLength:size options:MTLResourceStorageModeShared]; if (lockedbuffer == nil) { return SDL_OutOfMemory(); } @@ -1146,6 +1151,7 @@ static bool METAL_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, texturedata.lockedrect = *rect; texturedata.lockedbuffer = lockedbuffer; *pixels = [lockedbuffer contents]; + *pitch = (int)calculated_pitch; return true; } @@ -1192,7 +1198,8 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) // YV12 stores V before U, so the plane order is swapped for it. id firstplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureV : texturedata.mtltextureU; id secondplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureU : texturedata.mtltextureV; - int UVpitch = (pitch + 1) / 2; + const int bpp = SDL_BYTESPERPIXEL(texture->format); + const int UVpitch = ((pitch / bpp + 1) / 2) * bpp; [blitcmd copyFromBuffer:texturedata.lockedbuffer sourceOffset:rect.h * pitch @@ -1216,7 +1223,8 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) } if (texturedata.nv12) { - int UVpitch = 2 * ((pitch + 1) / 2); + const int bpp = SDL_BYTESPERPIXEL(texture->format); + const int UVpitch = ((pitch / bpp + 1) / 2) * 2 * bpp; [blitcmd copyFromBuffer:texturedata.lockedbuffer sourceOffset:rect.h * pitch diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index 0295e6a349..41e7be2512 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -166,8 +166,9 @@ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, si case SDL_PIXELFORMAT_NV12: /**< Planar mode: Y + U/V interleaved (2 planes) */ case SDL_PIXELFORMAT_NV21: /**< Planar mode: Y + V/U interleaved (2 planes) */ + case SDL_PIXELFORMAT_P010: /**< Planar mode: Y + U/V interleaved, 10 bit (2 planes) */ if (pitch) { - *pitch = w; + *pitch = w * SDL_BYTESPERPIXEL(format); } if (size) { @@ -237,7 +238,7 @@ static bool GetYUVPlanes(int width, int height, SDL_PixelFormat format, const vo case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_I0FL: pitches[0] = yuv_pitch; - pitches[1] = (pitches[0] + 1 * SDL_BYTESPERPIXEL(format)) / 2; + pitches[1] = ((pitches[0] / SDL_BYTESPERPIXEL(format) + 1) / 2) * SDL_BYTESPERPIXEL(format); pitches[2] = pitches[1]; planes[0] = (const Uint8 *)yuv; planes[1] = planes[0] + pitches[0] * height; @@ -1353,46 +1354,42 @@ static bool SDL_ConvertPixels_YUV_to_YUV_Copy(int width, int height, SDL_PixelFo } if (IsPlanar2x2Format(format)) { + const int bpp = SDL_BYTESPERPIXEL(format); + // Y plane + const size_t length = width * bpp; for (i = height; i--;) { - SDL_memcpy(dst, src, width); + SDL_memcpy(dst, src, length); src = (const Uint8 *)src + src_pitch; dst = (Uint8 *)dst + dst_pitch; } - if (format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV) { + if (format == SDL_PIXELFORMAT_YV12 || + format == SDL_PIXELFORMAT_IYUV || + format == SDL_PIXELFORMAT_I0FL) { // U and V planes are a quarter the size of the Y plane, rounded up - width = (width + 1) / 2; + width = ((width + 1) / 2) * bpp; height = (height + 1) / 2; - src_pitch = (src_pitch + 1) / 2; - dst_pitch = (dst_pitch + 1) / 2; + src_pitch = ((src_pitch / bpp + 1) / 2) * bpp; + dst_pitch = ((dst_pitch / bpp + 1) / 2) * bpp; for (i = height * 2; i--;) { SDL_memcpy(dst, src, width); src = (const Uint8 *)src + src_pitch; dst = (Uint8 *)dst + dst_pitch; } - } else if (format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21) { - // U/V plane is half the height of the Y plane, rounded up + } else if (format == SDL_PIXELFORMAT_NV12 || + format == SDL_PIXELFORMAT_NV21 || + format == SDL_PIXELFORMAT_P010) { + // U/V plane is half the height of the Y plane, rounded up, with packed CrCb + width = ((width + 1) / 2) * 2 * bpp; height = (height + 1) / 2; - width = ((width + 1) / 2) * 2; - src_pitch = ((src_pitch + 1) / 2) * 2; - dst_pitch = ((dst_pitch + 1) / 2) * 2; + src_pitch = ((src_pitch / bpp + 1) / 2) * 2 * bpp; + dst_pitch = ((dst_pitch / bpp + 1) / 2) * 2 * bpp; for (i = height; i--;) { SDL_memcpy(dst, src, width); src = (const Uint8 *)src + src_pitch; dst = (Uint8 *)dst + dst_pitch; } - } else if (format == SDL_PIXELFORMAT_P010) { - // U/V plane is half the height of the Y plane, rounded up - height = (height + 1) / 2; - width = ((width + 1) / 2) * 2; - src_pitch = ((src_pitch + 1) / 2) * 2; - dst_pitch = ((dst_pitch + 1) / 2) * 2; - for (i = height; i--;) { - SDL_memcpy(dst, src, width * sizeof(Uint16)); - src = (const Uint8 *)src + src_pitch; - dst = (Uint8 *)dst + dst_pitch; - } } return true; } diff --git a/test/testyuv.c b/test/testyuv.c index 767b616e40..1b1dc1070a 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -416,15 +416,16 @@ static bool create_textures(SDL_Renderer *renderer, SDL_Surface *original, SDL_P goto done; } if (planar && (yuv_format == SDL_PIXELFORMAT_YV12 || yuv_format == SDL_PIXELFORMAT_IYUV || yuv_format == SDL_PIXELFORMAT_I0FL)) { + const int bpp = SDL_BYTESPERPIXEL(yuv_format); const int Yrows = original->h; const int UVrows = ((original->h + 1) / 2); const int src_Ypitch = pitch; - const int src_UVpitch = ((pitch + 1 * SDL_BYTESPERPIXEL(yuv_format)) / 2); + const int src_UVpitch = ((pitch / bpp + 1) / 2) * bpp; const Uint8 *src_plane0 = (const Uint8 *)raw_yuv; const Uint8 *src_plane1 = src_plane0 + Yrows * src_Ypitch; const Uint8 *src_plane2 = src_plane1 + UVrows * src_UVpitch; const int Ypitch = pitch + 37; - const int UVpitch = ((Ypitch + 1 * SDL_BYTESPERPIXEL(yuv_format)) / 2); + const int UVpitch = ((Ypitch / bpp + 1) / 2) * bpp; Uint8 *plane0 = (Uint8 *)SDL_calloc(1, Yrows * Ypitch); Uint8 *plane1 = (Uint8 *)SDL_calloc(1, UVrows * UVpitch); Uint8 *plane2 = (Uint8 *)SDL_calloc(1, UVrows * UVpitch);