/* $Id: readpix.c,v 1.7 1997/02/03 20:31:15 brianp Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  2.2
 * 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: readpix.c,v $
 * Revision 1.7  1997/02/03 20:31:15  brianp
 * added a few DEFARRAY macros for BeOS
 *
 * Revision 1.6  1997/01/16 19:24:05  brianp
 * replaced a few abort()'s with gl_error() calls
 *
 * Revision 1.5  1996/12/20 20:28:04  brianp
 * use DEF/UNDEFARRAY() macros in read_color_pixels() for Mac compilers
 *
 * Revision 1.4  1996/11/01 03:20:47  brianp
 * reading GL_LUMINANCE pixels weren't clamped
 *
 * Revision 1.3  1996/09/27 01:29:47  brianp
 * added missing default cases to switches
 *
 * Revision 1.2  1996/09/15 14:18:37  brianp
 * now use GLframebuffer and GLvisual
 *
 * Revision 1.1  1996/09/13 01:38:16  brianp
 * Initial revision
 *
 */


#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "alphabuf.h"
#include "context.h"
#include "depth.h"
#include "feedback.h"
#include "dlist.h"
#include "macros.h"
#include "image.h"
#include "readpix.h"
#include "span.h"
#include "stencil.h"
#include "types.h"





/*
 * Read a block of color index pixels.
 */
static void read_index_pixels( GLcontext *ctx,
                               GLint x, GLint y,
			       GLsizei width, GLsizei height,
			       GLenum type, GLvoid *pixels )
{
   GLint i, j;
   GLuint a, s, k, l, start;

   /* error checking */
   if (ctx->Visual->RGBAflag) {
      gl_error( ctx, GL_INVALID_OPERATION, "glReadPixels" );
      return;
   }

   /* Size of each component */
   s = gl_sizeof_type( type );
   if (s<=0) {
      gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      return;
   }

   /* Compute packing parameters */
   a = ctx->Pack.Alignment;
   if (ctx->Pack.RowLength>0) {
      l = ctx->Pack.RowLength;
   }
   else {
      l = width;
   }
   /* k = offset between rows in components */
   if (s>=a) {
      k = l;
   }
   else {
      k = a/s * CEILING( s*l, a );
   }

   /* offset to first component returned */
   start = ctx->Pack.SkipRows * k + ctx->Pack.SkipPixels;

   /* process image row by row */
   for (j=0;j<height;j++,y++) {
      GLuint index[MAX_WIDTH];
      (*ctx->Driver.ReadIndexSpan)( ctx, width, x, y, index );

      if (ctx->Pixel.IndexShift!=0 || ctx->Pixel.IndexOffset!=0) {
	 GLuint s;
	 if (ctx->Pixel.IndexShift<0) {
	    /* right shift */
	    s = -ctx->Pixel.IndexShift;
	    for (i=0;i<width;i++) {
	       index[i] = (index[i] >> s) + ctx->Pixel.IndexOffset;
	    }
	 }
	 else {
	    /* left shift */
	    s = ctx->Pixel.IndexShift;
	    for (i=0;i<width;i++) {
	       index[i] = (index[i] << s) + ctx->Pixel.IndexOffset;
	    }
	 }
      }

      if (ctx->Pixel.MapColorFlag) {
	 for (i=0;i<width;i++) {
	    index[i] = ctx->Pixel.MapItoI[ index[i] ];
	 }
      }

      switch (type) {
	 case GL_UNSIGNED_BYTE:
	    {
	       GLubyte *dst = (GLubyte *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLubyte) index[i];
	       }
	    }
	    break;
	 case GL_BYTE:
	    {
	       GLbyte *dst = (GLbyte *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLbyte) index[i];
	       }
	    }
	    break;
	 case GL_UNSIGNED_SHORT:
	    {
	       GLushort *dst = (GLushort *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLushort) index[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap2( (GLushort *) pixels + start + j * k, width );
	       }
	    }
	    break;
	 case GL_SHORT:
	    {
	       GLshort *dst = (GLshort *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLshort) index[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap2( (GLushort *) pixels + start + j * k, width );
	       }
	    }
	    break;
	 case GL_UNSIGNED_INT:
	    {
	       GLuint *dst = (GLuint *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLuint) index[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start + j * k, width );
	       }
	    }
	    break;
	 case GL_INT:
	    {
	       GLint *dst = (GLint *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLint) index[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start + j * k, width );
	       }
	    }
	    break;
	 case GL_FLOAT:
	    {
	       GLfloat *dst = (GLfloat *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLfloat) index[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start + j * k, width );
	       }
	    }
	    break;
         default:
            gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      }
   }
}



static void read_depth_pixels( GLcontext *ctx,
                               GLint x, GLint y,
			       GLsizei width, GLsizei height,
			       GLenum type, GLvoid *pixels )
{
   GLint i, j;
   GLuint a, s, k, l, start;
   GLboolean bias_or_scale;

   /* Error checking */
   if (ctx->Visual->DepthBits<=0) {
      /* No depth buffer */
      gl_error( ctx, GL_INVALID_OPERATION, "glReadPixels" );
      return;
   }

   bias_or_scale = ctx->Pixel.DepthBias!=0.0 || ctx->Pixel.DepthScale!=1.0;

   /* Size of each component */
   s = gl_sizeof_type( type );
   if (s<=0) {
      gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      return;
   }

   /* Compute packing parameters */
   a = ctx->Pack.Alignment;
   if (ctx->Pack.RowLength>0) {
      l = ctx->Pack.RowLength;
   }
   else {
      l = width;
   }
   /* k = offset between rows in components */
   if (s>=a) {
      k = l;
   }
   else {
      k = a/s * CEILING( s*l, a );
   }

   /* offset to first component returned */
   start = ctx->Pack.SkipRows * k + ctx->Pack.SkipPixels;

   if (type==GL_UNSIGNED_SHORT && sizeof(GLdepth)==sizeof(GLushort)
       && !bias_or_scale && !ctx->Pack.SwapBytes) {
      /* Special case: directly read 16-bit unsigned depth values. */
      for (j=0;j<height;j++,y++) {
         GLushort *dst = (GLushort *) pixels + start + j * k;
         (*ctx->Driver.ReadDepthSpanInt)( ctx, width, x, y, (GLdepth*) dst);
      }
   }
   else if (type==GL_UNSIGNED_INT && sizeof(GLdepth)==sizeof(GLuint)
            && !bias_or_scale && !ctx->Pack.SwapBytes) {
      /* Special case: directly read 32-bit unsigned depth values. */
      /* Compute shift value to scale depth values up to 32-bit uints. */
      GLuint shift = 0;
      GLuint max = MAX_DEPTH;
      while ((max&0x80000000)==0) {
         max = max << 1;
         shift++;
      }
      for (j=0;j<height;j++,y++) {
         GLuint *dst = (GLuint *) pixels + start + j * k;
         (*ctx->Driver.ReadDepthSpanInt)( ctx, width, x, y, (GLdepth*) dst);
         for (i=0;i<width;i++) {
            dst[i] = dst[i] << shift;
         }
      }
   }
   else {
      /* General case (slow) */
      for (j=0;j<height;j++,y++) {
         GLfloat depth[MAX_WIDTH];

         (*ctx->Driver.ReadDepthSpanFloat)( ctx, width, x, y, depth );

         if (bias_or_scale) {
            for (i=0;i<width;i++) {
               GLfloat d;
               d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias;
               depth[i] = CLAMP( d, 0.0, 1.0 );
            }
         }

         switch (type) {
            case GL_UNSIGNED_BYTE:
               {
                  GLubyte *dst = (GLubyte *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = FLOAT_TO_UBYTE( depth[i] );
                  }
               }
               break;
            case GL_BYTE:
               {
                  GLbyte *dst = (GLbyte *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = FLOAT_TO_BYTE( depth[i] );
                  }
               }
               break;
            case GL_UNSIGNED_SHORT:
               {
                  GLushort *dst = (GLushort *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = FLOAT_TO_USHORT( depth[i] );
                  }
                  if (ctx->Pack.SwapBytes) {
                     gl_swap2( (GLushort *) pixels + start + j * k, width );
                  }
               }
               break;
            case GL_SHORT:
               {
                  GLshort *dst = (GLshort *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = FLOAT_TO_SHORT( depth[i] );
                  }
                  if (ctx->Pack.SwapBytes) {
                     gl_swap2( (GLushort *) pixels + start + j * k, width );
                  }
               }
               break;
            case GL_UNSIGNED_INT:
               {
                  GLuint *dst = (GLuint *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = FLOAT_TO_UINT( depth[i] );
                  }
                  if (ctx->Pack.SwapBytes) {
                     gl_swap4( (GLuint *) pixels + start + j * k, width );
                  }
               }
               break;
            case GL_INT:
               {
                  GLint *dst = (GLint *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = FLOAT_TO_INT( depth[i] );
                  }
                  if (ctx->Pack.SwapBytes) {
                     gl_swap4( (GLuint *) pixels + start + j * k, width );
                  }
               }
               break;
            case GL_FLOAT:
               {
                  GLfloat *dst = (GLfloat *) pixels + start + j * k;
                  for (i=0;i<width;i++) {
                     *dst++ = depth[i];
                  }
                  if (ctx->Pack.SwapBytes) {
                     gl_swap4( (GLuint *) pixels + start + j * k, width );
                  }
               }
               break;
            default:
               gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
         }
      }
   }
}




static void read_stencil_pixels( GLcontext *ctx,
                                 GLint x, GLint y,
				 GLsizei width, GLsizei height,
				 GLenum type, GLvoid *pixels )
{
   GLint i, j;
   GLuint a, s, k, l, start;
   GLboolean shift_or_offset;

   if (ctx->Visual->StencilBits<=0) {
      /* No stencil buffer */
      gl_error( ctx, GL_INVALID_OPERATION, "glReadPixels" );
      return;
   }

   shift_or_offset = ctx->Pixel.IndexShift!=0 || ctx->Pixel.IndexOffset!=0;

   /* Size of each component */
   s = gl_sizeof_type( type );
   if (s<=0) {
      gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      return;
   }

   /* Compute packing parameters */
   a = ctx->Pack.Alignment;
   if (ctx->Pack.RowLength>0) {
      l = ctx->Pack.RowLength;
   }
   else {
      l = width;
   }
   /* k = offset between rows in components */
   if (s>=a) {
      k = l;
   }
   else {
      k = a/s * CEILING( s*l, a );
   }

   /* offset to first component returned */
   start = ctx->Pack.SkipRows * k + ctx->Pack.SkipPixels;

   /* process image row by row */
   for (j=0;j<height;j++,y++) {
      GLubyte stencil[MAX_WIDTH];

      gl_read_stencil_span( ctx, width, x, y, stencil );

      if (shift_or_offset) {
	 GLuint s;
	 if (ctx->Pixel.IndexShift<0) {
	    /* right shift */
	    s = -ctx->Pixel.IndexShift;
	    for (i=0;i<width;i++) {
	       stencil[i] = (stencil[i] >> s) + ctx->Pixel.IndexOffset;
	    }
	 }
	 else {
	    /* left shift */
	    s = ctx->Pixel.IndexShift;
	    for (i=0;i<width;i++) {
	       stencil[i] = (stencil[i] << s) + ctx->Pixel.IndexOffset;
	    }
	 }
      }

      if (ctx->Pixel.MapStencilFlag) {
	 for (i=0;i<width;i++) {
	    stencil[i] = ctx->Pixel.MapStoS[ stencil[i] ];
	 }
      }

      switch (type) {
	 case GL_UNSIGNED_BYTE:
	    {
	       GLubyte *dst = (GLubyte *) pixels + start + j * k;
	       MEMCPY( dst, stencil, width );
	    }
	    break;
	 case GL_BYTE:
	    {
	       GLbyte *dst = (GLbyte  *) pixels + start + j * k;
	       MEMCPY( dst, stencil, width );
	    }
	    break;
	 case GL_UNSIGNED_SHORT:
	    {
	       GLushort *dst = (GLushort *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLushort) stencil[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap2( (GLushort *) pixels + start +j * k, width );
	       }
	    }
	    break;
	 case GL_SHORT:
	    {
	       GLshort *dst = (GLshort *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLshort) stencil[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap2( (GLushort *) pixels + start +j * k, width );
	       }
	    }
	    break;
	 case GL_UNSIGNED_INT:
	    {
	       GLuint *dst = (GLuint *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLuint) stencil[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start +j * k, width );
	       }
	    }
	    break;
	 case GL_INT:
	    {
	       GLint *dst = (GLint *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLint) stencil[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start +j * k, width );
	       }
	    }
	    break;
	 case GL_FLOAT:
	    {
	       GLfloat *dst = (GLfloat *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  *dst++ = (GLfloat) stencil[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start +j * k, width );
	       }
	    }
	    break;
         default:
            gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      }
   }
}



/*
 * Test if scaling or biasing of colors is needed.
 */
static GLboolean scale_or_bias_rgba( GLcontext *ctx )
{
   if (ctx->Pixel.RedScale!=1.0F   || ctx->Pixel.RedBias!=0.0F ||
       ctx->Pixel.GreenScale!=1.0F || ctx->Pixel.GreenBias!=0.0F ||
       ctx->Pixel.BlueScale!=1.0F  || ctx->Pixel.BlueBias!=0.0F ||
       ctx->Pixel.AlphaScale!=1.0F || ctx->Pixel.AlphaBias!=0.0F) {
      return GL_TRUE;
   }
   else {
      return GL_FALSE;
   }
}



/*
 * Apply scale and bias factors to an array of RGBA pixels.
 */
static void scale_and_bias_rgba( GLcontext *ctx,
                                 GLint n,
				 GLfloat red[], GLfloat green[],
				 GLfloat blue[], GLfloat alpha[] )
{
   register GLint i;
   register GLfloat r, g, b, a;

   for (i=0;i<n;i++) {
      r = red[i]   * ctx->Pixel.RedScale   + ctx->Pixel.RedBias;
      g = green[i] * ctx->Pixel.GreenScale + ctx->Pixel.GreenBias;
      b = blue[i]  * ctx->Pixel.BlueScale  + ctx->Pixel.BlueBias;
      a = alpha[i] * ctx->Pixel.AlphaScale + ctx->Pixel.AlphaBias;
      red[i]   = CLAMP( r, 0.0F, 1.0F );
      green[i] = CLAMP( g, 0.0F, 1.0F );
      blue[i]  = CLAMP( b, 0.0F, 1.0F );
      alpha[i] = CLAMP( a, 0.0F, 1.0F );
   }
}



/*
 * Apply pixel mapping to an array of RGBA pixels.
 */
static void map_rgba( GLcontext *ctx,
                      GLint n,
		      GLfloat red[], GLfloat green[],
		      GLfloat blue[], GLfloat alpha[] )
{
   GLfloat rscale = ctx->Pixel.MapRtoRsize-1;
   GLfloat gscale = ctx->Pixel.MapGtoGsize-1;
   GLfloat bscale = ctx->Pixel.MapBtoBsize-1;
   GLfloat ascale = ctx->Pixel.MapAtoAsize-1;
   GLint i;

   for (i=0;i<n;i++) {
      red[i]   = ctx->Pixel.MapRtoR[ (GLint) (red[i]   * rscale) ];
      green[i] = ctx->Pixel.MapGtoG[ (GLint) (green[i] * gscale) ];
      blue[i]  = ctx->Pixel.MapBtoB[ (GLint) (blue[i]  * bscale) ];
      alpha[i] = ctx->Pixel.MapAtoA[ (GLint) (alpha[i] * ascale) ];
   }
}




/*
 * Read R, G, B, A, RGB, L, or LA pixels.
 */
static void read_color_pixels( GLcontext *ctx,
                               GLint x, GLint y,
			       GLsizei width, GLsizei height,
			       GLenum format, GLenum type, GLvoid *pixels )
{
   GLint i, j, n, a, s, l, k;
   GLboolean scale_or_bias;
   DEFARRAY(GLfloat, red, MAX_WIDTH);
   DEFARRAY(GLfloat, green, MAX_WIDTH);
   DEFARRAY(GLfloat, blue, MAX_WIDTH);
   DEFARRAY(GLfloat, alpha, MAX_WIDTH);
   DEFARRAY(GLfloat, luminance, MAX_WIDTH);
   GLboolean r_flag, g_flag, b_flag, a_flag, l_flag;
   GLuint start;

   scale_or_bias = scale_or_bias_rgba( ctx );

   /* Determine how many / which components to return */
   r_flag = g_flag = b_flag = a_flag = l_flag = GL_FALSE;
   switch (format) {
      case GL_RED:				r_flag = GL_TRUE;  n = 1;  break;
      case GL_GREEN:				g_flag = GL_TRUE;  n = 1;  break;
      case GL_BLUE:				b_flag = GL_TRUE;  n = 1;  break;
      case GL_ALPHA:				a_flag = GL_TRUE;  n = 1;  break;
      case GL_LUMINANCE:			l_flag = GL_TRUE;  n = 1;  break;
      case GL_LUMINANCE_ALPHA:	       l_flag = a_flag = GL_TRUE;  n = 2;  break;
      case GL_RGB:	      r_flag = g_flag = b_flag = GL_TRUE;  n = 3;  break;
      case GL_RGBA:  r_flag = g_flag = b_flag = a_flag = GL_TRUE;  n = 4;  break;
      default:
	 gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(format)" );
         UNDEFARRAY( red );
         UNDEFARRAY( green );
         UNDEFARRAY( blue );
         UNDEFARRAY( alpha );
         UNDEFARRAY( luminance );
         return;
   }

   /* Size of each component */
   s = gl_sizeof_type( type );
   if (s<=0) {
      gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      UNDEFARRAY( red );
      UNDEFARRAY( green );
      UNDEFARRAY( blue );
      UNDEFARRAY( alpha );
      UNDEFARRAY( luminance );
      return;
   }

   /* Compute packing parameters */
   a = ctx->Pack.Alignment;
   if (ctx->Pack.RowLength>0) {
      l = ctx->Pack.RowLength;
   }
   else {
      l = width;
   }
   /* k = offset between rows in components */
   if (s>=a) {
      k = n * l;
   }
   else {
      k = a/s * CEILING( s*n*l, a );
   }

   /* offset to first component returned */
   start = ctx->Pack.SkipRows * k + ctx->Pack.SkipPixels * n;

   /* process image row by row */
   for (j=0;j<height;j++,y++) {

      /*
       * Read the pixels from frame buffer
       */
      if (ctx->Visual->RGBAflag) {
         DEFARRAY(GLubyte, r, MAX_WIDTH);
         DEFARRAY(GLubyte, g, MAX_WIDTH);
         DEFARRAY(GLubyte, b, MAX_WIDTH);
         DEFARRAY(GLubyte, a, MAX_WIDTH);
	 GLfloat rscale = 1.0F * ctx->Visual->InvRedScale;
	 GLfloat gscale = 1.0F * ctx->Visual->InvGreenScale;
	 GLfloat bscale = 1.0F * ctx->Visual->InvBlueScale;
	 GLfloat ascale = 1.0F * ctx->Visual->InvAlphaScale;

	 /* read colors and convert to floats */
	 (*ctx->Driver.ReadColorSpan)( ctx, width, x, y, r, g, b, a );
         if (ctx->RasterMask & ALPHABUF_BIT) {
            gl_read_alpha_span( ctx, width, x, y, a );
         }
	 for (i=0;i<width;i++) {
	    red[i]   = r[i] * rscale;
	    green[i] = g[i] * gscale;
	    blue[i]  = b[i] * bscale;
	    alpha[i] = a[i] * ascale;
	 }

	 if (scale_or_bias) {
	    scale_and_bias_rgba( ctx, width, red, green, blue, alpha );
	 }
	 if (ctx->Pixel.MapColorFlag) {
	    map_rgba( ctx, width, red, green, blue, alpha );
	 }
         UNDEFARRAY(r);
         UNDEFARRAY(g);
         UNDEFARRAY(b);
         UNDEFARRAY(a);
      }
      else {
	 /* convert CI values to RGBA */
	 GLuint index[MAX_WIDTH];
	 (*ctx->Driver.ReadIndexSpan)( ctx, width, x, y, index );

	 if (ctx->Pixel.IndexShift!=0 || ctx->Pixel.IndexOffset!=0) {
	    GLuint s;
	    if (ctx->Pixel.IndexShift<0) {
	       /* right shift */
	       s = -ctx->Pixel.IndexShift;
	       for (i=0;i<width;i++) {
		  index[i] = (index[i] >> s) + ctx->Pixel.IndexOffset;
	       }
	    }
	    else {
	       /* left shift */
	       s = ctx->Pixel.IndexShift;
	       for (i=0;i<width;i++) {
		  index[i] = (index[i] << s) + ctx->Pixel.IndexOffset;
	       }
	    }
	 }

	 for (i=0;i<width;i++) {
	    red[i]   = ctx->Pixel.MapItoR[ index[i] ];
	    green[i] = ctx->Pixel.MapItoG[ index[i] ];
	    blue[i]  = ctx->Pixel.MapItoB[ index[i] ];
	    alpha[i] = ctx->Pixel.MapItoA[ index[i] ];
	 }
      }

      if (l_flag) {
         for (i=0;i<width;i++) {
            GLfloat sum = red[i] + green[i] + blue[i];
            luminance[i] = CLAMP( sum, 0.0F, 1.0F );
         }
      }

      /*
       * Pack/transfer/store the pixels
       */

      switch (type) {
	 case GL_UNSIGNED_BYTE:
	    {
	       GLubyte *dst = (GLubyte *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = FLOAT_TO_UBYTE( red[i] );
		  if (g_flag)  *dst++ = FLOAT_TO_UBYTE( green[i] );
		  if (b_flag)  *dst++ = FLOAT_TO_UBYTE( blue[i] );
		  if (l_flag)  *dst++ = FLOAT_TO_UBYTE( luminance[i] );
		  if (a_flag)  *dst++ = FLOAT_TO_UBYTE( alpha[i] );
	       }
	    }
	    break;
	 case GL_BYTE:
	    {
	       GLbyte *dst = (GLbyte *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = FLOAT_TO_BYTE( red[i] );
		  if (g_flag)  *dst++ = FLOAT_TO_BYTE( green[i] );
		  if (b_flag)  *dst++ = FLOAT_TO_BYTE( blue[i] );
		  if (l_flag)  *dst++ = FLOAT_TO_BYTE( luminance[i] );
		  if (a_flag)  *dst++ = FLOAT_TO_BYTE( alpha[i] );
	       }
	    }
	    break;
	 case GL_UNSIGNED_SHORT:
	    {
	       GLushort *dst = (GLushort *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = FLOAT_TO_USHORT( red[i] );
		  if (g_flag)  *dst++ = FLOAT_TO_USHORT( green[i] );
		  if (b_flag)  *dst++ = FLOAT_TO_USHORT( blue[i] );
		  if (l_flag)  *dst++ = FLOAT_TO_USHORT( luminance[i] );
		  if (a_flag)  *dst++ = FLOAT_TO_USHORT( alpha[i] );
	       }
	    }
	    if (ctx->Pack.SwapBytes) {
	       gl_swap2( (GLushort *) pixels + start + j * k, width*n );
	    }
	    break;
	 case GL_SHORT:
	    {
	       GLshort *dst = (GLshort *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = FLOAT_TO_SHORT( red[i] );
		  if (g_flag)  *dst++ = FLOAT_TO_SHORT( green[i] );
		  if (b_flag)  *dst++ = FLOAT_TO_SHORT( blue[i] );
		  if (l_flag)  *dst++ = FLOAT_TO_SHORT( luminance[i] );
		  if (a_flag)  *dst++ = FLOAT_TO_SHORT( alpha[i] );
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap2( (GLushort *) pixels + start + j * k, width*n );
	       }
	    }
	    break;
	 case GL_UNSIGNED_INT:
	    {
	       GLuint *dst = (GLuint *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = FLOAT_TO_UINT( red[i] );
		  if (g_flag)  *dst++ = FLOAT_TO_UINT( green[i] );
		  if (b_flag)  *dst++ = FLOAT_TO_UINT( blue[i] );
		  if (l_flag)  *dst++ = FLOAT_TO_UINT( luminance[i] );
		  if (a_flag)  *dst++ = FLOAT_TO_UINT( alpha[i] );
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start + j * k, width*n );
	       }
	    }
	    break;
	 case GL_INT:
	    {
	       GLint *dst = (GLint *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = FLOAT_TO_INT( red[i] );
		  if (g_flag)  *dst++ = FLOAT_TO_INT( green[i] );
		  if (b_flag)  *dst++ = FLOAT_TO_INT( blue[i] );
		  if (l_flag)  *dst++ = FLOAT_TO_INT( luminance[i] );
		  if (a_flag)  *dst++ = FLOAT_TO_INT( alpha[i] );
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start + j * k, width*n );
	       }
	    }
	    break;
	 case GL_FLOAT:
	    {
	       GLfloat *dst = (GLfloat *) pixels + start + j * k;
	       for (i=0;i<width;i++) {
		  if (r_flag)  *dst++ = red[i];
		  if (g_flag)  *dst++ = green[i];
		  if (b_flag)  *dst++ = blue[i];
		  if (l_flag)  *dst++ = luminance[i];
		  if (a_flag)  *dst++ = alpha[i];
	       }
	       if (ctx->Pack.SwapBytes) {
		  gl_swap4( (GLuint *) pixels + start + j * k, width*n );
	       }
	    }
	    break;
         default:
            gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(type)" );
      }
   }
   UNDEFARRAY( red );
   UNDEFARRAY( green );
   UNDEFARRAY( blue );
   UNDEFARRAY( alpha );
   UNDEFARRAY( luminance );
}



void gl_ReadPixels( GLcontext *ctx,
                    GLint x, GLint y, GLsizei width, GLsizei height,
		    GLenum format, GLenum type, GLvoid *pixels )
{
   (void) (*ctx->Driver.SetBuffer)( ctx, ctx->Pixel.ReadBuffer );

   switch (format) {
      case GL_COLOR_INDEX:
         read_index_pixels( ctx, x, y, width, height, type, pixels );
	 break;
      case GL_STENCIL_INDEX:
	 read_stencil_pixels( ctx, x, y, width, height, type, pixels );
         break;
      case GL_DEPTH_COMPONENT:
	 read_depth_pixels( ctx, x, y, width, height, type, pixels );
	 break;
      case GL_RED:
      case GL_GREEN:
      case GL_BLUE:
      case GL_ALPHA:
      case GL_RGB:
      case GL_LUMINANCE:
      case GL_LUMINANCE_ALPHA:
      case GL_RGBA:
	 read_color_pixels( ctx, x, y, width, height, format, type, pixels );
	 break;
      default:
	 gl_error( ctx, GL_INVALID_ENUM, "glReadPixels(format)" );
   }

   (void) (*ctx->Driver.SetBuffer)( ctx, ctx->Color.DrawBuffer );
}
