Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
Updated blend semantics so blending uses the following formula:
Browse files Browse the repository at this point in the history
    dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
    dstA = srcA + (dstA * (1-srcA))
This allows proper compositing semantics without requiring premultiplied alpha.

Needs full unit test coverage and bug fixes!
  • Loading branch information
slouken committed Jul 23, 2013
1 parent a0b0b65 commit b844c81
Show file tree
Hide file tree
Showing 17 changed files with 362 additions and 1,001 deletions.
15 changes: 11 additions & 4 deletions include/SDL_blendmode.h
Expand Up @@ -39,10 +39,17 @@ extern "C" {
*/
typedef enum
{
SDL_BLENDMODE_NONE = 0x00000000, /**< No blending */
SDL_BLENDMODE_BLEND = 0x00000001, /**< dst = (src * A) + (dst * (1-A)) */
SDL_BLENDMODE_ADD = 0x00000002, /**< dst = (src * A) + dst */
SDL_BLENDMODE_MOD = 0x00000004 /**< dst = src * dst */
SDL_BLENDMODE_NONE = 0x00000000, /**< no blending
dstRGBA = srcRGBA */
SDL_BLENDMODE_BLEND = 0x00000001, /**< alpha blending
dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
dstA = srcA + (dstA * (1-srcA)) */
SDL_BLENDMODE_ADD = 0x00000002, /**< additive blending
dstRGB = (srcRGB * srcA) + dstRGB
dstA = dstA */
SDL_BLENDMODE_MOD = 0x00000004 /**< color modulate
dstRGB = srcRGB * dstRGB
dstA = dstA */
} SDL_BlendMode;

/* Ends C function definitions when using C++ */
Expand Down
30 changes: 14 additions & 16 deletions include/SDL_surface.h
Expand Up @@ -399,44 +399,42 @@ extern DECLSPEC int SDLCALL SDL_FillRects
*
* The blit function should not be called on a locked surface.
*
* The blit semantics for surfaces with and without alpha and colorkey
* The blit semantics for surfaces with and without blending and colorkey
* are defined as follows:
* \verbatim
RGBA->RGB:
SDL_SRCALPHA set:
alpha-blend (using alpha-channel).
Source surface blend mode set to SDL_BLENDMODE_BLEND:
alpha-blend (using the source alpha-channel and per-surface alpha)
SDL_SRCCOLORKEY ignored.
SDL_SRCALPHA not set:
Source surface blend mode set to SDL_BLENDMODE_NONE:
copy RGB.
if SDL_SRCCOLORKEY set, only copy the pixels matching the
RGB values of the source color key, ignoring alpha in the
comparison.
RGB->RGBA:
SDL_SRCALPHA set:
alpha-blend (using the source per-surface alpha value);
set destination alpha to opaque.
SDL_SRCALPHA not set:
Source surface blend mode set to SDL_BLENDMODE_BLEND:
alpha-blend (using the source per-surface alpha)
Source surface blend mode set to SDL_BLENDMODE_NONE:
copy RGB, set destination alpha to source per-surface alpha value.
both:
if SDL_SRCCOLORKEY set, only copy the pixels matching the
source color key.
RGBA->RGBA:
SDL_SRCALPHA set:
alpha-blend (using the source alpha channel) the RGB values;
leave destination alpha untouched. [Note: is this correct?]
Source surface blend mode set to SDL_BLENDMODE_BLEND:
alpha-blend (using the source alpha-channel and per-surface alpha)
SDL_SRCCOLORKEY ignored.
SDL_SRCALPHA not set:
Source surface blend mode set to SDL_BLENDMODE_NONE:
copy all of RGBA to the destination.
if SDL_SRCCOLORKEY set, only copy the pixels matching the
RGB values of the source color key, ignoring alpha in the
comparison.
comparison.
RGB->RGB:
SDL_SRCALPHA set:
alpha-blend (using the source per-surface alpha value).
SDL_SRCALPHA not set:
Source surface blend mode set to SDL_BLENDMODE_BLEND:
alpha-blend (using the source per-surface alpha)
Source surface blend mode set to SDL_BLENDMODE_NONE:
copy RGB.
both:
if SDL_SRCCOLORKEY set, only copy the pixels matching the
Expand Down
27 changes: 27 additions & 0 deletions src/render/direct3d/SDL_render_d3d.c
Expand Up @@ -227,6 +227,7 @@ typedef struct
D3DPRESENT_PARAMETERS pparams;
SDL_bool updateSize;
SDL_bool beginScene;
SDL_bool enableSeparateAlphaBlend;
D3DTEXTUREFILTERTYPE scaleMode;
IDirect3DSurface9 *defaultRenderTarget;
IDirect3DSurface9 *currentRenderTarget;
Expand Down Expand Up @@ -615,6 +616,10 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
}

if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) {
data->enableSeparateAlphaBlend = SDL_TRUE;
}

/* Set up parameters for rendering */
IDirect3DDevice9_SetVertexShader(data->device, NULL);
IDirect3DDevice9_SetFVF(data->device,
Expand All @@ -637,6 +642,10 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
D3DTA_TEXTURE);
IDirect3DDevice9_SetTextureStageState(data->device, 0, D3DTSS_ALPHAARG2,
D3DTA_DIFFUSE);
/* Enable separate alpha blend function, if possible */
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
}
/* Disable second texture stage, since we're done */
IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_COLOROP,
D3DTOP_DISABLE);
Expand Down Expand Up @@ -979,6 +988,12 @@ D3D_SetBlendMode(D3D_RenderData * data, int blendMode)
D3DBLEND_SRCALPHA);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
D3DBLEND_INVSRCALPHA);
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
D3DBLEND_ONE);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
D3DBLEND_INVSRCALPHA);
}
break;
case SDL_BLENDMODE_ADD:
IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
Expand All @@ -987,6 +1002,12 @@ D3D_SetBlendMode(D3D_RenderData * data, int blendMode)
D3DBLEND_SRCALPHA);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
D3DBLEND_ONE);
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
D3DBLEND_ZERO);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
D3DBLEND_ONE);
}
break;
case SDL_BLENDMODE_MOD:
IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE,
Expand All @@ -995,6 +1016,12 @@ D3D_SetBlendMode(D3D_RenderData * data, int blendMode)
D3DBLEND_ZERO);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND,
D3DBLEND_SRCCOLOR);
if (data->enableSeparateAlphaBlend) {
IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA,
D3DBLEND_ZERO);
IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA,
D3DBLEND_ONE);
}
break;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/render/opengl/SDL_glfuncs.h
Expand Up @@ -15,6 +15,7 @@ SDL_PROC_UNUSED(void, glBitmap,
(GLsizei, GLsizei, GLfloat, GLfloat, GLfloat, GLfloat,
const GLubyte *))
SDL_PROC(void, glBlendFunc, (GLenum, GLenum))
SDL_PROC(void, glBlendFuncSeparate, (GLenum, GLenum, GLenum, GLenum))
SDL_PROC_UNUSED(void, glCallList, (GLuint))
SDL_PROC_UNUSED(void, glCallLists, (GLsizei, GLenum, const GLvoid *))
SDL_PROC(void, glClear, (GLbitfield))
Expand Down
6 changes: 3 additions & 3 deletions src/render/opengl/SDL_render_gl.c
Expand Up @@ -924,17 +924,17 @@ GL_SetBlendMode(GL_RenderData * data, int blendMode)
case SDL_BLENDMODE_BLEND:
data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
data->glEnable(GL_BLEND);
data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
break;
case SDL_BLENDMODE_ADD:
data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
data->glEnable(GL_BLEND);
data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
break;
case SDL_BLENDMODE_MOD:
data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
data->glEnable(GL_BLEND);
data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
data->glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
break;
}
data->current.blendMode = blendMode;
Expand Down
1 change: 1 addition & 0 deletions src/render/opengles/SDL_glesfuncs.h
@@ -1,5 +1,6 @@
SDL_PROC(void, glBindTexture, (GLenum, GLuint))
SDL_PROC(void, glBlendFunc, (GLenum, GLenum))
SDL_PROC(void, glBlendFuncSeparateOES, (GLenum, GLenum, GLenum, GLenum))
SDL_PROC(void, glClear, (GLbitfield))
SDL_PROC(void, glClearColor, (GLclampf, GLclampf, GLclampf, GLclampf))
SDL_PROC(void, glColor4f, (GLfloat, GLfloat, GLfloat, GLfloat))
Expand Down
23 changes: 20 additions & 3 deletions src/render/opengles/SDL_render_gles.c
Expand Up @@ -121,6 +121,7 @@ typedef struct

SDL_bool useDrawTexture;
SDL_bool GL_OES_draw_texture_supported;
SDL_bool GL_OES_blend_func_separate_supported;
} GLES_RenderData;

typedef struct
Expand Down Expand Up @@ -376,6 +377,10 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
}
data->framebuffers = NULL;

if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) {
data->GL_OES_blend_func_separate_supported = SDL_TRUE;
}

/* Set up parameters for rendering */
GLES_ResetState(renderer);

Expand Down Expand Up @@ -680,17 +685,29 @@ GLES_SetBlendMode(GLES_RenderData * data, int blendMode)
case SDL_BLENDMODE_BLEND:
data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
data->glEnable(GL_BLEND);
data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (data->GL_OES_blend_func_separate_supported) {
data->glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
break;
case SDL_BLENDMODE_ADD:
data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
data->glEnable(GL_BLEND);
data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
if (data->GL_OES_blend_func_separate_supported) {
data->glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
} else {
data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
break;
case SDL_BLENDMODE_MOD:
data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
data->glEnable(GL_BLEND);
data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
if (data->GL_OES_blend_func_separate_supported) {
data->glBlendFuncSeparateOES(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
} else {
data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
}
break;
}
data->current.blendMode = blendMode;
Expand Down
2 changes: 1 addition & 1 deletion src/render/opengles2/SDL_gles2funcs.h
Expand Up @@ -2,7 +2,7 @@ SDL_PROC(void, glActiveTexture, (GLenum))
SDL_PROC(void, glAttachShader, (GLuint, GLuint))
SDL_PROC(void, glBindAttribLocation, (GLuint, GLuint, const char *))
SDL_PROC(void, glBindTexture, (GLenum, GLuint))
SDL_PROC(void, glBlendFunc, (GLenum, GLenum))
SDL_PROC(void, glBlendFuncSeparate, (GLenum, GLenum, GLenum, GLenum))
SDL_PROC(void, glClear, (GLbitfield))
SDL_PROC(void, glClearColor, (GLclampf, GLclampf, GLclampf, GLclampf))
SDL_PROC(void, glCompileShader, (GLuint))
Expand Down
6 changes: 3 additions & 3 deletions src/render/opengles2/SDL_render_gles2.c
Expand Up @@ -994,15 +994,15 @@ GLES2_SetBlendMode(GLES2_DriverContext *rdata, int blendMode)
break;
case SDL_BLENDMODE_BLEND:
rdata->glEnable(GL_BLEND);
rdata->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
rdata->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
break;
case SDL_BLENDMODE_ADD:
rdata->glEnable(GL_BLEND);
rdata->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
rdata->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
break;
case SDL_BLENDMODE_MOD:
rdata->glEnable(GL_BLEND);
rdata->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
rdata->glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
break;
}
rdata->current.blendMode = blendMode;
Expand Down
20 changes: 15 additions & 5 deletions src/video/SDL_blit.h
Expand Up @@ -440,12 +440,22 @@ do { \
} \
}

/* Blend the RGB values of two Pixels based on a source alpha value */
#define ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB) \
/* Blend the RGB values of two pixels with an alpha value */
#define ALPHA_BLEND_RGB(sR, sG, sB, A, dR, dG, dB) \
do { \
dR = ((((int)(sR-dR)*(int)A)/255)+dR); \
dG = ((((int)(sG-dG)*(int)A)/255)+dG); \
dB = ((((int)(sB-dB)*(int)A)/255)+dB); \
dR = ((((unsigned)(sR-dR)*(unsigned)A)/255)+dR); \
dG = ((((unsigned)(sG-dG)*(unsigned)A)/255)+dG); \
dB = ((((unsigned)(sB-dB)*(unsigned)A)/255)+dB); \
} while(0)


/* Blend the RGBA values of two pixels */
#define ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA) \
do { \
dR = ((((unsigned)(sR-dR)*(unsigned)sA)/255)+dR); \
dG = ((((unsigned)(sG-dG)*(unsigned)sA)/255)+dG); \
dB = ((((unsigned)(sB-dB)*(unsigned)sA)/255)+dB); \
dA = ((unsigned)sA+(unsigned)dA-((unsigned)sA*dA)/255); \
} while(0)


Expand Down
28 changes: 14 additions & 14 deletions src/video/SDL_blit_0.c
Expand Up @@ -363,7 +363,10 @@ BlitBtoNAlpha(SDL_BlitInfo * info)
SDL_PixelFormat *dstfmt = info->dst_fmt;
int dstbpp;
int c;
const int A = info->a;
Uint32 pixel;
unsigned sR, sG, sB;
unsigned dR, dG, dB, dA;
const unsigned A = info->a;

/* Set up some basic variables */
dstbpp = dstfmt->BytesPerPixel;
Expand All @@ -377,15 +380,12 @@ BlitBtoNAlpha(SDL_BlitInfo * info)
}
bit = (byte & 0x80) >> 7;
if (1) {
Uint32 pixel;
unsigned sR, sG, sB;
unsigned dR, dG, dB;
sR = srcpal[bit].r;
sG = srcpal[bit].g;
sB = srcpal[bit].b;
DISEMBLE_RGB(dst, dstbpp, dstfmt, pixel, dR, dG, dB);
ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB);
ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
byte <<= 1;
dst += dstbpp;
Expand All @@ -409,7 +409,10 @@ BlitBtoNAlphaKey(SDL_BlitInfo * info)
const SDL_Color *srcpal = srcfmt->palette->colors;
int dstbpp;
int c;
const int A = info->a;
Uint32 pixel;
unsigned sR, sG, sB;
unsigned dR, dG, dB, dA;
const unsigned A = info->a;
Uint32 ckey = info->colorkey;

/* Set up some basic variables */
Expand All @@ -424,15 +427,12 @@ BlitBtoNAlphaKey(SDL_BlitInfo * info)
}
bit = (byte & 0x80) >> 7;
if (bit != ckey) {
int sR, sG, sB;
int dR, dG, dB;
Uint32 pixel;
sR = srcpal[bit].r;
sG = srcpal[bit].g;
sB = srcpal[bit].b;
DISEMBLE_RGB(dst, dstbpp, dstfmt, pixel, dR, dG, dB);
ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB);
ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
byte <<= 1;
dst += dstbpp;
Expand Down

0 comments on commit b844c81

Please sign in to comment.