
/*
 * Mesa 3-D graphics library
 * Version:  2.3
 * 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.
 */


/*
 * amigamesartl.c
 *
 * Version 1.0  27 Jun 1998
 * by Jarno van der Linden
 * jarno@kcbbs.gen.nz
 *
 * Based on ddsample.c ver 1.5
 *
 * Version 1.1  02 Aug 1998
 * by Jarno van der Linden
 * jarno@kcbbs.gen.nz
 *
 * - Fixed several bugs in glClear()
 * - QUICKLOOP macros added
 * - Noticed that { *x = p; x++; } is faster than { *x++ = p; }
 * - Quantizer changed to plugin library
 * - Use environment variables to select quantizer
 *
 */


/*
 * This is a sample template for writing new Mesa device drivers.
 * You'll have to rewrite all the pseudo code below.
 *
 * Let's say you're interfacing Mesa to a window/operating system
 * called FOO.  Replace all occurances of FOOMesa with the real name
 * you select for your interface  (i.e. XMesa, WMesa, AmigaMesa).
 *
 * You'll have to design an API for clients to use, defined in a
 * header called Mesa/include/GL/FooMesa.h  Use the sample as an
 * example.  The API should at least have functions for creating
 * rendering contexts, binding rendering contexts to windows/frame
 * buffers, etc.
 *
 * Next, you'll have to write implementations for the device driver
 * functions described in dd.h
 *
 * Note that you'll usually have to flip Y coordinates since Mesa's
 * window coordinates start at the bottom and increase upward.  Most
 * window system's Y-axis increases downward
 *
 * Functions marked OPTIONAL may be completely omitted by your driver.
 *
 * Your Makefile should compile this module along with the rest of
 * the core Mesa library.
 */


#include <stdlib.h>
#include "GL/amigamesartl.h"
#include "context.h"
#include "depth.h"
#include "macros.h"
#include "matrix.h"
#include "types.h"
#include "vb.h"

#include "gl/quantizer.h"

#include <intuition/intuition.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/utility.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include <m68881.h>

#define RGBA(r,g,b,a)	(((r)<<24) | ((g)<<16) | ((b)<<8) | (a))

#define WINWIDTH(w)		((w)->Width - (w)->BorderLeft - (w)->BorderRight)
#define WINHEIGHT(w)	((w)->Height - (w)->BorderTop - (w)->BorderBottom)


#define QUICKLOOP16(n,l)	{ register int ql_var; \
								for(ql_var=0; ql_var<((n) & 15); ql_var++) \
								{ l; }\
								for(; ql_var<(n); ql_var+=16) \
								{ l; l; l; l; l; l; l; l; l; l; l; l; l; l; l; l; } \
							}

#define QUICKLOOP8(n,l)		{ register int ql_var; \
								for(ql_var=0; ql_var<((n) & 7); ql_var++) \
								{ l; }\
								for(; ql_var<(n); ql_var+=8) \
								{ l; l; l; l; l; l; l; l; } \
							}

#define QUICKLOOP4(n,l)		{ register int ql_var; \
								for(ql_var=0; ql_var<((n) & 3); ql_var++) \
								{ l; }\
								for(; ql_var<(n); ql_var+=4) \
								{ l; l; l; l; } \
							}

#define QUICKLOOP2(n,l)		{ register int ql_var; \
								for(ql_var=0; ql_var<((n) & 1); ql_var++) \
								{ l; }\
								for(; ql_var<(n); ql_var+=2) \
								{ l; l; } \
							}

#define QUICKLOOP1(n,l)		{ register int ql_var; \
								for(ql_var=0; ql_var<(n); ql_var++) \
								{ l; } \
							}


/*
 * This struct contains all device-driver state information.  Think of it
 * as an extension of the core GLcontext from types.h.
 */
struct amiga_mesa_rtl_context {
	GLcontext *gl_ctx;		/* the core library context */
	GLvisual *gl_visual;
	GLframebuffer *gl_buffer;	/* The depth, stencil, accum, etc buffers */
	GLboolean rgb_flag;			/* RGB mode? */
	struct Window *the_window;	/* Window pointer */
	struct DrawInfo *di;		/* DrawInfo pointer for screen info */
	unsigned long *buffer;		/* The image buffer */
	GLint width, height;		/* Size of image buffer */
	GLint wwidth, wheight;		/* Window width and height */
	unsigned long pixel;		/* current color index or RGBA pixel value */
	unsigned long clearpixel;	/* pixel for clearing the color buffers */
	unsigned long numcolours;	/* Number of colours to use for this context */
	unsigned long colourbase;	/* First colour table index */
	struct Library *quantizer;	/* Quantizer library */
	char qname[48];				/* Name of quantizer */
	ULONG qver;					/* Version of quantizer */
	/* etc... */
};


static AmigaMesaRTLContext Current = NULL;
static struct Library *quantizerBase = NULL;

static const char *renderer_string(void)
{
	return "AmigaMesaRTL";
}


static void clear_index( GLcontext *ctx, GLuint index )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* implement glClearIndex */
	/* usually just save the color index value in the amesartl struct */
	amesartl->clearpixel = amesartl->colourbase + index;
}


static void clear_color( GLcontext *ctx, GLubyte r, GLubyte g, GLubyte b, GLubyte a )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* implement glClearColor */
	/* color components are floats in [0,1] */
	/* usually just save the value in the amesartl struct */
	amesartl->clearpixel = RGBA(r,g,b,a);
}


static void clear( GLcontext *ctx,
		   GLboolean all, GLint x, GLint y, GLint width, GLint height )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	unsigned long *bp;
	unsigned char *bbp;
	unsigned long p;
/*
 * Clear the specified region of the current color buffer using the clear
 * color or index as specified by one of the two functions above.
 * If all==GL_TRUE, clear whole buffer, else just clear region defined
 * by x,y,width,height
 */

 	p = amesartl->clearpixel;
 	y = amesartl->wheight-1-y;

	if(amesartl->rgb_flag)
	{
		if(all)
		{
			bp = amesartl->buffer;
			QUICKLOOP16(amesartl->width*amesartl->height,
				{
					*bp = p;
					bp++;
				}
			)
		}
		else
		{
			bp = amesartl->buffer+y*amesartl->width+x;
			QUICKLOOP16(height,
				{
					QUICKLOOP16(width,
						{
							*bp = p;
							bp++;
						}
					)
					bp -= amesartl->width + width;
				}
			)
		}
	}
	else
	{
		if(all)
		{
			memset(amesartl->buffer, p, amesartl->width * amesartl->height);
		}
		else
		{
			bbp = ((unsigned char *)amesartl->buffer)+y*amesartl->width+x;
			QUICKLOOP16(height,
				{
					memset(bbp, p, width);
					bbp -= amesartl->width;
				}
			)
		}
	}
}


static void set_index( GLcontext *ctx, GLuint index )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* Set the current color index. */
	amesartl->pixel = amesartl->colourbase + index;
}


static void set_color( GLcontext *ctx, GLubyte r, GLubyte g, GLubyte b, GLubyte a )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* Set the current RGBA color. */
	/* r is in [0,ctx->Visual->RedScale]   */
	/* g is in [0,ctx->Visual->GreenScale] */
	/* b is in [0,ctx->Visual->BlueScale]  */
	/* a is in [0,ctx->Visual->AlphaScale] */
	amesartl->pixel = RGBA(r,g,b,a);
}


static GLboolean set_buffer( GLcontext *ctx, GLenum mode )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* set the current drawing/reading buffer, return GL_TRUE or GL_FALSE */
	/* for success/failure */
	if (mode==GL_FRONT)
	{
 		return GL_TRUE;
	}
	else
	{
		return GL_FALSE;
	}
}


static void get_buffer_size( GLcontext *ctx, GLuint *width, GLuint *height )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* return the width and height of the current buffer */
	/* if anything special has to been done when the buffer/window is */
	/* resized, do it now */
	GLuint rw,rh;

	*width = amesartl->wwidth = WINWIDTH(amesartl->the_window);
	*height = amesartl->wheight = WINHEIGHT(amesartl->the_window);

	rw = (((amesartl->wwidth + 15)>>4)<<4);
	rh = amesartl->wheight;

	if((rw != amesartl->width) || (rh != amesartl->height))
	{
		if(amesartl->buffer) free(amesartl->buffer);
		amesartl->buffer = NULL;

		amesartl->width = rw;
		amesartl->height = rh;

		amesartl->buffer = calloc(amesartl->width * amesartl->height, amesartl->rgb_flag ? sizeof(unsigned long) : sizeof(unsigned char));

		quantizerBase = amesartl->quantizer;
		ResizeQuantizer(amesartl->width,amesartl->height);
	}
}


static void write_color_span( GLcontext *ctx,
                              GLuint n, GLint x, GLint y,
                              const GLubyte red[], const GLubyte green[],
                              const GLubyte blue[], const GLubyte alpha[],
                              const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	unsigned long *bp;
	const GLubyte *ri,*gi,*bi,*ai,*mi;

	y = amesartl->wheight-1-y;
	bp = amesartl->buffer+y*amesartl->width+x;
	ri = red;
	gi = green;
	bi = blue;
	ai = alpha;
	mi = mask;

	if (mask)
	{
		QUICKLOOP1(n,
			{
				if (*mi)
					*bp = RGBA(*ri,*gi,*bi,*ai);
				mi++;
				bp++;
				ri++;
				gi++;
				bi++;
				ai++;
			}
		)
	}
	else
	{
		QUICKLOOP1(n,
			{
				*bp = RGBA(*ri,*gi,*bi,*ai);
				bp++;
				ri++;
				gi++;
				bi++;
				ai++;
			}
		)
	}
}


static void write_monocolor_span( GLcontext *ctx,
                                  GLuint n, GLint x, GLint y,
                                  const GLubyte mask[])
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	unsigned long *bp;
	const GLubyte *mi;
	unsigned long p;

	y = amesartl->wheight-1-y;
	bp = amesartl->buffer+y*amesartl->width+x;
	mi = mask;
	p = amesartl->pixel;

	QUICKLOOP1(n,
		{
			if (*mi)
				*bp = p;
			mi++;
			bp++;
		}
	)
}


static void write_color_pixels( GLcontext *ctx,
                                GLuint n, const GLint x[], const GLint y[],
                                const GLubyte r[], const GLubyte g[],
                                const GLubyte b[], const GLubyte a[],
                                const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const GLubyte *ri,*gi,*bi,*ai,*mi;
	const GLint *xi,*yi;

	ri = r;
	gi = g;
	bi = b;
	ai = a;
	mi = mask;
	xi = x;
	yi = y;

	QUICKLOOP1(n,
		{
			if (*mi)
				*(amesartl->buffer + (amesartl->wheight-1-*yi) * amesartl->width + *xi) = RGBA(*ri,*gi,*bi,*ai);
			mi++;
			ri++;
			gi++;
			bi++;
			ai++;
			xi++;
			yi++;
		}
	)
}


static void write_monocolor_pixels( GLcontext *ctx,
                                    GLuint n,
                                    const GLint x[], const GLint y[],
                                    const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const GLubyte *mi;
	const GLint *xi,*yi;
	unsigned long p;

	mi = mask;
	xi = x;
	yi = y;
	p = amesartl->pixel;

	QUICKLOOP1(n,
		{
			if (*mi)
				*(amesartl->buffer + (amesartl->wheight-1-*yi) * amesartl->width + *xi) = p;
			mi++;
			xi++;
			yi++;
		}
	)
}


static void write_index_span( GLcontext *ctx,
                              GLuint n, GLint x, GLint y,
                              const GLuint index[],
                              const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	unsigned char *bbp;
	const GLuint *ii;
	const GLubyte *mi;
	const unsigned long base = amesartl->colourbase;

	y = amesartl->wheight-1-y;
	bbp = ((unsigned char *)amesartl->buffer)+y*amesartl->width+x;
	ii = index;
	mi = mask;

	QUICKLOOP1(n,
		{
			if (*mi)
				*bbp = *ii + base;
			mi++;
			bbp++;
			ii++;
		}
	)
}


static void write_monoindex_span( GLcontext *ctx,
                                  GLuint n,GLint x,GLint y,const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	unsigned char *bbp;
	unsigned long p;
	const GLubyte *mi;

	y = amesartl->wheight-1-y;
	bbp = ((unsigned char *)amesartl->buffer)+y*amesartl->width+x;
	mi = mask;
	p = amesartl->pixel;

	QUICKLOOP1(n,
		{
			if (*mi)
				*bbp = p;
			mi++;
			bbp++;
		}
	)
}


static void write_index_pixels( GLcontext *ctx,
                                GLuint n, const GLint x[], const GLint y[],
                                const GLuint index[], const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const GLuint *ii;
	const GLubyte *mi;
	const GLint *xi,*yi;
	const unsigned long base = amesartl->colourbase;

	ii = index;
	mi = mask;
	xi = x;
	yi = y;

	QUICKLOOP1(n,
		{
			if (*mi)
				*(((unsigned char *)amesartl->buffer) + (amesartl->wheight-1-*yi) * amesartl->width + *xi) = *ii + base;
			mi++;
			ii++;
			xi++;
			yi++;
		}
	)
}


static void write_monoindex_pixels( GLcontext *ctx,
                                    GLuint n,
                                    const GLint x[], const GLint y[],
                                    const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	unsigned long p;
	const GLubyte *mi;
	const GLint *xi,*yi;

	p = amesartl->pixel;
	mi = mask;
	xi = x;
	yi = y;

	QUICKLOOP1(n,
		{
			if (*mi)
				*(((unsigned char *)amesartl->buffer) + (amesartl->wheight-1-*yi) * amesartl->width + *xi) = p;
			mi++;
			xi++;
			yi++;
		}
	)
}


static void read_index_span( GLcontext *ctx,
                             GLuint n, GLint x, GLint y, GLuint index[])
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const unsigned char *bbp;
	GLuint *ii;
	const unsigned long base = amesartl->colourbase;

	y = amesartl->wheight-1-y;
	bbp = ((unsigned char *)amesartl->buffer)+y*amesartl->width+x;
	ii = index;

	QUICKLOOP1(n,
		{
			*ii = *bbp - base;
			ii++;
			bbp++;
		}
	)
}


static void read_color_span( GLcontext *ctx,
                             GLuint n, GLint x, GLint y,
                             GLubyte red[], GLubyte green[],
                             GLubyte blue[], GLubyte alpha[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const unsigned char *bbp;
	GLubyte *ri,*gi,*bi,*ai;

	y = amesartl->wheight-1-y;
	bbp = (unsigned char *)(amesartl->buffer+y*amesartl->width+x);
	ri = red;
	gi = green;
	bi = blue;
	ai = alpha;

	QUICKLOOP1(n,
		{
			*ri = *bbp;
			ri++;
			bbp++;
			*gi = *bbp;
			gi++;
			bbp++;
			*bi = *bbp;
			bi++;
			bbp++;
			*ai = *bbp;
			ai++;
			bbp++;
		}
	)
}


static void read_index_pixels( GLcontext *ctx,
                               GLuint n, const GLint x[], const GLint y[],
                               GLuint indx[], const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const GLint *xi,*yi;
	GLuint *ii;
	const GLubyte *mi;
	const unsigned long base = amesartl->colourbase;

	ii = indx;
	xi = x;
	yi = y;
	mi = mask;
	QUICKLOOP1(n,
		{
			if(*mi)
				*ii = *(((unsigned char *)amesartl->buffer) + (amesartl->wheight-1-*yi) * amesartl->width + *xi) - base;
			mi++;
			xi++;
			yi++;
			ii++;
		}
	)
}


static void read_color_pixels( GLcontext *ctx,
                               GLuint n, const GLint x[], const GLint y[],
                               GLubyte red[], GLubyte green[],
                               GLubyte blue[], GLubyte alpha[],
                               const GLubyte mask[] )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	const unsigned char *bbp;
	GLubyte *ri,*gi,*bi,*ai;
	const GLint *xi,*yi;
	const GLubyte *mi;

	ri = red;
	gi = green;
	bi = blue;
	ai = alpha;
	xi = x;
	yi = y;
	mi = mask;

	QUICKLOOP1(n,
		{
			if(*mi)
			{
				bbp = (unsigned char *)(amesartl->buffer + (amesartl->wheight-1-*yi) *amesartl->width + *xi);
				*ri = *bbp;
				bbp++;
				*gi = *bbp;
				bbp++;
				*bi = *bbp;
				bbp++;
				*ai = *bbp;
				bbp++;
			}
			mi++;
			ri++;
			gi++;
			bi++;
			ai++;
			xi++;
			yi++;
		}
	)
}



static void finish( GLcontext *ctx )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* OPTIONAL FUNCTION: implements glFinish if possible */
}



static void flush( GLcontext *ctx )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* OPTIONAL FUNCTION: implements glFlush if possible */

	/* OK. The big one. Dump the buffer to the window */

	/* See if the window is the size we expect it to be. */
	/* If not, better not do anything, we might write into non-existent */
	/* window space. We expect a redraw soon anyway. */
	if((WINWIDTH(amesartl->the_window) != amesartl->wwidth) ||
	   (WINHEIGHT(amesartl->the_window) != amesartl->wheight))
		return;

	quantizerBase = amesartl->quantizer;
	Quantize(amesartl->buffer);
}


static GLboolean index_mask( GLcontext *ctx, GLuint mask )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* OPTIONAL FUNCTION: implement glIndexMask if possible, else
	 * return GL_FALSE
	 */
	return(GL_FALSE);
}


static GLboolean color_mask( GLcontext *ctx,
                             GLboolean rmask, GLboolean gmask,
                             GLboolean bmask, GLboolean amask)
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* OPTIONAL FUNCTION: implement glColorMask if possible, else
	 * return GL_FALSE
	 */
	return(GL_FALSE);
}


static GLboolean logicop( GLcontext *ctx, GLenum op )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/*
	 * OPTIONAL FUNCTION:
	 * Implements glLogicOp if possible.  Return GL_TRUE if the device driver
	 * can perform the operation, otherwise return GL_FALSE.  If GL_FALSE
	 * is returned, the logic op will be done in software by Mesa.
	 */
	return(GL_FALSE);
}


static void dither( GLcontext *ctx, GLboolean enable )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* OPTIONAL FUNCTION: enable/disable dithering if applicable */
}


static void error( GLcontext *ctx )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
	/* OPTIONAL FUNCTION: catch error */
}


/**********************************************************************/
/*****                 Optimized triangle rendering               *****/
/**********************************************************************/
/* (Based on osmesa.c) */

/*
 * Smooth-shaded, z triangle, RGBA color.
 */
static void smooth_color_z_triangle( GLcontext *ctx, GLuint v0, GLuint v1,
                                     GLuint v2, GLuint pv )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
#define INTERP_Z 1
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INNER_LOOP( LEFT, RIGHT, Y )							\
{																\
   const GLint len = RIGHT-LEFT;								\
   unsigned long *bp;											\
   GLdepth *zr = zRow;											\
   GLdepth z;													\
   bp = amesartl->buffer+(amesartl->wheight-1-Y)*amesartl->width+LEFT;	\
   QUICKLOOP1(len,												\
      {															\
         z = FixedToDepth(ffz);									\
         if (z < *zr) {											\
            *zr = z;											\
            *bp = RGBA(FixedToInt(ffr), FixedToInt(ffg),		\
                       FixedToInt(ffb), FixedToInt(ffa) );		\
         }														\
         ffr += fdrdx;  ffg += fdgdx;  ffb += fdbdx;  ffa += fdadx;	\
         ffz += fdzdx;											\
         bp++;													\
         zr++;													\
      }															\
   )															\
}
#include "tritemp.h"
}


/*
 * Flat-shaded, z triangle, RGBA color.
 */
static void flat_color_z_triangle( GLcontext *ctx, GLuint v0, GLuint v1,
                                   GLuint v2, GLuint pv )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
#define INTERP_Z 1
#define SETUP_CODE					\
   GLubyte r = VB->Color[pv][0];	\
   GLubyte g = VB->Color[pv][1];	\
   GLubyte b = VB->Color[pv][2];	\
   GLubyte a = VB->Color[pv][3];	\
   unsigned long p = RGBA(r,g,b,a);

#define INNER_LOOP( LEFT, RIGHT, Y )							\
{																\
   const GLint len = RIGHT-LEFT;								\
   unsigned long *bp;											\
   GLdepth *zr = zRow;											\
   GLdepth z;													\
   bp = amesartl->buffer+(amesartl->wheight-1-Y)*amesartl->width+LEFT;	\
   QUICKLOOP1(len,												\
      {															\
         z = FixedToDepth(ffz);									\
         if (z < *zr) {											\
            *zr = z;											\
            *bp = p;											\
         }														\
         ffz += fdzdx;											\
         bp++;													\
         zr++;													\
      }															\
   )															\
}
#include "tritemp.h"
}



/*
 * Smooth-shaded, z-less triangle, RGBA color.
 */
static void smooth_color_triangle( GLcontext *ctx, GLuint v0, GLuint v1,
                                   GLuint v2, GLuint pv )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
#define INTERP_RGB 1
#define INTERP_ALPHA 1
#define INNER_LOOP( LEFT, RIGHT, Y )							\
{																\
   const GLint len = RIGHT-LEFT;								\
   unsigned long *bp;											\
   bp = amesartl->buffer+(amesartl->wheight-1-Y)*amesartl->width+LEFT;	\
   QUICKLOOP1(len,												\
      {															\
         *bp = RGBA(FixedToInt(ffr), FixedToInt(ffg),			\
                    FixedToInt(ffb), FixedToInt(ffa) );			\
         ffr += fdrdx;  ffg += fdgdx;  ffb += fdbdx;  ffa += fdadx;	\
         bp++;													\
      }															\
   )															\
}
#include "tritemp.h"
}


/*
 * Flat-shaded, z triangle, RGBA color.
 */
static void flat_color_triangle( GLcontext *ctx, GLuint v0, GLuint v1,
                                 GLuint v2, GLuint pv )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;
#define SETUP_CODE					\
   GLubyte r = VB->Color[pv][0];	\
   GLubyte g = VB->Color[pv][1];	\
   GLubyte b = VB->Color[pv][2];	\
   GLubyte a = VB->Color[pv][3];	\
   unsigned long p = RGBA(r,g,b,a);

#define INNER_LOOP( LEFT, RIGHT, Y )							\
{																\
   const GLint len = RIGHT-LEFT;								\
   unsigned long *bp;											\
   bp = amesartl->buffer+(amesartl->wheight-1-Y)*amesartl->width+LEFT;	\
   QUICKLOOP1(len,												\
      {															\
         *bp = p;												\
         bp++;													\
      }															\
   )															\
}
#include "tritemp.h"
}


/*
 * Return pointer to an accelerated triangle function if possible.
 */
static triangle_func choose_triangle_function( GLcontext *ctx )
{
	struct amiga_mesa_rtl_context *amesartl = (struct amiga_mesa_rtl_context *) ctx->DriverCtx;

	if(amesartl->rgb_flag == GL_FALSE)
		return NULL;

	if (ctx->Polygon.SmoothFlag)     return NULL;
	if (ctx->Polygon.StippleFlag)    return NULL;
	if (ctx->Texture.Enabled)        return NULL;

	if (ctx->RasterMask==DEPTH_BIT
	    && ctx->Depth.Func==GL_LESS
	    && ctx->Depth.Mask==GL_TRUE)
	{
		if (ctx->Light.ShadeModel==GL_SMOOTH) {
			return smooth_color_z_triangle;
		}
		else
		{
			return flat_color_z_triangle;
		}
	}
	else if(ctx->RasterMask==0)
	{
		if (ctx->Light.ShadeModel==GL_SMOOTH) {
			return smooth_color_triangle;
		}
		else
		{
			return flat_color_triangle;
		}
	}

	return NULL;
}



/**********************************************************************/
/**********************************************************************/


static void setup_DD_pointers( GLcontext *ctx )
{
	/* Initialize all the pointers in the DD struct.  Do this whenever */
	/* a new context is made current or we change buffers via set_buffer! */

	ctx->Driver.RendererString = renderer_string;
	ctx->Driver.UpdateState = setup_DD_pointers;

	ctx->Driver.ClearIndex = clear_index;
	ctx->Driver.ClearColor = clear_color;
	ctx->Driver.Clear = clear;

	ctx->Driver.Index = set_index;
	ctx->Driver.Color = set_color;

	ctx->Driver.SetBuffer = set_buffer;
	ctx->Driver.GetBufferSize = get_buffer_size;

	/* Pixel/span writing functions: */
	ctx->Driver.WriteColorSpan       = write_color_span;
	ctx->Driver.WriteMonocolorSpan   = write_monocolor_span;
	ctx->Driver.WriteColorPixels     = write_color_pixels;
	ctx->Driver.WriteMonocolorPixels = write_monocolor_pixels;
	ctx->Driver.WriteIndexSpan       = write_index_span;
	ctx->Driver.WriteMonoindexSpan   = write_monoindex_span;
	ctx->Driver.WriteIndexPixels     = write_index_pixels;
	ctx->Driver.WriteMonoindexPixels = write_monoindex_pixels;

	/* Pixel/span reading functions: */
	ctx->Driver.ReadIndexSpan = read_index_span;
	ctx->Driver.ReadColorSpan = read_color_span;
	ctx->Driver.ReadIndexPixels = read_index_pixels;
	ctx->Driver.ReadColorPixels = read_color_pixels;

	ctx->Driver.TriangleFunc = choose_triangle_function( ctx );

	/*
	 * OPTIONAL FUNCTIONS:  these may be left uninitialized if the device
	 * driver can't/needn't implement them.
	 */
	ctx->Driver.Flush = flush;
#if 0
	ctx->Driver.Finish = finish;
	ctx->Driver.IndexMask = index_mask;
	ctx->Driver.ColorMask = color_mask;
	ctx->Driver.LogicOp = logicop;
	ctx->Driver.Dither = dither;
	ctx->Driver.Error = error;
	ctx->Driver.NearFar = near_far;
#endif
}



/**********************************************************************/
/*****               FOO/Mesa API Functions                       *****/
/**********************************************************************/


__asm __saveds AmigaMesaRTLContext AmigaMesaRTLCreateContextA( register __a0 struct Window *window, register __a1 struct TagItem *tags )
{
	AmigaMesaRTLContext c;
	GLboolean rgb_flag, alpha_flag, db_flag;
	GLint depth_bits, stencil_bits, accum_bits, index_bits;
	GLfloat red_scale, green_scale, blue_scale, alpha_scale;
	GLint red_bits, green_bits, blue_bits, alpha_bits;
	int maxnumcolours;
	char *strp,str[32];

	c = (AmigaMesaRTLContext) calloc( 1, sizeof(struct amiga_mesa_rtl_context) );
	if (!c)
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}

	rgb_flag = GetTagData(AMRTL_RGBAMode, (ULONG)TRUE, tags) ? GL_TRUE : GL_FALSE;
	rgb_flag = GetTagData(AMRTL_IndexMode, (ULONG)FALSE, tags) ? GL_FALSE : rgb_flag;

	if(rgb_flag)
	{
		/* RGB(A) mode */
		alpha_flag = GL_FALSE;
		db_flag = GL_FALSE;
		depth_bits = DEPTH_BITS;
		stencil_bits = STENCIL_BITS;
		accum_bits = ACCUM_BITS;
		index_bits = 0;
		red_scale = 255.0;
		green_scale = 255.0;
		blue_scale = 255.0;
		alpha_scale = 255.0;
		red_bits = 8;
		green_bits = 8;
		blue_bits = 8;
		alpha_bits = 0;
	}
	else
	{
		/* color index mode */
		alpha_flag = GL_FALSE;
		db_flag = GL_FALSE;
		depth_bits = DEPTH_BITS;
		stencil_bits = STENCIL_BITS;
		accum_bits = ACCUM_BITS;
		index_bits = 8;
		red_scale = 0.0;
		green_scale = 0.0;
		blue_scale = 0.0;
		alpha_scale = 0.0;
		red_bits = 8;
		green_bits = 8;
		blue_bits = 8;
		alpha_bits = 0;
	}

	/* Create core visual */
	c->gl_visual = gl_create_visual( rgb_flag,
	                                 alpha_flag,
	                                 db_flag,
	                                 depth_bits,
	                                 stencil_bits,
	                                 accum_bits,
	                                 index_bits,
	                                 red_scale, green_scale, blue_scale, alpha_scale,
	                                 red_bits, green_bits, blue_bits, alpha_bits );

	if(!c->gl_visual)
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}

	c->gl_ctx = gl_create_context( c->gl_visual,
	                               NULL,
	                               (void *) c );

	if(!c->gl_ctx)
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}

	c->gl_buffer = gl_create_framebuffer( c->gl_visual );
	if(!c->gl_buffer)
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}

	c->rgb_flag = rgb_flag;
	c->the_window = window;
	c->di = GetScreenDrawInfo(c->the_window->WScreen);
	if(!c->di)
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}
	c->buffer = NULL;
	c->width = 0;
	c->height = 0;
	c->wwidth = WINWIDTH(c->the_window);
	c->wheight = WINHEIGHT(c->the_window);
	c->pixel = 0;
	c->clearpixel = 0;
	maxnumcolours = 1L<<c->di->dri_Depth;
	c->numcolours = GetTagData(AMRTL_NumColours, maxnumcolours, tags);
	c->colourbase = GetTagData(AMRTL_ColourBase, (ULONG)0, tags);
	if(c->colourbase > (maxnumcolours-2))
		c->colourbase = maxnumcolours-2;
	if(c->colourbase < 0)
		c->colourbase = 0;
	if((c->colourbase + c->numcolours) > maxnumcolours)
	{
		c->numcolours = maxnumcolours-c->colourbase;
	}
	if(c->numcolours < 2)
		c->numcolours = 2;

	strcpy(c->qname,"quantizers/");
	if(GetVar("AmigaMesaRTL/Quantizer",str,32,0) == -1)
		strcpy(str,"dl1");
	strp = (char *)GetTagData(AMRTL_Quantizer, NULL, tags);
	if(strp)
	{
		strncpy(str,strp,32);
		str[31] = '\0';
	}
	strcat(c->qname,str);

	c->qver = 0;
	if(GetVar("AmigaMesaRTL/QuantizerVersion",str,32,0) != -1)
		c->qver = atol(str);
	c->qver = GetTagData(AMRTL_QuantizerVersion, c->qver, tags);

	c->quantizer = OpenLibrary(c->qname,c->qver);
	if(!c->quantizer)
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}

	quantizerBase = c->quantizer;
	if(!InitQuantizer(window, rgb_flag ? AMRTL_RGBAMode : AMRTL_IndexMode, c->numcolours, c->colourbase))
	{
		AmigaMesaRTLDestroyContext( c );
		return NULL;
	}

	return c;
}



__asm __saveds void AmigaMesaRTLDestroyContext( register __a0 AmigaMesaRTLContext c )
{
	if(c)
	{
		if(c->quantizer)
		{
			quantizerBase = c->quantizer;
			DeleteQuantizer();
			CloseLibrary(c->quantizer);
		}

		if(c->di) FreeScreenDrawInfo(c->the_window->WScreen, c->di);
		c->di = NULL;

		if(c->gl_buffer) gl_destroy_framebuffer( c->gl_buffer );
		c->gl_buffer = NULL;

		if(c->gl_ctx) gl_destroy_context( c->gl_ctx );
		c->gl_ctx = NULL;

		if(c->gl_visual) gl_destroy_visual( c->gl_visual );
		c->gl_visual = NULL;

		if(c->buffer) free(c->buffer);
		c->buffer = NULL;

		free(c);
	}
	c = NULL;
}



/*
 * Make the specified context the current one
 * Might also want to specify the window/drawable here, like for GLX.
 */
__asm __saveds void AmigaMesaRTLMakeCurrent( register __a0 AmigaMesaRTLContext c )
{
	gl_make_current( c->gl_ctx, c->gl_buffer );
	setup_DD_pointers( c->gl_ctx );
	gl_Viewport( c->gl_ctx, 0, 0, c->wwidth, c->wheight );

	Current = c;
}


__asm __saveds AmigaMesaRTLContext AmigaMesaRTLGetCurrentContext( void )
{
	return Current;
}


/* you may need to add other FOO/Mesa functions too... */

