mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-18 01:08:16 +00:00
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.
This commit is contained in:
@@ -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 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 width the width of the block to convert, in pixels.
|
||||||
* \param height the height 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.
|
* \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_format an SDL_PixelFormat value of the `dst` pixels format.
|
||||||
* \param dst a pointer to be filled in with premultiplied pixel data.
|
* \param dst a pointer to be filled in with premultiplied pixel data.
|
||||||
* \param dst_pitch the pitch of the destination pixels, in bytes.
|
* \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
|
* \returns 0 on success or a negative error code on failure; call
|
||||||
* SDL_GetError() for more information.
|
* SDL_GetError() for more information.
|
||||||
*
|
*
|
||||||
* \since This function is available since SDL 3.0.0.
|
* \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_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_PremultiplySurfaceAlpha(SDL_Surface *surface, SDL_bool linear);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a fast fill of a rectangle with a specific color.
|
* Perform a fast fill of a rectangle with a specific color.
|
||||||
|
@@ -610,6 +610,7 @@ SDL3_0.0.0 {
|
|||||||
SDL_PollEvent;
|
SDL_PollEvent;
|
||||||
SDL_PostSemaphore;
|
SDL_PostSemaphore;
|
||||||
SDL_PremultiplyAlpha;
|
SDL_PremultiplyAlpha;
|
||||||
|
SDL_PremultiplySurfaceAlpha;
|
||||||
SDL_PumpEvents;
|
SDL_PumpEvents;
|
||||||
SDL_PushEvent;
|
SDL_PushEvent;
|
||||||
SDL_PutAudioStreamData;
|
SDL_PutAudioStreamData;
|
||||||
|
@@ -635,6 +635,7 @@
|
|||||||
#define SDL_PollEvent SDL_PollEvent_REAL
|
#define SDL_PollEvent SDL_PollEvent_REAL
|
||||||
#define SDL_PostSemaphore SDL_PostSemaphore_REAL
|
#define SDL_PostSemaphore SDL_PostSemaphore_REAL
|
||||||
#define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
|
#define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
|
||||||
|
#define SDL_PremultiplySurfaceAlpha SDL_PremultiplySurfaceAlpha_REAL
|
||||||
#define SDL_PumpEvents SDL_PumpEvents_REAL
|
#define SDL_PumpEvents SDL_PumpEvents_REAL
|
||||||
#define SDL_PushEvent SDL_PushEvent_REAL
|
#define SDL_PushEvent SDL_PushEvent_REAL
|
||||||
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
|
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
|
||||||
|
@@ -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(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(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_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(void,SDL_PumpEvents,(void),(),)
|
||||||
SDL_DYNAPI_PROC(int,SDL_PushEvent,(SDL_Event *a),(a),return)
|
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)
|
SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return)
|
||||||
|
@@ -1769,15 +1769,12 @@ int SDL_ConvertPixels(int width, int height,
|
|||||||
/*
|
/*
|
||||||
* Premultiply the alpha on a block of pixels
|
* Premultiply the alpha on a block of pixels
|
||||||
*
|
*
|
||||||
* This is currently only implemented for SDL_PIXELFORMAT_ARGB8888
|
|
||||||
*
|
|
||||||
* Here are some ideas for optimization:
|
* Here are some ideas for optimization:
|
||||||
* https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha
|
* https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha
|
||||||
* https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data
|
* 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,
|
static void SDL_PremultiplyAlpha_AXYZ8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
|
||||||
SDL_PixelFormat dst_format, void *dst, int dst_pitch)
|
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
Uint32 srcpixel;
|
Uint32 srcpixel;
|
||||||
@@ -1785,25 +1782,6 @@ int SDL_PremultiplyAlpha(int width, int height,
|
|||||||
Uint32 dstpixel;
|
Uint32 dstpixel;
|
||||||
Uint32 dstR, dstG, dstB, dstA;
|
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--) {
|
while (height--) {
|
||||||
const Uint32 *src_px = (const Uint32 *)src;
|
const Uint32 *src_px = (const Uint32 *)src;
|
||||||
Uint32 *dst_px = (Uint32 *)dst;
|
Uint32 *dst_px = (Uint32 *)dst;
|
||||||
@@ -1825,7 +1803,192 @@ int SDL_PremultiplyAlpha(int width, int height,
|
|||||||
src = (const Uint8 *)src + src_pitch;
|
src = (const Uint8 *)src + src_pitch;
|
||||||
dst = (Uint8 *)dst + dst_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)
|
Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
|
||||||
|
@@ -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. */
|
straight-alpha pixels, so we always have to convert. */
|
||||||
SDL_PremultiplyAlpha(surface->w, surface->h,
|
SDL_PremultiplyAlpha(surface->w, surface->h,
|
||||||
surface->format, surface->pixels, surface->pitch,
|
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;
|
cursor->internal = curdata;
|
||||||
|
|
||||||
|
@@ -441,7 +441,7 @@ static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot
|
|||||||
/* Wayland requires premultiplied alpha for its surfaces. */
|
/* Wayland requires premultiplied alpha for its surfaces. */
|
||||||
SDL_PremultiplyAlpha(surface->w, surface->h,
|
SDL_PremultiplyAlpha(surface->w, surface->h,
|
||||||
surface->format, surface->pixels, surface->pitch,
|
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);
|
data->surface = wl_compositor_create_surface(wd->compositor);
|
||||||
wl_surface_set_user_data(data->surface, NULL);
|
wl_surface_set_user_data(data->surface, NULL);
|
||||||
|
Reference in New Issue
Block a user