Added SDL_ClearSurface() and SDL_ReadSurfacePixelFloat()

This commit is contained in:
Sam Lantinga 2024-07-17 16:25:00 -07:00
parent b07bd55baf
commit 02a072a1b7
5 changed files with 246 additions and 28 deletions

View file

@ -828,6 +828,25 @@ extern SDL_DECLSPEC int SDLCALL SDL_PremultiplyAlpha(int width, int height, SDL_
*/
extern SDL_DECLSPEC int SDLCALL SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, SDL_bool linear);
/**
* Clear a surface with a specific color, with floating point precision.
*
* This function handles all surface formats, and ignores any clip rectangle.
*
* If the surface is YUV, the color is assumed to be in the sRGB colorspace, otherwise the color is assumed to be in the colorspace of the suface.
*
* \param surface the SDL_Surface to clear.
* \param r the red component of the pixel, normally in the range 0-1
* \param g the green component of the pixel, normally in the range 0-1
* \param b the blue component of the pixel, normally in the range 0-1
* \param a the alpha component of the pixel, 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_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a);
/**
* Perform a fast fill of a rectangle with a specific color.
*
@ -1138,6 +1157,30 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_MapSurfaceRGBA(SDL_Surface *surface, Uint
*/
extern SDL_DECLSPEC int SDLCALL SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a);
/**
* Retrieves a single pixel from 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 read.
* \param x the horizontal coordinate, 0 <= x < width.
* \param y the vertical coordinate, 0 <= y < height.
* \param r a pointer filled in with the red channel, normally in the range 0-1, or NULL to ignore
* this channel.
* \param g a pointer filled in with the green channel, normally in the range 0-1, or NULL to
* ignore this channel.
* \param b a pointer filled in with the blue channel, normally in the range 0-1, or NULL to
* ignore this channel.
* \param a a pointer filled in with the alpha channel, normally in the range 0-1, or NULL to
* ignore this channel.
* \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_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}

View file

@ -35,6 +35,7 @@ SDL3_0.0.0 {
SDL_ClearComposition;
SDL_ClearError;
SDL_ClearProperty;
SDL_ClearSurface;
SDL_CloseAudioDevice;
SDL_CloseCamera;
SDL_CloseGamepad;
@ -627,6 +628,7 @@ SDL3_0.0.0 {
SDL_ReadS8;
SDL_ReadStorageFile;
SDL_ReadSurfacePixel;
SDL_ReadSurfacePixelFloat;
SDL_ReadU16BE;
SDL_ReadU16LE;
SDL_ReadU32BE;

View file

@ -60,6 +60,7 @@
#define SDL_ClearComposition SDL_ClearComposition_REAL
#define SDL_ClearError SDL_ClearError_REAL
#define SDL_ClearProperty SDL_ClearProperty_REAL
#define SDL_ClearSurface SDL_ClearSurface_REAL
#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL
#define SDL_CloseCamera SDL_CloseCamera_REAL
#define SDL_CloseGamepad SDL_CloseGamepad_REAL
@ -652,6 +653,7 @@
#define SDL_ReadS8 SDL_ReadS8_REAL
#define SDL_ReadStorageFile SDL_ReadStorageFile_REAL
#define SDL_ReadSurfacePixel SDL_ReadSurfacePixel_REAL
#define SDL_ReadSurfacePixelFloat SDL_ReadSurfacePixelFloat_REAL
#define SDL_ReadU16BE SDL_ReadU16BE_REAL
#define SDL_ReadU16LE SDL_ReadU16LE_REAL
#define SDL_ReadU32BE SDL_ReadU32BE_REAL

View file

@ -80,6 +80,7 @@ SDL_DYNAPI_PROC(int,SDL_ClearClipboardData,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_ClearComposition,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_ClearError,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_ClearProperty,(SDL_PropertiesID a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_ClearSurface,(SDL_Surface *a, float b, float c, float d, float e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseGamepad,(SDL_Gamepad *a),(a),)
@ -663,6 +664,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS64LE,(SDL_IOStream *a, Sint64 *b),(a,b),return
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS8,(SDL_IOStream *a, Sint8 *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_ReadStorageFile,(SDL_Storage *a, const char *b, void *c, Uint64 d),(a,b,c,d),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_ReadSurfacePixelFloat,(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_ReadU16BE,(SDL_IOStream *a, Uint16 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU16LE,(SDL_IOStream *a, Uint16 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU32BE,(SDL_IOStream *a, Uint32 *b),(a,b),return)

View file

@ -1991,6 +1991,63 @@ int SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, SDL_bool linear)
return SDL_PremultiplyAlphaPixelsAndColorspace(surface->w, surface->h, surface->format, colorspace, surface->internal->props, surface->pixels, surface->pitch, surface->format, colorspace, surface->internal->props, surface->pixels, surface->pitch, linear);
}
int SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a)
{
SDL_Rect clip_rect;
int result = -1;
if (!SDL_SurfaceValid(surface)) {
return SDL_InvalidParamError("surface");
}
SDL_GetSurfaceClipRect(surface, &clip_rect);
SDL_SetSurfaceClipRect(surface, NULL);
if (!SDL_ISPIXELFORMAT_FOURCC(surface->format) &&
SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32)) {
Uint32 color;
color = SDL_MapSurfaceRGBA(surface,
(Uint8)SDL_roundf(SDL_clamp(r, 0.0f, 1.0f) * 255.0f),
(Uint8)SDL_roundf(SDL_clamp(g, 0.0f, 1.0f) * 255.0f),
(Uint8)SDL_roundf(SDL_clamp(b, 0.0f, 1.0f) * 255.0f),
(Uint8)SDL_roundf(SDL_clamp(a, 0.0f, 1.0f) * 255.0f));
result = SDL_FillSurfaceRect(surface, NULL, color);
} else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
// We can't directly set an RGB value on a YUV surface
SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888);
if (!tmp) {
goto done;
}
if (SDL_ClearSurface(tmp, r, g, b, a) == 0) {
result = SDL_ConvertPixelsAndColorspace(surface->w, surface->h, tmp->format, SDL_GetSurfaceColorspace(tmp), tmp->internal->props, tmp->pixels, tmp->pitch, surface->format, SDL_GetSurfaceColorspace(surface), surface->internal->props, surface->pixels, surface->pitch);
}
SDL_DestroySurface(tmp);
} else {
// Take advantage of blit color conversion
SDL_Surface *tmp = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT);
if (!tmp) {
goto done;
}
SDL_SetSurfaceColorspace(tmp, SDL_GetSurfaceColorspace(surface));
float *pixels = (float *)tmp->pixels;
pixels[0] = r;
pixels[1] = g;
pixels[2] = b;
pixels[3] = a;
result = SDL_BlitSurfaceScaled(tmp, NULL, surface, NULL, SDL_SCALEMODE_NEAREST);
SDL_DestroySurface(tmp);
}
done:
SDL_SetSurfaceClipRect(surface, &clip_rect);
return result;
}
Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
{
return SDL_MapSurfaceRGBA(surface, r, g, b, SDL_ALPHA_OPAQUE);
@ -2014,7 +2071,31 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g,
Uint8 *p;
int result = -1;
if (!surface || !surface->format || !surface->pixels) {
if (r) {
*r = 0;
} else {
r = &unused;
}
if (g) {
*g = 0;
} else {
g = &unused;
}
if (b) {
*b = 0;
} else {
b = &unused;
}
if (a) {
*a = 0;
} else {
a = &unused;
}
if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) {
return SDL_InvalidParamError("surface");
}
@ -2026,22 +2107,6 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g,
return SDL_InvalidParamError("y");
}
if (!r) {
r = &unused;
}
if (!g) {
g = &unused;
}
if (!b) {
b = &unused;
}
if (!a) {
a = &unused;
}
bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format);
if (SDL_MUSTLOCK(surface)) {
@ -2052,7 +2117,24 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g,
p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel;
if (bytes_per_pixel > sizeof(pixel)) {
if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
/* Fill the appropriate number of least-significant bytes of pixel,
* leaving the most-significant bytes set to zero */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
SDL_memcpy(((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), p, bytes_per_pixel);
#else
SDL_memcpy(&pixel, p, bytes_per_pixel);
#endif
SDL_GetRGBA(pixel, surface->internal->format, surface->internal->palette, r, g, b, a);
result = 0;
} else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
// FIXME: We need code to extract a single macroblock from a YUV surface
SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
if (converted) {
result = SDL_ReadSurfacePixel(converted, x, y, r, g, b, a);
SDL_DestroySurface(converted);
}
} else {
/* This is really slow, but it gets the job done */
Uint8 rgba[4];
SDL_Colorspace colorspace = SDL_GetSurfaceColorspace(surface);
@ -2064,16 +2146,6 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g,
*a = rgba[3];
result = 0;
}
} else {
/* Fill the appropriate number of least-significant bytes of pixel,
* leaving the most-significant bytes set to zero */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
SDL_memcpy(((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), p, bytes_per_pixel);
#else
SDL_memcpy(&pixel, p, bytes_per_pixel);
#endif
SDL_GetRGBA(pixel, surface->internal->format, surface->internal->palette, r, g, b, a);
result = 0;
}
if (SDL_MUSTLOCK(surface)) {
@ -2082,6 +2154,103 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g,
return result;
}
int SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a)
{
float unused;
int result = -1;
if (r) {
*r = 0.0f;
} else {
r = &unused;
}
if (g) {
*g = 0.0f;
} else {
g = &unused;
}
if (b) {
*b = 0.0f;
} else {
b = &unused;
}
if (a) {
*a = 0.0f;
} else {
a = &unused;
}
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;
if (SDL_ReadSurfacePixel(surface, x, y, &r8, &g8, &b8, &a8) == 0) {
*r = (float)r8 / 255.0f;
*g = (float)g8 / 255.0f;
*b = (float)b8 / 255.0f;
*a = (float)a8 / 255.0f;
result = 0;
}
} else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
// FIXME: We need code to extract a single macroblock from a YUV surface
SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
if (converted) {
result = SDL_ReadSurfacePixelFloat(converted, x, y, r, g, b, a);
SDL_DestroySurface(converted);
}
} 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);
if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) {
SDL_memcpy(rgba, p, sizeof(rgba));
result = 0;
} else {
SDL_Colorspace src_colorspace = SDL_GetSurfaceColorspace(surface);
SDL_Colorspace dst_colorspace = (src_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB);
if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, src_colorspace, surface->internal->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA128_FLOAT, dst_colorspace, 0, rgba, sizeof(rgba)) == 0) {
result = 0;
}
}
if (result == 0) {
*r = rgba[0];
*g = rgba[1];
*b = rgba[2];
*a = rgba[3];
}
if (SDL_MUSTLOCK(surface)) {
SDL_UnlockSurface(surface);
}
}
return result;
}
/*
* Free a surface created by the above function.
*/