mesa: separate legacy stuff from gl_texture_unit into gl_fixedfunc_texture_unit
[mesa.git] / src / mesa / swrast / s_blend.c
index 95c83432a9c6cf9dbb85621c160b68ca6f816b64..8479b0bc06316281725bc4891bfa3cfc27dd0974 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.1
  *
  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
  *
  * 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.
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
  */
 
 
@@ -48,9 +48,6 @@
 #if defined(USE_MMX_ASM)
 #include "x86/mmx.h"
 #include "x86/common_x86_asm.h"
-#define _BLENDAPI _ASMAPI
-#else
-#define _BLENDAPI
 #endif
 
 
  * No-op means the framebuffer values remain unchanged.
  * Any chanType ok.
  */
-static void _BLENDAPI
-blend_noop(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+blend_noop(struct gl_context *ctx, GLuint n, const GLubyte mask[],
            GLvoid *src, const GLvoid *dst, GLenum chanType)
 {
    GLint bytes;
 
-   ASSERT(ctx->Color.BlendEquationRGB == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendEquationA == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendSrcRGB == GL_ZERO);
-   ASSERT(ctx->Color.BlendDstRGB == GL_ONE);
+   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 */
@@ -89,7 +86,7 @@ blend_noop(GLcontext *ctx, GLuint n, const GLubyte mask[],
    else
       bytes = 4 * n * sizeof(GLfloat);
 
-   _mesa_memcpy(src, dst, bytes);
+   memcpy(src, dst, bytes);
 }
 
 
@@ -97,14 +94,14 @@ blend_noop(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Special case for glBlendFunc(GL_ONE, GL_ZERO)
  * Any chanType ok.
  */
-static void _BLENDAPI
-blend_replace(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+blend_replace(struct gl_context *ctx, GLuint n, const GLubyte mask[],
               GLvoid *src, const GLvoid *dst, GLenum chanType)
 {
-   ASSERT(ctx->Color.BlendEquationRGB == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendEquationA == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendSrcRGB == GL_ONE);
-   ASSERT(ctx->Color.BlendDstRGB == GL_ZERO);
+   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;
@@ -117,21 +114,21 @@ blend_replace(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Common transparency blending mode:
  * glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
  */
-static void _BLENDAPI
-blend_transparency_ubyte(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+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.BlendEquationRGB == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendEquationA == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendSrcRGB == GL_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendSrcA == GL_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendDstRGB == GL_ONE_MINUS_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendDstA == GL_ONE_MINUS_SRC_ALPHA);
-   ASSERT(chanType == GL_UNSIGNED_BYTE);
+   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;
 
@@ -148,10 +145,10 @@ blend_transparency_ubyte(GLcontext *ctx, GLuint n, const GLubyte mask[],
             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);
+            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;
@@ -162,21 +159,21 @@ blend_transparency_ubyte(GLcontext *ctx, GLuint n, const GLubyte mask[],
 }
 
 
-static void _BLENDAPI
-blend_transparency_ushort(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+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.BlendEquationRGB == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendEquationA == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendSrcRGB == GL_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendSrcA == GL_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendDstRGB == GL_ONE_MINUS_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendDstA == GL_ONE_MINUS_SRC_ALPHA);
-   ASSERT(chanType == GL_UNSIGNED_SHORT);
+   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;
 
@@ -200,21 +197,21 @@ blend_transparency_ushort(GLcontext *ctx, GLuint n, const GLubyte mask[],
 }
 
 
-static void _BLENDAPI
-blend_transparency_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+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.BlendEquationRGB == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendEquationA == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendSrcRGB == GL_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendSrcA == GL_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendDstRGB == GL_ONE_MINUS_SRC_ALPHA);
-   ASSERT(ctx->Color.BlendDstA == GL_ONE_MINUS_SRC_ALPHA);
-   ASSERT(chanType == GL_FLOAT);
+   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;
 
@@ -242,16 +239,16 @@ blend_transparency_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Add src and dest: glBlendFunc(GL_ONE, GL_ONE).
  * Any chanType ok.
  */
-static void _BLENDAPI
-blend_add(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+blend_add(struct gl_context *ctx, GLuint n, const GLubyte mask[],
           GLvoid *src, const GLvoid *dst, GLenum chanType)
 {
    GLuint i;
 
-   ASSERT(ctx->Color.BlendEquationRGB == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendEquationA == GL_FUNC_ADD);
-   ASSERT(ctx->Color.BlendSrcRGB == GL_ONE);
-   ASSERT(ctx->Color.BlendDstRGB == GL_ONE);
+   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) {
@@ -289,7 +286,7 @@ blend_add(GLcontext *ctx, GLuint n, const GLubyte mask[],
    else {
       GLfloat (*rgba)[4] = (GLfloat (*)[4]) src;
       const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst;
-      ASSERT(chanType == GL_FLOAT);
+      assert(chanType == GL_FLOAT);
       for (i=0;i<n;i++) {
          if (mask[i]) {
             /* don't RGB clamp to max */
@@ -308,13 +305,13 @@ blend_add(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Blend min function.
  * Any chanType ok.
  */
-static void _BLENDAPI
-blend_min(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+blend_min(struct gl_context *ctx, GLuint n, const GLubyte mask[],
           GLvoid *src, const GLvoid *dst, GLenum chanType)
 {
    GLuint i;
-   ASSERT(ctx->Color.BlendEquationRGB == GL_MIN);
-   ASSERT(ctx->Color.BlendEquationA == GL_MIN);
+   assert(ctx->Color.Blend[0].EquationRGB == GL_MIN);
+   assert(ctx->Color.Blend[0].EquationA == GL_MIN);
    (void) ctx;
 
    if (chanType == GL_UNSIGNED_BYTE) {
@@ -344,7 +341,7 @@ blend_min(GLcontext *ctx, GLuint n, const GLubyte mask[],
    else {
       GLfloat (*rgba)[4] = (GLfloat (*)[4]) src;
       const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst;
-      ASSERT(chanType == GL_FLOAT);
+      assert(chanType == GL_FLOAT);
       for (i=0;i<n;i++) {
          if (mask[i]) {
             rgba[i][RCOMP] = MIN2( rgba[i][RCOMP], dest[i][RCOMP] );
@@ -361,13 +358,13 @@ blend_min(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Blend max function.
  * Any chanType ok.
  */
-static void _BLENDAPI
-blend_max(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+blend_max(struct gl_context *ctx, GLuint n, const GLubyte mask[],
           GLvoid *src, const GLvoid *dst, GLenum chanType)
 {
    GLuint i;
-   ASSERT(ctx->Color.BlendEquationRGB == GL_MAX);
-   ASSERT(ctx->Color.BlendEquationA == GL_MAX);
+   assert(ctx->Color.Blend[0].EquationRGB == GL_MAX);
+   assert(ctx->Color.Blend[0].EquationA == GL_MAX);
    (void) ctx;
 
    if (chanType == GL_UNSIGNED_BYTE) {
@@ -397,7 +394,7 @@ blend_max(GLcontext *ctx, GLuint n, const GLubyte mask[],
    else {
       GLfloat (*rgba)[4] = (GLfloat (*)[4]) src;
       const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst;
-      ASSERT(chanType == GL_FLOAT);
+      assert(chanType == GL_FLOAT);
       for (i=0;i<n;i++) {
          if (mask[i]) {
             rgba[i][RCOMP] = MAX2( rgba[i][RCOMP], dest[i][RCOMP] );
@@ -415,8 +412,8 @@ blend_max(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Modulate:  result = src * dest
  * Any chanType ok.
  */
-static void _BLENDAPI
-blend_modulate(GLcontext *ctx, GLuint n, const GLubyte mask[],
+static void
+blend_modulate(struct gl_context *ctx, GLuint n, const GLubyte mask[],
                GLvoid *src, const GLvoid *dst, GLenum chanType)
 {
    GLuint i;
@@ -450,7 +447,7 @@ blend_modulate(GLcontext *ctx, GLuint n, const GLubyte mask[],
    else {
       GLfloat (*rgba)[4] = (GLfloat (*)[4]) src;
       const GLfloat (*dest)[4] = (const GLfloat (*)[4]) dst;
-      ASSERT(chanType == GL_FLOAT);
+      assert(chanType == GL_FLOAT);
       for (i=0;i<n;i++) {
          if (mask[i]) {
             rgba[i][RCOMP] = rgba[i][RCOMP] * dest[i][RCOMP];
@@ -471,7 +468,7 @@ blend_modulate(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * \param dest  array of pixels from the dest color buffer
  */
 static void
-blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
+blend_general_float(struct gl_context *ctx, GLuint n, const GLubyte mask[],
                     GLfloat rgba[][4], GLfloat dest[][4],
                     GLenum chanType)
 {
@@ -500,7 +497,7 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
           */
 
          /* Source RGB factor */
-         switch (ctx->Color.BlendSrcRGB) {
+         switch (ctx->Color.Blend[0].SrcRGB) {
             case GL_ZERO:
                sR = sG = sB = 0.0F;
                break;
@@ -570,7 +567,7 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
          }
 
          /* Source Alpha factor */
-         switch (ctx->Color.BlendSrcA) {
+         switch (ctx->Color.Blend[0].SrcA) {
             case GL_ZERO:
                sA = 0.0F;
                break;
@@ -624,7 +621,7 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
          }
 
          /* Dest RGB factor */
-         switch (ctx->Color.BlendDstRGB) {
+         switch (ctx->Color.Blend[0].DstRGB) {
             case GL_ZERO:
                dR = dG = dB = 0.0F;
                break;
@@ -687,7 +684,7 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
          }
 
          /* Dest Alpha factor */
-         switch (ctx->Color.BlendDstA) {
+         switch (ctx->Color.Blend[0].DstA) {
             case GL_ZERO:
                dA = 0.0F;
                break;
@@ -738,7 +735,7 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
          }
 
          /* compute the blended RGB */
-         switch (ctx->Color.BlendEquationRGB) {
+         switch (ctx->Color.Blend[0].EquationRGB) {
          case GL_FUNC_ADD:
             r = Rs * sR + Rd * dR;
             g = Gs * sG + Gd * dG;
@@ -775,7 +772,7 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
          }
 
          /* compute the blended alpha */
-         switch (ctx->Color.BlendEquationA) {
+         switch (ctx->Color.Blend[0].EquationA) {
          case GL_FUNC_ADD:
             a = As * sA + Ad * dA;
             break;
@@ -816,10 +813,19 @@ blend_general_float(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Do any blending operation, any chanType.
  */
 static void
-blend_general(GLcontext *ctx, GLuint n, const GLubyte mask[],
+blend_general(struct gl_context *ctx, GLuint n, const GLubyte mask[],
               void *src, const void *dst, GLenum chanType)
 {
-   GLfloat rgbaF[MAX_WIDTH][4], destF[MAX_WIDTH][4];
+   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;
@@ -842,12 +848,8 @@ blend_general(GLcontext *ctx, GLuint n, const GLubyte mask[],
       blend_general_float(ctx, n, mask, rgbaF, destF, chanType);
       /* convert back to ubytes */
       for (i = 0; i < n; i++) {
-         if (mask[i]) {
-            UNCLAMPED_FLOAT_TO_UBYTE(rgba[i][RCOMP], rgbaF[i][RCOMP]);
-            UNCLAMPED_FLOAT_TO_UBYTE(rgba[i][GCOMP], rgbaF[i][GCOMP]);
-            UNCLAMPED_FLOAT_TO_UBYTE(rgba[i][BCOMP], rgbaF[i][BCOMP]);
-            UNCLAMPED_FLOAT_TO_UBYTE(rgba[i][ACOMP], rgbaF[i][ACOMP]);
-         }
+         if (mask[i])
+          _mesa_unclamped_float_rgba_to_ubyte(rgba[i], rgbaF[i]);
       }
    }
    else if (chanType == GL_UNSIGNED_SHORT) {
@@ -883,6 +885,9 @@ blend_general(GLcontext *ctx, GLuint n, const GLubyte mask[],
       blend_general_float(ctx, n, mask, (GLfloat (*)[4]) src,
                           (GLfloat (*)[4]) dst, chanType);
    }
+
+   free(rgbaF);
+   free(destF);
 }
 
 
@@ -892,16 +897,16 @@ blend_general(GLcontext *ctx, GLuint n, const GLubyte mask[],
  * Result: the ctx->Color.BlendFunc pointer is updated.
  */
 void
-_swrast_choose_blend_func(GLcontext *ctx, GLenum chanType)
+_swrast_choose_blend_func(struct gl_context *ctx, GLenum chanType)
 {
    SWcontext *swrast = SWRAST_CONTEXT(ctx);
-   const GLenum eq = ctx->Color.BlendEquationRGB;
-   const GLenum srcRGB = ctx->Color.BlendSrcRGB;
-   const GLenum dstRGB = ctx->Color.BlendDstRGB;
-   const GLenum srcA = ctx->Color.BlendSrcA;
-   const GLenum dstA = ctx->Color.BlendDstA;
+   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.BlendEquationRGB != ctx->Color.BlendEquationA) {
+   if (ctx->Color.Blend[0].EquationRGB != ctx->Color.Blend[0].EquationA) {
       swrast->BlendFunc = blend_general;
    }
    else if (eq == GL_MIN) {
@@ -985,15 +990,14 @@ _swrast_choose_blend_func(GLcontext *ctx, GLenum chanType)
  * pixel coordinates.
  */
 void
-_swrast_blend_span(GLcontext *ctx, struct gl_renderbuffer *rb, SWspan *span)
+_swrast_blend_span(struct gl_context *ctx, struct gl_renderbuffer *rb, SWspan *span)
 {
    SWcontext *swrast = SWRAST_CONTEXT(ctx);
    void *rbPixels;
 
-   ASSERT(span->end <= MAX_WIDTH);
-   ASSERT(span->arrayMask & SPAN_RGBA);
-   ASSERT(rb->DataType == span->array->ChanType);
-   ASSERT(!ctx->Color._LogicOpEnabled);
+   assert(span->end <= SWRAST_MAX_WIDTH);
+   assert(span->arrayMask & SPAN_RGBA);
+   assert(!ctx->Color.ColorLogicOpEnabled);
 
    rbPixels = _swrast_get_dest_rgba(ctx, rb, span);