/* * Mesa 3-D graphics library * Version: 7.1 * * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * \file swrast/s_blend.c * \brief software blending. * \author Brian Paul * * Only a few blend modes have been optimized (min, max, transparency, add) * more optimized cases can easily be added if needed. * Celestia uses glBlendFunc(GL_SRC_ALPHA, GL_ONE), for example. */ #include "main/glheader.h" #include "main/context.h" #include "main/colormac.h" #include "main/macros.h" #include "s_blend.h" #include "s_context.h" #include "s_span.h" #if defined(USE_MMX_ASM) #include "x86/mmx.h" #include "x86/common_x86_asm.h" #define _BLENDAPI _ASMAPI #else #define _BLENDAPI #endif /** * Integer divide by 255 * Declare "int divtemp" before using. * This satisfies Glean and should be reasonably fast. * Contributed by Nathan Hand. */ #define DIV255(X) (divtemp = (X), ((divtemp << 8) + divtemp + 256) >> 16) /** * Special case for glBlendFunc(GL_ZERO, GL_ONE). * No-op means the framebuffer values remain unchanged. * Any chanType ok. */ static void _BLENDAPI blend_noop(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLint bytes; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].EquationA == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].SrcRGB == GL_ZERO); ASSERT(ctx->Color.Blend[0].DstRGB == GL_ONE); (void) ctx; /* just memcpy */ if (chanType == GL_UNSIGNED_BYTE) bytes = 4 * n * sizeof(GLubyte); else if (chanType == GL_UNSIGNED_SHORT) bytes = 4 * n * sizeof(GLushort); else bytes = 4 * n * sizeof(GLfloat); memcpy(src, dst, bytes); } /** * Special case for glBlendFunc(GL_ONE, GL_ZERO) * Any chanType ok. */ static void _BLENDAPI blend_replace(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { ASSERT(ctx->Color.Blend[0].EquationRGB == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].EquationA == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].SrcRGB == GL_ONE); ASSERT(ctx->Color.Blend[0].DstRGB == GL_ZERO); (void) ctx; (void) n; (void) mask; (void) src; (void) dst; } /** * Common transparency blending mode: * glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). */ static void _BLENDAPI blend_transparency_ubyte(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLubyte (*rgba)[4] = (GLubyte (*)[4]) src; const GLubyte (*dest)[4] = (const GLubyte (*)[4]) dst; GLuint i; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].EquationA == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].SrcRGB == GL_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].SrcA == GL_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].DstRGB == GL_ONE_MINUS_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].DstA == GL_ONE_MINUS_SRC_ALPHA); ASSERT(chanType == GL_UNSIGNED_BYTE); (void) ctx; for (i = 0; i < n; i++) { if (mask[i]) { const GLint t = rgba[i][ACOMP]; /* t is in [0, 255] */ if (t == 0) { /* 0% alpha */ COPY_4UBV(rgba[i], dest[i]); } else if (t != 255) { GLint divtemp; const GLint r = DIV255((rgba[i][RCOMP] - dest[i][RCOMP]) * t) + dest[i][RCOMP]; const GLint g = DIV255((rgba[i][GCOMP] - dest[i][GCOMP]) * t) + dest[i][GCOMP]; const GLint b = DIV255((rgba[i][BCOMP] - dest[i][BCOMP]) * t) + dest[i][BCOMP]; const GLint a = DIV255((rgba[i][ACOMP] - dest[i][ACOMP]) * t) + dest[i][ACOMP]; ASSERT(r <= 255); ASSERT(g <= 255); ASSERT(b <= 255); ASSERT(a <= 255); rgba[i][RCOMP] = (GLubyte) r; rgba[i][GCOMP] = (GLubyte) g; rgba[i][BCOMP] = (GLubyte) b; rgba[i][ACOMP] = (GLubyte) a; } } } } static void _BLENDAPI blend_transparency_ushort(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLushort (*rgba)[4] = (GLushort (*)[4]) src; const GLushort (*dest)[4] = (const GLushort (*)[4]) dst; GLuint i; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].EquationA == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].SrcRGB == GL_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].SrcA == GL_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].DstRGB == GL_ONE_MINUS_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].DstA == GL_ONE_MINUS_SRC_ALPHA); ASSERT(chanType == GL_UNSIGNED_SHORT); (void) ctx; for (i = 0; i < n; i++) { if (mask[i]) { const GLint t = rgba[i][ACOMP]; if (t == 0) { /* 0% alpha */ COPY_4V(rgba[i], dest[i]); } else if (t != 65535) { const GLfloat tt = (GLfloat) t / 65535.0F; GLushort r = (GLushort) ((rgba[i][RCOMP] - dest[i][RCOMP]) * tt + dest[i][RCOMP]); GLushort g = (GLushort) ((rgba[i][GCOMP] - dest[i][GCOMP]) * tt + dest[i][GCOMP]); GLushort b = (GLushort) ((rgba[i][BCOMP] - dest[i][BCOMP]) * tt + dest[i][BCOMP]); GLushort a = (GLushort) ((rgba[i][ACOMP] - dest[i][ACOMP]) * tt + dest[i][ACOMP]); ASSIGN_4V(rgba[i], r, g, b, a); } } } } static void _BLENDAPI blend_transparency_float(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLfloat (*rgba)[4] = (GLfloat (*)[4]) src; const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst; GLuint i; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].EquationA == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].SrcRGB == GL_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].SrcA == GL_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].DstRGB == GL_ONE_MINUS_SRC_ALPHA); ASSERT(ctx->Color.Blend[0].DstA == GL_ONE_MINUS_SRC_ALPHA); ASSERT(chanType == GL_FLOAT); (void) ctx; for (i = 0; i < n; i++) { if (mask[i]) { const GLfloat t = rgba[i][ACOMP]; /* t in [0, 1] */ if (t == 0.0F) { /* 0% alpha */ COPY_4V(rgba[i], dest[i]); } else if (t != 1.0F) { GLfloat r = (rgba[i][RCOMP] - dest[i][RCOMP]) * t + dest[i][RCOMP]; GLfloat g = (rgba[i][GCOMP] - dest[i][GCOMP]) * t + dest[i][GCOMP]; GLfloat b = (rgba[i][BCOMP] - dest[i][BCOMP]) * t + dest[i][BCOMP]; GLfloat a = (rgba[i][ACOMP] - dest[i][ACOMP]) * t + dest[i][ACOMP]; ASSIGN_4V(rgba[i], r, g, b, a); } } } } /** * Add src and dest: glBlendFunc(GL_ONE, GL_ONE). * Any chanType ok. */ static void _BLENDAPI blend_add(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLuint i; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].EquationA == GL_FUNC_ADD); ASSERT(ctx->Color.Blend[0].SrcRGB == GL_ONE); ASSERT(ctx->Color.Blend[0].DstRGB == GL_ONE); (void) ctx; if (chanType == GL_UNSIGNED_BYTE) { GLubyte (*rgba)[4] = (GLubyte (*)[4]) src; const GLubyte (*dest)[4] = (const GLubyte (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { GLint r = rgba[i][RCOMP] + dest[i][RCOMP]; GLint g = rgba[i][GCOMP] + dest[i][GCOMP]; GLint b = rgba[i][BCOMP] + dest[i][BCOMP]; GLint a = rgba[i][ACOMP] + dest[i][ACOMP]; rgba[i][RCOMP] = (GLubyte) MIN2( r, 255 ); rgba[i][GCOMP] = (GLubyte) MIN2( g, 255 ); rgba[i][BCOMP] = (GLubyte) MIN2( b, 255 ); rgba[i][ACOMP] = (GLubyte) MIN2( a, 255 ); } } } else if (chanType == GL_UNSIGNED_SHORT) { GLushort (*rgba)[4] = (GLushort (*)[4]) src; const GLushort (*dest)[4] = (const GLushort (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { GLint r = rgba[i][RCOMP] + dest[i][RCOMP]; GLint g = rgba[i][GCOMP] + dest[i][GCOMP]; GLint b = rgba[i][BCOMP] + dest[i][BCOMP]; GLint a = rgba[i][ACOMP] + dest[i][ACOMP]; rgba[i][RCOMP] = (GLshort) MIN2( r, 255 ); rgba[i][GCOMP] = (GLshort) MIN2( g, 255 ); rgba[i][BCOMP] = (GLshort) MIN2( b, 255 ); rgba[i][ACOMP] = (GLshort) MIN2( a, 255 ); } } } else { GLfloat (*rgba)[4] = (GLfloat (*)[4]) src; const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst; ASSERT(chanType == GL_FLOAT); for (i=0;i<n;i++) { if (mask[i]) { /* don't RGB clamp to max */ rgba[i][RCOMP] += dest[i][RCOMP]; rgba[i][GCOMP] += dest[i][GCOMP]; rgba[i][BCOMP] += dest[i][BCOMP]; rgba[i][ACOMP] += dest[i][ACOMP]; } } } } /** * Blend min function. * Any chanType ok. */ static void _BLENDAPI blend_min(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLuint i; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_MIN); ASSERT(ctx->Color.Blend[0].EquationA == GL_MIN); (void) ctx; if (chanType == GL_UNSIGNED_BYTE) { GLubyte (*rgba)[4] = (GLubyte (*)[4]) src; const GLubyte (*dest)[4] = (const GLubyte (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = MIN2( rgba[i][RCOMP], dest[i][RCOMP] ); rgba[i][GCOMP] = MIN2( rgba[i][GCOMP], dest[i][GCOMP] ); rgba[i][BCOMP] = MIN2( rgba[i][BCOMP], dest[i][BCOMP] ); rgba[i][ACOMP] = MIN2( rgba[i][ACOMP], dest[i][ACOMP] ); } } } else if (chanType == GL_UNSIGNED_SHORT) { GLushort (*rgba)[4] = (GLushort (*)[4]) src; const GLushort (*dest)[4] = (const GLushort (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = MIN2( rgba[i][RCOMP], dest[i][RCOMP] ); rgba[i][GCOMP] = MIN2( rgba[i][GCOMP], dest[i][GCOMP] ); rgba[i][BCOMP] = MIN2( rgba[i][BCOMP], dest[i][BCOMP] ); rgba[i][ACOMP] = MIN2( rgba[i][ACOMP], dest[i][ACOMP] ); } } } else { GLfloat (*rgba)[4] = (GLfloat (*)[4]) src; const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst; ASSERT(chanType == GL_FLOAT); for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = MIN2( rgba[i][RCOMP], dest[i][RCOMP] ); rgba[i][GCOMP] = MIN2( rgba[i][GCOMP], dest[i][GCOMP] ); rgba[i][BCOMP] = MIN2( rgba[i][BCOMP], dest[i][BCOMP] ); rgba[i][ACOMP] = MIN2( rgba[i][ACOMP], dest[i][ACOMP] ); } } } } /** * Blend max function. * Any chanType ok. */ static void _BLENDAPI blend_max(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLuint i; ASSERT(ctx->Color.Blend[0].EquationRGB == GL_MAX); ASSERT(ctx->Color.Blend[0].EquationA == GL_MAX); (void) ctx; if (chanType == GL_UNSIGNED_BYTE) { GLubyte (*rgba)[4] = (GLubyte (*)[4]) src; const GLubyte (*dest)[4] = (const GLubyte (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = MAX2( rgba[i][RCOMP], dest[i][RCOMP] ); rgba[i][GCOMP] = MAX2( rgba[i][GCOMP], dest[i][GCOMP] ); rgba[i][BCOMP] = MAX2( rgba[i][BCOMP], dest[i][BCOMP] ); rgba[i][ACOMP] = MAX2( rgba[i][ACOMP], dest[i][ACOMP] ); } } } else if (chanType == GL_UNSIGNED_SHORT) { GLushort (*rgba)[4] = (GLushort (*)[4]) src; const GLushort (*dest)[4] = (const GLushort (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = MAX2( rgba[i][RCOMP], dest[i][RCOMP] ); rgba[i][GCOMP] = MAX2( rgba[i][GCOMP], dest[i][GCOMP] ); rgba[i][BCOMP] = MAX2( rgba[i][BCOMP], dest[i][BCOMP] ); rgba[i][ACOMP] = MAX2( rgba[i][ACOMP], dest[i][ACOMP] ); } } } else { GLfloat (*rgba)[4] = (GLfloat (*)[4]) src; const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst; ASSERT(chanType == GL_FLOAT); for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = MAX2( rgba[i][RCOMP], dest[i][RCOMP] ); rgba[i][GCOMP] = MAX2( rgba[i][GCOMP], dest[i][GCOMP] ); rgba[i][BCOMP] = MAX2( rgba[i][BCOMP], dest[i][BCOMP] ); rgba[i][ACOMP] = MAX2( rgba[i][ACOMP], dest[i][ACOMP] ); } } } } /** * Modulate: result = src * dest * Any chanType ok. */ static void _BLENDAPI blend_modulate(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLvoid *src, const GLvoid *dst, GLenum chanType) { GLuint i; (void) ctx; if (chanType == GL_UNSIGNED_BYTE) { GLubyte (*rgba)[4] = (GLubyte (*)[4]) src; const GLubyte (*dest)[4] = (const GLubyte (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { GLint divtemp; rgba[i][RCOMP] = DIV255(rgba[i][RCOMP] * dest[i][RCOMP]); rgba[i][GCOMP] = DIV255(rgba[i][GCOMP] * dest[i][GCOMP]); rgba[i][BCOMP] = DIV255(rgba[i][BCOMP] * dest[i][BCOMP]); rgba[i][ACOMP] = DIV255(rgba[i][ACOMP] * dest[i][ACOMP]); } } } else if (chanType == GL_UNSIGNED_SHORT) { GLushort (*rgba)[4] = (GLushort (*)[4]) src; const GLushort (*dest)[4] = (const GLushort (*)[4]) dst; for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = (rgba[i][RCOMP] * dest[i][RCOMP] + 65535) >> 16; rgba[i][GCOMP] = (rgba[i][GCOMP] * dest[i][GCOMP] + 65535) >> 16; rgba[i][BCOMP] = (rgba[i][BCOMP] * dest[i][BCOMP] + 65535) >> 16; rgba[i][ACOMP] = (rgba[i][ACOMP] * dest[i][ACOMP] + 65535) >> 16; } } } else { GLfloat (*rgba)[4] = (GLfloat (*)[4]) src; const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst; ASSERT(chanType == GL_FLOAT); for (i=0;i<n;i++) { if (mask[i]) { rgba[i][RCOMP] = rgba[i][RCOMP] * dest[i][RCOMP]; rgba[i][GCOMP] = rgba[i][GCOMP] * dest[i][GCOMP]; rgba[i][BCOMP] = rgba[i][BCOMP] * dest[i][BCOMP]; rgba[i][ACOMP] = rgba[i][ACOMP] * dest[i][ACOMP]; } } } } /** * Do any blending operation, using floating point. * \param n number of pixels * \param mask fragment writemask array * \param rgba array of incoming (and modified) pixels * \param dest array of pixels from the dest color buffer */ static void blend_general_float(struct gl_context *ctx, GLuint n, const GLubyte mask[], GLfloat rgba[][4], GLfloat dest[][4], GLenum chanType) { GLuint i; for (i = 0; i < n; i++) { if (mask[i]) { /* Incoming/source Color */ const GLfloat Rs = rgba[i][RCOMP]; const GLfloat Gs = rgba[i][GCOMP]; const GLfloat Bs = rgba[i][BCOMP]; const GLfloat As = rgba[i][ACOMP]; /* Frame buffer/dest color */ const GLfloat Rd = dest[i][RCOMP]; const GLfloat Gd = dest[i][GCOMP]; const GLfloat Bd = dest[i][BCOMP]; const GLfloat Ad = dest[i][ACOMP]; GLfloat sR, sG, sB, sA; /* Source factor */ GLfloat dR, dG, dB, dA; /* Dest factor */ GLfloat r, g, b, a; /* result color */ /* XXX for the case of constant blend terms we could init * the sX and dX variables just once before the loop. */ /* Source RGB factor */ switch (ctx->Color.Blend[0].SrcRGB) { case GL_ZERO: sR = sG = sB = 0.0F; break; case GL_ONE: sR = sG = sB = 1.0F; break; case GL_DST_COLOR: sR = Rd; sG = Gd; sB = Bd; break; case GL_ONE_MINUS_DST_COLOR: sR = 1.0F - Rd; sG = 1.0F - Gd; sB = 1.0F - Bd; break; case GL_SRC_ALPHA: sR = sG = sB = As; break; case GL_ONE_MINUS_SRC_ALPHA: sR = sG = sB = 1.0F - As; break; case GL_DST_ALPHA: sR = sG = sB = Ad; break; case GL_ONE_MINUS_DST_ALPHA: sR = sG = sB = 1.0F - Ad; break; case GL_SRC_ALPHA_SATURATE: if (As < 1.0F - Ad) { sR = sG = sB = As; } else { sR = sG = sB = 1.0F - Ad; } break; case GL_CONSTANT_COLOR: sR = ctx->Color.BlendColor[0]; sG = ctx->Color.BlendColor[1]; sB = ctx->Color.BlendColor[2]; break; case GL_ONE_MINUS_CONSTANT_COLOR: sR = 1.0F - ctx->Color.BlendColor[0]; sG = 1.0F - ctx->Color.BlendColor[1]; sB = 1.0F - ctx->Color.BlendColor[2]; break; case GL_CONSTANT_ALPHA: sR = sG = sB = ctx->Color.BlendColor[3]; break; case GL_ONE_MINUS_CONSTANT_ALPHA: sR = sG = sB = 1.0F - ctx->Color.BlendColor[3]; break; case GL_SRC_COLOR: sR = Rs; sG = Gs; sB = Bs; break; case GL_ONE_MINUS_SRC_COLOR: sR = 1.0F - Rs; sG = 1.0F - Gs; sB = 1.0F - Bs; break; default: /* this should never happen */ _mesa_problem(ctx, "Bad blend source RGB factor in blend_general_float"); return; } /* Source Alpha factor */ switch (ctx->Color.Blend[0].SrcA) { case GL_ZERO: sA = 0.0F; break; case GL_ONE: sA = 1.0F; break; case GL_DST_COLOR: sA = Ad; break; case GL_ONE_MINUS_DST_COLOR: sA = 1.0F - Ad; break; case GL_SRC_ALPHA: sA = As; break; case GL_ONE_MINUS_SRC_ALPHA: sA = 1.0F - As; break; case GL_DST_ALPHA: sA = Ad; break; case GL_ONE_MINUS_DST_ALPHA: sA = 1.0F - Ad; break; case GL_SRC_ALPHA_SATURATE: sA = 1.0; break; case GL_CONSTANT_COLOR: sA = ctx->Color.BlendColor[3]; break; case GL_ONE_MINUS_CONSTANT_COLOR: sA = 1.0F - ctx->Color.BlendColor[3]; break; case GL_CONSTANT_ALPHA: sA = ctx->Color.BlendColor[3]; break; case GL_ONE_MINUS_CONSTANT_ALPHA: sA = 1.0F - ctx->Color.BlendColor[3]; break; case GL_SRC_COLOR: sA = As; break; case GL_ONE_MINUS_SRC_COLOR: sA = 1.0F - As; break; default: /* this should never happen */ sA = 0.0F; _mesa_problem(ctx, "Bad blend source A factor in blend_general_float"); return; } /* Dest RGB factor */ switch (ctx->Color.Blend[0].DstRGB) { case GL_ZERO: dR = dG = dB = 0.0F; break; case GL_ONE: dR = dG = dB = 1.0F; break; case GL_SRC_COLOR: dR = Rs; dG = Gs; dB = Bs; break; case GL_ONE_MINUS_SRC_COLOR: dR = 1.0F - Rs; dG = 1.0F - Gs; dB = 1.0F - Bs; break; case GL_SRC_ALPHA: dR = dG = dB = As; break; case GL_ONE_MINUS_SRC_ALPHA: dR = dG = dB = 1.0F - As; break; case GL_DST_ALPHA: dR = dG = dB = Ad; break; case GL_ONE_MINUS_DST_ALPHA: dR = dG = dB = 1.0F - Ad; break; case GL_CONSTANT_COLOR: dR = ctx->Color.BlendColor[0]; dG = ctx->Color.BlendColor[1]; dB = ctx->Color.BlendColor[2]; break; case GL_ONE_MINUS_CONSTANT_COLOR: dR = 1.0F - ctx->Color.BlendColor[0]; dG = 1.0F - ctx->Color.BlendColor[1]; dB = 1.0F - ctx->Color.BlendColor[2]; break; case GL_CONSTANT_ALPHA: dR = dG = dB = ctx->Color.BlendColor[3]; break; case GL_ONE_MINUS_CONSTANT_ALPHA: dR = dG = dB = 1.0F - ctx->Color.BlendColor[3]; break; case GL_DST_COLOR: dR = Rd; dG = Gd; dB = Bd; break; case GL_ONE_MINUS_DST_COLOR: dR = 1.0F - Rd; dG = 1.0F - Gd; dB = 1.0F - Bd; break; default: /* this should never happen */ dR = dG = dB = 0.0F; _mesa_problem(ctx, "Bad blend dest RGB factor in blend_general_float"); return; } /* Dest Alpha factor */ switch (ctx->Color.Blend[0].DstA) { case GL_ZERO: dA = 0.0F; break; case GL_ONE: dA = 1.0F; break; case GL_SRC_COLOR: dA = As; break; case GL_ONE_MINUS_SRC_COLOR: dA = 1.0F - As; break; case GL_SRC_ALPHA: dA = As; break; case GL_ONE_MINUS_SRC_ALPHA: dA = 1.0F - As; break; case GL_DST_ALPHA: dA = Ad; break; case GL_ONE_MINUS_DST_ALPHA: dA = 1.0F - Ad; break; case GL_CONSTANT_COLOR: dA = ctx->Color.BlendColor[3]; break; case GL_ONE_MINUS_CONSTANT_COLOR: dA = 1.0F - ctx->Color.BlendColor[3]; break; case GL_CONSTANT_ALPHA: dA = ctx->Color.BlendColor[3]; break; case GL_ONE_MINUS_CONSTANT_ALPHA: dA = 1.0F - ctx->Color.BlendColor[3]; break; case GL_DST_COLOR: dA = Ad; break; case GL_ONE_MINUS_DST_COLOR: dA = 1.0F - Ad; break; default: /* this should never happen */ dA = 0.0F; _mesa_problem(ctx, "Bad blend dest A factor in blend_general_float"); return; } /* compute the blended RGB */ switch (ctx->Color.Blend[0].EquationRGB) { case GL_FUNC_ADD: r = Rs * sR + Rd * dR; g = Gs * sG + Gd * dG; b = Bs * sB + Bd * dB; a = As * sA + Ad * dA; break; case GL_FUNC_SUBTRACT: r = Rs * sR - Rd * dR; g = Gs * sG - Gd * dG; b = Bs * sB - Bd * dB; a = As * sA - Ad * dA; break; case GL_FUNC_REVERSE_SUBTRACT: r = Rd * dR - Rs * sR; g = Gd * dG - Gs * sG; b = Bd * dB - Bs * sB; a = Ad * dA - As * sA; break; case GL_MIN: r = MIN2( Rd, Rs ); g = MIN2( Gd, Gs ); b = MIN2( Bd, Bs ); break; case GL_MAX: r = MAX2( Rd, Rs ); g = MAX2( Gd, Gs ); b = MAX2( Bd, Bs ); break; default: /* should never get here */ r = g = b = 0.0F; /* silence uninitialized var warning */ _mesa_problem(ctx, "unexpected BlendEquation in blend_general()"); return; } /* compute the blended alpha */ switch (ctx->Color.Blend[0].EquationA) { case GL_FUNC_ADD: a = As * sA + Ad * dA; break; case GL_FUNC_SUBTRACT: a = As * sA - Ad * dA; break; case GL_FUNC_REVERSE_SUBTRACT: a = Ad * dA - As * sA; break; case GL_MIN: a = MIN2( Ad, As ); break; case GL_MAX: a = MAX2( Ad, As ); break; default: /* should never get here */ a = 0.0F; /* silence uninitialized var warning */ _mesa_problem(ctx, "unexpected BlendEquation in blend_general()"); return; } /* final clamping */ #if 0 rgba[i][RCOMP] = MAX2( r, 0.0F ); rgba[i][GCOMP] = MAX2( g, 0.0F ); rgba[i][BCOMP] = MAX2( b, 0.0F ); rgba[i][ACOMP] = CLAMP( a, 0.0F, 1.0F ); #else ASSIGN_4V(rgba[i], r, g, b, a); #endif } } } /** * Do any blending operation, any chanType. */ static void blend_general(struct gl_context *ctx, GLuint n, const GLubyte mask[], void *src, const void *dst, GLenum chanType) { GLfloat (*rgbaF)[4], (*destF)[4]; rgbaF = (GLfloat (*)[4]) malloc(4 * n * sizeof(GLfloat)); destF = (GLfloat (*)[4]) malloc(4 * n * sizeof(GLfloat)); if (!rgbaF || !destF) { free(rgbaF); free(destF); _mesa_error(ctx, GL_OUT_OF_MEMORY, "blending"); return; } if (chanType == GL_UNSIGNED_BYTE) { GLubyte (*rgba)[4] = (GLubyte (*)[4]) src; const GLubyte (*dest)[4] = (const GLubyte (*)[4]) dst; GLuint i; /* convert ubytes to floats */ for (i = 0; i < n; i++) { if (mask[i]) { rgbaF[i][RCOMP] = UBYTE_TO_FLOAT(rgba[i][RCOMP]); rgbaF[i][GCOMP] = UBYTE_TO_FLOAT(rgba[i][GCOMP]); rgbaF[i][BCOMP] = UBYTE_TO_FLOAT(rgba[i][BCOMP]); rgbaF[i][ACOMP] = UBYTE_TO_FLOAT(rgba[i][ACOMP]); destF[i][RCOMP] = UBYTE_TO_FLOAT(dest[i][RCOMP]); destF[i][GCOMP] = UBYTE_TO_FLOAT(dest[i][GCOMP]); destF[i][BCOMP] = UBYTE_TO_FLOAT(dest[i][BCOMP]); destF[i][ACOMP] = UBYTE_TO_FLOAT(dest[i][ACOMP]); } } /* do blend */ blend_general_float(ctx, n, mask, rgbaF, destF, chanType); /* convert back to ubytes */ for (i = 0; i < n; i++) { if (mask[i]) _mesa_unclamped_float_rgba_to_ubyte(rgba[i], rgbaF[i]); } } else if (chanType == GL_UNSIGNED_SHORT) { GLushort (*rgba)[4] = (GLushort (*)[4]) src; const GLushort (*dest)[4] = (const GLushort (*)[4]) dst; GLuint i; /* convert ushorts to floats */ for (i = 0; i < n; i++) { if (mask[i]) { rgbaF[i][RCOMP] = USHORT_TO_FLOAT(rgba[i][RCOMP]); rgbaF[i][GCOMP] = USHORT_TO_FLOAT(rgba[i][GCOMP]); rgbaF[i][BCOMP] = USHORT_TO_FLOAT(rgba[i][BCOMP]); rgbaF[i][ACOMP] = USHORT_TO_FLOAT(rgba[i][ACOMP]); destF[i][RCOMP] = USHORT_TO_FLOAT(dest[i][RCOMP]); destF[i][GCOMP] = USHORT_TO_FLOAT(dest[i][GCOMP]); destF[i][BCOMP] = USHORT_TO_FLOAT(dest[i][BCOMP]); destF[i][ACOMP] = USHORT_TO_FLOAT(dest[i][ACOMP]); } } /* do blend */ blend_general_float(ctx, n, mask, rgbaF, destF, chanType); /* convert back to ushorts */ for (i = 0; i < n; i++) { if (mask[i]) { UNCLAMPED_FLOAT_TO_USHORT(rgba[i][RCOMP], rgbaF[i][RCOMP]); UNCLAMPED_FLOAT_TO_USHORT(rgba[i][GCOMP], rgbaF[i][GCOMP]); UNCLAMPED_FLOAT_TO_USHORT(rgba[i][BCOMP], rgbaF[i][BCOMP]); UNCLAMPED_FLOAT_TO_USHORT(rgba[i][ACOMP], rgbaF[i][ACOMP]); } } } else { blend_general_float(ctx, n, mask, (GLfloat (*)[4]) src, (GLfloat (*)[4]) dst, chanType); } free(rgbaF); free(destF); } /** * Analyze current blending parameters to pick fastest blending function. * Result: the ctx->Color.BlendFunc pointer is updated. */ void _swrast_choose_blend_func(struct gl_context *ctx, GLenum chanType) { SWcontext *swrast = SWRAST_CONTEXT(ctx); const GLenum eq = ctx->Color.Blend[0].EquationRGB; const GLenum srcRGB = ctx->Color.Blend[0].SrcRGB; const GLenum dstRGB = ctx->Color.Blend[0].DstRGB; const GLenum srcA = ctx->Color.Blend[0].SrcA; const GLenum dstA = ctx->Color.Blend[0].DstA; if (ctx->Color.Blend[0].EquationRGB != ctx->Color.Blend[0].EquationA) { swrast->BlendFunc = blend_general; } else if (eq == GL_MIN) { /* Note: GL_MIN ignores the blending weight factors */ #if defined(USE_MMX_ASM) if (cpu_has_mmx && chanType == GL_UNSIGNED_BYTE) { swrast->BlendFunc = _mesa_mmx_blend_min; } else #endif swrast->BlendFunc = blend_min; } else if (eq == GL_MAX) { /* Note: GL_MAX ignores the blending weight factors */ #if defined(USE_MMX_ASM) if (cpu_has_mmx && chanType == GL_UNSIGNED_BYTE) { swrast->BlendFunc = _mesa_mmx_blend_max; } else #endif swrast->BlendFunc = blend_max; } else if (srcRGB != srcA || dstRGB != dstA) { swrast->BlendFunc = blend_general; } else if (eq == GL_FUNC_ADD && srcRGB == GL_SRC_ALPHA && dstRGB == GL_ONE_MINUS_SRC_ALPHA) { #if defined(USE_MMX_ASM) if (cpu_has_mmx && chanType == GL_UNSIGNED_BYTE) { swrast->BlendFunc = _mesa_mmx_blend_transparency; } else #endif { if (chanType == GL_UNSIGNED_BYTE) swrast->BlendFunc = blend_transparency_ubyte; else if (chanType == GL_UNSIGNED_SHORT) swrast->BlendFunc = blend_transparency_ushort; else swrast->BlendFunc = blend_transparency_float; } } else if (eq == GL_FUNC_ADD && srcRGB == GL_ONE && dstRGB == GL_ONE) { #if defined(USE_MMX_ASM) if (cpu_has_mmx && chanType == GL_UNSIGNED_BYTE) { swrast->BlendFunc = _mesa_mmx_blend_add; } else #endif swrast->BlendFunc = blend_add; } else if (((eq == GL_FUNC_ADD || eq == GL_FUNC_REVERSE_SUBTRACT) && (srcRGB == GL_ZERO && dstRGB == GL_SRC_COLOR)) || ((eq == GL_FUNC_ADD || eq == GL_FUNC_SUBTRACT) && (srcRGB == GL_DST_COLOR && dstRGB == GL_ZERO))) { #if defined(USE_MMX_ASM) if (cpu_has_mmx && chanType == GL_UNSIGNED_BYTE) { swrast->BlendFunc = _mesa_mmx_blend_modulate; } else #endif swrast->BlendFunc = blend_modulate; } else if (eq == GL_FUNC_ADD && srcRGB == GL_ZERO && dstRGB == GL_ONE) { swrast->BlendFunc = blend_noop; } else if (eq == GL_FUNC_ADD && srcRGB == GL_ONE && dstRGB == GL_ZERO) { swrast->BlendFunc = blend_replace; } else { swrast->BlendFunc = blend_general; } } /** * Apply the blending operator to a span of pixels. * We can handle horizontal runs of pixels (spans) or arrays of x/y * pixel coordinates. */ void _swrast_blend_span(struct gl_context *ctx, struct gl_renderbuffer *rb, SWspan *span) { SWcontext *swrast = SWRAST_CONTEXT(ctx); void *rbPixels; ASSERT(span->end <= SWRAST_MAX_WIDTH); ASSERT(span->arrayMask & SPAN_RGBA); ASSERT(!ctx->Color.ColorLogicOpEnabled); rbPixels = _swrast_get_dest_rgba(ctx, rb, span); swrast->BlendFunc(ctx, span->end, span->array->mask, span->array->rgba, rbPixels, span->array->ChanType); }