mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-06-03 02:17:39 +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:
parent
b99ea1ff75
commit
334962b056
7 changed files with 210 additions and 31 deletions
|
@ -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);
|
||||
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.
|
||||
|
|
|
@ -610,6 +610,7 @@ SDL3_0.0.0 {
|
|||
SDL_PollEvent;
|
||||
SDL_PostSemaphore;
|
||||
SDL_PremultiplyAlpha;
|
||||
SDL_PremultiplySurfaceAlpha;
|
||||
SDL_PumpEvents;
|
||||
SDL_PushEvent;
|
||||
SDL_PutAudioStreamData;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue