/* $Id: lines.c,v 1.18 1997/07/24 01:24:11 brianp Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  2.4
 * Copyright (C) 1995-1997  Brian Paul
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
 * $Log: lines.c,v $
 * Revision 1.18  1997/07/24 01:24:11  brianp
 * changed precompiled header symbol from PCH to PC_HEADER
 *
 * Revision 1.17  1997/07/05 16:03:51  brianp
 * fixed PB overflow problem
 *
 * Revision 1.16  1997/06/20 02:01:49  brianp
 * changed color components from GLfixed to GLubyte
 *
 * Revision 1.15  1997/06/03 01:38:22  brianp
 * fixed divide by zero problem in feedback function (William Mitchell)
 *
 * Revision 1.14  1997/05/28 03:25:26  brianp
 * added precompiled header (PCH) support
 *
 * Revision 1.13  1997/05/03 00:51:02  brianp
 * removed calls to gl_texturing_enabled()
 *
 * Revision 1.12  1997/04/14 02:00:39  brianp
 * #include "texstate.h" instead of "texture.h"
 *
 * Revision 1.11  1997/04/12 12:25:01  brianp
 * replaced ctx->LineFunc with ctx->Driver.LineFunc, fixed PB->count bug
 *
 * Revision 1.10  1997/03/16 02:07:31  brianp
 * now use linetemp.h in line drawing functions
 *
 * Revision 1.9  1997/03/08 02:04:27  brianp
 * better implementation of feedback function
 *
 * Revision 1.8  1997/02/09 18:44:20  brianp
 * added GL_EXT_texture3D support
 *
 * Revision 1.7  1997/01/09 19:48:00  brianp
 * now call gl_texturing_enabled()
 *
 * Revision 1.6  1996/11/08 02:21:21  brianp
 * added null drawing function for GL_NO_RASTER
 *
 * Revision 1.5  1996/09/27 01:28:56  brianp
 * removed unused variables
 *
 * Revision 1.4  1996/09/25 02:01:54  brianp
 * new texture coord interpolation
 *
 * Revision 1.3  1996/09/15 14:18:10  brianp
 * now use GLframebuffer and GLvisual
 *
 * Revision 1.2  1996/09/15 01:48:58  brianp
 * removed #define NULL 0
 *
 * Revision 1.1  1996/09/13 01:38:16  brianp
 * Initial revision
 *
 */


#ifdef PC_HEADER
#include "all.h"
#else
#include "context.h"
#include "depth.h"
#include "feedback.h"
#include "lines.h"
#include "dlist.h"
#include "macros.h"
#include "pb.h"
#include "texstate.h"
#include "types.h"
#include "vb.h"
#endif



void gl_LineWidth( GLcontext *ctx, GLfloat width )
{
   if (width<=0.0) {
      gl_error( ctx, GL_INVALID_VALUE, "glLineWidth" );
      return;
   }
   if (INSIDE_BEGIN_END(ctx)) {
      gl_error( ctx, GL_INVALID_OPERATION, "glLineWidth" );
      return;
   }
   ctx->Line.Width = width;
   ctx->NewState |= NEW_RASTER_OPS;
}



void gl_LineStipple( GLcontext *ctx, GLint factor, GLushort pattern )
{
   if (INSIDE_BEGIN_END(ctx)) {
      gl_error( ctx, GL_INVALID_OPERATION, "glLineStipple" );
      return;
   }
   ctx->Line.StippleFactor = CLAMP( factor, 1, 256 );
   ctx->Line.StipplePattern = pattern;
   ctx->NewState |= NEW_RASTER_OPS;
}



/**********************************************************************/
/*****                    Rasterization                           *****/
/**********************************************************************/


/*
 * There are 4 pairs (RGBA, CI) of line drawing functions:
 *   1. simple:  width=1 and no special rasterization functions (fastest)
 *   2. flat:  width=1, non-stippled, flat-shaded, any raster operations
 *   3. smooth:  width=1, non-stippled, smooth-shaded, any raster operations
 *   4. general:  any other kind of line (slowest)
 */


/*
 * All line drawing functions have the same arguments:
 * v1, v2 - indexes of first and second endpoints into vertex buffer arrays
 * pv     - provoking vertex: which vertex color/index to use for flat shading.
 */



static void feedback_line( GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv )
{
   struct vertex_buffer *VB = ctx->VB;
   GLfloat x1, y1, z1, w1;
   GLfloat x2, y2, z2, w2;
   GLfloat tex1[4], tex2[4], invq;
   GLfloat invRedScale   = ctx->Visual->InvRedScale;
   GLfloat invGreenScale = ctx->Visual->InvGreenScale;
   GLfloat invBlueScale  = ctx->Visual->InvBlueScale;
   GLfloat invAlphaScale = ctx->Visual->InvAlphaScale;

   x1 = VB->Win[v1][0];
   y1 = VB->Win[v1][1];
   z1 = VB->Win[v1][2] / DEPTH_SCALE;
   w1 = VB->Clip[v1][3];

   x2 = VB->Win[v2][0];
   y2 = VB->Win[v2][1];
   z2 = VB->Win[v2][2] / DEPTH_SCALE;
   w2 = VB->Clip[v2][3];

   invq = (VB->TexCoord[v1][3]==0.0) ? 1.0 : (1.0F / VB->TexCoord[v1][3]);
   tex1[0] = VB->TexCoord[v1][0] * invq;
   tex1[1] = VB->TexCoord[v1][1] * invq;
   tex1[2] = VB->TexCoord[v1][2] * invq;
   tex1[3] = VB->TexCoord[v1][3];
   invq = (VB->TexCoord[v2][3]==0.0) ? 1.0 : (1.0F / VB->TexCoord[v2][3]);
   tex2[0] = VB->TexCoord[v2][0] * invq;
   tex2[1] = VB->TexCoord[v2][1] * invq;
   tex2[2] = VB->TexCoord[v2][2] * invq;
   tex2[3] = VB->TexCoord[v2][3];

   if (ctx->StippleCounter==0) {
      FEEDBACK_TOKEN( ctx, (GLfloat) GL_LINE_RESET_TOKEN );
   }
   else {
      FEEDBACK_TOKEN( ctx, (GLfloat) GL_LINE_TOKEN );
   }

   {
      GLfloat color[4];
      /* convert color from integer to a float in [0,1] */
      color[0] = (GLfloat) VB->Color[pv][0] * invRedScale;
      color[1] = (GLfloat) VB->Color[pv][1] * invGreenScale;
      color[2] = (GLfloat) VB->Color[pv][2] * invBlueScale;
      color[3] = (GLfloat) VB->Color[pv][3] * invAlphaScale;
      gl_feedback_vertex( ctx, x1,y1,z1,w1, color,
                          (GLfloat) VB->Index[pv], tex1 );
      gl_feedback_vertex( ctx, x2,y2,z2,w2, color,
                          (GLfloat) VB->Index[pv], tex2 );
   }

   ctx->StippleCounter++;
}



static void select_line( GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv )
{
   gl_update_hitflag( ctx, ctx->VB->Win[v1][2] / DEPTH_SCALE );
   gl_update_hitflag( ctx, ctx->VB->Win[v2][2] / DEPTH_SCALE );
}



#if MAX_WIDTH > MAX_HEIGHT
#  define MAXPOINTS MAX_WIDTH
#else
#  define MAXPOINTS MAX_HEIGHT
#endif


/* Flat, color index line */
static void flat_ci_line( GLcontext *ctx,
                          GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   PB_SET_INDEX( ctx, ctx->PB, ctx->VB->Index[pvert] );
   count = ctx->PB->count;

#define INTERP_XY 1

#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Flat, color index line with Z interpolation/testing */
static void flat_ci_z_line( GLcontext *ctx,
                            GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   PB_SET_INDEX( ctx, ctx->PB, ctx->VB->Index[pvert] );
   count = ctx->PB->count;

#define INTERP_XY 1
#define INTERP_Z 1

#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbz[count] = Z;		\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Flat-shaded, RGBA line */
static void flat_rgba_line( GLcontext *ctx,
                            GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLubyte *color = ctx->VB->Color[pvert];
   PB_SET_COLOR( ctx, ctx->PB, color[0], color[1], color[2], color[3] );
   count = ctx->PB->count;

#define INTERP_XY 1

#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Flat-shaded, RGBA line with Z interpolation/testing */
static void flat_rgba_z_line( GLcontext *ctx,
                              GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLubyte *color = ctx->VB->Color[pvert];
   PB_SET_COLOR( ctx, ctx->PB, color[0], color[1], color[2], color[3] );
   count = ctx->PB->count;

#define INTERP_XY 1
#define INTERP_Z 1

#define PLOT(X,Y)	\
	pbx[count] = X;	\
	pby[count] = Y;	\
	pbz[count] = Z;	\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Smooth shaded, color index line */
static void smooth_ci_line( GLcontext *ctx,
                            GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLuint *pbi = ctx->PB->i;

#define INTERP_XY 1
#define INTERP_INDEX 1

#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbi[count] = I;		\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Smooth shaded, color index line with Z interpolation/testing */
static void smooth_ci_z_line( GLcontext *ctx,
                              GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLuint *pbi = ctx->PB->i;

#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_INDEX 1

#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbz[count] = Z;		\
	pbi[count] = I;		\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Smooth-shaded, RGBA line */
static void smooth_rgba_line( GLcontext *ctx,
                       	      GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLubyte *pbr = ctx->PB->r;
   GLubyte *pbg = ctx->PB->g;
   GLubyte *pbb = ctx->PB->b;
   GLubyte *pba = ctx->PB->a;

#define INTERP_XY 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1

#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbr[count] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);	\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Smooth-shaded, RGBA line with Z interpolation/testing */
static void smooth_rgba_z_line( GLcontext *ctx,
                       	        GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLubyte *pbr = ctx->PB->r;
   GLubyte *pbg = ctx->PB->g;
   GLubyte *pbb = ctx->PB->b;
   GLubyte *pba = ctx->PB->a;

#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1

#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbr[count] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);	\
	count++;

#include "linetemp.h"

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}


#define CHECK_FULL(count)			\
	if (count >= PB_SIZE-MAX_WIDTH) {	\
	   ctx->PB->count = count;		\
	   gl_flush_pb(ctx);			\
	   count = ctx->PB->count;		\
	}



/* Smooth shaded, color index, any width, maybe stippled */
static void general_smooth_ci_line( GLcontext *ctx,
                           	    GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLuint *pbi = ctx->PB->i;

   if (ctx->Line.StippleFlag) {
      /* stippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_INDEX 1
#define WIDE 1
#define STIPPLE 1
#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbz[count] = Z;		\
	pbi[count] = I;		\
	count++;		\
	CHECK_FULL(count);
#include "linetemp.h"
   }
   else {
      /* unstippled */
      if (ctx->Line.Width==2.0F) {
         /* special case: unstippled and width=2 */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_INDEX 1
#define XMAJOR_PLOT(X,Y)			\
	pbx[count] = X;  pbx[count+1] = X;	\
	pby[count] = Y;  pby[count+1] = Y+1;	\
	pbz[count] = Z;  pbz[count+1] = Z;	\
	pbi[count] = I;  pbi[count+1] = I;	\
	count += 2;
#define YMAJOR_PLOT(X,Y)			\
	pbx[count] = X;  pbx[count+1] = X+1;	\
	pby[count] = Y;  pby[count+1] = Y;	\
	pbz[count] = Z;  pbz[count+1] = Z;	\
	pbi[count] = I;  pbi[count+1] = I;	\
	count += 2;
#include "linetemp.h"
      }
      else {
         /* unstippled, any width */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_INDEX 1
#define WIDE 1
#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbz[count] = Z;		\
	pbi[count] = I;		\
	count++;		\
	CHECK_FULL(count);
#include "linetemp.h"
      }
   }

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}


/* Flat shaded, color index, any width, maybe stippled */
static void general_flat_ci_line( GLcontext *ctx,
                                  GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   PB_SET_INDEX( ctx, ctx->PB, ctx->VB->Index[pvert] );
   count = ctx->PB->count;

   if (ctx->Line.StippleFlag) {
      /* stippled, any width */
#define INTERP_XY 1
#define INTERP_Z 1
#define WIDE 1
#define STIPPLE 1
#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbz[count] = Z;		\
	count++;		\
	CHECK_FULL(count);
#include "linetemp.h"
   }
   else {
      /* unstippled */
      if (ctx->Line.Width==2.0F) {
         /* special case: unstippled and width=2 */
#define INTERP_XY 1
#define INTERP_Z 1
#define XMAJOR_PLOT(X,Y)			\
	pbx[count] = X;  pbx[count+1] = X;	\
	pby[count] = Y;  pby[count+1] = Y+1;	\
	pbz[count] = Z;  pbz[count+1] = Z;	\
	count += 2;
#define YMAJOR_PLOT(X,Y)			\
	pbx[count] = X;  pbx[count+1] = X+1;	\
	pby[count] = Y;  pby[count+1] = Y;	\
	pbz[count] = Z;  pbz[count+1] = Z;	\
	count += 2;
#include "linetemp.h"
      }
      else {
         /* unstippled, any width */
#define INTERP_XY 1
#define INTERP_Z 1
#define WIDE 1
#define PLOT(X,Y)		\
	pbx[count] = X;		\
	pby[count] = Y;		\
	pbz[count] = Z;		\
	count++;		\
	CHECK_FULL(count);
#include "linetemp.h"
      }
   }

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



static void general_smooth_rgba_line( GLcontext *ctx,
                                      GLuint vert0, GLuint vert1, GLuint pvert)
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLubyte *pbr = ctx->PB->r;
   GLubyte *pbg = ctx->PB->g;
   GLubyte *pbb = ctx->PB->b;
   GLubyte *pba = ctx->PB->a;

   if (ctx->Line.StippleFlag) {
      /* stippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define WIDE 1
#define STIPPLE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbr[count] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);	\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
   }
   else {
      /* unstippled */
      if (ctx->Line.Width==2.0F) {
         /* special case: unstippled and width=2 */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define XMAJOR_PLOT(X,Y)						\
	pbx[count] = X;  pbx[count+1] = X;				\
	pby[count] = Y;  pby[count+1] = Y+1;				\
	pbz[count] = Z;  pbz[count+1] = Z;				\
	pbr[count] = FixedToInt(r0);  pbr[count+1] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);  pbg[count+1] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);  pbb[count+1] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);  pba[count+1] = FixedToInt(a0);	\
	count += 2;
#define YMAJOR_PLOT(X,Y)						\
	pbx[count] = X;  pbx[count+1] = X+1;				\
	pby[count] = Y;  pby[count+1] = Y;				\
	pbz[count] = Z;  pbz[count+1] = Z;				\
	pbr[count] = FixedToInt(r0);  pbr[count+1] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);  pbg[count+1] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);  pbb[count+1] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);  pba[count+1] = FixedToInt(a0);	\
	count += 2;
#include "linetemp.h"
      }
      else {
         /* unstippled, any width */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define WIDE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbr[count] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);	\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
      }
   }

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}


static void general_flat_rgba_line( GLcontext *ctx,
                                    GLuint vert0, GLuint vert1, GLuint pvert )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLubyte *color = ctx->VB->Color[pvert];
   PB_SET_COLOR( ctx, ctx->PB, color[0], color[1], color[2], color[3] );
   count = ctx->PB->count;

   if (ctx->Line.StippleFlag) {
      /* stippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define WIDE 1
#define STIPPLE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
   }
   else {
      /* unstippled */
      if (ctx->Line.Width==2.0F) {
         /* special case: unstippled and width=2 */
#define INTERP_XY 1
#define INTERP_Z 1
#define XMAJOR_PLOT(X,Y)			\
	pbx[count] = X;  pbx[count+1] = X;	\
	pby[count] = Y;  pby[count+1] = Y+1;	\
	pbz[count] = Z;  pbz[count+1] = Z;	\
	count += 2;
#define YMAJOR_PLOT(X,Y)			\
	pbx[count] = X;  pbx[count+1] = X+1;	\
	pby[count] = Y;  pby[count+1] = Y;	\
	pbz[count] = Z;  pbz[count+1] = Z;	\
	count += 2;
#include "linetemp.h"
      }
      else {
         /* unstippled, any width */
#define INTERP_XY 1
#define INTERP_Z 1
#define WIDE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
      }
   }

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Flat-shaded, textured, any width, maybe stippled */
static void flat_textured_line( GLcontext *ctx,
                                GLuint vert0, GLuint vert1, GLuint pv )
{
   GLint count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLfloat *pbs = ctx->PB->s;
   GLfloat *pbt = ctx->PB->t;
   GLfloat *pbu = ctx->PB->u;
   GLubyte *color = ctx->VB->Color[pv];
   PB_SET_COLOR( ctx, ctx->PB, color[0], color[1], color[2], color[3] );
   count = ctx->PB->count;

   if (ctx->Line.StippleFlag) {
      /* stippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_STW 1
#define INTERP_UV 1
#define WIDE 1
#define STIPPLE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbs[count] = s0 / w0;		\
	pbt[count] = t0 / w0;		\
	pbu[count] = u0 / w0;		\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
   }
   else {
      /* unstippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_STW 1
#define INTERP_UV 1
#define WIDE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbs[count] = s0 / w0;		\
	pbt[count] = t0 / w0;		\
	pbu[count] = u0 / w0;		\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
   }

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/* Smooth-shaded, textured, any width, maybe stippled */
static void smooth_textured_line( GLcontext *ctx,
                                  GLuint vert0, GLuint vert1, GLuint pv )
{
   GLint count = ctx->PB->count;
   GLint *pbx = ctx->PB->x;
   GLint *pby = ctx->PB->y;
   GLdepth *pbz = ctx->PB->z;
   GLfloat *pbs = ctx->PB->s;
   GLfloat *pbt = ctx->PB->t;
   GLfloat *pbu = ctx->PB->u;
   GLubyte *pbr = ctx->PB->r;
   GLubyte *pbg = ctx->PB->g;
   GLubyte *pbb = ctx->PB->b;
   GLubyte *pba = ctx->PB->a;

   if (ctx->Line.StippleFlag) {
      /* stippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_STW 1
#define INTERP_UV 1
#define WIDE 1
#define STIPPLE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbs[count] = s0 / w0;		\
	pbt[count] = t0 / w0;		\
	pbu[count] = u0 / w0;		\
	pbr[count] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);	\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
   }
   else {
      /* unstippled */
#define INTERP_XY 1
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INTERP_STW 1
#define INTERP_UV 1
#define WIDE 1
#define PLOT(X,Y)			\
	pbx[count] = X;			\
	pby[count] = Y;			\
	pbz[count] = Z;			\
	pbs[count] = s0 / w0;		\
	pbt[count] = t0 / w0;		\
	pbu[count] = u0 / w0;		\
	pbr[count] = FixedToInt(r0);	\
	pbg[count] = FixedToInt(g0);	\
	pbb[count] = FixedToInt(b0);	\
	pba[count] = FixedToInt(a0);	\
	count++;			\
	CHECK_FULL(count);
#include "linetemp.h"
   }

   ctx->PB->count = count;
   PB_CHECK_FLUSH( ctx, ctx->PB );
}



/*
 * Null rasterizer for measuring transformation speed.
 */
static void null_line( GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv )
{
}



/*
 * Determine which line drawing function to use given the current
 * rendering context.
 */
void gl_set_line_function( GLcontext *ctx )
{
   GLboolean rgbmode = ctx->Visual->RGBAflag;
   /* TODO: antialiased lines */

   if (ctx->RenderMode==GL_RENDER) {
      if (ctx->NoRaster) {
         ctx->Driver.LineFunc = null_line;
         return;
      }
      if (ctx->Driver.LineFunc) {
         /* Device driver will draw lines. */
         ctx->Driver.LineFunc = ctx->Driver.LineFunc;
      }
      else if (ctx->Texture.Enabled) {
         if (ctx->Light.ShadeModel==GL_SMOOTH) {
            ctx->Driver.LineFunc = smooth_textured_line;
         }
         else {
            ctx->Driver.LineFunc = flat_textured_line;
         }
      }
      else if (ctx->Line.Width!=1.0 || ctx->Line.StippleFlag
               || ctx->Line.SmoothFlag || ctx->Texture.Enabled) {
         if (ctx->Light.ShadeModel==GL_SMOOTH) {
            ctx->Driver.LineFunc = rgbmode ? general_smooth_rgba_line
                                           : general_smooth_ci_line;
         }
         else {
            ctx->Driver.LineFunc = rgbmode ? general_flat_rgba_line
                                           : general_flat_ci_line;
         }
      }
      else {
	 if (ctx->Light.ShadeModel==GL_SMOOTH) {
	    /* Width==1, non-stippled, smooth-shaded */
            if (ctx->Depth.Test
	        || (ctx->Fog.Enabled && ctx->Hint.Fog==GL_NICEST)) {
               ctx->Driver.LineFunc = rgbmode ? smooth_rgba_z_line
                                              : smooth_ci_z_line;
            }
            else {
               ctx->Driver.LineFunc = rgbmode ? smooth_rgba_line
                                              : smooth_ci_line;
            }
	 }
         else {
	    /* Width==1, non-stippled, flat-shaded */
            if (ctx->Depth.Test
                || (ctx->Fog.Enabled && ctx->Hint.Fog==GL_NICEST)) {
               ctx->Driver.LineFunc = rgbmode ? flat_rgba_z_line
                                              : flat_ci_z_line;
            }
            else {
               ctx->Driver.LineFunc = rgbmode ? flat_rgba_line
                                              : flat_ci_line;
            }
         }
      }
   }
   else if (ctx->RenderMode==GL_FEEDBACK) {
      ctx->Driver.LineFunc = feedback_line;
   }
   else {
      /* GL_SELECT mode */
      ctx->Driver.LineFunc = select_line;
   }
}

