diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index df035a8798..c95f1f10f8 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -1211,6 +1211,49 @@ extern SDL_DECLSPEC int SDLCALL SDL_ReadSurfacePixel(SDL_Surface *surface, int x */ extern SDL_DECLSPEC int SDLCALL SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a); +/** + * Writes a single pixel to a surface. + * + * This function prioritizes correctness over speed: it is suitable for unit + * tests, but is not intended for use in a game engine. + * + * Like SDL_MapRGBA, this uses the entire 0..255 range when converting color + * components from pixel formats with less than 8 bits per RGB component. + * + * \param surface the surface to write. + * \param x the horizontal coordinate, 0 <= x < width. + * \param y the vertical coordinate, 0 <= y < height. + * \param r the red channel value, 0-255. + * \param g the green channel value, 0-255. + * \param b the blue channel value, 0-255. + * \param a the alpha channel value, 0-255. + * \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 SDL_DECLSPEC int SDLCALL SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/** + * Writes a single pixel to a surface. + * + * This function prioritizes correctness over speed: it is suitable for unit + * tests, but is not intended for use in a game engine. + * + * \param surface the surface to write. + * \param x the horizontal coordinate, 0 <= x < width. + * \param y the vertical coordinate, 0 <= y < height. + * \param r the red channel value, normally in the range 0-1. + * \param g the green channel value, normally in the range 0-1. + * \param b the blue channel value, normally in the range 0-1. + * \param a the alpha channel value, normally in the range 0-1. + * \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 SDL_DECLSPEC int SDLCALL SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 98c9e98c2f..42279bc6b0 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -875,6 +875,8 @@ SDL3_0.0.0 { SDL_WriteS64LE; SDL_WriteS8; SDL_WriteStorageFile; + SDL_WriteSurfacePixel; + SDL_WriteSurfacePixelFloat; SDL_WriteU16BE; SDL_WriteU16LE; SDL_WriteU32BE; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 629db5f943..f5bf713eb7 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -900,6 +900,8 @@ #define SDL_WriteS64LE SDL_WriteS64LE_REAL #define SDL_WriteS8 SDL_WriteS8_REAL #define SDL_WriteStorageFile SDL_WriteStorageFile_REAL +#define SDL_WriteSurfacePixel SDL_WriteSurfacePixel_REAL +#define SDL_WriteSurfacePixelFloat SDL_WriteSurfacePixelFloat_REAL #define SDL_WriteU16BE SDL_WriteU16BE_REAL #define SDL_WriteU16LE SDL_WriteU16LE_REAL #define SDL_WriteU32BE SDL_WriteU32BE_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 55c8a0b32a..fd2ce9de43 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -910,6 +910,8 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS64BE,(SDL_IOStream *a, Sint64 b),(a,b),return SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS64LE,(SDL_IOStream *a, Sint64 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS8,(SDL_IOStream *a, Sint8 b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_WriteStorageFile,(SDL_Storage *a, const char *b, const void *c, Uint64 d),(a,b,c,d),return) +SDL_DYNAPI_PROC(int,SDL_WriteSurfacePixel,(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_WriteSurfacePixelFloat,(SDL_Surface *a, int b, int c, float d, float e, float f, float g),(a,b,c,d,e,f,g),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU16BE,(SDL_IOStream *a, Uint16 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU16LE,(SDL_IOStream *a, Uint16 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU32BE,(SDL_IOStream *a, Uint32 b),(a,b),return) diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index bc378441a3..336d1191aa 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -2459,6 +2459,126 @@ int SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, floa return result; } +int SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Uint32 pixel = 0; + size_t bytes_per_pixel; + Uint8 *p; + int result = -1; + + if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { + return SDL_InvalidParamError("surface"); + } + + if (x < 0 || x >= surface->w) { + return SDL_InvalidParamError("x"); + } + + if (y < 0 || y >= surface->h) { + return SDL_InvalidParamError("y"); + } + + bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); + + if (SDL_MUSTLOCK(surface)) { + if (SDL_LockSurface(surface) < 0) { + return -1; + } + } + + p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; + + if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { + pixel = SDL_MapRGBA(surface->internal->format, surface->internal->palette, r, g, b, a); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + SDL_memcpy(p, ((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), bytes_per_pixel); +#else + SDL_memcpy(p, &pixel, bytes_per_pixel); +#endif + result = 0; + } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { + result = SDL_Unsupported(); + } else { + /* This is really slow, but it gets the job done */ + Uint8 rgba[4]; + SDL_Colorspace colorspace = SDL_GetSurfaceColorspace(surface); + + rgba[0] = r; + rgba[1] = g; + rgba[2] = b; + rgba[3] = a; + result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba), surface->format, colorspace, surface->internal->props, p, surface->pitch); + } + + if (SDL_MUSTLOCK(surface)) { + SDL_UnlockSurface(surface); + } + return result; +} + +int SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a) +{ + int result = -1; + + if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { + return SDL_InvalidParamError("surface"); + } + + if (x < 0 || x >= surface->w) { + return SDL_InvalidParamError("x"); + } + + if (y < 0 || y >= surface->h) { + return SDL_InvalidParamError("y"); + } + + if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { + Uint8 r8, g8, b8, a8; + + r8 = (Uint8)SDL_round(SDL_clamp(r, 0.0f, 1.0f) * 255.0f); + g8 = (Uint8)SDL_round(SDL_clamp(g, 0.0f, 1.0f) * 255.0f); + b8 = (Uint8)SDL_round(SDL_clamp(b, 0.0f, 1.0f) * 255.0f); + a8 = (Uint8)SDL_round(SDL_clamp(a, 0.0f, 1.0f) * 255.0f); + if (SDL_WriteSurfacePixel(surface, x, y, r8, g8, b8, a8) == 0) { + result = 0; + } + } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { + result = SDL_Unsupported(); + } else { + /* This is really slow, but it gets the job done */ + float rgba[4]; + Uint8 *p; + + if (SDL_MUSTLOCK(surface)) { + if (SDL_LockSurface(surface) < 0) { + return -1; + } + } + + p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); + + rgba[0] = r; + rgba[1] = g; + rgba[2] = b; + rgba[3] = a; + + if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { + SDL_memcpy(p, rgba, sizeof(rgba)); + result = 0; + } else { + SDL_Colorspace dst_colorspace = SDL_GetSurfaceColorspace(surface); + SDL_Colorspace src_colorspace = (dst_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); + + result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT, src_colorspace, 0, rgba, sizeof(rgba), surface->format, dst_colorspace, surface->internal->props, p, surface->pitch); + } + + if (SDL_MUSTLOCK(surface)) { + SDL_UnlockSurface(surface); + } + } + return result; +} + /* * Free a surface created by the above function. */