1
0
Эх сурвалжийг харах

Fixed YUV size and pitch calculations

Sam Lantinga 1 долоо хоног өмнө
parent
commit
fe2bca9748

+ 4 - 3
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];
             }
         }

+ 7 - 2
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 <d3d9.h>
@@ -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;
             }

+ 10 - 4
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 <d3d11_1.h>
 #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;
             }

+ 11 - 5
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;
             }

+ 13 - 21
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;

+ 17 - 9
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 <CoreVideo/CoreVideo.h>
 #import <Metal/Metal.h>
@@ -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<MTLTexture> firstplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureV : texturedata.mtltextureU;
             id<MTLTexture> 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<MTLBuffer> 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<MTLTexture> firstplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureV : texturedata.mtltextureU;
             id<MTLTexture> 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

+ 20 - 23
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;
     }

+ 3 - 2
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);