From 308906ba2542df132e424d4fcee2995578989cec Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 20 Jan 2024 06:31:37 -0800 Subject: [PATCH] Added SDL_FlipSurface() to flip a surface vertically or horizontally Fixes https://github.com/libsdl-org/SDL/issues/8857 --- WhatsNew.txt | 1 + docs/README-migration.md | 1 + include/SDL3/SDL_render.h | 14 +---- include/SDL3/SDL_surface.h | 21 +++++++ src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 3 +- src/render/SDL_render.c | 4 +- src/render/SDL_sysrender.h | 2 +- src/render/psp/SDL_render_psp.c | 2 +- src/render/software/SDL_render_sw.c | 6 +- src/video/SDL_surface.c | 86 +++++++++++++++++++++++++++++ test/testautomation_surface.c | 63 ++++++++++++++++++++- 13 files changed, 184 insertions(+), 21 deletions(-) diff --git a/WhatsNew.txt b/WhatsNew.txt index 1b84c49b9..3090648bb 100644 --- a/WhatsNew.txt +++ b/WhatsNew.txt @@ -17,6 +17,7 @@ General: * Added SDL_GetPrimaryDisplay() to get the instance ID of the primary display * Added SDL_GetWindowParent() to get the parent of popup windows * Added SDL_CreateSurface() and SDL_CreateSurfaceFrom() which replace SDL_CreateRGBSurface*(), and can also be used to create YUV surfaces +* Added SDL_FlipSurface() to flip a surface vertically or horizontally * Added SDL_GetJoysticks(), SDL_GetJoystickInstanceName(), SDL_GetJoystickInstancePath(), SDL_GetJoystickInstancePlayerIndex(), SDL_GetJoystickInstanceGUID(), SDL_GetJoystickInstanceVendor(), SDL_GetJoystickInstanceProduct(), SDL_GetJoystickInstanceProductVersion(), and SDL_GetJoystickInstanceType() to directly query the list of available joysticks * Added SDL_GetGamepads(), SDL_GetGamepadInstanceName(), SDL_GetGamepadInstancePath(), SDL_GetGamepadInstancePlayerIndex(), SDL_GetGamepadInstanceGUID(), SDL_GetGamepadInstanceVendor(), SDL_GetGamepadInstanceProduct(), SDL_GetGamepadInstanceProductVersion(), and SDL_GetGamepadInstanceType() to directly query the list of available gamepads * Added SDL_GetSensors(), SDL_GetSensorInstanceName(), SDL_GetSensorInstanceType(), and SDL_GetSensorInstanceNonPortableType() to directly query the list of available sensors diff --git a/docs/README-migration.md b/docs/README-migration.md index 8f6043de2..6b3b3b85a 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -1037,6 +1037,7 @@ The following functions have been removed: * SDL_SetTextureUserData() - use SDL_GetTextureProperties() instead The following symbols have been renamed: +* SDL_RendererFlip => SDL_FlipMode * SDL_ScaleModeBest => SDL_SCALEMODE_BEST * SDL_ScaleModeLinear => SDL_SCALEMODE_LINEAR * SDL_ScaleModeNearest => SDL_SCALEMODE_NEAREST diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 2a5958dfe..e6d762dfb 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -105,16 +105,6 @@ typedef enum SDL_TEXTUREACCESS_TARGET /**< Texture can be used as a render target */ } SDL_TextureAccess; -/** - * Flip constants for SDL_RenderTextureRotated - */ -typedef enum -{ - SDL_FLIP_NONE = 0x00000000, /**< Do not flip */ - SDL_FLIP_HORIZONTAL = 0x00000001, /**< flip horizontally */ - SDL_FLIP_VERTICAL = 0x00000002 /**< flip vertically */ -} SDL_RendererFlip; - /** * How the logical size is mapped to the output */ @@ -1494,7 +1484,7 @@ extern DECLSPEC int SDLCALL SDL_RenderTexture(SDL_Renderer *renderer, SDL_Textur * \param center A pointer to a point indicating the point around which * dstrect will be rotated (if NULL, rotation will be done * around dstrect.w/2, dstrect.h/2). - * \param flip An SDL_RendererFlip value stating which flipping actions should + * \param flip An SDL_FlipMode value stating which flipping actions should * be performed on the texture * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. @@ -1504,7 +1494,7 @@ extern DECLSPEC int SDLCALL SDL_RenderTexture(SDL_Renderer *renderer, SDL_Textur extern DECLSPEC int SDLCALL SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, - const SDL_RendererFlip flip); + const SDL_FlipMode flip); /** * Render a list of triangles, optionally using a texture and indices into the diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 63706aa52..3f378a7d8 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -75,6 +75,15 @@ typedef enum SDL_SCALEMODE_BEST /**< anisotropic filtering */ } SDL_ScaleMode; +/** + * The flip mode + */ +typedef enum +{ + SDL_FLIP_NONE, /**< Do not flip */ + SDL_FLIP_HORIZONTAL, /**< flip horizontally */ + SDL_FLIP_VERTICAL /**< flip vertically */ +} SDL_FlipMode; /** * A collection of pixels used in software blitting. @@ -602,6 +611,18 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetSurfaceClipRect(SDL_Surface *surface, extern DECLSPEC int SDLCALL SDL_GetSurfaceClipRect(SDL_Surface *surface, SDL_Rect *rect); +/* + * Flip a surface vertically or horizontally. + * + * \param surface the surface to flip + * \param flip the direction to flip + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip); + /* * Creates a new surface identical to the existing surface. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index a2ee7d22d..43825dd5d 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -964,6 +964,7 @@ SDL3_0.0.0 { SDL_GetHapticInstanceID; SDL_GetHapticName; SDL_ReadSurfacePixel; + SDL_FlipSurface; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index c1ff1b866..5c1160c78 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -989,3 +989,4 @@ #define SDL_GetHapticInstanceID SDL_GetHapticInstanceID_REAL #define SDL_GetHapticName SDL_GetHapticName_REAL #define SDL_ReadSurfacePixel SDL_ReadSurfacePixel_REAL +#define SDL_FlipSurface SDL_FlipSurface_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index dc9291c45..39aa35774 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -580,7 +580,7 @@ SDL_DYNAPI_PROC(int,SDL_RenderReadPixels,(SDL_Renderer *a, const SDL_Rect *b, Ui SDL_DYNAPI_PROC(int,SDL_RenderRect,(SDL_Renderer *a, const SDL_FRect *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_RenderRects,(SDL_Renderer *a, const SDL_FRect *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_RenderTexture,(SDL_Renderer *a, SDL_Texture *b, const SDL_FRect *c, const SDL_FRect *d),(a,b,c,d),return) -SDL_DYNAPI_PROC(int,SDL_RenderTextureRotated,(SDL_Renderer *a, SDL_Texture *b, const SDL_FRect *c, const SDL_FRect *d, const double e, const SDL_FPoint *f, const SDL_RendererFlip g),(a,b,c,d,e,f,g),return) +SDL_DYNAPI_PROC(int,SDL_RenderTextureRotated,(SDL_Renderer *a, SDL_Texture *b, const SDL_FRect *c, const SDL_FRect *d, const double e, const SDL_FPoint *f, const SDL_FlipMode g),(a,b,c,d,e,f,g),return) SDL_DYNAPI_PROC(SDL_AssertState,SDL_ReportAssertion,(SDL_AssertData *a, const char *b, const char *c, int d),(a,b,c,d),return) SDL_DYNAPI_PROC(void,SDL_ResetAssertionReport,(void),(),) SDL_DYNAPI_PROC(SDL_bool,SDL_ResetHint,(const char *a),(a),return) @@ -1014,3 +1014,4 @@ SDL_DYNAPI_PROC(SDL_Haptic*,SDL_GetHapticFromInstanceID,(SDL_HapticID a),(a),ret SDL_DYNAPI_PROC(SDL_HapticID,SDL_GetHapticInstanceID,(SDL_Haptic *a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetHapticName,(SDL_Haptic *a),(a),return) SDL_DYNAPI_PROC(int,SDL_ReadSurfacePixel,(SDL_Surface *a, int b, int c, Uint8 *d, Uint8 *e, Uint8 *f, Uint8 *g),(a,b,c,d,e,f,g),return) +SDL_DYNAPI_PROC(int,SDL_FlipSurface,(SDL_Surface *a, SDL_FlipMode b),(a,b),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 06da0b920..0643c3e16 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -611,7 +611,7 @@ static int QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_ static int QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcquad, const SDL_FRect *dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y) + const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) { SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY_EX, texture); int retval = -1; @@ -3296,7 +3296,7 @@ int SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FR int SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) + const double angle, const SDL_FPoint *center, const SDL_FlipMode flip) { SDL_FRect real_srcrect; SDL_FRect real_dstrect; diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index ec4b34ab0..85a76e4dd 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -173,7 +173,7 @@ struct SDL_Renderer const SDL_FRect *srcrect, const SDL_FRect *dstrect); int (*QueueCopyEx)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, const SDL_FRect *srcquad, const SDL_FRect *dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y); + const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y); int (*QueueGeometry)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, const float *xy, int xy_stride, const SDL_Color *color, int color_stride, const float *uv, int uv_stride, int num_vertices, const void *indices, int num_indices, int size_indices, diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index aa6434fd1..c2115caf7 100644 --- a/src/render/psp/SDL_render_psp.c +++ b/src/render/psp/SDL_render_psp.c @@ -842,7 +842,7 @@ static int PSP_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Tex static int PSP_QueueCopyEx(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y) + const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) { VertTV *verts = (VertTV *)SDL_AllocateRenderVertices(renderer, 4 * sizeof(VertTV), 4, &cmd->data.draw.first); const float centerx = center->x; diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 18c8b36e8..29651aa79 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -257,14 +257,14 @@ typedef struct CopyExData SDL_Rect dstrect; double angle; SDL_FPoint center; - SDL_RendererFlip flip; + SDL_FlipMode flip; float scale_x; float scale_y; } CopyExData; static int SW_QueueCopyEx(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y) + const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) { CopyExData *verts = (CopyExData *)SDL_AllocateRenderVertices(renderer, sizeof(CopyExData), 0, &cmd->data.draw.first); @@ -311,7 +311,7 @@ static int Blit_to_Screen(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *surf static int SW_RenderCopyEx(SDL_Renderer *renderer, SDL_Surface *surface, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *final_rect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y) + const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y) { SDL_Surface *src = (SDL_Surface *)texture->driverdata; SDL_Rect tmp_rect; diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index ffe9df476..9a52b13fb 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -1078,11 +1078,97 @@ void SDL_UnlockSurface(SDL_Surface *surface) #endif } +static int SDL_FlipSurfaceHorizontal(SDL_Surface *surface) +{ + SDL_bool isstack; + Uint8 *row, *a, *b, *tmp; + int i, j, bpp; + + if (surface->format->BitsPerPixel < 8) { + /* We could implement this if needed, but we'd have to flip sets of bits within a byte */ + return SDL_Unsupported(); + } + + if (surface->h <= 0) { + return 0; + } + + if (surface->w <= 1) { + return 0; + } + + bpp = surface->format->BytesPerPixel; + row = (Uint8 *)surface->pixels; + tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack); + for (i = surface->h; i--; ) { + a = row; + b = a + (surface->w - 1) * bpp; + for (j = surface->w / 2; j--; ) { + SDL_memcpy(tmp, a, bpp); + SDL_memcpy(a, b, bpp); + SDL_memcpy(b, tmp, bpp); + a += bpp; + b -= bpp; + } + row += surface->pitch; + } + SDL_small_free(tmp, isstack); + return 0; +} + +static int SDL_FlipSurfaceVertical(SDL_Surface *surface) +{ + SDL_bool isstack; + Uint8 *a, *b, *tmp; + int i; + + if (surface->h <= 1) { + return 0; + } + + a = (Uint8 *)surface->pixels; + b = a + (surface->h - 1) * surface->pitch; + tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack); + for (i = surface->h / 2; i--; ) { + SDL_memcpy(tmp, a, surface->pitch); + SDL_memcpy(a, b, surface->pitch); + SDL_memcpy(b, tmp, surface->pitch); + a += surface->pitch; + b -= surface->pitch; + } + SDL_small_free(tmp, isstack); + return 0; +} + +int SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip) +{ + if (!surface || !surface->format) { + return SDL_InvalidParamError("surface"); + } + if (!surface->pixels) { + return 0; + } + + switch (flip) { + case SDL_FLIP_HORIZONTAL: + return SDL_FlipSurfaceHorizontal(surface); + case SDL_FLIP_VERTICAL: + return SDL_FlipSurfaceVertical(surface); + default: + return SDL_InvalidParamError("flip"); + } +} + /* * Creates a new surface identical to the existing surface */ SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) { + if (!surface) { + SDL_InvalidParamError("surface"); + return NULL; + } + return SDL_ConvertSurface(surface, surface->format); } diff --git a/test/testautomation_surface.c b/test/testautomation_surface.c index def9c35f0..00a63e33c 100644 --- a/test/testautomation_surface.c +++ b/test/testautomation_surface.c @@ -22,6 +22,15 @@ #include "testautomation_suites.h" #include "testautomation_images.h" + +#define CHECK_FUNC(FUNC, PARAMS) \ +{ \ + int result = FUNC PARAMS; \ + if (result != 0) { \ + SDLTest_AssertCheck(result == 0, "Validate result from %s, expected: 0, got: %i, %s", #FUNC, result, SDL_GetError()); \ + } \ +} + /* ================= Test Case Implementation ================== */ /* Shared test surface */ @@ -791,6 +800,54 @@ static int surface_testOverflow(void *arg) return TEST_COMPLETED; } +static int surface_testFlip(void *arg) +{ + SDL_Surface *surface; + Uint8 *pixels; + int offset; + const char *expectedError; + + surface = SDL_CreateSurface(3, 3, SDL_PIXELFORMAT_RGB24); + SDLTest_AssertCheck(surface != NULL, "SDL_CreateSurface()"); + + SDL_ClearError(); + expectedError = "Parameter 'surface' is invalid"; + SDL_FlipSurface(NULL, SDL_FLIP_HORIZONTAL); + SDLTest_AssertCheck(SDL_strcmp(SDL_GetError(), expectedError) == 0, + "Expected \"%s\", got \"%s\"", expectedError, SDL_GetError()); + + SDL_ClearError(); + expectedError = "Parameter 'flip' is invalid"; + SDL_FlipSurface(surface, SDL_FLIP_NONE); + SDLTest_AssertCheck(SDL_strcmp(SDL_GetError(), expectedError) == 0, + "Expected \"%s\", got \"%s\"", expectedError, SDL_GetError()); + + pixels = (Uint8 *)surface->pixels; + *pixels = 0xFF; + offset = 0; + + SDLTest_AssertPass("Call to SDL_FlipSurface(surface, SDL_FLIP_VERTICAL)"); + CHECK_FUNC(SDL_FlipSurface, (surface, SDL_FLIP_VERTICAL)); + SDLTest_AssertCheck(pixels[offset] == 0x00, + "Expected pixels[%d] == 0x00 got 0x%.2X", offset, pixels[offset]); + offset = 2 * surface->pitch; + SDLTest_AssertCheck(pixels[offset] == 0xFF, + "Expected pixels[%d] == 0xFF got 0x%.2X", offset, pixels[offset]); + + SDLTest_AssertPass("Call to SDL_FlipSurface(surface, SDL_FLIP_HORIZONTAL)"); + CHECK_FUNC(SDL_FlipSurface, (surface, SDL_FLIP_HORIZONTAL)); + SDLTest_AssertCheck(pixels[offset] == 0x00, + "Expected pixels[%d] == 0x00 got 0x%.2X", offset, pixels[offset]); + offset += (surface->w - 1) * surface->format->BytesPerPixel; + SDLTest_AssertCheck(pixels[offset] == 0xFF, + "Expected pixels[%d] == 0xFF got 0x%.2X", offset, pixels[offset]); + + SDL_DestroySurface(surface); + + return TEST_COMPLETED; +} + + /* ================= Test References ================== */ /* Surface test cases */ @@ -849,11 +906,15 @@ static const SDLTest_TestCaseReference surfaceTestOverflow = { surface_testOverflow, "surface_testOverflow", "Test overflow detection.", TEST_ENABLED }; +static const SDLTest_TestCaseReference surfaceTestFlip = { + surface_testFlip, "surface_testFlip", "Test surface flipping.", TEST_ENABLED +}; + /* Sequence of Surface test cases */ static const SDLTest_TestCaseReference *surfaceTests[] = { &surfaceTest1, &surfaceTest2, &surfaceTest3, &surfaceTest4, &surfaceTest5, &surfaceTest6, &surfaceTest7, &surfaceTest8, &surfaceTest9, &surfaceTest10, - &surfaceTest11, &surfaceTest12, &surfaceTestOverflow, NULL + &surfaceTest11, &surfaceTest12, &surfaceTestOverflow, &surfaceTestFlip, NULL }; /* Surface test suite (global) */