/****************************************************************************
*
*				 	   Mesa bindings for SciTech MGL
*
* Language:		ANSI C
* Environment:	Any
*
* Description:	Main Mesa 3D library interface between the MGL and
*               Mesa. For maximum peformance we call the MGL's internal
*				rendering functions directly so we can bypass all the
*				clipping and regular MGL API stuff as all that is already
*				handled by Mesa before the calls get here.
*
*				Note also that all rendering is inherently single threaded
*				so we optimize for this case by using a global current
*				context structure.
*
*				This module contains generic interface routines.
*
* 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.
*
****************************************************************************/

#include "mgl/mmesap.h"
#pragma hdrstop
#include "GL/mglmesa.h"
#include <string.h>
#include <stdlib.h>

/*--------------------------- Global Variables ----------------------------*/

gmode_t			_MGL_mi;				/* MGL mode information			*/
pixel_format_t	_MGL_pf;				/* MGL mode pixel format		*/
vecs			_MGL_vecs;				/* MGL Rendering vectors 		*/
MGLRC			_MM_rc;		   			/* Current rendering context	*/
MGLRC			*_MM_rcPtr = NULL;		/* Pointer to current context	*/

/*------------------------- Implementation --------------------------------*/

#pragma warn -par

/**********************************************************************/
/*****              Miscellaneous device driver funcs             *****/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
mode	- Rendering mode

REMARKS:
Called when glBegin is called. We use this oportunity to enable direct
framebuffer access for our rendering functions.
****************************************************************************/
static void begin(
    GLcontext *ctx)
{
    if (_MGL_vecs.beginDirectAccess)
        _MGL_vecs.beginDirectAccess();
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Called when glEnd is called. We use this oportunity to disable direct
framebuffer access for our rendering functions.
****************************************************************************/
static void end(
	GLcontext *ctx)
{
    if (_MGL_vecs.endDirectAccess)
        _MGL_vecs.endDirectAccess();
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Called when glFinish is called. We simply do nothing here.
****************************************************************************/
static void finish(
	GLcontext *ctx)
{
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Called when glFlush is called. We simply do nothing here.
****************************************************************************/
static void flush(
	GLcontext *ctx)
{
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
index	- New color index for buffer clears

REMARKS:
Sets the current buffer clear color for color index buffers.
****************************************************************************/
static void clear_index(
	GLcontext *ctx,
	GLuint index)
{
	RC.clearColor = index;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
r,g,b,a	- New RGBA color values for buffer clears

REMARKS:
Set the current buffer clear color for TrueColor buffers. Note that Mesa
passes us color values pre-scaled to the proper number of color bits:

	[0,ctx->Visual->RedScale]
	[0,ctx->Visual->GreenScale]
	[0,ctx->Visual->BlueScale]
	[0,ctx->Visual->AlphaScale]

****************************************************************************/
static void clear_color(
	GLcontext *ctx,
	GLubyte r,
	GLubyte g,
	GLubyte b,
	GLubyte a)
{
	RC.clearColor = MGL_packColorFast(&PF,r,g,b);
}

/****************************************************************************
PARAMETERS:
ctx					- Mesa rendering context
mask				- Mask for buffers to clear
all					- True to clear entire buffer
x,y,width,height	- Rectangle to clear

REMARKS:
Clears the buffer in the current clear color. Because we only handle
color buffer clearing in here, we only clear the color buffer bit so that
the Mesa generic code will clear the remaining buffers.
****************************************************************************/
static GLbitfield clear(
	GLcontext *ctx,
	GLbitfield mask,
	GLboolean all,
	GLint x,
	GLint y,
	GLint width,
	GLint height)
{
   	if (mask & GL_COLOR_BUFFER_BIT) {
		VECS.SetForeColor(RC.clearColor);
		VECS.SetMix(GA_REPLACE_MIX);
		if (all)
			VECS.solid.DrawRect(0,0,MI.xRes+1,MI.yRes+1);
		else
			VECS.solid.DrawRect(x,y,width,height);
        }
	return mask & (~GL_COLOR_BUFFER_BIT);
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
index	- New color index

REMARKS:
Sets the current solid color for color index buffers.
****************************************************************************/
static void set_index(
	GLcontext *ctx,
	GLuint index)
{
	RC.color = index;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
r,g,b,a	- New RGBA color value

REMARKS:
Set the current solid color for TrueColor buffers. Note that Mesa passes
us color values pre-scaled to the proper number of color bits:

	[0,ctx->Visual->RedScale]
	[0,ctx->Visual->GreenScale]
	[0,ctx->Visual->BlueScale]
	[0,ctx->Visual->AlphaScale]

****************************************************************************/
static void set_color(
	GLcontext *ctx,
	GLubyte r,
	GLubyte g,
	GLubyte b,
	GLubyte a)
{
	RC.color = MGL_packColorFast(&PF,r,g,b);
	RC.red = r;
	RC.green = g;
    RC.blue = b;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
mask	- Color index mask

RETURNS:
True if we implement masking, false if not

REMARKS:
Implements color index masking ala glIndexMask if possible. We currently
return false for this function.
****************************************************************************/
static GLboolean index_mask(
	GLcontext *ctx,
	GLuint mask)
{
	return GL_FALSE;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
rmask	- Red mask
gmask	- Green mask
bmask	- Blue mask
amask	- Alpha mask

RETURNS:
True if we implement masking, false if not

REMARKS:
Implements RGB color masking ala glColorMask if possible. We currently
return false for this function.
****************************************************************************/
static GLboolean color_mask(
	GLcontext *ctx,
	GLboolean rmask,
	GLboolean gmask,
	GLboolean bmask,
	GLboolean amask)
{
	return GL_FALSE;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
op		- New logic op to enable

RETURNS:
True if we implement the logic operation in our driver, false if not

REMARKS:
Implements logic operations ala glLogicOp if possible. For op's that we dont
support we simply return GL_FALSE and it is implemented in software by Mesa.
****************************************************************************/
static GLboolean logicop(
	GLcontext *ctx,
	GLenum op)
{
#if 0
	int	mode;

	switch (op) {
		case GL_COPY:	mode = MGL_REPLACE_MODE;	break;
		case GL_AND:	mode = MGL_AND_MODE;		break;
		case GL_OR:		mode = MGL_OR_MODE;			break;
		case GL_XOR:	mode = MGL_XOR_MODE;		break;
		default:  		return GL_FALSE;
		}
	DC.r.setWriteMode(mode);
	return GL_TRUE;
#else
	return GL_FALSE;
#endif
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
enable	- True to enable dithering, false to disable it

REMARKS:
Enables dithering mode if applicable.
****************************************************************************/
static void dither(
	GLcontext *ctx,
	GLboolean enable)
{
	if (enable) 
		RC.pixelformat = RC.dithered_pf;
	else
		RC.pixelformat = RC.undithered_pf;
	setup_DD_pointers(ctx);
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
mode	- Buffer mode to enable

REMARKS:
Sets the buffer access mode to the front buffer or the back buffer for the
application. The MGL always allows direct access to both buffers in double
buffered fullscreen modes, but when rendering to a memory device context
(ie: when rendering in a window in the windows environment) we only have
a single buffer available.
****************************************************************************/
static GLboolean set_buffer(
	GLcontext *ctx,
	GLenum mode)
{
	if (!RC.gl_vis->DBflag) {
		MGL_makeCurrentDC(RC.dc);
		return (mode = GL_FRONT);
		}
	if (RC.dc->mi.maxPage > 0) {
		/* Hardware page flipping */
		if (mode == GL_FRONT)
			MGL_setActivePage(RC.dc,RC.frontbuffer);
		else if (mode == GL_BACK)
			MGL_setActivePage(RC.dc,RC.backbuffer);
		else if (mode == GL_FRONT_LEFT)
			MGL_setActivePage(RC.dc,RC.frontbuffer | MGL_LEFT_BUFFER);
		else if (mode == GL_FRONT_RIGHT)
			MGL_setActivePage(RC.dc,RC.frontbuffer | MGL_RIGHT_BUFFER);
		else if (mode == GL_BACK_LEFT)
			MGL_setActivePage(RC.dc,RC.backbuffer | MGL_LEFT_BUFFER);
		else if (mode == GL_BACK_RIGHT)
			MGL_setActivePage(RC.dc,RC.backbuffer | MGL_RIGHT_BUFFER);
		else
			return GL_FALSE;
		}
	if (RC.memdc) {
		/* Software double buffering using a memory DC. In some cases
		 * we may not be able to access the display memory surface for the
		 * front buffer in which case this will fail.
		 */
		// TODO: need to figure out how to handle dual memory back buffers
		//		 for stereo rendering with only two main display pages!
		if (mode == GL_FRONT && (MGL_surfaceAccessType(RC.dc) != MGL_NO_ACCESS)) 
			MGL_makeCurrentDC(RC.dc);
		else if (mode == GL_BACK)
			MGL_makeCurrentDC(RC.memdc);
		else
			return GL_FALSE;
		}
	else {
		/* Hardware page flipping only */
		MGL_makeCurrentDC(RC.dc);
		}
	RC.bufferMode = mode;
	setup_DD_pointers(ctx);
	return GL_TRUE;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context
width	- Place to store buffer width
height	- Place to store buffer height

REMARKS:
Returns the current buffer width and height. We simply return the currently
active device context's width and height, which will change if the buffer
is resized in a windowed environment.
****************************************************************************/
static void get_buffer_size(
	GLcontext *ctx,
	GLuint *width,
	GLuint *height)
{
	if (MI.xRes != RC.dispdc->mi.xRes || MI.yRes != RC.dispdc->mi.yRes) {
		MI.xRes = RC.dispdc->mi.xRes;
		MI.yRes = RC.dispdc->mi.yRes;

		/* Resize our system memory back buffer if we have one */
		if (RC.memdc) {
			MGL_destroyDC(RC.memdc);
			RC.memdc = MGL_createMemoryDC(MI.xRes+1,MI.yRes+1,MI.bitsPerPixel,&PF);
			if (!RC.memdc)
				exit(1);
			RC.dc = RC.memdc;
			if (RC.dispdc->mi.bitsPerPixel == 8) {
            	palette_t pal[256];
				MGL_getPalette(RC.dispdc,pal,256,0);
				MGL_setPalette(RC.memdc,pal,256,0);
				MGL_realizePalette(RC.memdc,256,0,-1);
				}
			MI = RC.dc->mi;
			}
		}
	*width = MI.xRes+1;
	*height = MI.yRes+1;
	RC.bottom = MI.yRes;
}

/**********************************************************************/
/***                    Point rendering                             ***/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Analyze context state to see if we can provide a fast points drawing
function, like those in points.c.  Otherwise, return NULL.
****************************************************************************/
points_func mmesa_get_points_func(
	GLcontext *ctx)
{
#if 0
	if (ctx->Point.Size == 1.0F && !ctx->Point.SmoothFlag && ctx->RasterMask == 0
			&& !ctx->Texture.Enabled) {
		return draw_points;
		}
#endif
	return NULL;
}

/**********************************************************************/
/***                      Line rendering                            ***/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Analyze context state to see if we can provide a fast line drawing function,
like those in lines.c.  Otherwise, return NULL.
****************************************************************************/
line_func mmesa_get_line_func(
	GLcontext *ctx)
{
#if 0
   XMesaContext xmesa = (XMesaContext) ctx->DriverCtx;
   int depth = xmesa->xm_visual->visinfo->depth;

   if (ctx->Line.SmoothFlag)              return NULL;
   if (ctx->Texture.Enabled)              return NULL;
   if (ctx->Light.ShadeModel!=GL_FLAT)    return NULL;

   if (xmesa->xm_buffer->buffer==XIMAGE
       && ctx->RasterMask==DEPTH_BIT
       && ctx->Depth.Func==GL_LESS
       && ctx->Depth.Mask==GL_TRUE
       && ctx->Line.Width==1.0F
       && ctx->Line.StippleFlag==GL_FALSE) {
      switch (xmesa->pixelformat) {
         case PF_TRUECOLOR:
            return flat_TRUECOLOR_z_line;
		 case PF_8A8B8G8R:
            return flat_8A8B8G8R_z_line;
         case PF_8R8G8B:
            return flat_8R8G8B_z_line;
         case PF_5R6G5B:
            return flat_5R6G5B_z_line;
         case PF_DITHER:
            return (depth==8) ? flat_DITHER8_z_line : NULL;
         case PF_LOOKUP:
            return (depth==8) ? flat_LOOKUP8_z_line : NULL;
         case PF_HPCR:
            return flat_HPCR_z_line;
         default:
            return NULL;
      }
   }
   if (xmesa->xm_buffer->buffer==XIMAGE
       && ctx->RasterMask==0
       && ctx->Line.Width==1.0F
       && ctx->Line.StippleFlag==GL_FALSE) {
      switch (xmesa->pixelformat) {
         case PF_TRUECOLOR:
            return flat_TRUECOLOR_line;
         case PF_8A8B8G8R:
            return flat_8A8B8G8R_line;
         case PF_8R8G8B:
            return flat_8R8G8B_line;
         case PF_5R6G5B:
            return flat_5R6G5B_line;
         case PF_DITHER:
            return (depth==8) ? flat_DITHER8_line : NULL;
         case PF_LOOKUP:
            return (depth==8) ? flat_LOOKUP8_line : NULL;
         case PF_HPCR:
            return flat_HPCR_line;
	 default:
	    return NULL;
	  }
   }
   if (xmesa->xm_buffer->buffer!=XIMAGE && ctx->RasterMask==0) {
	  setup_x_line_options( ctx );
	  return flat_pixmap_line;
   }
#endif
	return NULL;
}

/**********************************************************************/
/***                   Triangle rendering                            ***/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Analyze context state to see if we can provide a fast triangle drawing
function, like those in tris.c.  Otherwise, return NULL.
****************************************************************************/
triangle_func mmesa_get_triangle_func(
	GLcontext *ctx)
{
#if 0
   XMesaContext xmesa = (XMesaContext) ctx->DriverCtx;
   int depth = xmesa->xm_visual->visinfo->depth;

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

   if (xmesa->xm_buffer->buffer==XIMAGE) {
      if (   ctx->Light.ShadeModel==GL_SMOOTH
          && ctx->RasterMask==DEPTH_BIT
          && ctx->Depth.Func==GL_LESS
          && ctx->Depth.Mask==GL_TRUE
          && ctx->Polygon.StippleFlag==GL_FALSE) {
         switch (xmesa->pixelformat) {
            case PF_TRUECOLOR:
	       return smooth_TRUECOLOR_z_triangle;
            case PF_8A8B8G8R:
               return smooth_8A8B8G8R_z_triangle;
            case PF_8R8G8B:
               return smooth_8R8G8B_z_triangle;
            case PF_5R6G5B:
               return smooth_5R6G5B_z_triangle;
            case PF_HPCR:
	       return smooth_HPCR_z_triangle;
            case PF_DITHER:
               return (depth==8) ? smooth_DITHER8_z_triangle
                                        : smooth_DITHER_z_triangle;
            case PF_LOOKUP:
               return (depth==8) ? smooth_LOOKUP8_z_triangle : NULL;
            default:
               return NULL;
         }
      }
      if (   ctx->Light.ShadeModel==GL_FLAT
          && ctx->RasterMask==DEPTH_BIT
          && ctx->Depth.Func==GL_LESS
          && ctx->Depth.Mask==GL_TRUE
          && ctx->Polygon.StippleFlag==GL_FALSE) {
         switch (xmesa->pixelformat) {
            case PF_TRUECOLOR:
	       return flat_TRUECOLOR_z_triangle;
            case PF_8A8B8G8R:
               return flat_8A8B8G8R_z_triangle;
            case PF_8R8G8B:
               return flat_8R8G8B_z_triangle;
            case PF_5R6G5B:
               return flat_5R6G5B_z_triangle;
            case PF_HPCR:
	       return flat_HPCR_z_triangle;
            case PF_DITHER:
               return (depth==8) ? flat_DITHER8_z_triangle
                                        : flat_DITHER_z_triangle;
            case PF_LOOKUP:
               return (depth==8) ? flat_LOOKUP8_z_triangle : NULL;
            default:
               return NULL;
         }
      }
      if (   ctx->RasterMask==0   /* no depth test */
          && ctx->Light.ShadeModel==GL_SMOOTH
          && ctx->Polygon.StippleFlag==GL_FALSE) {
         switch (xmesa->pixelformat) {
            case PF_TRUECOLOR:
	       return smooth_TRUECOLOR_triangle;
            case PF_8A8B8G8R:
               return smooth_8A8B8G8R_triangle;
            case PF_8R8G8B:
               return smooth_8R8G8B_triangle;
            case PF_5R6G5B:
               return smooth_5R6G5B_triangle;
            case PF_HPCR:
	       return smooth_HPCR_triangle;
            case PF_DITHER:
               return (depth==8) ? smooth_DITHER8_triangle
                                        : smooth_DITHER_triangle;
            case PF_LOOKUP:
               return (depth==8) ? smooth_LOOKUP8_triangle : NULL;
            default:
               return NULL;
         }
      }

      if (   ctx->RasterMask==0   /* no depth test */
          && ctx->Light.ShadeModel==GL_FLAT
          && ctx->Polygon.StippleFlag==GL_FALSE) {
         switch (xmesa->pixelformat) {
            case PF_TRUECOLOR:
	       return flat_TRUECOLOR_triangle;
            case PF_8A8B8G8R:
               return flat_8A8B8G8R_triangle;
            case PF_8R8G8B:
               return flat_8R8G8B_triangle;
            case PF_5R6G5B:
               return flat_5R6G5B_triangle;
            case PF_HPCR:
	       return flat_HPCR_triangle;
            case PF_DITHER:
               return (depth==8) ? flat_DITHER8_triangle
                                        : flat_DITHER_triangle;
            case PF_LOOKUP:
               return (depth==8) ? flat_LOOKUP8_triangle : NULL;
            default:
               return NULL;
         }
      }

      return NULL;
   }
   else {
      /* pixmap */
      if (ctx->Light.ShadeModel==GL_FLAT && ctx->RasterMask==0) {
         setup_x_polygon_options( ctx );
         return flat_pixmap_triangle;
      }
	  return NULL;
   }
#endif
	return NULL;
}

/**********************************************************************/
/***                      Quad rendering                            ***/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Analyze context state to see if we can provide a fast quad drawing
function, like those in quads.c.  Otherwise, return NULL.
****************************************************************************/
quad_func mmesa_get_quad_func(GLcontext *ctx)
{
	return NULL;
}

/**********************************************************************/
/***                   2D Rectangle Rendering                       ***/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Analyze context state to see if we can provide a fast rectangle drawing
function, like those in rects.c.  Otherwise, return NULL.
****************************************************************************/
rect_func mmesa_get_rect_func(
	GLcontext *ctx)
{
	return NULL;
}

/**********************************************************************/
/*****                 Miscellaneous functions                    *****/
/**********************************************************************/

/****************************************************************************
REMARKS:
Returns the name of our renderer.
****************************************************************************/
static const char *renderer_string(void)
{
   return "SciTech MGL " MGL_VERSION_STR;
}

/****************************************************************************
PARAMETERS:
ctx		- Mesa rendering context

REMARKS:
Sets up all the device driver rendering pointers for the current rendering
state. This function is where we swap in high performance rendering
functions when the rendering state allows this.
****************************************************************************/
static void setup_DD_pointers(
	GLcontext *ctx)
{
	ibool	doDither = false;

	/* Mandatory functions that are always the same */
	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;

	/* Driver functions that are always the same */
    ctx->Driver.RenderStart     = begin;
    ctx->Driver.RenderFinish    = end;
	ctx->Driver.Flush 			= flush;
	ctx->Driver.Finish 			= finish;
	ctx->Driver.IndexMask 		= index_mask;
	ctx->Driver.ColorMask 		= color_mask;
	ctx->Driver.LogicOp 		= logicop;
	ctx->Driver.Dither 			= dither;

	/* Accelerated point, line and triangle functions */
	ctx->Driver.PointsFunc		= mmesa_get_points_func(ctx);
	ctx->Driver.LineFunc 		= mmesa_get_line_func(ctx);
	ctx->Driver.TriangleFunc 	= mmesa_get_triangle_func(ctx);
	ctx->Driver.QuadFunc 		= mmesa_get_quad_func(ctx);
	ctx->Driver.RectFunc 		= mmesa_get_rect_func(ctx);

    /* Accelerated bitmap drawing functions */
#if 0
	ctx->Driver.DrawPixels 		= draw_pixels;
	ctx->Driver.Bitmap 			= draw_bitmap;
#endif

	/* Optimized pixel span reading/writing functions. Note that we use
	 * a (void*) cast as we have removed the 'const'ness of the parameters
	 * passed to these functions so we can use the pointers to step through
	 * our arrays efficiently.
	 */
	switch (RC.pixelformat) {
		case PF_INDEX:
			ctx->Driver.WriteCI32Span       	= (void*)_mmesa_write_span_ci;
			ctx->Driver.WriteCI8Span       		= (void*)_mmesa_write_span_ci8;
			ctx->Driver.WriteMonoCISpan   		= (void*)_mmesa_write_span_mono_ci;
			ctx->Driver.WriteCI32Pixels     	= (void*)_mmesa_write_pixels_ci;
			ctx->Driver.WriteMonoCIPixels 		= (void*)_mmesa_write_pixels_mono_ci;
			ctx->Driver.ReadCI32Span 		 	= (void*)_mmesa_read_span_ci;
			ctx->Driver.ReadCI32Pixels 	 		= (void*)_mmesa_read_pixels_ci;
			break;
		case PF_RGB8:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_8_8;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_8_8;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_8;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_8_8;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_8;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_8_8;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_8_8;
            doDither = true;
			break;
		case PF_DITHER8:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_8_DITHER8;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_8_DITHER8;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_8_DITHER8;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_8_DITHER8;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_8_DITHER8;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_8_8;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_8_8;
            doDither = true;
			break;
		case PF_RGB555:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_16_555;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_16_555;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_16;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_16_555;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_16;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_16_555;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_16_555;
			break;
		case PF_DITHER555:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_16_DITHER555;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_16_DITHER555;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_16_DITHER555;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_16_DITHER555;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_16_DITHER555;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_16_555;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_16_555;
            doDither = true;
			break;
		case PF_RGB565:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_16_565;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_16_565;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_16;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_16_565;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_16;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_16_565;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_16_565;
			break;
		case PF_DITHER565:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_16_DITHER565;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_16_DITHER565;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_16_DITHER565;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_16_DITHER565;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_16_DITHER565;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_16_565;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_16_565;
            doDither = true;
			break;
		case PF_RGB888:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_24_RGB;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_24_RGB;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_24_RGB;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_24_RGB;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_24_RGB;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_24_RGB;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_24_RGB;
			break;
		case PF_BGR888:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_24_BGR;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_24_BGR;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_24_BGR;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_24_BGR;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_24_BGR;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_24_BGR;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_24_BGR;
			break;
		case PF_ARGB8888:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_32_ARGB;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_32_ARGB;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_32;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_32_ARGB;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_32;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_32_ARGB;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_32_ARGB;
			break;
		case PF_ABGR8888:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_32_ABGR;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_32_ABGR;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_32;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_32_ABGR;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_32;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_32_ABGR;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_32_ABGR;
			break;
		case PF_RGBA8888:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_32_RGBA;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_32_RGBA;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_32;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_32_RGBA;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_32;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_32_RGBA;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_32_RGBA;
			break;
		case PF_BGRA8888:
			ctx->Driver.WriteRGBASpan       	= (void*)_mmesa_write_span_32_BGRA;
			ctx->Driver.WriteRGBSpan       		= (void*)_mmesa_write_span_rgb_32_BGRA;
			ctx->Driver.WriteMonoRGBASpan   	= (void*)_mmesa_write_span_mono_32;
			ctx->Driver.WriteRGBAPixels     	= (void*)_mmesa_write_pixels_32_BGRA;
			ctx->Driver.WriteMonoRGBAPixels 	= (void*)_mmesa_write_pixels_mono_32;
			ctx->Driver.ReadRGBASpan 		 	= (void*)_mmesa_read_span_32_BGRA;
			ctx->Driver.ReadRGBAPixels 	 		= (void*)_mmesa_read_pixels_32_BGRA;
			break;
		}
}

/**********************************************************************/
/*****               MGLMesa API Functions                        *****/
/**********************************************************************/

/****************************************************************************
PARAMETERS:
dc		- MGL device context
visual	- Structure containing visual context information

REMARKS:
This function examines the visual passed to use to deteremine if we support
the requested capabilities. If we don't we will modify the structure for
the capabilities that we do support (ie: lowering the depth buffer size
to 16 bits etc).
****************************************************************************/
void MGLAPI MGLMesaChooseVisual(
	MGLDC *dc,
	MGLVisual *visual)
{
	/* Force the depth buffer, stencil buffer and accum buffers to our sizes */
	if (visual->depth_size)
		visual->depth_size = 8 * sizeof(GLdepth);
	if (visual->stencil_size)
		visual->stencil_size = 8 * sizeof(GLstencil);
	if (visual->accum_size)
		visual->accum_size = 8 * sizeof(GLaccum);

	/* Force RGB mode for DirectColor and TrueColor modes */
	if (dc->mi.bitsPerPixel > 8)
		visual->rgb_flag = GL_TRUE;
	if (MGL_isMemoryDC(dc))
		visual->db_flag = GL_FALSE;
	if (MGL_isWindowedDC(dc) || MGL_surfaceAccessType(dc) == MGL_NO_ACCESS)
    	visual->db_flag = GL_TRUE;
}

/****************************************************************************
PARAMETERS:
mask	- Mask of bits to count

RETURNS:
Number of one bits in the mask.

REMARKS:
Counts the number of set bits in a mask and determines the shift amount.
****************************************************************************/
static int countBits(
	uint mask)
{
	int i = 0;

	if (mask) {
		while (mask & (1 << i)) {
			i++;
			};
		}
	return i;
}

/****************************************************************************
PARAMETERS:
dc		- MGL device context
visual	- Structure containing visual context information

RETURNS:
True on success, false if visual creation failed

REMARKS:
This function attempts to create a Mesa visual for the device context. If
this succeeds then Mesa can properly handle the requested visual.
****************************************************************************/
ibool MGLAPI MGLMesaSetVisual(
	MGLDC *dc,
	MGLVisual *vis)
{
	GLfloat red_scale, green_scale, blue_scale, alpha_scale;
	GLint red_bits, green_bits, blue_bits, alpha_bits;

	/* Do quick check on incoming visual */
	if (dc->mi.bitsPerPixel > 8 && !vis->rgb_flag)
		return false;
	if (dc->mi.bitsPerPixel < 8)
		return false; 
	if ((dc->mi.xRes+1 > MAX_WIDTH) || (dc->mi.yRes+1 > MAX_HEIGHT))
    	return false;
	if (MGL_isMemoryDC(dc) && vis->db_flag)
		return false;
	if ((MGL_isWindowedDC(dc) || MGL_surfaceAccessType(dc) == MGL_NO_ACCESS) && !vis->db_flag)
		return false;
	if (vis->stereo_flag && !(dc->flags & MGL_STEREO_ACCESS))
		return false;

	/* Create color channel scale factors from pixel format info */
	red_scale   = (GLfloat)dc->pf.redMask;
	green_scale = (GLfloat)dc->pf.greenMask;
	blue_scale  = (GLfloat)dc->pf.blueMask;
	alpha_scale = 255.0;

	red_bits 	= countBits(dc->pf.redMask);	
	green_bits 	= countBits(dc->pf.greenMask);	
	blue_bits 	= countBits(dc->pf.blueMask);	
	alpha_bits 	= countBits(dc->pf.rsvdMask);

	MGL_clearCurrentDC();
	dc->visual = gl_create_visual( vis->rgb_flag, 
								   vis->alpha_flag, 
								   vis->db_flag,
								   vis->stereo_flag,
								   vis->depth_size, 
								   vis->stencil_size, 
								   vis->accum_size,
								   dc->mi.bitsPerPixel,
								   red_bits,
								   green_bits,
								   blue_bits,
								   alpha_bits);
	return (dc->visual != NULL);
}

/****************************************************************************
PARAMETERS:
dc			- MGLDC to create rendering context for
forceMemDC	- True to force rendering to memory back buffer

RETURNS:
True on success, false if visual creation failed

REMARKS:
Attempts to create a Mesa rendering context for the device context. The
application programmer must first call MGLMesaSetVisual on the MGLDC to
set the visual format before calling this function.

If we dont have hardware page flipping or the user has forced memory
buffering with the forceMemDC function then we will allocate a system
memory back buffer for double buffering. Note that if we have hardware
page flipping and system buffering has been forced, we will still flip
between hardware pages to eliminate all tearing. This option is intended
mainly as a performance option for systems where system memory rendering
is faster.
****************************************************************************/
ibool MGLAPI MGLMesaCreateContext(
	MGLDC *dc,
	ibool forceMemDC)
{
	palette_t	pal[256];

	/* Ensure that the visual has been created first */
	MGL_clearCurrentDC();
	if (!dc->visual)
		return false;

	/* Allocate memory for the rendering context structure and create it */
	if ((dc->rc = (void*)calloc(1,sizeof(MGLRC))) == NULL)
		goto Error;
	dc->rc->dispdc = dc;
	dc->rc->gl_ctx = gl_create_context(dc->visual,NULL,(void*)dc->rc, GL_TRUE);
	if (!dc->rc)
		goto Error;

	/* Allocate the ancillary buffers */
	if ((dc->rc->gl_buffer = gl_create_framebuffer(dc->visual)) == NULL)
		goto Error;

	/* Allocate back memory DC buffer if necessary */
	if ((dc->visual->DBflag && dc->mi.maxPage == 0) || forceMemDC || (MGL_surfaceAccessType(dc) == MGL_NO_ACCESS)) {
		dc->rc->memdc = MGL_createMemoryDC(dc->mi.xRes+1,dc->mi.yRes+1,dc->mi.bitsPerPixel,&dc->pf);
		if (!dc->rc->memdc)
			goto Error;
		dc->rc->dc = dc->rc->memdc;
		// TODO: Need to figure out how to handle stereo rendering with
		//	 	 dual memory back buffers, which is what we need if we don't
		//		 have enough memory.
		}
	else
		dc->rc->dc = dc->rc->dispdc;

	/* Initialize double buffering */
	if (dc->visual->DBflag && dc->mi.maxPage > 0) {
		MGL_setVisualPage(dc,dc->rc->frontbuffer = 0,false);
		MGL_setActivePage(dc,dc->rc->backbuffer = 1);
		}

	/* Initialize the private rendering context information */
	if (!dc->visual->RGBAflag) {
		dc->rc->dithered_pf = dc->rc->undithered_pf = PF_INDEX;
		MGL_getPalette(dc,pal,256,0);
		}
	else {
		if (dc->mi.bitsPerPixel > 8) {
			/* DirectColor or TrueColor display */
			if (dc->mi.bitsPerPixel == 15) {
				dc->rc->undithered_pf = PF_RGB555;
				dc->rc->dithered_pf = PF_DITHER555;
				}
			else if (dc->mi.bitsPerPixel == 16) {
				dc->rc->undithered_pf = PF_RGB565;
				dc->rc->dithered_pf = PF_DITHER565;
				}
			else if (dc->mi.bitsPerPixel == 24) {
            	if (dc->pf.redPos == 0)
					dc->rc->undithered_pf = dc->rc->dithered_pf = PF_BGR888;
				else
					dc->rc->undithered_pf = dc->rc->dithered_pf = PF_RGB888;
				}
			else {
            	if (dc->pf.redPos == 0)
					dc->rc->undithered_pf = dc->rc->dithered_pf = PF_ABGR8888;
				else if (dc->pf.redPos == 8)
					dc->rc->undithered_pf = dc->rc->dithered_pf = PF_BGRA8888;
				else if (dc->pf.redPos == 16)
					dc->rc->undithered_pf = dc->rc->dithered_pf = PF_ARGB8888;
				else
					dc->rc->undithered_pf = dc->rc->dithered_pf = PF_RGBA8888;
				}
			}
		else {
			/* 8bpp RGB display */
			dc->rc->undithered_pf = PF_RGB8;
			dc->rc->dithered_pf = PF_DITHER8;
			MGL_getHalfTonePalette(pal);
			}
		}
	dc->rc->gl_vis = dc->visual;
	dc->rc->bottom = dc->mi.yRes+1;
	dc->rc->pixelformat = dc->rc->dithered_pf;
	dc->rc->bufferMode = dc->visual->DBflag ? GL_BACK : GL_FRONT;
	setup_DD_pointers(dc->rc->gl_ctx);

	/* Setup the default palette for the device context */
	if (dc->mi.bitsPerPixel == 8) {
		MGLMesaSetPalette(dc,pal,256,0);
		MGLMesaRealizePalette(dc,256,0,false);
		}
	return true;

Error:
	MGLMesaDestroyContext(dc);
	return false;
}

/****************************************************************************
PARAMETERS:
dc	- MGLDC to destroy rendering context for

REMARKS:
Destroys the rendering context and returns the MGLDC back to it's original
state before 3D rendering was enabled.
****************************************************************************/
void MGLAPI MGLMesaDestroyContext(
	MGLDC *dc)
{
	if (dc->rc) {
		if (dc->rc->memdc)
        	MGL_destroyDC(dc->rc->memdc);
		gl_destroy_framebuffer(dc->rc->gl_buffer);
		gl_destroy_context(dc->rc->gl_ctx);
		free(dc->rc);
		}
	dc->rc = NULL;
	gl_destroy_visual(dc->visual);
	dc->visual = NULL;
}

/****************************************************************************
PARAMETERS:
dc	- MGLDC to make current

REMARKS:
Makes the passed in MGLDC the current rendering context for OpenGL functions.
Note that we cache a global copy of this rendering context for speed (we
are single threaded) so we flush this back when the context is made a
different one.
****************************************************************************/
void MGLAPI MGLMesaMakeCurrent(
	MGLDC *dc)
{
	if (dc && dc->rc == _MM_rcPtr)
    	return;
	if (_MM_rcPtr) {
		*_MM_rcPtr = RC;				/* 'Write back' the old RC		*/
		_MM_rcPtr = NULL;				/* This RC is no longer cached	*/
		gl_make_current(NULL, NULL);
		MGL_clearCurrentDC();
		}
	if (dc) {
		gl_make_current(dc->rc->gl_ctx, dc->rc->gl_buffer);
		_MM_rcPtr = dc->rc;				/* Cache DC in global structure	*/
		RC = *dc->rc;					/* Save pointer to original DC	*/
		MI = RC.dc->mi;					/* Save cached MGL internals	*/
		PF = RC.dc->pf;
		VECS = RC.dc->r;
		set_buffer(dc->rc->gl_ctx,RC.bufferMode);
		if (RC.gl_ctx->Viewport.Width == 0) {
			/* initialize viewport to window size */
			glViewport(0, 0, MI.xRes+1, MI.yRes+1);
			RC.gl_ctx->Scissor.Width = MI.xRes+1;
			RC.gl_ctx->Scissor.Height = MI.yRes+1;
			}
		}
}

/****************************************************************************
PARAMETERS:
dc		- MGLDC to swap buffers for
waitVRT	- True to wait for vertical retrace

REMARKS:
Swaps the display buffers for the MGL device context. If we have a memory
DC this is blitted to the display, and if we have harware page flipping
we flip hardware display pages.
****************************************************************************/
void MGLAPI MGLMesaSwapBuffers(
	MGLDC *dc,
	ibool waitVRT)
{
	if (!RC.gl_vis->DBflag)
		return;
	if (RC.memdc) {
		/* We have a memory buffer so blit it to the screen */
		MGL_bitBltCoord(RC.dispdc,RC.memdc,0,0,MI.xRes+1,MI.yRes+1,0,0,MGL_REPLACE_MODE);
		}
	if (RC.dc->mi.maxPage > 0) {
		/* Hardware page flipping */
		RC.frontbuffer ^= 1;
		RC.backbuffer = RC.frontbuffer ^ 1;
		if (RC.bufferMode == GL_FRONT)
			MGL_setActivePage(RC.dc,RC.frontbuffer);
		else if (RC.bufferMode == GL_BACK)
			MGL_setActivePage(RC.dc,RC.backbuffer);
		else if (RC.bufferMode == GL_FRONT_LEFT)
			MGL_setActivePage(RC.dc,RC.frontbuffer | MGL_LEFT_BUFFER);
		else if (RC.bufferMode == GL_FRONT_RIGHT)
			MGL_setActivePage(RC.dc,RC.frontbuffer | MGL_RIGHT_BUFFER);
		else if (RC.bufferMode == GL_BACK_LEFT)
			MGL_setActivePage(RC.dc,RC.backbuffer | MGL_LEFT_BUFFER);
		else if (RC.bufferMode == GL_BACK_RIGHT)
			MGL_setActivePage(RC.dc,RC.backbuffer | MGL_RIGHT_BUFFER);
		MGL_setVisualPage(RC.dc,RC.frontbuffer,waitVRT);
		}
}

/****************************************************************************
PARAMETERS:
dc				- MGLDC to set palette entry for
entry			- Index of entry to set
red,green,blue	- Color values for palette entry

REMARKS:
Sets a single color palette entry in the device context.
****************************************************************************/
void MGLAPI MGLMesaSetPaletteEntry(
	MGLDC *dc,
	int entry,
	uchar red,
	uchar green,
	uchar blue)
{
	MGL_setPaletteEntry(dc,entry,red,green,blue);
	if (dc->rc && dc->rc->memdc)
		MGL_setPaletteEntry(dc->rc->memdc,entry,red,green,blue);
}

/****************************************************************************
PARAMETERS:
dc			- MGLDC to set palette for
pal			- Array of palette values to set
numColors	- Number of color values to set
startIndex	- Starting index for first color to program

REMARKS:
Sets a number of color palette entries in the rendering context.
****************************************************************************/
void MGLAPI MGLMesaSetPalette(
	MGLDC *dc,
	palette_t *pal,
	int numColors,
	int startIndex)
{
	MGL_setPalette(dc,pal,numColors,startIndex);
	if (dc->rc && dc->rc->memdc)
		MGL_setPalette(dc->rc->memdc,pal,numColors,startIndex);
}

/****************************************************************************
PARAMETERS:
dc			- MGLDC to realize palette for
numColors	- Number of color values to set
startIndex	- Starting index for first color to program
waitVRT		- True to wait for vertical retrace

REMARKS:
Realizes the hardware palette for the rendering context.
****************************************************************************/
void MGLAPI MGLMesaRealizePalette(
	MGLDC *dc,
	int numColors,
	int startIndex,
	int waitVRT)
{
	MGL_realizePalette(dc,numColors,startIndex,waitVRT);
	if (dc->rc && dc->rc->memdc)
		MGL_realizePalette(dc->rc->memdc,numColors,startIndex,waitVRT);
}

/****************************************************************************
REMARKS:
Resizes the internal buffers inside Mesa.
****************************************************************************/
void MGLAPI MGLMesaResizeBuffers(void)
{
	glResizeBuffersMESA();
}

/****************************************************************************
REMARKS:
Returns the address of extension functions.
****************************************************************************/
void * MGLAPI MGLMesaGetProcAddress(
	const char *procName)
{
	/* TODO: Need to implement all extension functions in here. We also
	 * 		 need to implement the extensions strings in our driver
	 *		 as well.
	 */
	return NULL;
}
