/* $Id: s_triangle.c,v 1.8 2001/01/05 02:26:49 keithw Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  3.5
 * 
 * Copyright (C) 1999-2000  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.
 */


/*
 * Triangle rasterizers
 * When the device driver doesn't implement triangle rasterization Mesa
 * will use these functions to draw triangles.
 */


#include "glheader.h"
#include "context.h"
#include "macros.h"
#include "mem.h"
#include "mmath.h"
#include "teximage.h"
#include "texstate.h"

#include "s_aatriangle.h"
#include "s_context.h"
#include "s_depth.h"
#include "s_feedback.h"
#include "s_span.h"
#include "s_triangle.h"
 
GLboolean gl_cull_triangle( GLcontext *ctx,
			    const SWvertex *v0, 
			    const SWvertex *v1, 
			    const SWvertex *v2 )
{
   GLfloat ex = v1->win[0] - v0->win[0];
   GLfloat ey = v1->win[1] - v0->win[1];
   GLfloat fx = v2->win[0] - v0->win[0];
   GLfloat fy = v2->win[1] - v0->win[1];
   GLfloat c = ex*fy-ey*fx;

   if (c * SWRAST_CONTEXT(ctx)->_backface_sign > 0)
      return 0;
   
   return 1;
}


/*
 * Render a flat-shaded color index triangle.
 */
static void flat_ci_triangle( GLcontext *ctx,
			      const SWvertex *v0, 
			      const SWvertex *v1, 
			      const SWvertex *v2 )
{
#define INTERP_Z 1

#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   const GLint n = RIGHT-LEFT;				\
	   GLint i;				                \
	   GLdepth zspan[MAX_WIDTH];				\
	   GLfixed fogspan[MAX_WIDTH];				\
	   if (n>0) {						\
	      for (i=0;i<n;i++) {				\
		 zspan[i] = FixedToDepth(ffz);			\
		 ffz += fdzdx;					\
		 fogspan[i] = fffog / 256;			\
		 fffog += fdfogdx;				\
	      }							\
	      gl_write_monoindex_span( ctx, n, LEFT, Y, zspan,	\
                         fogspan, v0->index, GL_POLYGON );	\
	   }							\
	}

#include "s_tritemp.h"	      
}



/*
 * Render a smooth-shaded color index triangle.
 */
static void smooth_ci_triangle( GLcontext *ctx,
				const SWvertex *v0, 
				const SWvertex *v1, 
				const SWvertex *v2 )
{
#define INTERP_Z 1
#define INTERP_INDEX 1

#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   const GLint n = RIGHT-LEFT;				\
	   GLint i;				                \
	   GLdepth zspan[MAX_WIDTH];				\
           GLfixed fogspan[MAX_WIDTH];				\
           GLuint index[MAX_WIDTH];				\
	   if (n>0) {						\
	      for (i=0;i<n;i++) {				\
		 zspan[i] = FixedToDepth(ffz);			\
		 ffz += fdzdx;					\
                 index[i] = FixedToInt(ffi);			\
		 ffi += fdidx;					\
		 fogspan[i] = fffog / 256;			\
		 fffog += fdfogdx;				\
	      }							\
	      gl_write_index_span( ctx, n, LEFT, Y, zspan, fogspan,	\
	                           index, GL_POLYGON );		\
	   }							\
	}

#include "s_tritemp.h"
}



/*
 * Render a flat-shaded RGBA triangle.
 */
static void flat_rgba_triangle( GLcontext *ctx,
				const SWvertex *v0,
				const SWvertex *v1,
				const SWvertex *v2 )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE

#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   const GLint n = RIGHT-LEFT;				\
	   GLint i;						\
	   GLdepth zspan[MAX_WIDTH];				\
	   GLfixed fogspan[MAX_WIDTH];				\
	   if (n>0) {						\
	      for (i=0;i<n;i++) {				\
		 zspan[i] = FixedToDepth(ffz);			\
		 ffz += fdzdx;					\
		 fogspan[i] = fffog / 256;			\
		 fffog += fdfogdx;				\
	      }							\
              gl_write_monocolor_span( ctx, n, LEFT, Y, zspan,	\
                                       fogspan, v2->color,	\
			               GL_POLYGON );		\
	   }							\
	}

#include "s_tritemp.h"

   ASSERT(!ctx->Texture._ReallyEnabled);  /* texturing must be off */
   ASSERT(ctx->Light.ShadeModel==GL_FLAT);
}



/*
 * Render a smooth-shaded RGBA triangle.
 */
static void smooth_rgba_triangle( GLcontext *ctx,
				  const SWvertex *v0,
				  const SWvertex *v1,
				  const SWvertex *v2 )
{

#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1

#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   const GLint n = RIGHT-LEFT;                          \
	   GLint i;				                \
	   GLdepth zspan[MAX_WIDTH];				\
	   GLchan rgba[MAX_WIDTH][4];				\
	   GLfixed fogspan[MAX_WIDTH];				\
	   if (n>0) {						\
	      for (i=0;i<n;i++) {				\
		 zspan[i] = FixedToDepth(ffz);			\
		 rgba[i][RCOMP] = FixedToInt(ffr);		\
		 rgba[i][GCOMP] = FixedToInt(ffg);		\
		 rgba[i][BCOMP] = FixedToInt(ffb);		\
		 rgba[i][ACOMP] = FixedToInt(ffa);		\
		 fogspan[i] = fffog / 256;			\
		 fffog += fdfogdx;				\
		 ffz += fdzdx;					\
		 ffr += fdrdx;					\
		 ffg += fdgdx;					\
		 ffb += fdbdx;					\
		 ffa += fdadx;					\
	      }							\
	      gl_write_rgba_span( ctx, n, LEFT, Y,		\
                                  (CONST GLdepth *) zspan,	\
                                  fogspan,                      \
	                          rgba, GL_POLYGON );		\
	   }							\
	}

#include "s_tritemp.h"

   ASSERT(!ctx->Texture._ReallyEnabled);  /* texturing must be off */
   ASSERT(ctx->Light.ShadeModel==GL_SMOOTH);
}


/*
 * Render an RGB, GL_DECAL, textured triangle.
 * Interpolate S,T only w/out mipmapping or perspective correction.
 *
 * No fog.
 */
static void simple_textured_triangle( GLcontext *ctx,
				      const SWvertex *v0,
				      const SWvertex *v1,
				      const SWvertex *v2 )
{
#define INTERP_INT_TEX 1
#define S_SCALE twidth
#define T_SCALE theight
#define SETUP_CODE							\
   struct gl_texture_object *obj = ctx->Texture.Unit[0].Current2D;	\
   GLint b = obj->BaseLevel;						\
   GLfloat twidth = (GLfloat) obj->Image[b]->Width;			\
   GLfloat theight = (GLfloat) obj->Image[b]->Height;			\
   GLint twidth_log2 = obj->Image[b]->WidthLog2;			\
   GLchan *texture = obj->Image[b]->Data;				\
   GLint smask = obj->Image[b]->Width - 1;				\
   GLint tmask = obj->Image[b]->Height - 1;				\
   if (!texture) {							\
      if (!_mesa_get_teximages_from_driver(ctx, obj))			\
         return;							\
      texture = obj->Image[b]->Data;					\
      ASSERT(texture);							\
   }

#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   CONST GLint n = RIGHT-LEFT;				\
	   GLint i;				                \
	   GLchan rgb[MAX_WIDTH][3];				\
	   if (n>0) {						\
              ffs -= FIXED_HALF; /* off-by-one error? */        \
              fft -= FIXED_HALF;                                \
	      for (i=0;i<n;i++) {				\
                 GLint s = FixedToInt(ffs) & smask;		\
                 GLint t = FixedToInt(fft) & tmask;		\
                 GLint pos = (t << twidth_log2) + s;		\
                 pos = pos + pos + pos;  /* multiply by 3 */	\
                 rgb[i][RCOMP] = texture[pos];			\
                 rgb[i][GCOMP] = texture[pos+1];		\
                 rgb[i][BCOMP] = texture[pos+2];		\
		 ffs += fdsdx;					\
		 fft += fdtdx;					\
	      }							\
              (*ctx->Driver.WriteRGBSpan)( ctx, n, LEFT, Y,	\
                           (CONST GLchan (*)[3]) rgb, NULL );	\
	   }							\
	}

#include "s_tritemp.h"
}


/*
 * Render an RGB, GL_DECAL, textured triangle.
 * Interpolate S,T, GL_LESS depth test, w/out mipmapping or
 * perspective correction.
 * 
 * No fog.
 */
static void simple_z_textured_triangle( GLcontext *ctx,
					const SWvertex *v0,
					const SWvertex *v1,
					const SWvertex *v2 )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_INT_TEX 1
#define S_SCALE twidth
#define T_SCALE theight
#define SETUP_CODE							\
   struct gl_texture_object *obj = ctx->Texture.Unit[0].Current2D;	\
   GLint b = obj->BaseLevel;						\
   GLfloat twidth = (GLfloat) obj->Image[b]->Width;			\
   GLfloat theight = (GLfloat) obj->Image[b]->Height;			\
   GLint twidth_log2 = obj->Image[b]->WidthLog2;			\
   GLchan *texture = obj->Image[b]->Data;				\
   GLint smask = obj->Image[b]->Width - 1;				\
   GLint tmask = obj->Image[b]->Height - 1;				\
   if (!texture) {							\
      if (!_mesa_get_teximages_from_driver(ctx, obj))			\
         return;							\
      texture = obj->Image[b]->Data;					\
      ASSERT(texture);							\
   }

#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   CONST GLint n = RIGHT-LEFT;                          \
	   GLint i;				                \
	   GLchan rgb[MAX_WIDTH][3];				\
           GLubyte mask[MAX_WIDTH];				\
           (void) fffog;                                        \
	   if (n>0) {						\
              ffs -= FIXED_HALF; /* off-by-one error? */        \
              fft -= FIXED_HALF;                                \
	      for (i=0;i<n;i++) {				\
                 GLdepth z = FixedToDepth(ffz);			\
                 if (z < zRow[i]) {				\
                    GLint s = FixedToInt(ffs) & smask;		\
                    GLint t = FixedToInt(fft) & tmask;		\
                    GLint pos = (t << twidth_log2) + s;		\
                    pos = pos + pos + pos;  /* multiply by 3 */	\
                    rgb[i][RCOMP] = texture[pos];		\
                    rgb[i][GCOMP] = texture[pos+1];		\
                    rgb[i][BCOMP] = texture[pos+2];		\
		    zRow[i] = z;				\
                    mask[i] = 1;				\
                 }						\
                 else {						\
                    mask[i] = 0;				\
                 }						\
		 ffz += fdzdx;					\
		 ffs += fdsdx;					\
		 fft += fdtdx;					\
	      }							\
              (*ctx->Driver.WriteRGBSpan)( ctx, n, LEFT, Y,	\
                           (CONST GLchan (*)[3]) rgb, mask );	\
	   }							\
	}

#include "s_tritemp.h"
}



/*
 * Render an RGB/RGBA textured triangle without perspective correction.
 */
static void affine_textured_triangle( GLcontext *ctx,
				      const SWvertex *v0,
				      const SWvertex *v1,
				      const SWvertex *v2 )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_INT_TEX 1
#define S_SCALE twidth
#define T_SCALE theight
#define SETUP_CODE							\
   struct gl_texture_unit *unit = ctx->Texture.Unit+0;			\
   struct gl_texture_object *obj = unit->Current2D;			\
   GLint b = obj->BaseLevel;						\
   GLfloat twidth = (GLfloat) obj->Image[b]->Width;			\
   GLfloat theight = (GLfloat) obj->Image[b]->Height;			\
   GLint twidth_log2 = obj->Image[b]->WidthLog2;			\
   GLchan *texture = obj->Image[b]->Data;				\
   GLint smask = obj->Image[b]->Width - 1;				\
   GLint tmask = obj->Image[b]->Height - 1;                             \
   GLint format = obj->Image[b]->Format;                                \
   GLint filter = obj->MinFilter;                                       \
   GLint envmode = unit->EnvMode;                                       \
   GLint comp, tbytesline, tsize;                                       \
   GLfixed er, eg, eb, ea;                                              \
   GLint tr, tg, tb, ta;                                                \
   if (!texture) {							\
      if (!_mesa_get_teximages_from_driver(ctx, obj))			\
         return;							\
      texture = obj->Image[b]->Data;					\
      ASSERT(texture);							\
   }									\
   if (envmode == GL_BLEND || envmode == GL_ADD) {                      \
      /* potential off-by-one error here? (1.0f -> 2048 -> 0) */        \
      er = FloatToFixed(unit->EnvColor[0]);                             \
      eg = FloatToFixed(unit->EnvColor[1]);                             \
      eb = FloatToFixed(unit->EnvColor[2]);                             \
      ea = FloatToFixed(unit->EnvColor[3]);                             \
   }                                                                    \
   switch (format) {                                                    \
   case GL_ALPHA:                                                       \
   case GL_LUMINANCE:                                                   \
   case GL_INTENSITY:                                                   \
      comp = 1;                                                         \
      break;                                                            \
   case GL_LUMINANCE_ALPHA:                                             \
      comp = 2;                                                         \
      break;                                                            \
   case GL_RGB:                                                         \
      comp = 3;                                                         \
      break;                                                            \
   case GL_RGBA:                                                        \
      comp = 4;                                                         \
      break;                                                            \
   default:                                                             \
      gl_problem(NULL, "Bad texture format in affine_texture_triangle");\
      return;                                                           \
   }                                                                    \
   tbytesline = obj->Image[b]->Width * comp;                            \
   tsize = theight * tbytesline;


  /* Instead of defining a function for each mode, a test is done 
   * between the outer and inner loops. This is to reduce code size
   * and complexity. Observe that an optimizing compiler kills 
   * unused variables (for instance tf,sf,ti,si in case of GL_NEAREST).
   */ 

#define NEAREST_RGB    \
        tr = tex00[0]; \
        tg = tex00[1]; \
        tb = tex00[2]; \
        ta = 0xff

#define LINEAR_RGB                                                      \
	tr = (ti * (si * tex00[0] + sf * tex01[0]) +                    \
              tf * (si * tex10[0] + sf * tex11[0])) >> 2 * FIXED_SHIFT; \
	tg = (ti * (si * tex00[1] + sf * tex01[1]) +                    \
              tf * (si * tex10[1] + sf * tex11[1])) >> 2 * FIXED_SHIFT; \
	tb = (ti * (si * tex00[2] + sf * tex01[2]) +                    \
              tf * (si * tex10[2] + sf * tex11[2])) >> 2 * FIXED_SHIFT; \
	ta = 0xff

#define NEAREST_RGBA   \
        tr = tex00[0]; \
        tg = tex00[1]; \
        tb = tex00[2]; \
        ta = tex00[3]

#define LINEAR_RGBA                                                     \
	tr = (ti * (si * tex00[0] + sf * tex01[0]) +                    \
              tf * (si * tex10[0] + sf * tex11[0])) >> 2 * FIXED_SHIFT; \
	tg = (ti * (si * tex00[1] + sf * tex01[1]) +                    \
              tf * (si * tex10[1] + sf * tex11[1])) >> 2 * FIXED_SHIFT; \
	tb = (ti * (si * tex00[2] + sf * tex01[2]) +                    \
              tf * (si * tex10[2] + sf * tex11[2])) >> 2 * FIXED_SHIFT; \
	ta = (ti * (si * tex00[3] + sf * tex01[3]) +                    \
              tf * (si * tex10[3] + sf * tex11[3])) >> 2 * FIXED_SHIFT

#define MODULATE                                       \
        dest[0] = ffr * (tr + 1) >> (FIXED_SHIFT + 8); \
        dest[1] = ffg * (tg + 1) >> (FIXED_SHIFT + 8); \
        dest[2] = ffb * (tb + 1) >> (FIXED_SHIFT + 8); \
        dest[3] = ffa * (ta + 1) >> (FIXED_SHIFT + 8)

#define DECAL                                                                \
	dest[0] = ((0xff - ta) * ffr + ((ta + 1) * tr << FIXED_SHIFT)) >> (FIXED_SHIFT + 8); \
	dest[1] = ((0xff - ta) * ffg + ((ta + 1) * tg << FIXED_SHIFT)) >> (FIXED_SHIFT + 8); \
	dest[2] = ((0xff - ta) * ffb + ((ta + 1) * tb << FIXED_SHIFT)) >> (FIXED_SHIFT + 8); \
	dest[3] = FixedToInt(ffa)

#define BLEND                                                               \
        dest[0] = ((0xff - tr) * ffr + (tr + 1) * er) >> (FIXED_SHIFT + 8); \
        dest[1] = ((0xff - tg) * ffg + (tg + 1) * eg) >> (FIXED_SHIFT + 8); \
        dest[2] = ((0xff - tb) * ffb + (tb + 1) * eb) >> (FIXED_SHIFT + 8); \
        dest[3] = ffa * (ta + 1) >> (FIXED_SHIFT + 8)

#define REPLACE       \
        dest[0] = tr; \
        dest[1] = tg; \
        dest[2] = tb; \
        dest[3] = ta

#define ADD                                                          \
        dest[0] = ((ffr << 8) + (tr + 1) * er) >> (FIXED_SHIFT + 8); \
        dest[1] = ((ffg << 8) + (tg + 1) * eg) >> (FIXED_SHIFT + 8); \
        dest[2] = ((ffb << 8) + (tb + 1) * eb) >> (FIXED_SHIFT + 8); \
        dest[3] = ffa * (ta + 1) >> (FIXED_SHIFT + 8)

/* shortcuts */

#define NEAREST_RGB_REPLACE  NEAREST_RGB;REPLACE

#define NEAREST_RGBA_REPLACE  *(GLint *)dest = *(GLint *)tex00

#define SPAN1(DO_TEX,COMP)                                 \
	for (i=0;i<n;i++) {                                \
           GLint s = FixedToInt(ffs) & smask;              \
           GLint t = FixedToInt(fft) & tmask;              \
           GLint pos = (t << twidth_log2) + s;             \
           GLchan *tex00 = texture + COMP * pos;           \
	   zspan[i] = FixedToDepth(ffz);                   \
	   fogspan[i] = fffog / 256;                       \
           DO_TEX;                                         \
	   fffog += fdfogdx;                               \
	   ffz += fdzdx;                                   \
           ffr += fdrdx;                                   \
	   ffg += fdgdx;                                   \
           ffb += fdbdx;                                   \
	   ffa += fdadx;                                   \
	   ffs += fdsdx;                                   \
	   fft += fdtdx;                                   \
           dest += 4;                                      \
	}

#define SPAN2(DO_TEX,COMP)                                 \
	for (i=0;i<n;i++) {                                \
           GLint s = FixedToInt(ffs) & smask;              \
           GLint t = FixedToInt(fft) & tmask;              \
           GLint sf = ffs & FIXED_FRAC_MASK;               \
           GLint tf = fft & FIXED_FRAC_MASK;               \
           GLint si = FIXED_FRAC_MASK - sf;                \
           GLint ti = FIXED_FRAC_MASK - tf;                \
           GLint pos = (t << twidth_log2) + s;             \
           GLchan *tex00 = texture + COMP * pos;           \
           GLchan *tex10 = tex00 + tbytesline;             \
           GLchan *tex01 = tex00 + COMP;                   \
           GLchan *tex11 = tex10 + COMP;                   \
           if (t == tmask) {                               \
              tex10 -= tsize;                              \
              tex11 -= tsize;                              \
           }                                               \
           if (s == smask) {                               \
              tex01 -= tbytesline;                         \
              tex11 -= tbytesline;                         \
           }                                               \
	   zspan[i] = FixedToDepth(ffz);                   \
	   fogspan[i] = fffog / 256;                       \
           DO_TEX;                                         \
	   fffog += fdfogdx;                               \
	   ffz += fdzdx;                                   \
           ffr += fdrdx;                                   \
	   ffg += fdgdx;                                   \
           ffb += fdbdx;                                   \
	   ffa += fdadx;                                   \
	   ffs += fdsdx;                                   \
	   fft += fdtdx;                                   \
           dest += 4;                                      \
	}

/* here comes the heavy part.. (something for the compiler to chew on) */
#define INNER_LOOP( LEFT, RIGHT, Y )	                   \
	{				                   \
           CONST GLint n = RIGHT-LEFT;	                   \
	   GLint i;                                        \
	   GLdepth zspan[MAX_WIDTH];	                   \
	   GLfixed fogspan[MAX_WIDTH];	                   \
	   GLchan rgba[MAX_WIDTH][4];                      \
	   if (n>0) {                                      \
              GLchan *dest = rgba[0];                      \
              ffs -= FIXED_HALF; /* off-by-one error? */   \
              fft -= FIXED_HALF;                           \
              switch (filter) {                            \
   	      case GL_NEAREST:                             \
		 switch (format) {                         \
                 case GL_RGB:                              \
	            switch (envmode) {                     \
	            case GL_MODULATE:                      \
                       SPAN1(NEAREST_RGB;MODULATE,3);      \
                       break;                              \
	            case GL_DECAL:                         \
                    case GL_REPLACE:                       \
                       SPAN1(NEAREST_RGB_REPLACE,3);       \
                       break;                              \
                    case GL_BLEND:                         \
                       SPAN1(NEAREST_RGB;BLEND,3);         \
                       break;                              \
		    case GL_ADD:                           \
		       SPAN1(NEAREST_RGB;ADD,3);           \
		       break;                              \
                    default: /* unexpected env mode */     \
                       abort();                            \
	            }                                      \
                    break;                                 \
		 case GL_RGBA:                             \
		    switch(envmode) {                      \
		    case GL_MODULATE:                      \
                       SPAN1(NEAREST_RGBA;MODULATE,4);     \
                       break;                              \
		    case GL_DECAL:                         \
                       SPAN1(NEAREST_RGBA;DECAL,4);        \
                       break;                              \
		    case GL_BLEND:                         \
                       SPAN1(NEAREST_RGBA;BLEND,4);        \
                       break;                              \
		    case GL_ADD:                           \
		       SPAN1(NEAREST_RGBA;ADD,4);          \
		       break;                              \
		    case GL_REPLACE:                       \
                       SPAN1(NEAREST_RGBA_REPLACE,4);      \
                       break;                              \
                    default: /* unexpected env mode */     \
                       abort();                            \
		    }                                      \
                    break;                                 \
	         }                                         \
                 break;                                    \
	      case GL_LINEAR:                              \
                 ffs -= FIXED_HALF;                        \
                 fft -= FIXED_HALF;                        \
		 switch (format) {                         \
		 case GL_RGB:                              \
		    switch (envmode) {                     \
		    case GL_MODULATE:                      \
		       SPAN2(LINEAR_RGB;MODULATE,3);       \
                       break;                              \
		    case GL_DECAL:                         \
		    case GL_REPLACE:                       \
                       SPAN2(LINEAR_RGB;REPLACE,3);        \
                       break;                              \
		    case GL_BLEND:                         \
		       SPAN2(LINEAR_RGB;BLEND,3);          \
		       break;                              \
		    case GL_ADD:                           \
		       SPAN2(LINEAR_RGB;ADD,3);            \
		       break;                              \
                    default: /* unexpected env mode */     \
                       abort();                            \
		    }                                      \
		    break;                                 \
		 case GL_RGBA:                             \
		    switch (envmode) {                     \
		    case GL_MODULATE:                      \
		       SPAN2(LINEAR_RGBA;MODULATE,4);      \
		       break;                              \
		    case GL_DECAL:                         \
		       SPAN2(LINEAR_RGBA;DECAL,4);         \
		       break;                              \
		    case GL_BLEND:                         \
		       SPAN2(LINEAR_RGBA;BLEND,4);         \
		       break;                              \
		    case GL_ADD:                           \
		       SPAN2(LINEAR_RGBA;ADD,4);           \
		       break;                              \
		    case GL_REPLACE:                       \
		       SPAN2(LINEAR_RGBA;REPLACE,4);       \
		       break;                              \
                    default: /* unexpected env mode */     \
                       abort();                            \
		    }                                      \
		    break;                                 \
	         }                                         \
                 break;                                    \
	      }                                            \
              gl_write_rgba_span(ctx, n, LEFT, Y, zspan,   \
                                 fogspan,                  \
                                 rgba, GL_POLYGON);        \
              /* explicit kill of variables: */            \
              ffr = ffg = ffb = ffa = 0;                   \
           }                                               \
	}

#include "s_tritemp.h"
#undef SPAN1
#undef SPAN2
}



/*
 * Render an perspective corrected RGB/RGBA textured triangle.
 * The Q (aka V in Mesa) coordinate must be zero such that the divide
 * by interpolated Q/W comes out right.
 *
 * This function only renders textured triangles that use GL_NEAREST.
 * Perspective correction works right.
 *
 * This function written by Klaus Niederkrueger <klaus@math.leidenuniv.nl>
 * Send all questions and bug reports to him.
 */
#if 0 /* XXX disabled because of texcoord interpolation errors */
static void near_persp_textured_triangle(GLcontext *ctx,
					 const SWvertex *v0,
					 const SWvertex *v1,
					 const SWvertex *v2 )
{
/* The BIAS value is used to shift negative values into positive values.
 * Without this, negative texture values don't GL_REPEAT correctly at just
 * below zero, because (int)-0.5 = 0 = (int)0.5. We're not going to worry
 * about texture coords less than -BIAS. This could be fixed by using 
 * FLOORF etc. instead, but this is slower...
 */
#define BIAS 4096.0F

#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_TEX 1
#define SETUP_CODE							\
   struct gl_texture_unit *unit = ctx->Texture.Unit+0;			\
   struct gl_texture_object *obj = unit->Current2D;			\
   const GLint b = obj->BaseLevel;					\
   const GLfloat twidth = (GLfloat) obj->Image[b]->Width;	      	\
   const GLfloat theight = (GLfloat) obj->Image[b]->Height;		\
   const GLint twidth_log2 = obj->Image[b]->WidthLog2;			\
   GLchan *texture = obj->Image[b]->Data;				\
   const GLint smask = (obj->Image[b]->Width - 1);                      \
   const GLint tmask = (obj->Image[b]->Height - 1);                     \
   const GLint format = obj->Image[b]->Format;                          \
   const GLint envmode = unit->EnvMode;                                 \
   GLfloat sscale, tscale;                                              \
   GLfixed er, eg, eb, ea;                                              \
   GLint tr, tg, tb, ta;                                                \
   if (!texture) {							\
      if (!_mesa_get_teximages_from_driver(ctx, obj))			\
         return;							\
      texture = obj->Image[b]->Data;					\
      ASSERT(texture);							\
   }									\
   if (envmode == GL_BLEND || envmode == GL_ADD) {                      \
      er = FloatToFixed(unit->EnvColor[0]);                             \
      eg = FloatToFixed(unit->EnvColor[1]);                             \
      eb = FloatToFixed(unit->EnvColor[2]);                             \
      ea = FloatToFixed(unit->EnvColor[3]);                             \
   }                                                                    \
   sscale = twidth;                                                     \
   tscale = theight;                                                    \


#define OLD_SPAN(DO_TEX,COMP)                         \
   for (i=0;i<n;i++) {                                \
      GLfloat invQ = 1.0f / vv;                       \
      GLint s = (int)(SS * invQ + BIAS) & smask;      \
      GLint t = (int)(TT * invQ + BIAS) & tmask;      \
      GLint pos = COMP * ((t << twidth_log2) + s);    \
      GLchan *tex00 = texture + pos;                  \
      zspan[i] = FixedToDepth(ffz);                   \
      fogspan[i] = fffog / 256;                       \
      DO_TEX;                                         \
      fffog += fdfogdx;                               \
      ffz += fdzdx;                                   \
      ffr += fdrdx;                                   \
      ffg += fdgdx;                                   \
      ffb += fdbdx;                                   \
      ffa += fdadx;                                   \
      SS += dSdx;                                     \
      TT += dTdx;                                     \
      vv += dvdx;                                     \
      dest += 4;                                      \
   }

#define X_Y_TEX_COORD(X, Y) ((((int)(X) & tmask) << twidth_log2) + ((int)(Y) & smask))
#define Y_X_TEX_COORD(X, Y) ((((int)(Y) & tmask) << twidth_log2) + ((int)(X) & smask))

#define SPAN1(DO_TEX, COMP, TEX_COORD) {                          \
   GLfloat x_max = CEILF(x_tex);                                  \
   GLfloat y_max = y_tex + (x_max - x_tex) * dy_dx;               \
   GLint j, x_m = (int)x_max;                                     \
   GLint pos;                                                     \
   if ((int)y_max != (int)y_tex) {                                \
      GLfloat x_mid = x_tex + (CEILF(y_tex)-y_tex) * dx_dy;       \
      j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);    \
      pos = COMP * TEX_COORD(x_tex, y_tex);                       \
      DRAW_LINE (DO_TEX);                                         \
      y_tex = y_max;                                              \
   }                                                              \
   nominator += vv * x_max;                                       \
   denominator -= dvdx * x_max;                                   \
   j = nominator / denominator;                                   \
   pos = COMP * TEX_COORD(x_tex, y_tex);                          \
   DRAW_LINE (DO_TEX);                                            \
   while (i<n)  {                                                 \
      y_tex = y_max;                                              \
      y_max += dy_dx;                                             \
      if ((int)y_max != (int)y_tex) {                             \
         GLfloat x_mid = (CEILF(y_tex)-y_tex) * dx_dy;            \
         j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid); \
         pos = COMP * TEX_COORD(x_m, y_tex);                      \
         DRAW_LINE (DO_TEX);                                      \
         y_tex = y_max;                                           \
      }                                                           \
      nominator += vv;                                            \
      denominator -= dvdx;                                        \
      j = nominator/denominator;                                  \
      pos = COMP * TEX_COORD(x_m, y_tex);                         \
      DRAW_LINE (DO_TEX);                                         \
      x_m ++;                                                     \
   }                                                              \
}

#define SPAN2(DO_TEX, COMP, TEX_COORD)  {			\
   GLfloat x_max = CEILF (x_tex);				\
   GLfloat y_max = y_tex + (x_max - x_tex) * dy_dx;		\
   GLint j, x_m = (int) x_max;					\
   GLint pos;							\
   if ((int)y_max != (int)y_tex) {				\
      GLfloat x_mid = x_tex + (FLOORF(y_tex)-y_tex) * dx_dy;	\
      j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);	\
      pos = COMP * TEX_COORD(x_tex, y_tex);			\
      DRAW_LINE (DO_TEX);					\
      y_tex = y_max;						\
   }								\
   nominator += vv * x_max;					\
   denominator -= dvdx * x_max;					\
   j = nominator / denominator;					\
   pos = COMP * TEX_COORD(x_tex, y_tex);			\
   DRAW_LINE (DO_TEX);						\
   while (i<n)  {						\
      y_tex = y_max;						\
      y_max += dy_dx;						\
      if ((int)y_max != (int)y_tex) {				\
         GLfloat x_mid = (FLOORF(y_tex)-y_tex) * dx_dy;		\
         j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);\
         pos = COMP * TEX_COORD(x_m, y_tex);			\
         DRAW_LINE (DO_TEX);					\
         y_tex = y_max;						\
      }								\
      nominator += vv;						\
      denominator -= dvdx;					\
      j = nominator/denominator;				\
      pos = COMP * TEX_COORD(x_m, y_tex);			\
      DRAW_LINE (DO_TEX);					\
      x_m ++;							\
   }								\
} 

#define SPAN3(DO_TEX, COMP, TEX_COORD) {				\
   GLfloat x_min = FLOORF (x_tex);					\
   GLfloat y_min = y_tex + (x_min - x_tex) * dy_dx;			\
   GLint j, x_m = (int)x_min;						\
   GLint pos;								\
   if ((int)y_min != (int)y_tex) {					\
      GLfloat x_mid = x_tex + (CEILF(y_tex)-y_tex) * dx_dy;		\
      j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);		\
      pos = COMP * TEX_COORD(x_m, y_tex);				\
      DRAW_LINE (DO_TEX);						\
      y_tex = y_min;							\
   }									\
   nominator += vv*x_min;						\
   denominator -= dvdx*x_min;						\
   j = nominator / denominator;						\
   pos = COMP * TEX_COORD(x_m, y_tex);					\
   DRAW_LINE (DO_TEX);							\
   while (i<n) {							\
      x_m --;								\
      y_tex = y_min;							\
      y_min -= dy_dx;							\
      if ((int)y_min != (int)y_tex) {					\
         GLfloat x_mid = (CEILF(y_tex)-y_tex) * dx_dy;			\
         j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);	\
         pos = COMP * TEX_COORD(x_m, y_tex);				\
         DRAW_LINE (DO_TEX);						\
         y_tex = y_min;							\
      }									\
      nominator -= vv;							\
      denominator += dvdx;						\
      j = nominator/denominator;					\
      pos = COMP * TEX_COORD(x_m, y_tex);				\
      DRAW_LINE (DO_TEX);						\
   }									\
}
					
#define SPAN4(DO_TEX, COMP, TEX_COORD)					\
{									\
   GLfloat x_min = FLOORF(x_tex);					\
   GLint x_m = (int)x_min;						\
   GLfloat y_min = y_tex + (x_min - x_tex) * dy_dx;			\
   GLint j;								\
   GLint pos;								\
   if ((int)y_min != (int)y_tex) {					\
      GLfloat x_mid = x_tex + (FLOORF(y_tex)-y_tex) * dx_dy;		\
      j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);		\
      pos = COMP * TEX_COORD(x_m, y_tex);				\
      DRAW_LINE (DO_TEX);						\
      y_tex = y_min;							\
   }									\
   nominator += vv * x_min;						\
   denominator -= dvdx * x_min;						\
   j = nominator / denominator;						\
   pos = COMP * TEX_COORD(x_m, y_tex);					\
   DRAW_LINE (DO_TEX);							\
   while (i<n)  {							\
      x_m --;								\
      y_tex = y_min;							\
      y_min -= dy_dx;							\
      if ((int)y_min != (int)y_tex) {					\
         GLfloat x_mid = (FLOORF(y_tex)-y_tex) * dx_dy;			\
         j = (nominator + vv * x_mid)/(denominator - dvdx*x_mid);	\
         pos = COMP * TEX_COORD(x_m, (y_tex));				\
         DRAW_LINE (DO_TEX);						\
         y_tex = y_min;							\
      }									\
      nominator -= vv;							\
      denominator += dvdx;						\
      j = nominator/denominator;					\
      pos = COMP * TEX_COORD(x_m, y_tex);				\
      DRAW_LINE (DO_TEX);						\
   }									\
}

#define DRAW_LINE(DO_TEX)		\
   {					\
      GLchan *tex00 = texture + pos;	\
      if (j>n || j<-100000)		\
         j = n;				\
      while (i<j) {			\
         zspan[i] = FixedToDepth(ffz);	\
         fogspan[i] = fffog / 256;      \
         DO_TEX;			\
         fffog += fdfogdx;              \
         ffz += fdzdx;			\
         ffr += fdrdx;			\
         ffg += fdgdx;			\
         ffb += fdbdx;			\
         ffa += fdadx;			\
         dest += 4;			\
         i++;				\
      }					\
   }

#define INNER_LOOP( LEFT, RIGHT, Y )					\
   {									\
      GLint i = 0;							\
      const GLint n = RIGHT-LEFT;					\
      GLdepth zspan[MAX_WIDTH];						\
      GLfixed fogspan[MAX_WIDTH];					\
      GLchan rgba[MAX_WIDTH][4];					\
      (void)uu; /* please GCC */					\
      if (n > 0) {							\
         GLchan *dest = rgba[0];					\
         GLfloat SS = ss * sscale;					\
         GLfloat TT = tt * tscale;					\
         GLfloat dSdx = dsdx * sscale;					\
         GLfloat dTdx = dtdx * tscale;					\
         GLfloat x_tex;					                \
         GLfloat y_tex;					                \
         GLfloat dx_tex;	                                        \
         GLfloat dy_tex;	                                        \
         if (n<5) /* When line very short, setup-time > speed-gain. */	\
            goto old_span;  /* So: take old method */			\
         x_tex = SS / vv,					        \
            y_tex = TT / vv;					        \
         dx_tex = (SS + n * dSdx) / (vv + n * dvdx) - x_tex,	        \
            dy_tex = (TT + n * dTdx) / (vv + n * dvdx) - y_tex;	        \
	 /* Choose between walking over texture or over pixelline: */	\
	 /* If there are few texels, walk over texture otherwise   */	\
	 /* walk over pixelarray. The quotient on the right side   */	\
	 /* should give the timeratio needed to draw one texel in  */	\
	 /* comparison to one pixel. Depends on CPU.	 	   */	\
         if (dx_tex*dx_tex + dy_tex*dy_tex < (n*n)/16) {		\
            x_tex += BIAS;						\
            y_tex += BIAS;						\
            if (dx_tex*dx_tex > dy_tex*dy_tex) {			\
	    /* if (FABSF(dx_tex) > FABSF(dy_tex)) */			\
               GLfloat nominator = - SS - vv * BIAS;			\
               GLfloat denominator = dvdx * BIAS + dSdx;		\
               GLfloat dy_dx;						\
               GLfloat dx_dy;						\
               if (dy_tex != 0.0f) {					\
                  dy_dx = dy_tex / dx_tex;                              \
                  dx_dy = 1.0f/dy_dx;					\
               }                                                        \
               else                                                     \
                  dy_dx = 0.0f;                                         \
               if (dx_tex > 0.0f) {					\
                  if (dy_tex > 0.0f) {					\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN1(NEAREST_RGB;MODULATE,3, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                        case GL_REPLACE:				\
                           SPAN1(NEAREST_RGB_REPLACE,3, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN1(NEAREST_RGB;BLEND,3, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN1(NEAREST_RGB;ADD,3, Y_X_TEX_COORD);     \
		           break;                                       \
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                        case GL_MODULATE:				\
                           SPAN1(NEAREST_RGBA;MODULATE,4, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN1(NEAREST_RGBA;DECAL,4, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN1(NEAREST_RGBA;BLEND,4, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN1(NEAREST_RGBA;ADD,4, Y_X_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN1(NEAREST_RGBA_REPLACE,4, Y_X_TEX_COORD);\
                           break;					\
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
                  else {  /* dy_tex <= 0.0f */				\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN2(NEAREST_RGB;MODULATE,3, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                        case GL_REPLACE:				\
                           SPAN2(NEAREST_RGB_REPLACE,3, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN2(NEAREST_RGB;BLEND,3, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN2(NEAREST_RGB;ADD,3, Y_X_TEX_COORD);     \
		           break;                                       \
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                        case GL_MODULATE:				\
                           SPAN2(NEAREST_RGBA;MODULATE,4, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN2(NEAREST_RGBA;DECAL,4, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN2(NEAREST_RGBA;BLEND,4, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN2(NEAREST_RGBA;ADD,4, Y_X_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN2(NEAREST_RGBA_REPLACE,4, Y_X_TEX_COORD);\
                           break;					\
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
               }							\
               else { /* dx_tex < 0.0f */				\
                  if (dy_tex > 0.0f) {					\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN3(NEAREST_RGB;MODULATE,3, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                        case GL_REPLACE:				\
                           SPAN3(NEAREST_RGB_REPLACE,3, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN3(NEAREST_RGB;BLEND,3, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN3(NEAREST_RGB;ADD,3, Y_X_TEX_COORD);     \
		           break;                                       \
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                        case GL_MODULATE:				\
                           SPAN3(NEAREST_RGBA;MODULATE,4, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN3(NEAREST_RGBA;DECAL,4, Y_X_TEX_COORD);	\
                           break;					\
                          case GL_BLEND:				\
                           SPAN3(NEAREST_RGBA;BLEND,4, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN3(NEAREST_RGBA;ADD,4, Y_X_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN3(NEAREST_RGBA_REPLACE,4, Y_X_TEX_COORD);\
                           break;					\
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
                  else {  /* dy_tex <= 0.0f */				\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN4(NEAREST_RGB;MODULATE,3, Y_X_TEX_COORD);\
                           break;					\
                          case GL_DECAL:				\
                        case GL_REPLACE:				\
                           SPAN4(NEAREST_RGB_REPLACE,3, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN4(NEAREST_RGB;BLEND,3, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN4(NEAREST_RGB;ADD,3, Y_X_TEX_COORD);     \
		           break;                                       \
                        default:					\
                           abort();					\
                        }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                            case GL_MODULATE:				\
                           SPAN4(NEAREST_RGBA;MODULATE,4, Y_X_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN4(NEAREST_RGBA;DECAL,4, Y_X_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN4(NEAREST_RGBA;BLEND,4, Y_X_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN4(NEAREST_RGBA;ADD,4, Y_X_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN4(NEAREST_RGBA_REPLACE,4, Y_X_TEX_COORD);\
                           break;					\
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
               }							\
            }								\
            else {  /* FABSF(dx_tex) > FABSF(dy_tex) */		        \
               GLfloat swap;						\
               GLfloat dy_dx;						\
               GLfloat dx_dy;						\
               GLfloat nominator, denominator;				\
               if (dx_tex == 0.0f /* &&  dy_tex == 0.0f*/)		\
                  goto old_span; /* case so special, that use old */    \
               /* swap some x-values and y-values */			\
               SS = TT;							\
               dSdx = dTdx;						\
               swap = x_tex, x_tex = y_tex, y_tex = swap;		\
               swap = dx_tex, dx_tex = dy_tex, dy_tex = swap;		\
               nominator = - SS - vv * BIAS;				\
               denominator = dvdx * BIAS + dSdx;			\
               if (dy_tex != 0.0f) {					\
                  dy_dx = dy_tex / dx_tex;                              \
                  dx_dy = 1.0f/dy_dx;					\
               }                                                        \
               else                                                     \
                  dy_dx = 0.0f;                                         \
               if (dx_tex > 0.0f) {					\
                  if (dy_tex > 0.0f) {					\
		     switch (format) {					\
		     case GL_RGB:					\
			switch (envmode) {				\
			case GL_MODULATE:				\
                           SPAN1(NEAREST_RGB;MODULATE,3, X_Y_TEX_COORD);\
			   break;					\
                        case GL_DECAL:					\
                        case GL_REPLACE:                       		\
                           SPAN1(NEAREST_RGB_REPLACE,3, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN1(NEAREST_RGB;BLEND,3, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN1(NEAREST_RGB;ADD,3, X_Y_TEX_COORD);     \
		           break;                                       \
                        default: /* unexpected env mode */		\
                           abort();					\
                        }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                        case GL_MODULATE:				\
                           SPAN1(NEAREST_RGBA;MODULATE,4, X_Y_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN1(NEAREST_RGBA;DECAL,4, X_Y_TEX_COORD);  \
                           break;					\
                        case GL_BLEND:					\
                           SPAN1(NEAREST_RGBA;BLEND,4, X_Y_TEX_COORD);  \
                           break;					\
		        case GL_ADD:                                    \
		           SPAN1(NEAREST_RGBA;ADD,4, X_Y_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN1(NEAREST_RGBA_REPLACE,4, X_Y_TEX_COORD);\
                           break;					\
                        default:					\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
                  else { /* dy_tex <= 0.0f */				\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN2(NEAREST_RGB;MODULATE,3, X_Y_TEX_COORD);\
                           break;					\
                          case GL_DECAL:				\
                        case GL_REPLACE:				\
                           SPAN2(NEAREST_RGB_REPLACE,3, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN2(NEAREST_RGB;BLEND,3, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN2(NEAREST_RGB;ADD,3, X_Y_TEX_COORD);     \
		           break;                                       \
                        default:					\
                           abort();					\
                            }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                        case GL_MODULATE:				\
                           SPAN2(NEAREST_RGBA;MODULATE,4, X_Y_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN2(NEAREST_RGBA;DECAL,4, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:				        \
                           SPAN2(NEAREST_RGBA;BLEND,4, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN2(NEAREST_RGBA;ADD,4, X_Y_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN2(NEAREST_RGBA_REPLACE,4, X_Y_TEX_COORD);\
                           break;					\
                        default:					\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
               }							\
               else { /* dx_tex < 0.0f */				\
                  if (dy_tex > 0.0f) {					\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN3(NEAREST_RGB;MODULATE,3, X_Y_TEX_COORD);\
                           break;					\
                        case GL_DECAL:				\
                        case GL_REPLACE:				\
                           SPAN3(NEAREST_RGB_REPLACE,3, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN3(NEAREST_RGB;BLEND,3, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN3(NEAREST_RGB;ADD,3, X_Y_TEX_COORD);     \
		           break;                                       \
                        default:					\
                           abort();					\
                        }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {				\
                           case GL_MODULATE:				\
                           SPAN3(NEAREST_RGBA;MODULATE,4, X_Y_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN3(NEAREST_RGBA;DECAL,4, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN3(NEAREST_RGBA;BLEND,4, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN3(NEAREST_RGBA;ADD,4, X_Y_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN3(NEAREST_RGBA_REPLACE,4, X_Y_TEX_COORD);\
                           break;					\
                        default:					\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
                  else { /* dy_tex <= 0.0f */				\
                     switch (format) {					\
                     case GL_RGB:					\
                        switch (envmode) {				\
                        case GL_MODULATE:				\
                           SPAN4(NEAREST_RGB;MODULATE,3, X_Y_TEX_COORD);\
                           break;					\
                          case GL_DECAL:				\
                        case GL_REPLACE:				\
                           SPAN4(NEAREST_RGB_REPLACE,3, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:					\
                           SPAN4(NEAREST_RGB;BLEND,3, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN4(NEAREST_RGB;ADD,3, X_Y_TEX_COORD);     \
		           break;                                       \
                        default:					\
                           abort();					\
                            }						\
                        break;						\
                     case GL_RGBA:					\
                        switch(envmode) {                      		\
                        case GL_MODULATE:				\
                           SPAN4(NEAREST_RGBA;MODULATE,4, X_Y_TEX_COORD);\
                           break;					\
                        case GL_DECAL:					\
                           SPAN4(NEAREST_RGBA;DECAL,4, X_Y_TEX_COORD);	\
                           break;					\
                        case GL_BLEND:				        \
                           SPAN4(NEAREST_RGBA;BLEND,4, X_Y_TEX_COORD);	\
                           break;					\
		        case GL_ADD:                                    \
		           SPAN4(NEAREST_RGBA;ADD,4, X_Y_TEX_COORD);    \
		           break;                                       \
                        case GL_REPLACE:				\
                           SPAN4(NEAREST_RGBA_REPLACE,4, X_Y_TEX_COORD);\
                           break;					\
                        default:					\
                           abort();					\
                        }						\
                        break;						\
                     }							\
                  }							\
               }							\
            }								\
         }								\
         else {                                                		\
            old_span:                                                   \
            switch (format) {                                  		\
            case GL_RGB:						\
               switch (envmode) {                              		\
               case GL_MODULATE:					\
                  OLD_SPAN(NEAREST_RGB;MODULATE,3);			\
                  break;						\
               case GL_DECAL:						\
               case GL_REPLACE:						\
                  OLD_SPAN(NEAREST_RGB_REPLACE,3);			\
                  break;						\
               case GL_BLEND:						\
                  OLD_SPAN(NEAREST_RGB;BLEND,3);			\
                  break;						\
	       case GL_ADD:                                             \
		  OLD_SPAN(NEAREST_RGB;ADD,3);                          \
		  break;                                                \
               default:							\
                  abort();						\
               }							\
               break;							\
            case GL_RGBA:						\
               switch(envmode) {                               		\
               case GL_MODULATE:					\
                  OLD_SPAN(NEAREST_RGBA;MODULATE,4);			\
                  break;						\
               case GL_DECAL:						\
                  OLD_SPAN(NEAREST_RGBA;DECAL,4);			\
                  break;						\
               case GL_BLEND:						\
                  OLD_SPAN(NEAREST_RGBA;BLEND,4);			\
                  break;						\
	       case GL_ADD:                                             \
		  OLD_SPAN(NEAREST_RGBA;ADD,4);                         \
		  break;                                                \
               case GL_REPLACE:						\
                  OLD_SPAN(NEAREST_RGBA_REPLACE,4);			\
                  break;						\
               default:							\
                  abort();						\
               }							\
               break;							\
            }								\
         }								\
         gl_write_rgba_span( ctx, n, LEFT, Y, zspan,			\
                             fogspan, rgba, GL_POLYGON);		\
         ffr = ffg = ffb = ffa = 0;					\
      }									\
   }									\

#include "s_tritemp.h"
#undef OLD_SPAN
#undef SPAN1
#undef SPAN2
#undef SPAN3
#undef SPAN4
#undef X_Y_TEX_COORD
#undef Y_X_TEX_COORD
#undef DRAW_LINE
#undef BIAS
}
#endif


/*
 * Render an perspective corrected RGB/RGBA textured triangle.
 * The Q (aka V in Mesa) coordinate must be zero such that the divide
 * by interpolated Q/W comes out right.
 *
 * This function written by Klaus Niederkrueger <klaus@math.leidenuniv.nl>
 * Send all questions and bug reports to him.
 */
#if 0 /* XXX disabled because of texcoord interpolation errors */
static void lin_persp_textured_triangle( GLcontext *ctx,
					 const SWvertex *v0,
					 const SWvertex *v1,
					 const SWvertex *v2 )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_TEX 1
#define SETUP_CODE							\
   struct gl_texture_unit *unit = ctx->Texture.Unit+0;			\
   struct gl_texture_object *obj = unit->Current2D;			\
   const GLint b = obj->BaseLevel;					\
   const GLfloat twidth = (GLfloat) obj->Image[b]->Width;	        \
   const GLfloat theight = (GLfloat) obj->Image[b]->Height;		\
   const GLint twidth_log2 = obj->Image[b]->WidthLog2;			\
   GLchan *texture = obj->Image[b]->Data;				\
   const GLint smask = (obj->Image[b]->Width - 1);                      \
   const GLint tmask = (obj->Image[b]->Height - 1);                     \
   const GLint format = obj->Image[b]->Format;                          \
   const GLint envmode = unit->EnvMode;                                 \
   GLfloat sscale, tscale;                                              \
   GLint comp, tbytesline, tsize;                                       \
   GLfixed er, eg, eb, ea;                                              \
   GLint tr, tg, tb, ta;                                                \
   if (!texture) {							\
      if (!_mesa_get_teximages_from_driver(ctx, obj))			\
         return;							\
      texture = obj->Image[b]->Data;					\
      ASSERT(texture);							\
   }									\
   if (envmode == GL_BLEND || envmode == GL_ADD) {                      \
      er = FloatToFixed(unit->EnvColor[0]);                             \
      eg = FloatToFixed(unit->EnvColor[1]);                             \
      eb = FloatToFixed(unit->EnvColor[2]);                             \
      ea = FloatToFixed(unit->EnvColor[3]);                             \
   }                                                                    \
   switch (format) {                                                    \
   case GL_ALPHA:							\
   case GL_LUMINANCE:							\
   case GL_INTENSITY:							\
      comp = 1;                                                         \
      break;                                                            \
   case GL_LUMINANCE_ALPHA:						\
      comp = 2;                                                         \
      break;                                                            \
   case GL_RGB:								\
      comp = 3;                                                         \
      break;                                                            \
   case GL_RGBA:							\
      comp = 4;                                                         \
      break;                                                            \
   default:								\
      gl_problem(NULL, "Bad texture format in lin_persp_texture_triangle"); \
      return;                                                           \
   }                                                                    \
   sscale = FIXED_SCALE * twidth;                                       \
   tscale = FIXED_SCALE * theight;                                      \
   tbytesline = obj->Image[b]->Width * comp;                            \
   tsize = theight * tbytesline;


#define SPAN(DO_TEX,COMP)                                  \
        for (i=0;i<n;i++) {                                \
           GLfloat invQ = 1.0f / vv;                       \
           GLfixed ffs = (int)(SS * invQ);                 \
           GLfixed fft = (int)(TT * invQ);                 \
	   GLint s = FixedToInt(ffs) & smask;              \
	   GLint t = FixedToInt(fft) & tmask;              \
           GLint sf = ffs & FIXED_FRAC_MASK;               \
           GLint tf = fft & FIXED_FRAC_MASK;               \
           GLint si = FIXED_FRAC_MASK - sf;                \
           GLint ti = FIXED_FRAC_MASK - tf;                \
           GLint pos = COMP * ((t << twidth_log2) + s);    \
           GLchan *tex00 = texture + pos;                  \
           GLchan *tex10 = tex00 + tbytesline;             \
           GLchan *tex01 = tex00 + COMP;                   \
           GLchan *tex11 = tex10 + COMP;                   \
           if (t == tmask) {                               \
              tex10 -= tsize;                              \
              tex11 -= tsize;                              \
           }                                               \
           if (s == smask) {                               \
              tex01 -= tbytesline;                         \
              tex11 -= tbytesline;                         \
           }                                               \
	   zspan[i] = FixedToDepth(ffz);                   \
	   fogspan[i] = fffog / 256;                       \
           DO_TEX;                                         \
	   fffog += fdfogdx;                               \
	   ffz += fdzdx;                                   \
           ffr += fdrdx;                                   \
	   ffg += fdgdx;                                   \
           ffb += fdbdx;                                   \
	   ffa += fdadx;                                   \
           SS += dSdx;                                     \
           TT += dTdx;                                     \
	   vv += dvdx;                                     \
           dest += 4;                                      \
	}

#define INNER_LOOP( LEFT, RIGHT, Y )			\
   {							\
      GLint i;						\
      const GLint n = RIGHT-LEFT;			\
      GLdepth zspan[MAX_WIDTH];				\
      GLfixed fogspan[MAX_WIDTH];			\
      GLchan rgba[MAX_WIDTH][4];			\
      (void) uu; /* please GCC */			\
      if (n > 0) {					\
         GLfloat SS = ss * sscale;			\
         GLfloat TT = tt * tscale;			\
         GLfloat dSdx = dsdx * sscale;			\
         GLfloat dTdx = dtdx * tscale;			\
         GLchan *dest = rgba[0];			\
         SS -= 0.5f * FIXED_SCALE * vv;			\
         TT -= 0.5f * FIXED_SCALE * vv;			\
         switch (format) {				\
         case GL_RGB:					\
            switch (envmode) {				\
            case GL_MODULATE:				\
               SPAN(LINEAR_RGB;MODULATE,3);		\
               break;					\
            case GL_DECAL:				\
            case GL_REPLACE:				\
               SPAN(LINEAR_RGB;REPLACE,3);		\
               break;					\
            case GL_BLEND:				\
               SPAN(LINEAR_RGB;BLEND,3);		\
               break;					\
            case GL_ADD:                                \
	       SPAN(LINEAR_RGB;ADD,3);                  \
	       break;                                   \
            default:					\
               abort();					\
            }						\
            break;					\
         case GL_RGBA:					\
            switch (envmode) {				\
            case GL_MODULATE:				\
               SPAN(LINEAR_RGBA;MODULATE,4);		\
               break;					\
            case GL_DECAL:				\
               SPAN(LINEAR_RGBA;DECAL,4);		\
               break;					\
            case GL_BLEND:				\
               SPAN(LINEAR_RGBA;BLEND,4);		\
               break;					\
            case GL_REPLACE:				\
               SPAN(LINEAR_RGBA;REPLACE,4);		\
               break;					\
            case GL_ADD:                                \
               SPAN(LINEAR_RGBA;ADD,4);                 \
               break;                                   \
            default: /* unexpected env mode */		\
               abort();					\
            }						\
         }						\
         gl_write_rgba_span( ctx, n, LEFT, Y, zspan,	\
                             fogspan,                   \
                             rgba, GL_POLYGON );	\
         ffr = ffg = ffb = ffa = 0;			\
      }							\
   }

#include "s_tritemp.h"
#undef SPAN
}
#endif


/*
 * Render a smooth-shaded, textured, RGBA triangle.
 * Interpolate S,T,U with perspective correction, w/out mipmapping.
 * Note: we use texture coordinates S,T,U,V instead of S,T,R,Q because
 * R is already used for red.
 */
static void general_textured_triangle( GLcontext *ctx,
				       const SWvertex *v0,
				       const SWvertex *v1,
				       const SWvertex *v2 )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_TEX 1
#define SETUP_CODE						\
   GLboolean flat_shade = (ctx->Light.ShadeModel==GL_FLAT);	\
   GLint r, g, b, a;						\
   if (flat_shade) {						\
      r = v2->color[0];						\
      g = v2->color[1];						\
      b = v2->color[2];						\
      a = v2->color[3];						\
   }
#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   GLint i;                                             \
           const GLint n = RIGHT-LEFT;				\
	   GLdepth zspan[MAX_WIDTH];				\
	   GLfixed fogspan[MAX_WIDTH];				\
	   GLchan rgba[MAX_WIDTH][4];				\
           GLfloat s[MAX_WIDTH], t[MAX_WIDTH], u[MAX_WIDTH];	\
	   if (n>0) {						\
              if (flat_shade) {					\
                 for (i=0;i<n;i++) {				\
		    GLdouble invQ = 1.0 / vv;			\
		    zspan[i] = FixedToDepth(ffz);		\
		    fogspan[i] = fffog / 256;          		\
		    rgba[i][RCOMP] = r;				\
		    rgba[i][GCOMP] = g;				\
		    rgba[i][BCOMP] = b;				\
		    rgba[i][ACOMP] = a;				\
		    s[i] = ss*invQ;				\
		    t[i] = tt*invQ;				\
	 	    u[i] = uu*invQ;				\
 		    fffog += fdfogdx;				\
		    ffz += fdzdx;				\
		    ss += dsdx;					\
		    tt += dtdx;					\
		    uu += dudx;					\
		    vv += dvdx;					\
		 }						\
              }							\
              else {						\
                 for (i=0;i<n;i++) {				\
		    GLdouble invQ = 1.0 / vv;			\
		    zspan[i] = FixedToDepth(ffz);		\
		    rgba[i][RCOMP] = FixedToInt(ffr);		\
		    rgba[i][GCOMP] = FixedToInt(ffg);		\
		    rgba[i][BCOMP] = FixedToInt(ffb);		\
		    rgba[i][ACOMP] = FixedToInt(ffa);		\
		    fogspan[i] = fffog / 256;         		\
		    s[i] = ss*invQ;				\
		    t[i] = tt*invQ;				\
		    u[i] = uu*invQ;				\
		    fffog += fdfogdx;				\
		    ffz += fdzdx;				\
		    ffr += fdrdx;				\
		    ffg += fdgdx;				\
		    ffb += fdbdx;				\
		    ffa += fdadx;				\
		    ss += dsdx;					\
		    tt += dtdx;					\
		    uu += dudx;					\
		    vv += dvdx;					\
		 }						\
              }							\
	      gl_write_texture_span( ctx, n, LEFT, Y, zspan, fogspan,	\
                                     s, t, u, NULL, 		\
	                             rgba, \
                                     NULL, GL_POLYGON );	\
	   }							\
	}

#include "s_tritemp.h"
}


/*
 * Render a smooth-shaded, textured, RGBA triangle with separate specular
 * color interpolation.
 * Interpolate S,T,U with perspective correction, w/out mipmapping.
 * Note: we use texture coordinates S,T,U,V instead of S,T,R,Q because
 * R is already used for red.
 */
static void general_textured_spec_triangle1( GLcontext *ctx,
					     const SWvertex *v0,
					     const SWvertex *v1,
					     const SWvertex *v2,
                                             GLdepth zspan[MAX_WIDTH],
                                             GLfixed fogspan[MAX_WIDTH],
                                             GLchan rgba[MAX_WIDTH][4],
                                             GLchan spec[MAX_WIDTH][4] )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_SPEC 1
#define INTERP_ALPHA 1
#define INTERP_TEX 1
#define SETUP_CODE						\
   GLboolean flat_shade = (ctx->Light.ShadeModel==GL_FLAT);	\
   GLint r, g, b, a, sr, sg, sb;				\
   if (flat_shade) {						\
      r = v2->color[0];						\
      g = v2->color[1];						\
      b = v2->color[2];						\
      a = v2->color[3];						\
      sr = v2->specular[0]; 					\
      sg = v2->specular[1]; 					\
      sb = v2->specular[2]; 					\
   }
#define INNER_LOOP( LEFT, RIGHT, Y )				\
	{							\
	   GLint i;                                             \
           const GLint n = RIGHT-LEFT;				\
           GLfloat s[MAX_WIDTH], t[MAX_WIDTH], u[MAX_WIDTH];	\
	   if (n>0) {						\
              if (flat_shade) {					\
                 for (i=0;i<n;i++) {				\
		    GLdouble invQ = 1.0 / vv;			\
		    zspan[i] = FixedToDepth(ffz);		\
		    fogspan[i] = fffog / 256;			\
		    rgba[i][RCOMP] = r;				\
		    rgba[i][GCOMP] = g;				\
		    rgba[i][BCOMP] = b;				\
		    rgba[i][ACOMP] = a;				\
		    spec[i][RCOMP] = sr;			\
		    spec[i][GCOMP] = sg;			\
		    spec[i][BCOMP] = sb;			\
		    s[i] = ss*invQ;				\
		    t[i] = tt*invQ;				\
		    u[i] = uu*invQ;				\
		    fffog += fdfogdx;				\
		    ffz += fdzdx;				\
		    ss += dsdx;					\
		    tt += dtdx;					\
		    uu += dudx;					\
		    vv += dvdx;					\
		 }						\
              }							\
              else {						\
                 for (i=0;i<n;i++) {				\
		    GLdouble invQ = 1.0 / vv;			\
		    zspan[i] = FixedToDepth(ffz);		\
		    fogspan[i] = fffog / 256;			\
		    rgba[i][RCOMP] = FixedToInt(ffr);		\
		    rgba[i][GCOMP] = FixedToInt(ffg);		\
		    rgba[i][BCOMP] = FixedToInt(ffb);		\
		    rgba[i][ACOMP] = FixedToInt(ffa);		\
		    spec[i][RCOMP] = FixedToInt(ffsr);		\
		    spec[i][GCOMP] = FixedToInt(ffsg);		\
		    spec[i][BCOMP] = FixedToInt(ffsb);		\
		    s[i] = ss*invQ;				\
		    t[i] = tt*invQ;				\
		    u[i] = uu*invQ;				\
		    fffog += fdfogdx;				\
		    ffz += fdzdx;				\
		    ffr += fdrdx;				\
		    ffg += fdgdx;				\
		    ffb += fdbdx;				\
		    ffa += fdadx;				\
		    ffsr += fdsrdx;				\
		    ffsg += fdsgdx;				\
		    ffsb += fdsbdx;				\
		    ss += dsdx;					\
		    tt += dtdx;					\
		    uu += dudx;					\
		    vv += dvdx;					\
		 }						\
              }							\
	      gl_write_texture_span( ctx, n, LEFT, Y, zspan,	\
                                   fogspan,                     \
                                   s, t, u, NULL, rgba,		\
                                   (CONST GLchan (*)[4]) spec,	\
	                           GL_POLYGON );		\
	   }							\
	}

#include "s_tritemp.h"
}



/*
 * Compute the lambda value for a fragment. (texture level of detail)
 */
static INLINE GLfloat
compute_lambda( GLfloat dsdx, GLfloat dsdy, GLfloat dtdx, GLfloat dtdy,
                GLfloat invQ, GLfloat width, GLfloat height ) 
{
   GLfloat dudx = dsdx * invQ * width;
   GLfloat dudy = dsdy * invQ * width;
   GLfloat dvdx = dtdx * invQ * height;
   GLfloat dvdy = dtdy * invQ * height;
   GLfloat r1 = dudx * dudx + dudy * dudy;
   GLfloat r2 = dvdx * dvdx + dvdy * dvdy;
   GLfloat rho2 = r1 + r2;     /* used to be:  rho2 = MAX2(r1,r2); */
   /* return log base 2 of rho */
   return log(rho2) * 1.442695 * 0.5;       /* 1.442695 = 1/log(2) */
}


/*
 * Render a smooth-shaded, textured, RGBA triangle.
 * Interpolate S,T,U with perspective correction and compute lambda for
 * each fragment.  Lambda is used to determine whether to use the
 * minification or magnification filter.  If minification and using
 * mipmaps, lambda is also used to select the texture level of detail.
 */
static void lambda_textured_triangle1( GLcontext *ctx,
				       const SWvertex *v0,
				       const SWvertex *v1,
				       const SWvertex *v2, 
                                       GLfloat s[MAX_WIDTH],
                                       GLfloat t[MAX_WIDTH],
                                       GLfloat u[MAX_WIDTH] )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_TEX 1

#define SETUP_CODE							\
   const struct gl_texture_object *obj = ctx->Texture.Unit[0]._Current;	\
   const GLint baseLevel = obj->BaseLevel;				\
   const struct gl_texture_image *texImage = obj->Image[baseLevel];	\
   const GLfloat twidth = (GLfloat) texImage->Width;			\
   const GLfloat theight = (GLfloat) texImage->Height;			\
   const GLboolean flat_shade = (ctx->Light.ShadeModel==GL_FLAT);	\
   GLint r, g, b, a;							\
   if (flat_shade) {							\
      r = v2->color[0];							\
      g = v2->color[1];							\
      b = v2->color[2];							\
      a = v2->color[3];							\
   }

#define INNER_LOOP( LEFT, RIGHT, Y )					\
	{								\
	   GLint i;                                                     \
           const GLint n = RIGHT-LEFT;					\
	   GLdepth zspan[MAX_WIDTH];					\
	   GLfixed fogspan[MAX_WIDTH];					\
	   GLchan rgba[MAX_WIDTH][4];					\
	   GLfloat lambda[MAX_WIDTH];					\
	   if (n>0) {							\
	      if (flat_shade) {						\
		 for (i=0;i<n;i++) {					\
		    GLdouble invQ = 1.0 / vv;				\
		    zspan[i] = FixedToDepth(ffz);			\
		    fogspan[i] = fffog / 256;          			\
		    rgba[i][RCOMP] = r;					\
		    rgba[i][GCOMP] = g;					\
		    rgba[i][BCOMP] = b;					\
		    rgba[i][ACOMP] = a;					\
		    s[i] = ss*invQ;					\
		    t[i] = tt*invQ;					\
		    u[i] = uu*invQ;					\
		    lambda[i] = compute_lambda( dsdx, dsdy, dtdx, dtdy,	\
						invQ, twidth, theight );\
		    ffz += fdzdx;					\
		    fffog += fdfogdx;					\
		    ss += dsdx;						\
		    tt += dtdx;						\
		    uu += dudx;						\
		    vv += dvdx;						\
		 }							\
              }								\
              else {							\
		 for (i=0;i<n;i++) {					\
		    GLdouble invQ = 1.0 / vv;				\
		    zspan[i] = FixedToDepth(ffz);			\
		    fogspan[i] = fffog / 256;          			\
		    rgba[i][RCOMP] = FixedToInt(ffr);			\
		    rgba[i][GCOMP] = FixedToInt(ffg);			\
		    rgba[i][BCOMP] = FixedToInt(ffb);			\
		    rgba[i][ACOMP] = FixedToInt(ffa);			\
		    s[i] = ss*invQ;					\
		    t[i] = tt*invQ;					\
		    u[i] = uu*invQ;					\
		    lambda[i] = compute_lambda( dsdx, dsdy, dtdx, dtdy,	\
						invQ, twidth, theight );\
		    ffz += fdzdx;					\
		    fffog += fdfogdx;					\
		    ffr += fdrdx;					\
		    ffg += fdgdx;					\
		    ffb += fdbdx;					\
		    ffa += fdadx;					\
		    ss += dsdx;						\
		    tt += dtdx;						\
		    uu += dudx;						\
		    vv += dvdx;						\
		 }							\
              }								\
	      gl_write_texture_span( ctx, n, LEFT, Y, zspan, fogspan,	\
                                     s, t, u, lambda,	 		\
	                             rgba, NULL, GL_POLYGON );		\
	   }								\
	}

#include "s_tritemp.h"
}


/*
 * Render a smooth-shaded, textured, RGBA triangle with separate specular
 * interpolation.
 * Interpolate S,T,U with perspective correction and compute lambda for
 * each fragment.  Lambda is used to determine whether to use the
 * minification or magnification filter.  If minification and using
 * mipmaps, lambda is also used to select the texture level of detail.
 */
static void lambda_textured_spec_triangle1( GLcontext *ctx,
					    const SWvertex *v0,
					    const SWvertex *v1,
					    const SWvertex *v2,
                                            GLfloat s[MAX_WIDTH],
                                            GLfloat t[MAX_WIDTH],
                                            GLfloat u[MAX_WIDTH] )
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_SPEC 1
#define INTERP_ALPHA 1
#define INTERP_TEX 1

#define SETUP_CODE							\
   const struct gl_texture_object *obj = ctx->Texture.Unit[0]._Current;	\
   const GLint baseLevel = obj->BaseLevel;				\
   const struct gl_texture_image *texImage = obj->Image[baseLevel];	\
   const GLfloat twidth = (GLfloat) texImage->Width;			\
   const GLfloat theight = (GLfloat) texImage->Height;			\
   const GLboolean flat_shade = (ctx->Light.ShadeModel==GL_FLAT);	\
   GLint r, g, b, a, sr, sg, sb;					\
   if (flat_shade) {							\
      r = v2->color[0];							\
      g = v2->color[1];							\
      b = v2->color[2];							\
      a = v2->color[3];							\
      sr = v2->specular[0];						\
      sg = v2->specular[1];						\
      sb = v2->specular[2];						\
   }

#define INNER_LOOP( LEFT, RIGHT, Y )					\
	{								\
	   GLint i;                                                     \
           const GLint n = RIGHT-LEFT;					\
	   GLdepth zspan[MAX_WIDTH];					\
	   GLfixed fogspan[MAX_WIDTH];					\
	   GLchan spec[MAX_WIDTH][4];					\
           GLchan rgba[MAX_WIDTH][4];					\
	   GLfloat lambda[MAX_WIDTH];					\
	   if (n>0) {							\
	      if (flat_shade) {						\
		 for (i=0;i<n;i++) {					\
		    GLdouble invQ = 1.0 / vv;				\
		    zspan[i] = FixedToDepth(ffz);			\
		    fogspan[i] = fffog / 256;				\
		    rgba[i][RCOMP] = r;					\
		    rgba[i][GCOMP] = g;					\
		    rgba[i][BCOMP] = b;					\
		    rgba[i][ACOMP] = a;					\
		    spec[i][RCOMP] = sr;				\
		    spec[i][GCOMP] = sg;				\
		    spec[i][BCOMP] = sb;				\
		    s[i] = ss*invQ;					\
		    t[i] = tt*invQ;					\
		    u[i] = uu*invQ;					\
		    lambda[i] = compute_lambda( dsdx, dsdy, dtdx, dtdy,	\
						invQ, twidth, theight );\
		    fffog += fdfogdx;					\
		    ffz += fdzdx;					\
		    ss += dsdx;						\
		    tt += dtdx;						\
		    uu += dudx;						\
		    vv += dvdx;						\
		 }							\
              }								\
              else {							\
		 for (i=0;i<n;i++) {					\
		    GLdouble invQ = 1.0 / vv;				\
		    zspan[i] = FixedToDepth(ffz);			\
		    fogspan[i] = fffog / 256;				\
		    rgba[i][RCOMP] = FixedToInt(ffr);			\
		    rgba[i][GCOMP] = FixedToInt(ffg);			\
		    rgba[i][BCOMP] = FixedToInt(ffb);			\
		    rgba[i][ACOMP] = FixedToInt(ffa);			\
		    spec[i][RCOMP] = FixedToInt(ffsr);			\
		    spec[i][GCOMP] = FixedToInt(ffsg);			\
		    spec[i][BCOMP] = FixedToInt(ffsb);			\
		    s[i] = ss*invQ;					\
		    t[i] = tt*invQ;					\
		    u[i] = uu*invQ;					\
		    lambda[i] = compute_lambda( dsdx, dsdy, dtdx, dtdy,	\
						invQ, twidth, theight );\
		    fffog += fdfogdx;					\
		    ffz += fdzdx;					\
		    ffr += fdrdx;					\
		    ffg += fdgdx;					\
		    ffb += fdbdx;					\
		    ffa += fdadx;					\
		    ffsr += fdsrdx;					\
		    ffsg += fdsgdx;					\
		    ffsb += fdsbdx;					\
		    ss += dsdx;						\
		    tt += dtdx;						\
		    uu += dudx;						\
		    vv += dvdx;						\
		 }							\
              }								\
	      gl_write_texture_span( ctx, n, LEFT, Y, zspan, fogspan,	\
                                     s, t, u, lambda,	 		\
	                             rgba, (CONST GLchan (*)[4]) spec,	\
                                     GL_POLYGON );			\
	   }								\
	}

#include "s_tritemp.h"
}


/*
 * This is the big one!
 * Interpolate Z, RGB, Alpha, and two sets of texture coordinates.
 * Yup, it's slow.
 */
static void 
lambda_multitextured_triangle1( GLcontext *ctx,
				const SWvertex *v0,
				const SWvertex *v1,
				const SWvertex *v2,
				GLfloat s[MAX_TEXTURE_UNITS][MAX_WIDTH],
				GLfloat t[MAX_TEXTURE_UNITS][MAX_WIDTH],
				GLfloat u[MAX_TEXTURE_UNITS][MAX_WIDTH])
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_MULTITEX 1

#define SETUP_CODE								\
   GLchan rgba[MAX_WIDTH][4];							\
   const GLboolean flat_shade = (ctx->Light.ShadeModel==GL_FLAT);		\
   GLfloat twidth[MAX_TEXTURE_UNITS], theight[MAX_TEXTURE_UNITS];		\
   GLint r, g, b, a;								\
   if (flat_shade) {								\
      r = v2->color[0];								\
      g = v2->color[1];								\
      b = v2->color[2];								\
      a = v2->color[3];								\
   }										\
   {										\
      GLuint unit;								\
      for (unit = 0; unit < ctx->Const.MaxTextureUnits; unit++) {		\
         if (ctx->Texture.Unit[unit]._ReallyEnabled) {				\
            const struct gl_texture_object *obj = ctx->Texture.Unit[unit]._Current; \
            const GLint baseLevel = obj->BaseLevel;				\
            const struct gl_texture_image *texImage = obj->Image[baseLevel];	\
            twidth[unit] = (GLfloat) texImage->Width;				\
            theight[unit] = (GLfloat) texImage->Height;				\
         }									\
      }										\
   }



#define INNER_LOOP( LEFT, RIGHT, Y )						\
   {										\
      GLint i;									\
      const GLint n = RIGHT-LEFT;						\
      GLdepth zspan[MAX_WIDTH];							\
      GLfixed fogspan[MAX_WIDTH];						\
      GLfloat lambda[MAX_TEXTURE_UNITS][MAX_WIDTH];				\
      if (n > 0) {								\
         if (flat_shade) {							\
	    for (i=0;i<n;i++) {							\
	       zspan[i] = FixedToDepth(ffz);					\
	       fogspan[i] = fffog / 256;					\
               fffog += fdfogdx;						\
	       ffz += fdzdx;							\
	       rgba[i][RCOMP] = r;						\
	       rgba[i][GCOMP] = g;						\
	       rgba[i][BCOMP] = b;						\
	       rgba[i][ACOMP] = a;						\
	       {								\
		  GLuint unit;							\
		  for (unit = 0; unit < ctx->Const.MaxTextureUnits; unit++) {	\
		     if (ctx->Texture.Unit[unit]._ReallyEnabled) {		\
			GLdouble invQ = 1.0 / vv[unit];				\
			s[unit][i] = ss[unit] * invQ;				\
			t[unit][i] = tt[unit] * invQ;				\
			u[unit][i] = uu[unit] * invQ;				\
			lambda[unit][i] = compute_lambda(dsdx[unit], dsdy[unit],\
				   dtdx[unit], dtdy[unit], invQ,		\
				   twidth[unit], theight[unit] );		\
			ss[unit] += dsdx[unit];					\
			tt[unit] += dtdx[unit];					\
			uu[unit] += dudx[unit];					\
			vv[unit] += dvdx[unit];					\
		     }								\
		  }								\
	       }								\
	    }									\
	 }									\
	 else { /* smooth shade */						\
	    for (i=0;i<n;i++) {							\
	       zspan[i] = FixedToDepth(ffz);					\
	       fogspan[i] = fffog / 256;					\
	       ffz += fdzdx;							\
	       fffog += fdfogdx;						\
	       rgba[i][RCOMP] = FixedToInt(ffr);				\
	       rgba[i][GCOMP] = FixedToInt(ffg);				\
	       rgba[i][BCOMP] = FixedToInt(ffb);				\
	       rgba[i][ACOMP] = FixedToInt(ffa);				\
	       ffr += fdrdx;							\
	       ffg += fdgdx;							\
	       ffb += fdbdx;							\
	       ffa += fdadx;							\
	       {								\
		  GLuint unit;							\
		  for (unit = 0; unit < ctx->Const.MaxTextureUnits; unit++) {	\
		     if (ctx->Texture.Unit[unit]._ReallyEnabled) {		\
			GLdouble invQ = 1.0 / vv[unit];				\
			s[unit][i] = ss[unit] * invQ;				\
			t[unit][i] = tt[unit] * invQ;				\
			u[unit][i] = uu[unit] * invQ;				\
			lambda[unit][i] = compute_lambda(dsdx[unit], dsdy[unit],\
				   dtdx[unit], dtdy[unit], invQ,		\
				   twidth[unit], theight[unit] );		\
			ss[unit] += dsdx[unit];					\
			tt[unit] += dtdx[unit];					\
			uu[unit] += dudx[unit];					\
			vv[unit] += dvdx[unit];					\
		     }								\
		  }								\
	       }								\
	    }									\
	 }									\
	 gl_write_multitexture_span( ctx, n, LEFT, Y, zspan, fogspan,		\
				     (const GLfloat (*)[MAX_WIDTH]) s,		\
				     (const GLfloat (*)[MAX_WIDTH]) t,		\
				     (const GLfloat (*)[MAX_WIDTH]) u,		\
				     (GLfloat (*)[MAX_WIDTH]) lambda,		\
				     rgba, NULL, GL_POLYGON );			\
      }										\
   }
#include "s_tritemp.h"
}


/*
 * These wrappers are needed to deal with the 32KB / stack frame limit
 * on Mac / PowerPC systems.
 */

static void general_textured_spec_triangle(GLcontext *ctx,
					   const SWvertex *v0,
					   const SWvertex *v1,
					   const SWvertex *v2 )
{
   GLdepth zspan[MAX_WIDTH];
   GLfixed fogspan[MAX_WIDTH];	                   
   GLchan rgba[MAX_WIDTH][4], spec[MAX_WIDTH][4];
   general_textured_spec_triangle1(ctx,v0,v1,v2,zspan,fogspan,rgba,spec);
}

static void lambda_textured_triangle( GLcontext *ctx,
				      const SWvertex *v0,
				      const SWvertex *v1,
				      const SWvertex *v2 )
{
   GLfloat s[MAX_WIDTH], t[MAX_WIDTH], u[MAX_WIDTH];
   lambda_textured_triangle1(ctx,v0,v1,v2,s,t,u);
}

static void lambda_textured_spec_triangle( GLcontext *ctx,
					   const SWvertex *v0,
					   const SWvertex *v1,
					   const SWvertex *v2 )
{
   GLfloat s[MAX_WIDTH];
   GLfloat t[MAX_WIDTH];
   GLfloat u[MAX_WIDTH];
   lambda_textured_spec_triangle1(ctx,v0,v1,v2,s,t,u);
}


static void lambda_multitextured_triangle( GLcontext *ctx,
					   const SWvertex *v0,
					   const SWvertex *v1,
					   const SWvertex *v2 )
{

   GLfloat s[MAX_TEXTURE_UNITS][MAX_WIDTH];
   GLfloat t[MAX_TEXTURE_UNITS][MAX_WIDTH];
   DEFMARRAY(GLfloat,u,MAX_TEXTURE_UNITS,MAX_WIDTH);
   CHECKARRAY(u,return);
   
   lambda_multitextured_triangle1(ctx,v0,v1,v2,s,t,u);
   
   UNDEFARRAY(u);
}



static void occlusion_zless_triangle( GLcontext *ctx,
				      const SWvertex *v0,
				      const SWvertex *v1,
				      const SWvertex *v2 )
{
   if (ctx->OcclusionResult) {
      return;
   }

#define DO_OCCLUSION_TEST
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
#define INNER_LOOP( LEFT, RIGHT, Y )		\
   {						\
      GLint i;                                  \
      const GLint len = RIGHT-LEFT;		\
      for (i=0;i<len;i++) {			\
	 GLdepth z = FixedToDepth(ffz);		\
         (void) fffog;                          \
	 if (z < zRow[i]) {			\
	    ctx->OcclusionResult = GL_TRUE;	\
	    return;				\
	 }					\
	 ffz += fdzdx;				\
      }						\
   }
#include "s_tritemp.h"
}





#if 0
# define dputs(s) puts(s)
#else
# define dputs(s)
#endif



/*
 * Determine which triangle rendering function to use given the current
 * rendering context.
 *
 * Please update the summary flag _SWRAST_NEW_TRIANGLE if you add or
 * remove tests to this code.
 */
void 
_swrast_choose_triangle( GLcontext *ctx )
{
   SWcontext *swrast = SWRAST_CONTEXT(ctx);
   const GLboolean rgbmode = ctx->Visual.RGBAflag;

   if (ctx->RenderMode==GL_RENDER) {

      if (ctx->Polygon.SmoothFlag) {
         _mesa_set_aa_triangle_function(ctx);
         ASSERT(swrast->Triangle);
         return;
      }

      if (ctx->Depth.OcclusionTest &&
          ctx->Depth.Test &&
          ctx->Depth.Mask == GL_FALSE &&
          ctx->Depth.Func == GL_LESS &&
          !ctx->Stencil.Enabled) {
         if ((rgbmode &&
              ctx->Color.ColorMask[0] == 0 && 
              ctx->Color.ColorMask[1] == 0 && 
              ctx->Color.ColorMask[2] == 0 &&
              ctx->Color.ColorMask[3] == 0)
             ||
             (!rgbmode && ctx->Color.IndexMask == 0)) {
            dputs("occlusion_test_triangle");
            swrast->Triangle = occlusion_zless_triangle;
            return;
         }
      }

      if (ctx->Texture._ReallyEnabled) {
         /* Ugh, we do a _lot_ of tests to pick the best textured tri func */
	 GLint format, filter;
	 const struct gl_texture_object *current2Dtex = ctx->Texture.Unit[0].Current2D;
         const struct gl_texture_image *image;
         /* First see if we can used an optimized 2-D texture function */
         if (ctx->Texture._ReallyEnabled==TEXTURE0_2D
             && current2Dtex->WrapS==GL_REPEAT
	     && current2Dtex->WrapT==GL_REPEAT
             && ((image = current2Dtex->Image[current2Dtex->BaseLevel]) != 0)  /* correct! */
             && image->Border==0
             && ((format = image->Format)==GL_RGB || format==GL_RGBA)
	     && (filter = current2Dtex->MinFilter)==current2Dtex->MagFilter
	     && ctx->Light.Model.ColorControl==GL_SINGLE_COLOR
	     && ctx->Texture.Unit[0].EnvMode!=GL_COMBINE_EXT) {

	    if (ctx->Hint.PerspectiveCorrection==GL_FASTEST) {
	     
	       if (filter==GL_NEAREST
		   && format==GL_RGB
		   && (ctx->Texture.Unit[0].EnvMode==GL_REPLACE
		       || ctx->Texture.Unit[0].EnvMode==GL_DECAL)
		   && ((swrast->_RasterMask==DEPTH_BIT
			&& ctx->Depth.Func==GL_LESS
			&& ctx->Depth.Mask==GL_TRUE)
		       || swrast->_RasterMask==0)
		   && ctx->Polygon.StippleFlag==GL_FALSE) {

		  if (swrast->_RasterMask==DEPTH_BIT) {
		     swrast->Triangle = simple_z_textured_triangle;
		     dputs("simple_z_textured_triangle");
		  }
		  else {
		     swrast->Triangle = simple_textured_triangle;
		     dputs("simple_textured_triangle");
		  }
	       }
	       else {
                  if (ctx->Texture.Unit[0].EnvMode==GL_ADD) {
                     swrast->Triangle = general_textured_triangle;
                     dputs("general_textured_triangle");
                  }
                  else {
                     swrast->Triangle = affine_textured_triangle;
                     dputs("affine_textured_triangle");
                  }
	       }
	    }
	    else {
#if 00 /* XXX these function have problems with texture coord interpolation */
	       if (filter==GL_NEAREST) {
		 swrast->Triangle = near_persp_textured_triangle;
		 dputs("near_persp_textured_triangle");
	       }
	       else {
		 swrast->Triangle = lin_persp_textured_triangle;
		 dputs("lin_persp_textured_triangle");
	       }
#endif
               swrast->Triangle = general_textured_triangle;
	    }
	 }
         else {
            /* More complicated textures (mipmap, multi-tex, sep specular) */
            GLboolean needLambda;
            /* if mag filter != min filter we need to compute lambda */
            const struct gl_texture_object *obj = ctx->Texture.Unit[0]._Current;
            if (obj && obj->MinFilter != obj->MagFilter)
               needLambda = GL_TRUE;
            else
               needLambda = GL_FALSE;
            if (swrast->_MultiTextureEnabled) {
               swrast->Triangle = lambda_multitextured_triangle;
	       dputs("lambda_multitextured_triangle");
            }
            else if ((ctx->Light.Enabled &&
		      ctx->Light.Model.ColorControl==GL_SEPARATE_SPECULAR_COLOR)
		     || ctx->Fog.ColorSumEnabled) {
               /* separate specular color interpolation */
               if (needLambda) {
                  swrast->Triangle = lambda_textured_spec_triangle;
		  dputs("lambda_textured_spec_triangle");
	       }
               else {
                  swrast->Triangle = general_textured_spec_triangle;
		  dputs("general_textured_spec_triangle");
	       }
            }
            else {
               if (needLambda) {
                  swrast->Triangle = lambda_textured_triangle;
		  dputs("lambda_textured_triangle");
	       }
               else {
                  swrast->Triangle = general_textured_triangle;
		  dputs("general_textured_triangle");
	       }
            }
         }
      }
      else {
	 if (ctx->Light.ShadeModel==GL_SMOOTH) {
	    /* smooth shaded, no texturing, stippled or some raster ops */
            if (rgbmode) {
               dputs("smooth_rgba_triangle");
               swrast->Triangle = smooth_rgba_triangle;
            }
            else {
               dputs("smooth_ci_triangle");
               swrast->Triangle = smooth_ci_triangle;
            }
	 }
	 else {
	    /* flat shaded, no texturing, stippled or some raster ops */
            if (rgbmode) {
               dputs("flat_rgba_triangle");
               swrast->Triangle = flat_rgba_triangle;
            }
            else {
               dputs("flat_ci_triangle");
               swrast->Triangle = flat_ci_triangle;
            }
	 }
      }
   }
   else if (ctx->RenderMode==GL_FEEDBACK) {
      swrast->Triangle = gl_feedback_triangle;
   }
   else {
      /* GL_SELECT mode */
      swrast->Triangle = gl_select_triangle;
   }
}