Bläddra i källkod

Enhancements for SDL_PremultiplyAlpha()

The function can now convert between pixels of different formats, and takes a parameter to control whether the premultiplication is done in sRGB or linear space.

Also added SDL_PremultiplySurfaceAlpha(), which can premultiply the pixels of a surface in-place.
Sam Lantinga 9 månader sedan
förälder
incheckning
334962b056

+ 16 - 3
include/SDL3/SDL_surface.h

@@ -798,8 +798,6 @@ extern SDL_DECLSPEC int SDLCALL SDL_ConvertPixelsAndColorspace(int width, int he
  *
  * This is safe to use with src == dst, but not for other overlapping areas.
  *
- * This function is currently only implemented for SDL_PIXELFORMAT_ARGB8888.
- *
  * \param width the width of the block to convert, in pixels.
  * \param height the height of the block to convert, in pixels.
  * \param src_format an SDL_PixelFormat value of the `src` pixels format.
@@ -808,12 +806,27 @@ extern SDL_DECLSPEC int SDLCALL SDL_ConvertPixelsAndColorspace(int width, int he
  * \param dst_format an SDL_PixelFormat value of the `dst` pixels format.
  * \param dst a pointer to be filled in with premultiplied pixel data.
  * \param dst_pitch the pitch of the destination pixels, in bytes.
+ * \param linear SDL_TRUE to convert from sRGB to linear space for the alpha multiplication, SDL_FALSE to do multiplication in sRGB space.
+ * \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_PremultiplyAlpha(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch, SDL_bool linear);
+
+/**
+ * Premultiply the alpha in a surface.
+ *
+ * This is safe to use with src == dst, but not for other overlapping areas.
+ *
+ * \param surface the surface to modify.
+ * \param linear SDL_TRUE to convert from sRGB to linear space for the alpha multiplication, SDL_FALSE to do multiplication in sRGB space.
  * \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_PremultiplyAlpha(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch);
+extern SDL_DECLSPEC int SDLCALL SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, SDL_bool linear);
 
 /**
  * Perform a fast fill of a rectangle with a specific color.

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -610,6 +610,7 @@ SDL3_0.0.0 {
     SDL_PollEvent;
     SDL_PostSemaphore;
     SDL_PremultiplyAlpha;
+    SDL_PremultiplySurfaceAlpha;
     SDL_PumpEvents;
     SDL_PushEvent;
     SDL_PutAudioStreamData;

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -635,6 +635,7 @@
 #define SDL_PollEvent SDL_PollEvent_REAL
 #define SDL_PostSemaphore SDL_PostSemaphore_REAL
 #define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
+#define SDL_PremultiplySurfaceAlpha SDL_PremultiplySurfaceAlpha_REAL
 #define SDL_PumpEvents SDL_PumpEvents_REAL
 #define SDL_PushEvent SDL_PushEvent_REAL
 #define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL

+ 2 - 1
src/dynapi/SDL_dynapi_procs.h

@@ -645,7 +645,8 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_PenConnected,(SDL_PenID a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_PlayHapticRumble,(SDL_Haptic *a, float b, Uint32 c),(a,b,c),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_PollEvent,(SDL_Event *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_PostSemaphore,(SDL_Semaphore *a),(a),return)
-SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, SDL_PixelFormat c, const void *d, int e, SDL_PixelFormat f, void *g, int h),(a,b,c,d,e,f,g,h),return)
+SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, SDL_PixelFormat c, const void *d, int e, SDL_PixelFormat f, void *g, int h, SDL_bool i),(a,b,c,d,e,f,g,h,i),return)
+SDL_DYNAPI_PROC(int,SDL_PremultiplySurfaceAlpha,(SDL_Surface *a, SDL_bool b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_PumpEvents,(void),(),)
 SDL_DYNAPI_PROC(int,SDL_PushEvent,(SDL_Event *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return)

+ 188 - 25
src/video/SDL_surface.c

@@ -1769,15 +1769,12 @@ int SDL_ConvertPixels(int width, int height,
 /*
  * Premultiply the alpha on a block of pixels
  *
- * This is currently only implemented for SDL_PIXELFORMAT_ARGB8888
- *
  * Here are some ideas for optimization:
  * https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha
  * https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data
  */
-int SDL_PremultiplyAlpha(int width, int height,
-                         SDL_PixelFormat src_format, const void *src, int src_pitch,
-                         SDL_PixelFormat dst_format, void *dst, int dst_pitch)
+
+static void SDL_PremultiplyAlpha_AXYZ8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
 {
     int c;
     Uint32 srcpixel;
@@ -1785,25 +1782,6 @@ int SDL_PremultiplyAlpha(int width, int height,
     Uint32 dstpixel;
     Uint32 dstR, dstG, dstB, dstA;
 
-    if (!src) {
-        return SDL_InvalidParamError("src");
-    }
-    if (!src_pitch) {
-        return SDL_InvalidParamError("src_pitch");
-    }
-    if (!dst) {
-        return SDL_InvalidParamError("dst");
-    }
-    if (!dst_pitch) {
-        return SDL_InvalidParamError("dst_pitch");
-    }
-    if (src_format != SDL_PIXELFORMAT_ARGB8888) {
-        return SDL_InvalidParamError("src_format");
-    }
-    if (dst_format != SDL_PIXELFORMAT_ARGB8888) {
-        return SDL_InvalidParamError("dst_format");
-    }
-
     while (height--) {
         const Uint32 *src_px = (const Uint32 *)src;
         Uint32 *dst_px = (Uint32 *)dst;
@@ -1825,7 +1803,192 @@ int SDL_PremultiplyAlpha(int width, int height,
         src = (const Uint8 *)src + src_pitch;
         dst = (Uint8 *)dst + dst_pitch;
     }
-    return 0;
+}
+
+static void SDL_PremultiplyAlpha_XYZA8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
+{
+    int c;
+    Uint32 srcpixel;
+    Uint32 srcR, srcG, srcB, srcA;
+    Uint32 dstpixel;
+    Uint32 dstR, dstG, dstB, dstA;
+
+    while (height--) {
+        const Uint32 *src_px = (const Uint32 *)src;
+        Uint32 *dst_px = (Uint32 *)dst;
+        for (c = width; c; --c) {
+            /* Component bytes extraction. */
+            srcpixel = *src_px++;
+            RGBA_FROM_RGBA8888(srcpixel, srcR, srcG, srcB, srcA);
+
+            /* Alpha pre-multiplication of each component. */
+            dstA = srcA;
+            dstR = (srcA * srcR) / 255;
+            dstG = (srcA * srcG) / 255;
+            dstB = (srcA * srcB) / 255;
+
+            /* RGBA8888 pixel recomposition. */
+            RGBA8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA);
+            *dst_px++ = dstpixel;
+        }
+        src = (const Uint8 *)src + src_pitch;
+        dst = (Uint8 *)dst + dst_pitch;
+    }
+}
+
+static void SDL_PremultiplyAlpha_AXYZ128(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
+{
+    int c;
+    float flR, flG, flB, flA;
+
+    while (height--) {
+        const float *src_px = (const float *)src;
+        float *dst_px = (float *)dst;
+        for (c = width; c; --c) {
+            flA = *src_px++;
+            flR = *src_px++;
+            flG = *src_px++;
+            flB = *src_px++;
+
+            /* Alpha pre-multiplication of each component. */
+            flR *= flA;
+            flG *= flA;
+            flB *= flA;
+
+            *dst_px++ = flA;
+            *dst_px++ = flR;
+            *dst_px++ = flG;
+            *dst_px++ = flB;
+        }
+        src = (const Uint8 *)src + src_pitch;
+        dst = (Uint8 *)dst + dst_pitch;
+    }
+}
+
+static int SDL_PremultiplyAlphaPixelsAndColorspace(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch, SDL_bool linear)
+{
+    SDL_Surface *convert = NULL;
+    void *final_dst = dst;
+    int final_dst_pitch = dst_pitch;
+    SDL_PixelFormat format;
+    SDL_Colorspace colorspace;
+    int result = -1;
+
+    if (!src) {
+        return SDL_InvalidParamError("src");
+    }
+    if (!src_pitch) {
+        return SDL_InvalidParamError("src_pitch");
+    }
+    if (!dst) {
+        return SDL_InvalidParamError("dst");
+    }
+    if (!dst_pitch) {
+        return SDL_InvalidParamError("dst_pitch");
+    }
+
+    // Use a high precision format if we're converting to linear colorspace or using high precision pixel formats
+    if (linear ||
+        SDL_ISPIXELFORMAT_10BIT(src_format) || SDL_BITSPERPIXEL(src_format) > 32 ||
+        SDL_ISPIXELFORMAT_10BIT(dst_format) || SDL_BITSPERPIXEL(dst_format) > 32) {
+        if (src_format == SDL_PIXELFORMAT_ARGB128_FLOAT ||
+            src_format == SDL_PIXELFORMAT_ABGR128_FLOAT) {
+            format = src_format;
+        } else {
+            format = SDL_PIXELFORMAT_ARGB128_FLOAT;
+        }
+    } else {
+        if (src_format == SDL_PIXELFORMAT_ARGB8888 ||
+            src_format == SDL_PIXELFORMAT_ABGR8888 ||
+            src_format == SDL_PIXELFORMAT_RGBA8888 ||
+            src_format == SDL_PIXELFORMAT_BGRA8888) {
+            format = src_format;
+        } else {
+            format = SDL_PIXELFORMAT_ARGB8888;
+        }
+    }
+    if (linear) {
+        colorspace = SDL_COLORSPACE_SRGB_LINEAR;
+    } else {
+        colorspace = SDL_COLORSPACE_SRGB;
+    }
+
+    if (src_format != format || src_colorspace != colorspace) {
+        convert = SDL_CreateSurface(width, height, format);
+        if (!convert) {
+            goto done;
+        }
+        if (SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, format, colorspace, 0, convert->pixels, convert->pitch) < 0) {
+            goto done;
+        }
+
+        src = convert->pixels;
+        src_pitch = convert->pitch;
+        dst = convert->pixels;
+        dst_pitch = convert->pitch;
+
+    } else if (dst_format != format || dst_colorspace != colorspace) {
+        convert = SDL_CreateSurface(width, height, format);
+        if (!convert) {
+            goto done;
+        }
+        dst = convert->pixels;
+        dst_pitch = convert->pitch;
+    }
+
+    switch (format) {
+    case SDL_PIXELFORMAT_ARGB8888:
+    case SDL_PIXELFORMAT_ABGR8888:
+        SDL_PremultiplyAlpha_AXYZ8888(width, height, src, src_pitch, dst, dst_pitch);
+        break;
+    case SDL_PIXELFORMAT_RGBA8888:
+    case SDL_PIXELFORMAT_BGRA8888:
+        SDL_PremultiplyAlpha_XYZA8888(width, height, src, src_pitch, dst, dst_pitch);
+        break;
+    case SDL_PIXELFORMAT_ARGB128_FLOAT:
+    case SDL_PIXELFORMAT_ABGR128_FLOAT:
+        SDL_PremultiplyAlpha_AXYZ128(width, height, src, src_pitch, dst, dst_pitch);
+        break;
+    default:
+        SDL_SetError("Unexpected internal pixel format");
+        goto done;
+    }
+
+    if (dst != final_dst) {
+        if (SDL_ConvertPixelsAndColorspace(width, height, format, colorspace, 0, convert->pixels, convert->pitch, dst_format, dst_colorspace, dst_properties, final_dst, final_dst_pitch) < 0) {
+            goto done;
+        }
+    }
+    result = 0;
+
+done:
+    if (convert) {
+        SDL_DestroySurface(convert);
+    }
+    return result;
+}
+
+int SDL_PremultiplyAlpha(int width, int height,
+                         SDL_PixelFormat src_format, const void *src, int src_pitch,
+                         SDL_PixelFormat dst_format, void *dst, int dst_pitch, SDL_bool linear)
+{
+    SDL_Colorspace src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format);
+    SDL_Colorspace dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format);
+
+    return SDL_PremultiplyAlphaPixelsAndColorspace(width, height, src_format, src_colorspace, 0, src, src_pitch, dst_format, dst_colorspace, 0, dst, dst_pitch, linear);
+}
+
+int SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, SDL_bool linear)
+{
+    SDL_Colorspace colorspace;
+
+    if (!SDL_SurfaceValid(surface)) {
+        return SDL_InvalidParamError("surface");
+    }
+
+    colorspace = SDL_GetSurfaceColorspace(surface);
+
+    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);
 }
 
 Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)

+ 1 - 1
src/video/kmsdrm/SDL_kmsdrmmouse.c

@@ -267,7 +267,7 @@ static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface *surface, int hot_x, int hot_
        straight-alpha pixels, so we always have to convert. */
     SDL_PremultiplyAlpha(surface->w, surface->h,
                          surface->format, surface->pixels, surface->pitch,
-                         SDL_PIXELFORMAT_ARGB8888, curdata->buffer, surface->w * 4);
+                         SDL_PIXELFORMAT_ARGB8888, curdata->buffer, surface->w * 4, SDL_TRUE);
 
     cursor->internal = curdata;
 

+ 1 - 1
src/video/wayland/SDL_waylandmouse.c

@@ -441,7 +441,7 @@ static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot
         /* Wayland requires premultiplied alpha for its surfaces. */
         SDL_PremultiplyAlpha(surface->w, surface->h,
                              surface->format, surface->pixels, surface->pitch,
-                             SDL_PIXELFORMAT_ARGB8888, data->cursor_data.custom.shm_data, surface->w * 4);
+                             SDL_PIXELFORMAT_ARGB8888, data->cursor_data.custom.shm_data, surface->w * 4, SDL_TRUE);
 
         data->surface = wl_compositor_create_surface(wd->compositor);
         wl_surface_set_user_data(data->surface, NULL);