diff --git a/WhatsNew.txt b/WhatsNew.txt index df864598f5e5f..2050644382919 100644 --- a/WhatsNew.txt +++ b/WhatsNew.txt @@ -37,7 +37,7 @@ General: * Added SDL_FLIP_HORIZONTAL_AND_VERTICAL to flip a surface both horizontally and vertically * Added SDL_LoadPNG(), SDL_LoadPNG_IO(), SDL_SavePNG(), and SDL_SavePNG_IO() to load and save PNG images * Added SDL_LoadSurface() and SDL_LoadSurface_IO() to detect BMP and PNG formats and load them as surfaces -* Added SDL_PROP_SURFACE_ROTATION_NUMBER to indicate the rotation needed to display camera images upright +* Added SDL_PROP_SURFACE_ROTATION_FLOAT to indicate the rotation needed to display camera images upright * Added SDL_RotateSurface() to create a rotated copy of a surface * SDL_EVENT_WINDOW_EXPOSED now sets data1 to true if it is sent during live resizing * Added SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, which is sent when the usable desktop bounds change diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 8be939c8014ca..437eeaccf3550 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -241,7 +241,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * left edge of the image, if this surface is being used as a cursor. * - `SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER`: the hotspot pixel offset from the * top edge of the image, if this surface is being used as a cursor. - * - `SDL_PROP_SURFACE_ROTATION_NUMBER`: the number of degrees a surface's + * - `SDL_PROP_SURFACE_ROTATION_FLOAT`: the number of degrees a surface's * data is meant to be rotated clockwise to make the image right-side up. * Default 0. This is used by the camera API, if a mobile device is oriented * differently than what its camera provides (i.e. - the camera always @@ -263,7 +263,7 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surfac #define SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING "SDL.surface.tonemap" #define SDL_PROP_SURFACE_HOTSPOT_X_NUMBER "SDL.surface.hotspot.x" #define SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER "SDL.surface.hotspot.y" -#define SDL_PROP_SURFACE_ROTATION_NUMBER "SDL.surface.rotation" +#define SDL_PROP_SURFACE_ROTATION_FLOAT "SDL.surface.rotation" /** * Set the colorspace used by a surface. @@ -1027,6 +1027,14 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FlipSurface(SDL_Surface *surface, SDL_FlipM * larger than the original, with the background filled in with the colorkey, * if available, or RGBA 255/255/255/0 if not. * + * If `surface` has the SDL_PROP_SURFACE_ROTATION_FLOAT property set on it, + * the new copy will have the adjusted value set: if the rotation property is + * 90 and `angle` was 30, the new surface will have a property value of 60 + * (that is: to be upright vs gravity, this surface needs to rotate 60 more + * degrees). However, note that further rotations on the new surface in this + * example will produce unexpected results, since the image will have resized + * and padded to accommodate the not-90 degree angle. + * * \param surface the surface to rotate. * \param angle the rotation angle, in degrees. * \returns a rotated copy of the surface or NULL on failure; call diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index b3c3e5e82cc7b..b9a11b52dcf80 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -150,7 +150,7 @@ static size_t GetFrameBufLen(const SDL_CameraSpec *spec) return wxh * SDL_BYTESPERPIXEL(fmt); } -static SDL_CameraFrameResult ZombieAcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult ZombieAcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { const SDL_CameraSpec *spec = &device->actual_spec; @@ -832,7 +832,7 @@ bool SDL_CameraThreadIterate(SDL_Camera *device) SDL_Surface *output_surface = NULL; SurfaceList *slist = NULL; Uint64 timestampNS = 0; - int rotation = 0; + float rotation = 0.0f; // AcquireFrame SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! const SDL_CameraFrameResult rc = device->AcquireFrame(device, device->acquire_surface, ×tampNS, &rotation); @@ -929,7 +929,7 @@ bool SDL_CameraThreadIterate(SDL_Camera *device) acquired->pixels = NULL; acquired->pitch = 0; - SDL_SetNumberProperty(SDL_GetSurfaceProperties(output_surface), SDL_PROP_SURFACE_ROTATION_NUMBER, rotation); + SDL_SetFloatProperty(SDL_GetSurfaceProperties(output_surface), SDL_PROP_SURFACE_ROTATION_FLOAT, rotation); // make the filled output surface available to the app. SDL_LockMutex(device->lock); diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 151f73d65a0e7..14a6e87c8d745 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -99,7 +99,7 @@ struct SDL_Camera // These are, initially, set from camera_driver, but we might swap them out with Zombie versions on disconnect/failure. bool (*WaitDevice)(SDL_Camera *device); - SDL_CameraFrameResult (*AcquireFrame)(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation); + SDL_CameraFrameResult (*AcquireFrame)(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation); void (*ReleaseFrame)(SDL_Camera *device, SDL_Surface *frame); // All supported formats/dimensions for this device. @@ -178,7 +178,7 @@ typedef struct SDL_CameraDriverImpl bool (*OpenDevice)(SDL_Camera *device, const SDL_CameraSpec *spec); void (*CloseDevice)(SDL_Camera *device); bool (*WaitDevice)(SDL_Camera *device); - SDL_CameraFrameResult (*AcquireFrame)(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation); // set frame->pixels, frame->pitch, *timestampNS, and *rotation! + SDL_CameraFrameResult (*AcquireFrame)(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation); // set frame->pixels, frame->pitch, *timestampNS, and *rotation! void (*ReleaseFrame)(SDL_Camera *device, SDL_Surface *frame); // Reclaim frame->pixels and frame->pitch! void (*FreeDeviceHandle)(SDL_Camera *device); // SDL is done with this device; free the handle from SDL_AddCamera() void (*Deinitialize)(void); diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index e52c78f3a747b..ed1164cec62fd 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -296,7 +296,7 @@ static bool ANDROIDCAMERA_WaitDevice(SDL_Camera *device) return true; // this isn't used atm, since we run our own thread via onImageAvailable callbacks. } -static SDL_CameraFrameResult ANDROIDCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult ANDROIDCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { SDL_CameraFrameResult result = SDL_CAMERA_FRAME_READY; media_status_t res; @@ -380,7 +380,7 @@ static SDL_CameraFrameResult ANDROIDCAMERA_AcquireFrame(SDL_Camera *device, SDL_ dev_rotation = -dev_rotation; // we want to subtract this value, instead of add, if back-facing. } - *rotation = dev_rotation + device->hidden->rotation; // current phone orientation, static camera orientation in relation to phone. + *rotation = (float) (dev_rotation + device->hidden->rotation); // current phone orientation, static camera orientation in relation to phone. return result; } diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index b50ceba953008..f02c4acc21fa4 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -157,7 +157,7 @@ static bool COREMEDIA_WaitDevice(SDL_Camera *device) return true; // this isn't used atm, since we run our own thread out of Grand Central Dispatch. } -static SDL_CameraFrameResult COREMEDIA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult COREMEDIA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { SDL_CameraFrameResult result = SDL_CAMERA_FRAME_READY; SDLPrivateCameraData *hidden = (__bridge SDLPrivateCameraData *) device->hidden; @@ -243,21 +243,21 @@ static SDL_CameraFrameResult COREMEDIA_AcquireFrame(SDL_Camera *device, SDL_Surf // there is probably math for this, but this is easy to slap into a table. // rotation = rotations[uiorientation-1][devorientation-1]; if (device->position == SDL_CAMERA_POSITION_BACK_FACING) { - static const int back_rotations[4][4] = { + static const Uint16 back_rotations[4][4] = { { 90, 90, 90, 90 }, // ui portrait { 270, 270, 270, 270 }, // ui portait upside down { 0, 0, 0, 0 }, // ui landscape left { 180, 180, 180, 180 } // ui landscape right }; - *rotation = back_rotations[ui_orientation - 1][device_orientation - 1]; + *rotation = (float) back_rotations[ui_orientation - 1][device_orientation - 1]; } else { - static const int front_rotations[4][4] = { + static const Uint16 front_rotations[4][4] = { { 90, 90, 270, 270 }, // ui portrait { 270, 270, 90, 90 }, // ui portait upside down { 0, 0, 180, 180 }, // ui landscape left { 180, 180, 0, 0 } // ui landscape right }; - *rotation = front_rotations[ui_orientation - 1][device_orientation - 1]; + *rotation = (float) front_rotations[ui_orientation - 1][device_orientation - 1]; } #endif diff --git a/src/camera/dummy/SDL_camera_dummy.c b/src/camera/dummy/SDL_camera_dummy.c index 981c2cecb8739..8de3194ae8931 100644 --- a/src/camera/dummy/SDL_camera_dummy.c +++ b/src/camera/dummy/SDL_camera_dummy.c @@ -38,7 +38,7 @@ static bool DUMMYCAMERA_WaitDevice(SDL_Camera *device) return SDL_Unsupported(); } -static SDL_CameraFrameResult DUMMYCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult DUMMYCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { SDL_Unsupported(); return SDL_CAMERA_FRAME_ERROR; diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c index 8eb67b7f845f6..f5096bfb91a58 100644 --- a/src/camera/emscripten/SDL_camera_emscripten.c +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -39,7 +39,7 @@ static bool EMSCRIPTENCAMERA_WaitDevice(SDL_Camera *device) return false; } -static SDL_CameraFrameResult EMSCRIPTENCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult EMSCRIPTENCAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { void *rgba = SDL_malloc(device->actual_spec.width * device->actual_spec.height * 4); if (!rgba) { diff --git a/src/camera/mediafoundation/SDL_camera_mediafoundation.c b/src/camera/mediafoundation/SDL_camera_mediafoundation.c index 4abe2294d4026..c57ff63e47a6a 100644 --- a/src/camera/mediafoundation/SDL_camera_mediafoundation.c +++ b/src/camera/mediafoundation/SDL_camera_mediafoundation.c @@ -430,7 +430,7 @@ static void SDLCALL CleanupIMFMediaBuffer(void *userdata, void *value) SDL_free(objs); } -static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { SDL_assert(device->hidden->current_sample != NULL); @@ -562,7 +562,7 @@ static SDL_CameraFrameResult MEDIAFOUNDATION_CopyFrame(SDL_Surface *frame, const return SDL_CAMERA_FRAME_READY; } -static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult MEDIAFOUNDATION_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { SDL_assert(device->hidden->current_sample != NULL); diff --git a/src/camera/pipewire/SDL_camera_pipewire.c b/src/camera/pipewire/SDL_camera_pipewire.c index 610cd71fabfab..b2aae04a5b177 100644 --- a/src/camera/pipewire/SDL_camera_pipewire.c +++ b/src/camera/pipewire/SDL_camera_pipewire.c @@ -577,7 +577,7 @@ static bool PIPEWIRECAMERA_WaitDevice(SDL_Camera *device) return true; } -static SDL_CameraFrameResult PIPEWIRECAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult PIPEWIRECAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { struct pw_buffer *b; diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 0fd078bb309dc..5fee26d3f3251 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -125,7 +125,7 @@ static bool V4L2_WaitDevice(SDL_Camera *device) return false; } -static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { const int fd = device->hidden->fd; const io_method io = device->hidden->io; diff --git a/src/camera/vita/SDL_camera_vita.c b/src/camera/vita/SDL_camera_vita.c index ff492b383afc6..3badeaed5dd15 100644 --- a/src/camera/vita/SDL_camera_vita.c +++ b/src/camera/vita/SDL_camera_vita.c @@ -190,7 +190,7 @@ static bool VITACAMERA_WaitDevice(SDL_Camera *device) return true; } -static SDL_CameraFrameResult VITACAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, int *rotation) +static SDL_CameraFrameResult VITACAMERA_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS, float *rotation) { SceCameraRead read = {0}; read.size = sizeof(SceCameraRead); diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 22709207d93ce..3fc5f609014bd 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -2198,6 +2198,14 @@ SDL_Surface *SDL_RotateSurface(SDL_Surface *surface, float angle) SDL_DestroySurface(convert); } } + + if (rotated) { + if (SDL_HasProperty(surface->props, SDL_PROP_SURFACE_ROTATION_FLOAT)) { + const float rotation = (SDL_GetNumberProperty(surface->props, SDL_PROP_SURFACE_ROTATION_FLOAT, 0) - angle); + SDL_SetFloatProperty(SDL_GetSurfaceProperties(rotated), SDL_PROP_SURFACE_ROTATION_FLOAT, rotation); + } + } + return rotated; } diff --git a/test/testcamera.c b/test/testcamera.c index 985852575160f..941a071c4666a 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -364,7 +364,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) // device might be rotated to a different one (like an iPhone providing portrait images even if you hold // the phone in landscape mode). The rotation is how far to rotate the image clockwise to put it right-side // up, for how the user would expect it to be for how they are holding the device. - const int rotation = (int) SDL_GetNumberProperty(SDL_GetSurfaceProperties(frame_current), SDL_PROP_SURFACE_ROTATION_NUMBER, 0); + const float rotation = SDL_GetFloatProperty(SDL_GetSurfaceProperties(frame_current), SDL_PROP_SURFACE_ROTATION_FLOAT, 0.0f); SDL_GetRenderOutputSize(renderer, &win_w, &win_h); d.x = ((win_w - texture->w) / 2.0f); d.y = ((win_h - texture->h) / 2.0f);