Bläddra i källkod

Added SDL_ClearSurface() and SDL_ReadSurfacePixelFloat()

Sam Lantinga 9 månader sedan
förälder
incheckning
02a072a1b7

+ 43 - 0
include/SDL3/SDL_surface.h

@@ -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
 }

+ 2 - 0
src/dynapi/SDL_dynapi.sym

@@ -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;

+ 2 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -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

+ 2 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -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)

+ 194 - 25
src/video/SDL_surface.c

@@ -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,32 +2071,40 @@ 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) {
-        return SDL_InvalidParamError("surface");
+    if (r) {
+        *r = 0;
+    } else {
+        r = &unused;
     }
 
-    if (x < 0 || x >= surface->w) {
-        return SDL_InvalidParamError("x");
+    if (g) {
+        *g = 0;
+    } else {
+        g = &unused;
     }
 
-    if (y < 0 || y >= surface->h) {
-        return SDL_InvalidParamError("y");
+    if (b) {
+        *b = 0;
+    } else {
+        b = &unused;
     }
 
-    if (!r) {
-        r = &unused;
+    if (a) {
+        *a = 0;
+    } else {
+        a = &unused;
     }
 
-    if (!g) {
-        g = &unused;
+    if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) {
+        return SDL_InvalidParamError("surface");
     }
 
-    if (!b) {
-        b = &unused;
+    if (x < 0 || x >= surface->w) {
+        return SDL_InvalidParamError("x");
     }
 
-    if (!a) {
-        a = &unused;
+    if (y < 0 || y >= surface->h) {
+        return SDL_InvalidParamError("y");
     }
 
     bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format);
@@ -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.
  */