Remove unused texunit parameter to ctx->Driver.GenerateMipmap()
[mesa.git] / src / mesa / main / drawpix.c
index fdb290e720f3e428986a13ef854a65ebf02c24ad..fde933843095522911387a0804b379076b836cd7 100644 (file)
@@ -1,21 +1,19 @@
-/* $Id: drawpix.c,v 1.27 2000/08/16 17:32:42 brianp Exp $ */
-
 /*
  * Mesa 3-D graphics library
- * Version:  3.4
- * 
- * Copyright (C) 1999-2000  Brian Paul   All Rights Reserved.
- * 
+ * Version:  7.1
+ *
+ * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * to deal in the Software without restriction, including without limitation
  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  * and/or sell copies of the Software, and to permit persons to whom the
  * Software is furnished to do so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included
  * in all copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-
-#ifdef PC_HEADER
-#include "all.h"
-#else
 #include "glheader.h"
+#include "imports.h"
+#include "bufferobj.h"
 #include "context.h"
 #include "drawpix.h"
 #include "feedback.h"
+#include "framebuffer.h"
 #include "image.h"
-#include "macros.h"
-#include "mem.h"
-#include "mmath.h"
-#include "pixel.h"
-#include "pixeltex.h"
-#include "span.h"
 #include "state.h"
-#include "stencil.h"
-#include "texture.h"
-#include "types.h"
-#include "zoom.h"
-#endif
 
 
-
-/*
- * Given the dest position, size and skipPixels and skipRows values
- * for a glDrawPixels command, perform clipping of the image bounds
- * so the result lies withing the context's buffer bounds.
- * Return:  GL_TRUE if image is ready for drawing
- *          GL_FALSE if image was completely clipped away (draw nothing)
- */
-GLboolean
-_mesa_clip_pixelrect(const GLcontext *ctx,
-                     GLint *destX, GLint *destY,
-                     GLsizei *width, GLsizei *height,
-                     GLint *skipPixels, GLint *skipRows)
-{
-   const GLframebuffer *buffer = ctx->DrawBuffer;
-
-   /* left clipping */
-   if (*destX < buffer->Xmin) {
-      *skipPixels += (buffer->Xmin - *destX);
-      *width -= (buffer->Xmin - *destX);
-      *destX = buffer->Xmin;
-   }
-   /* right clipping */
-   if (*destX + *width > buffer->Xmax)
-      *width -= (*destX + *width - buffer->Xmax - 1);
-
-   if (*width <= 0)
-      return GL_FALSE;
-
-   /* bottom clipping */
-   if (*destY < buffer->Ymin) {
-      *skipRows += (buffer->Ymin - *destY);
-      *height -= (buffer->Ymin - *destY);
-      *destY = buffer->Ymin;
-   }
-   /* top clipping */
-   if (*destY + *height > buffer->Ymax)
-      *height -= (*destY + *height - buffer->Ymax - 1);
-
-   if (*height <= 0)
-      return GL_TRUE;
-
-   return GL_TRUE;
-}
-
-
-
-/*
- * Try to do a fast and simple RGB(a) glDrawPixels.
- * Return:  GL_TRUE if success, GL_FALSE if slow path must be used instead
+/**
+ * Do error checking of the format/type parameters to glReadPixels and
+ * glDrawPixels.
+ * \param drawing if GL_TRUE do checking for DrawPixels, else do checking
+ *                for ReadPixels.
+ * \return GL_TRUE if error detected, GL_FALSE if no errors
  */
 static GLboolean
-simple_DrawPixels( GLcontext *ctx, GLint x, GLint y,
-                   GLsizei width, GLsizei height, GLenum format, GLenum type,
-                   const GLvoid *pixels )
+error_check_format_type(GLcontext *ctx, GLenum format, GLenum type,
+                        GLboolean drawing)
 {
-   const struct gl_pixelstore_attrib *unpack = &ctx->Unpack;
-   GLubyte rgb[MAX_WIDTH][3];
-   GLubyte rgba[MAX_WIDTH][4];
-
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glDrawPixels", 
-                                                 GL_FALSE);
+   const char *readDraw = drawing ? "Draw" : "Read";
 
-
-   if (!ctx->Current.RasterPosValid) {
-      /* no-op */
+   if (ctx->Extensions.EXT_packed_depth_stencil
+       && type == GL_UNSIGNED_INT_24_8_EXT
+       && format != GL_DEPTH_STENCIL_EXT) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "gl%sPixels(format is not GL_DEPTH_STENCIL_EXT)", readDraw);
       return GL_TRUE;
    }
 
-   if ((ctx->RasterMask&(~(SCISSOR_BIT|WINCLIP_BIT)))==0
-       && !ctx->Pixel.ScaleOrBiasRGBA
-       && !ctx->Pixel.ScaleOrBiasRGBApcm
-       && ctx->ColorMatrix.type == MATRIX_IDENTITY
-       && !ctx->Pixel.ColorTableEnabled
-       && !ctx->Pixel.PostColorMatrixColorTableEnabled
-       && !ctx->Pixel.PostConvolutionColorTableEnabled
-       && !ctx->Pixel.MinMaxEnabled
-       && !ctx->Pixel.HistogramEnabled
-       && ctx->Pixel.IndexShift==0 && ctx->Pixel.IndexOffset==0
-       && ctx->Pixel.MapColorFlag==0
-       && ctx->Texture.ReallyEnabled == 0
-       && unpack->Alignment==1
-       && !unpack->SwapBytes
-       && !unpack->LsbFirst) {
-
-      GLint destX = x;
-      GLint destY = y;
-      GLint drawWidth = width;           /* actual width drawn */
-      GLint drawHeight = height;         /* actual height drawn */
-      GLint skipPixels = unpack->SkipPixels;
-      GLint skipRows = unpack->SkipRows;
-      GLint rowLength;
-      GLdepth zSpan[MAX_WIDTH];  /* only used when zooming */
-      GLint zoomY0;
-
-      if (unpack->RowLength > 0)
-         rowLength = unpack->RowLength;
-      else
-         rowLength = width;
-
-      /* If we're not using pixel zoom then do all clipping calculations
-       * now.  Otherwise, we'll let the gl_write_zoomed_*_span() functions
-       * handle the clipping.
-       */
-      if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-         /* horizontal clipping */
-         if (destX < ctx->DrawBuffer->Xmin) {
-            skipPixels += (ctx->DrawBuffer->Xmin - destX);
-            drawWidth  -= (ctx->DrawBuffer->Xmin - destX);
-            destX = ctx->DrawBuffer->Xmin;
-         }
-         if (destX + drawWidth > ctx->DrawBuffer->Xmax)
-            drawWidth -= (destX + drawWidth - ctx->DrawBuffer->Xmax - 1);
-         if (drawWidth <= 0)
-            return GL_TRUE;
-
-         /* vertical clipping */
-         if (destY < ctx->DrawBuffer->Ymin) {
-            skipRows   += (ctx->DrawBuffer->Ymin - destY);
-            drawHeight -= (ctx->DrawBuffer->Ymin - destY);
-            destY = ctx->DrawBuffer->Ymin;
-         }
-         if (destY + drawHeight > ctx->DrawBuffer->Ymax)
-            drawHeight -= (destY + drawHeight - ctx->DrawBuffer->Ymax - 1);
-         if (drawHeight <= 0)
-            return GL_TRUE;
+   /* basic combinations test */
+   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
+      _mesa_error(ctx, GL_INVALID_ENUM,
+                  "gl%sPixels(format or type)", readDraw);
+      return GL_TRUE;
+   }
 
-         zoomY0 = 0;  /* not used - silence compiler warning */
+   /* additional checks */
+   switch (format) {
+   case GL_RED:
+   case GL_GREEN:
+   case GL_BLUE:
+   case GL_ALPHA:
+   case GL_LUMINANCE:
+   case GL_LUMINANCE_ALPHA:
+   case GL_RGB:
+   case GL_BGR:
+   case GL_RGBA:
+   case GL_BGRA:
+   case GL_ABGR_EXT:
+      if (drawing && !ctx->Visual.rgbMode) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                   "glDrawPixels(drawing RGB pixels into color index buffer)");
+         return GL_TRUE;
       }
-      else {
-         /* setup array of fragment Z value to pass to zoom function */
-         GLdepth z = (GLdepth) (ctx->Current.RasterPos[2] * ctx->Visual->DepthMaxF);
-         GLint i;
-         assert(drawWidth < MAX_WIDTH);
-         for (i=0; i<drawWidth; i++)
-            zSpan[i] = z;
-
-         /* save Y value of first row */
-         zoomY0 = (GLint) (ctx->Current.RasterPos[1] + 0.5F);
+      if (!drawing && !_mesa_dest_buffer_exists(ctx, GL_COLOR)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glReadPixels(no color buffer)");
+         return GL_TRUE;
       }
-
-
-      /*
-       * Ready to draw!
-       * The window region at (destX, destY) of size (drawWidth, drawHeight)
-       * will be written to.
-       * We'll take pixel data from buffer pointed to by "pixels" but we'll
-       * skip "skipRows" rows and skip "skipPixels" pixels/row.
-       */
-
-      if (format==GL_RGBA && type==GL_UNSIGNED_BYTE) {
-         if (ctx->Visual->RGBAflag) {
-            GLubyte *src = (GLubyte *) pixels
-               + (skipRows * rowLength + skipPixels) * 4;
-            if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-               /* no zooming */
-               GLint row;
-               for (row=0; row<drawHeight; row++) {
-                  (*ctx->Driver.WriteRGBASpan)(ctx, drawWidth, destX, destY,
-                                               (void *) src, NULL);
-                  src += rowLength * 4;
-                  destY++;
-               }
-            }
-            else {
-               /* with zooming */
-               GLint row;
-               for (row=0; row<drawHeight; row++) {
-                  gl_write_zoomed_rgba_span(ctx, drawWidth, destX, destY,
-                                            zSpan, (void *) src, zoomY0);
-                  src += rowLength * 4;
-                  destY++;
-               }
-            }
-         }
+      break;
+   case GL_COLOR_INDEX:
+      if (!drawing && ctx->Visual.rgbMode) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                    "glReadPixels(reading color index format from RGB buffer)");
          return GL_TRUE;
       }
-      else if (format==GL_RGB && type==GL_UNSIGNED_BYTE) {
-         if (ctx->Visual->RGBAflag) {
-            GLubyte *src = (GLubyte *) pixels
-               + (skipRows * rowLength + skipPixels) * 3;
-            if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-               GLint row;
-               for (row=0; row<drawHeight; row++) {
-                  (*ctx->Driver.WriteRGBSpan)(ctx, drawWidth, destX, destY,
-                                              (void *) src, NULL);
-                  src += rowLength * 3;
-                  destY++;
-               }
-            }
-            else {
-               /* with zooming */
-               GLint row;
-               for (row=0; row<drawHeight; row++) {
-                  gl_write_zoomed_rgb_span(ctx, drawWidth, destX, destY,
-                                           zSpan, (void *) src, zoomY0);
-                  src += rowLength * 3;
-                  destY++;
-               }
-            }
-         }
+      if (!drawing && !_mesa_dest_buffer_exists(ctx, GL_COLOR)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glReadPixels(no color buffer)");
          return GL_TRUE;
       }
-      else if (format==GL_LUMINANCE && type==GL_UNSIGNED_BYTE) {
-         if (ctx->Visual->RGBAflag) {
-            GLubyte *src = (GLubyte *) pixels
-               + (skipRows * rowLength + skipPixels);
-            if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-               /* no zooming */
-               GLint row;
-               assert(drawWidth < MAX_WIDTH);
-               for (row=0; row<drawHeight; row++) {
-                  GLint i;
-                 for (i=0;i<drawWidth;i++) {
-                     rgb[i][0] = src[i];
-                     rgb[i][1] = src[i];
-                     rgb[i][2] = src[i];
-                 }
-                  (*ctx->Driver.WriteRGBSpan)(ctx, drawWidth, destX, destY,
-                                              (void *) rgb, NULL);
-                  src += rowLength;
-                  destY++;
-               }
-            }
-            else {
-               /* with zooming */
-               GLint row;
-               assert(drawWidth < MAX_WIDTH);
-               for (row=0; row<drawHeight; row++) {
-                  GLint i;
-                 for (i=0;i<drawWidth;i++) {
-                     rgb[i][0] = src[i];
-                     rgb[i][1] = src[i];
-                     rgb[i][2] = src[i];
-                 }
-                  gl_write_zoomed_rgb_span(ctx, drawWidth, destX, destY,
-                                           zSpan, (void *) rgb, zoomY0);
-                  src += rowLength;
-                  destY++;
-               }
-            }
-         }
+      break;
+   case GL_STENCIL_INDEX:
+      if ((drawing && !_mesa_dest_buffer_exists(ctx, format)) ||
+          (!drawing && !_mesa_source_buffer_exists(ctx, format))) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "gl%sPixels(no stencil buffer)", readDraw);
          return GL_TRUE;
       }
-      else if (format==GL_LUMINANCE_ALPHA && type==GL_UNSIGNED_BYTE) {
-         if (ctx->Visual->RGBAflag) {
-            GLubyte *src = (GLubyte *) pixels
-               + (skipRows * rowLength + skipPixels)*2;
-            if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-               /* no zooming */
-               GLint row;
-               assert(drawWidth < MAX_WIDTH);
-               for (row=0; row<drawHeight; row++) {
-                  GLint i;
-                  GLubyte *ptr = src;
-                 for (i=0;i<drawWidth;i++) {
-                     rgba[i][0] = *ptr;
-                     rgba[i][1] = *ptr;
-                     rgba[i][2] = *ptr++;
-                     rgba[i][3] = *ptr++;
-                 }
-                  (*ctx->Driver.WriteRGBASpan)(ctx, drawWidth, destX, destY,
-                                               (void *) rgba, NULL);
-                  src += rowLength*2;
-                  destY++;
-               }
-            }
-            else {
-               /* with zooming */
-               GLint row;
-               assert(drawWidth < MAX_WIDTH);
-               for (row=0; row<drawHeight; row++) {
-                  GLubyte *ptr = src;
-                  GLint i;
-                 for (i=0;i<drawWidth;i++) {
-                     rgba[i][0] = *ptr;
-                     rgba[i][1] = *ptr;
-                     rgba[i][2] = *ptr++;
-                     rgba[i][3] = *ptr++;
-                 }
-                  gl_write_zoomed_rgba_span(ctx, drawWidth, destX, destY,
-                                            zSpan, (void *) rgba, zoomY0);
-                  src += rowLength*2;
-                  destY++;
-               }
-            }
-         }
+      break;
+   case GL_DEPTH_COMPONENT:
+      if (!drawing && !_mesa_source_buffer_exists(ctx, format)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "gl%sPixels(no depth buffer)", readDraw);
          return GL_TRUE;
       }
-      else if (format==GL_COLOR_INDEX && type==GL_UNSIGNED_BYTE) {
-         GLubyte *src = (GLubyte *) pixels + skipRows * rowLength + skipPixels;
-         if (ctx->Visual->RGBAflag) {
-            /* convert CI data to RGBA */
-            if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-               /* no zooming */
-               GLint row;
-               for (row=0; row<drawHeight; row++) {
-                  assert(drawWidth < MAX_WIDTH);
-                  _mesa_map_ci8_to_rgba(ctx, drawWidth, src, rgba);
-                  (*ctx->Driver.WriteRGBASpan)(ctx, drawWidth, destX, destY,
-                                               (const GLubyte (*)[4])rgba, 
-                                              NULL);
-                  src += rowLength;
-                  destY++;
-               }
-               return GL_TRUE;
-            }
-            else {
-               /* with zooming */
-               GLint row;
-               for (row=0; row<drawHeight; row++) {
-                  assert(drawWidth < MAX_WIDTH);
-                  _mesa_map_ci8_to_rgba(ctx, drawWidth, src, rgba);
-                  gl_write_zoomed_rgba_span(ctx, drawWidth, destX, destY,
-                                            zSpan, (void *) rgba, zoomY0);
-                  src += rowLength;
-                  destY++;
-               }
-               return GL_TRUE;
-            }
-         }
-         else {
-            /* write CI data to CI frame buffer */
-            GLint row;
-            if (ctx->Pixel.ZoomX==1.0F && ctx->Pixel.ZoomY==1.0F) {
-               /* no zooming */
-               for (row=0; row<drawHeight; row++) {
-                  (*ctx->Driver.WriteCI8Span)(ctx, drawWidth, destX, destY,
-                                              src, NULL);
-                  src += rowLength;
-                  destY++;
-               }
-               return GL_TRUE;
-            }
-            else {
-               /* with zooming */
-               return GL_FALSE;
-            }
-         }
+      break;
+   case GL_DEPTH_STENCIL_EXT:
+      if (!ctx->Extensions.EXT_packed_depth_stencil ||
+          type != GL_UNSIGNED_INT_24_8_EXT) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "gl%sPixels(type)", readDraw);
+         return GL_TRUE;
       }
-      else {
-         /* can't handle this pixel format and/or data type here */
-         return GL_FALSE;
+      if ((drawing && !_mesa_dest_buffer_exists(ctx, format)) ||
+          (!drawing && !_mesa_source_buffer_exists(ctx, format))) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "gl%sPixels(no depth or stencil buffer)", readDraw);
+         return GL_TRUE;
       }
+      break;
+   default:
+      /* this should have been caught in _mesa_is_legal_format_type() */
+      _mesa_problem(ctx, "unexpected format in _mesa_%sPixels", readDraw);
+      return GL_TRUE;
    }
 
-   /* can't do a simple draw, have to use slow path */
+   /* no errors */
    return GL_FALSE;
 }
+      
 
 
+#if _HAVE_FULL_GL
 
 /*
- * Do glDrawPixels of index pixels.
+ * Execute glDrawPixels
  */
-static void
-draw_index_pixels( GLcontext *ctx, GLint x, GLint y,
-                   GLsizei width, GLsizei height,
-                   GLenum type, const GLvoid *pixels )
+void GLAPIENTRY
+_mesa_DrawPixels( GLsizei width, GLsizei height,
+                  GLenum format, GLenum type, const GLvoid *pixels )
 {
-   const GLboolean zoom = ctx->Pixel.ZoomX!=1.0 || ctx->Pixel.ZoomY!=1.0;
-   const GLint desty = y;
-   GLint row, drawWidth;
-   GLdepth zspan[MAX_WIDTH];
-
-   drawWidth = (width > MAX_WIDTH) ? MAX_WIDTH : width;
-
-   /* Fragment depth values */
-   if (ctx->Depth.Test || ctx->Fog.Enabled) {
-      GLdepth zval = (GLdepth) (ctx->Current.RasterPos[2] * ctx->Visual->DepthMaxF);
-      GLint i;
-      for (i = 0; i < drawWidth; i++) {
-        zspan[i] = zval;
-      }
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   if (width < 0 || height < 0) {
+      _mesa_error( ctx, GL_INVALID_VALUE, "glDrawPixels(width or height < 0" );
+      return;
    }
 
-   /*
-    * General solution
-    */
-   for (row = 0; row < height; row++, y++) {
-      GLuint indexes[MAX_WIDTH];
-      const GLvoid *source = _mesa_image_address(&ctx->Unpack,
-                    pixels, width, height, GL_COLOR_INDEX, type, 0, row, 0);
-      _mesa_unpack_index_span(ctx, drawWidth, GL_UNSIGNED_INT, indexes,
-                              type, source, &ctx->Unpack, GL_TRUE);
-      if (zoom) {
-         gl_write_zoomed_index_span(ctx, drawWidth, x, y, zspan, indexes, desty);
-      }
-      else {
-         gl_write_index_span(ctx, drawWidth, x, y, zspan, indexes, GL_BITMAP);
+   if (ctx->NewState) {
+      _mesa_update_state(ctx);
+   }
+
+   if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glDrawPixels (invalid fragment program)");
+      return;
+   }
+
+   if (error_check_format_type(ctx, format, type, GL_TRUE)) {
+      /* found an error */
+      return;
+   }
+
+   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glDrawPixels(incomplete framebuffer)" );
+      return;
+   }
+
+   if (!ctx->Current.RasterPosValid) {
+      return;
+   }
+
+   if (ctx->RenderMode == GL_RENDER) {
+      /* Round, to satisfy conformance tests (matches SGI's OpenGL) */
+      GLint x = IROUND(ctx->Current.RasterPos[0]);
+      GLint y = IROUND(ctx->Current.RasterPos[1]);
+
+      if (ctx->Unpack.BufferObj->Name) {
+         /* unpack from PBO */
+         if (!_mesa_validate_pbo_access(2, &ctx->Unpack, width, height, 1,
+                                        format, type, pixels)) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glDrawPixels(invalid PBO access)");
+            return;
+         }
+         if (ctx->Unpack.BufferObj->Pointer) {
+            /* buffer is mapped - that's an error */
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glDrawPixels(PBO is mapped)");
+            return;
+         }
       }
+
+      ctx->Driver.DrawPixels(ctx, x, y, width, height, format, type,
+                            &ctx->Unpack, pixels);
+   }
+   else if (ctx->RenderMode == GL_FEEDBACK) {
+      /* Feedback the current raster pos info */
+      FLUSH_CURRENT( ctx, 0 );
+      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_DRAW_PIXEL_TOKEN );
+      _mesa_feedback_vertex( ctx,
+                             ctx->Current.RasterPos,
+                             ctx->Current.RasterColor,
+                             ctx->Current.RasterIndex,
+                             ctx->Current.RasterTexCoords[0] );
+   }
+   else {
+      ASSERT(ctx->RenderMode == GL_SELECT);
+      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
    }
 }
 
 
-
-/*
- * Do glDrawPixels of stencil image.  The image datatype may either
- * be GLubyte or GLbitmap.
- */
-static void 
-draw_stencil_pixels( GLcontext *ctx, GLint x, GLint y,
-                     GLsizei width, GLsizei height,
-                     GLenum type, const GLvoid *pixels )
+void GLAPIENTRY
+_mesa_CopyPixels( GLint srcx, GLint srcy, GLsizei width, GLsizei height,
+                  GLenum type )
 {
-   const GLboolean zoom = ctx->Pixel.ZoomX!=1.0 || ctx->Pixel.ZoomY!=1.0;
-   const GLboolean shift_or_offset = ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset;
-   const GLint desty = y;
-   GLint row, drawWidth;
-
-   if (type != GL_BYTE &&
-       type != GL_UNSIGNED_BYTE &&
-       type != GL_SHORT &&
-       type != GL_UNSIGNED_SHORT &&
-       type != GL_INT &&
-       type != GL_UNSIGNED_INT &&
-       type != GL_FLOAT &&
-       type != GL_BITMAP) {
-      gl_error( ctx, GL_INVALID_ENUM, "glDrawPixels(stencil type)");
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   if (ctx->NewState) {
+      _mesa_update_state(ctx);
+   }
+
+   if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glCopyPixels (invalid fragment program)");
       return;
    }
 
-   drawWidth = (width > MAX_WIDTH) ? MAX_WIDTH : width;
+   if (width < 0 || height < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glCopyPixels(width or height < 0)");
+      return;
+   }
 
-   for (row = 0; row < height; row++, y++) {
-      GLstencil values[MAX_WIDTH];
-      GLenum destType = (sizeof(GLstencil) == sizeof(GLubyte))
-                      ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT;
-      const GLvoid *source = _mesa_image_address(&ctx->Unpack,
-                    pixels, width, height, GL_COLOR_INDEX, type, 0, row, 0);
-      _mesa_unpack_index_span(ctx, drawWidth, destType, values,
-                              type, source, &ctx->Unpack, GL_FALSE);
-      if (shift_or_offset) {
-         _mesa_shift_and_offset_stencil( ctx, drawWidth, values );
-      }
-      if (ctx->Pixel.MapStencilFlag) {
-         _mesa_map_stencil( ctx, drawWidth, values );
-      }
+   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
+       ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glCopyPixels(incomplete framebuffer)" );
+      return;
+   }
 
-      if (zoom) {
-         gl_write_zoomed_stencil_span( ctx, (GLuint) drawWidth, x, y,
-                                       values, desty );
-      }
-      else {
-         _mesa_write_stencil_span( ctx, (GLuint) drawWidth, x, y, values );
-      }
+   if (!_mesa_source_buffer_exists(ctx, type) ||
+       !_mesa_dest_buffer_exists(ctx, type)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glCopyPixels(missing source or dest buffer)");
+      return;
+   }
+
+   if (!ctx->Current.RasterPosValid) {
+      return;
+   }
+
+   if (ctx->RenderMode == GL_RENDER) {
+      /* Round to satisfy conformance tests (matches SGI's OpenGL) */
+      GLint destx = IROUND(ctx->Current.RasterPos[0]);
+      GLint desty = IROUND(ctx->Current.RasterPos[1]);
+      ctx->Driver.CopyPixels( ctx, srcx, srcy, width, height, destx, desty,
+                             type );
+   }
+   else if (ctx->RenderMode == GL_FEEDBACK) {
+      FLUSH_CURRENT( ctx, 0 );
+      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_COPY_PIXEL_TOKEN );
+      _mesa_feedback_vertex( ctx, 
+                             ctx->Current.RasterPos,
+                             ctx->Current.RasterColor,
+                             ctx->Current.RasterIndex,
+                             ctx->Current.RasterTexCoords[0] );
+   }
+   else {
+      ASSERT(ctx->RenderMode == GL_SELECT);
+      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
    }
 }
 
+#endif /* _HAVE_FULL_GL */
 
 
-/*
- * Do a glDrawPixels of depth values.
- */
-static void
-draw_depth_pixels( GLcontext *ctx, GLint x, GLint y,
-                   GLsizei width, GLsizei height,
-                   GLenum type, const GLvoid *pixels )
+
+void GLAPIENTRY
+_mesa_ReadPixels( GLint x, GLint y, GLsizei width, GLsizei height,
+                 GLenum format, GLenum type, GLvoid *pixels )
 {
-   const GLboolean bias_or_scale = ctx->Pixel.DepthBias!=0.0 || ctx->Pixel.DepthScale!=1.0;
-   const GLboolean zoom = ctx->Pixel.ZoomX!=1.0 || ctx->Pixel.ZoomY!=1.0;
-   const GLint desty = y;
-   GLubyte rgba[MAX_WIDTH][4];
-   GLuint ispan[MAX_WIDTH];
-   GLint drawWidth = (width > MAX_WIDTH) ? MAX_WIDTH : width;
-
-   if (type != GL_BYTE
-       && type != GL_UNSIGNED_BYTE
-       && type != GL_SHORT
-       && type != GL_UNSIGNED_SHORT
-       && type != GL_INT
-       && type != GL_UNSIGNED_INT
-       && type != GL_FLOAT) {
-      gl_error(ctx, GL_INVALID_ENUM, "glDrawPixels(type)");
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   if (width < 0 || height < 0) {
+      _mesa_error( ctx, GL_INVALID_VALUE,
+                   "glReadPixels(width=%d height=%d)", width, height );
       return;
    }
 
-   /* Colors or indexes */
-   if (ctx->Visual->RGBAflag) {
-      GLint r = (GLint) (ctx->Current.RasterColor[0] * 255.0F);
-      GLint g = (GLint) (ctx->Current.RasterColor[1] * 255.0F);
-      GLint b = (GLint) (ctx->Current.RasterColor[2] * 255.0F);
-      GLint a = (GLint) (ctx->Current.RasterColor[3] * 255.0F);
-      GLint i;
-      for (i = 0; i < drawWidth; i++) {
-         rgba[i][RCOMP] = r;
-         rgba[i][GCOMP] = g;
-         rgba[i][BCOMP] = b;
-         rgba[i][ACOMP] = a;
-      }
+   if (ctx->NewState)
+      _mesa_update_state(ctx);
+
+   if (error_check_format_type(ctx, format, type, GL_FALSE)) {
+      /* found an error */
+      return;
    }
-   else {
-      GLint i;
-      for (i = 0; i < drawWidth; i++) {
-        ispan[i] = ctx->Current.RasterIndex;
-      }
+
+   if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glReadPixels(incomplete framebuffer)" );
+      return;
    }
 
-   if (type==GL_UNSIGNED_SHORT && sizeof(GLdepth)==sizeof(GLushort)
-       && !bias_or_scale && !zoom && ctx->Visual->RGBAflag) {
-      /* Special case: directly write 16-bit depth values */
-      GLint row;
-      for (row = 0; row < height; row++, y++) {
-         GLdepth zspan[MAX_WIDTH];
-         const GLushort *zptr = _mesa_image_address(&ctx->Unpack,
-                pixels, width, height, GL_DEPTH_COMPONENT, type, 0, row, 0);
-         GLint i;
-         for (i = 0; i < width; i++)
-            zspan[i] = zptr[i];
-         gl_write_rgba_span( ctx, width, x, y, zspan, rgba, GL_BITMAP );
-      }
+   if (!_mesa_source_buffer_exists(ctx, format)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(no readbuffer)");
+      return;
    }
-   else if (type==GL_UNSIGNED_INT && ctx->Visual->DepthBits == 32
-       && !bias_or_scale && !zoom && ctx->Visual->RGBAflag) {
-      /* Special case: directly write 32-bit depth values */
-      GLint row;
-      for (row = 0; row < height; row++, y++) {
-         const GLuint *zptr = _mesa_image_address(&ctx->Unpack,
-                pixels, width, height, GL_DEPTH_COMPONENT, type, 0, row, 0);
-         gl_write_rgba_span( ctx, width, x, y, zptr, rgba, GL_BITMAP );
+
+   if (ctx->Pack.BufferObj->Name) {
+      if (!_mesa_validate_pbo_access(2, &ctx->Pack, width, height, 1,
+                                     format, type, pixels)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glReadPixels(invalid PBO access)");
+         return;
       }
-   }
-   else {
-      /* General case */
-      GLint row;
-      for (row = 0; row < height; row++, y++) {
-         GLdepth zspan[MAX_WIDTH];
-         const GLvoid *src = _mesa_image_address(&ctx->Unpack,
-                pixels, width, height, GL_DEPTH_COMPONENT, type, 0, row, 0);
-         _mesa_unpack_depth_span( ctx, drawWidth, zspan, type, src,
-                                  &ctx->Unpack, GL_TRUE );
-         if (ctx->Visual->RGBAflag) {
-            if (zoom) {
-               gl_write_zoomed_rgba_span(ctx, width, x, y, zspan,
-                                         (const GLubyte (*)[4])rgba, desty);
-            }
-            else {
-               gl_write_rgba_span(ctx, width, x, y, zspan, rgba, GL_BITMAP);
-            }
-         }
-         else {
-            if (zoom) {
-               gl_write_zoomed_index_span(ctx, width, x, y, zspan,
-                                          ispan, GL_BITMAP);
-            }
-            else {
-               gl_write_index_span(ctx, width, x, y, zspan, ispan, GL_BITMAP);
-            }
-         }
 
+      if (ctx->Pack.BufferObj->Pointer) {
+         /* buffer is mapped - that's an error */
+         _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(PBO is mapped)");
+         return;
       }
    }
+
+   ctx->Driver.ReadPixels(ctx, x, y, width, height,
+                         format, type, &ctx->Pack, pixels);
 }
 
 
-/*
- * Do glDrawPixels of RGBA pixels.
- */
-static void
-draw_rgba_pixels( GLcontext *ctx, GLint x, GLint y,
-                  GLsizei width, GLsizei height,
-                  GLenum format, GLenum type, const GLvoid *pixels )
+
+void GLAPIENTRY
+_mesa_Bitmap( GLsizei width, GLsizei height,
+              GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove,
+              const GLubyte *bitmap )
 {
-   const struct gl_pixelstore_attrib *unpack = &ctx->Unpack;
-   const GLboolean zoom = ctx->Pixel.ZoomX!=1.0 || ctx->Pixel.ZoomY!=1.0;
-   const GLint desty = y;
-   GLdepth zspan[MAX_WIDTH];
-   GLboolean quickDraw;
-
-   /* Try an optimized glDrawPixels first */
-   if (simple_DrawPixels(ctx, x, y, width, height, format, type, pixels))
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   if (width < 0 || height < 0) {
+      _mesa_error( ctx, GL_INVALID_VALUE, "glBitmap(width or height < 0)" );
       return;
+   }
 
-   /* Fragment depth values */
-   if (ctx->Depth.Test || ctx->Fog.Enabled) {
-      /* fill in array of z values */
-      GLdepth z = (GLdepth) (ctx->Current.RasterPos[2] * ctx->Visual->DepthMaxF);
-      GLint i;
-      for (i=0;i<width;i++) {
-        zspan[i] = z;
-      }
+   if (!ctx->Current.RasterPosValid) {
+      return;    /* do nothing */
    }
 
+   if (ctx->NewState) {
+      _mesa_update_state(ctx);
+   }
 
-   if (ctx->RasterMask == 0 && !zoom
-       && x >= 0 && y >= 0
-       && x + width <= ctx->DrawBuffer->Width
-       && y + height <= ctx->DrawBuffer->Height) {
-      quickDraw = GL_TRUE;
+   if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glBitmap (invalid fragment program)");
+      return;
    }
-   else {
-      quickDraw = GL_FALSE;
-   }
-
-   /*
-    * General solution
-    */
-   {
-      GLubyte rgba[MAX_WIDTH][4];
-      GLint row;
-      if (width > MAX_WIDTH)
-         width = MAX_WIDTH;
-      for (row = 0; row < height; row++, y++) {
-         const GLvoid *source = _mesa_image_address(unpack,
-                  pixels, width, height, format, type, 0, row, 0);
-         _mesa_unpack_ubyte_color_span(ctx, width, GL_RGBA, (void*) rgba,
-                   format, type, source, unpack, GL_TRUE);
-         if ((ctx->Pixel.MinMaxEnabled && ctx->MinMax.Sink) ||
-             (ctx->Pixel.HistogramEnabled && ctx->Histogram.Sink))
-            continue;
-
-         if (ctx->Texture.ReallyEnabled && ctx->Pixel.PixelTextureEnabled) {
-            GLfloat s[MAX_WIDTH], t[MAX_WIDTH], r[MAX_WIDTH], q[MAX_WIDTH];
-            GLubyte primary_rgba[MAX_WIDTH][4];
-            GLuint unit;
-            /* XXX not sure how multitexture is supposed to work here */
-
-            MEMCPY(primary_rgba, rgba, 4 * width * sizeof(GLubyte));
-
-            for (unit = 0; unit < MAX_TEXTURE_UNITS; unit++) {
-               _mesa_pixeltexgen(ctx, width, (const GLubyte (*)[4]) rgba,
-                                 s, t, r, q);
-               gl_texture_pixels(ctx, unit, width, s, t, r, NULL,
-                                 primary_rgba, rgba);
-            }
-         }
 
-         if (quickDraw) {
-            (*ctx->Driver.WriteRGBASpan)( ctx, width, x, y,
-                                          (CONST GLubyte (*)[]) rgba, NULL);
-         }
-         else if (zoom) {
-            gl_write_zoomed_rgba_span( ctx, width, x, y, zspan, 
-                                      (CONST GLubyte (*)[]) rgba, desty );
+   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glBitmap(incomplete framebuffer)");
+      return;
+   }
+
+   if (ctx->RenderMode == GL_RENDER) {
+      /* Truncate, to satisfy conformance tests (matches SGI's OpenGL). */
+      const GLfloat epsilon = 0.0001;
+      GLint x = IFLOOR(ctx->Current.RasterPos[0] + epsilon - xorig);
+      GLint y = IFLOOR(ctx->Current.RasterPos[1] + epsilon - yorig);
+
+      if (ctx->Unpack.BufferObj->Name) {
+         /* unpack from PBO */
+         if (!_mesa_validate_pbo_access(2, &ctx->Unpack, width, height, 1,
+                                        GL_COLOR_INDEX, GL_BITMAP,
+                                        (GLvoid *) bitmap)) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glBitmap(invalid PBO access)");
+            return;
          }
-         else {
-            gl_write_rgba_span( ctx, (GLuint) width, x, y, zspan, rgba, GL_BITMAP);
+         if (ctx->Unpack.BufferObj->Pointer) {
+            /* buffer is mapped - that's an error */
+            _mesa_error(ctx, GL_INVALID_OPERATION, "glBitmap(PBO is mapped)");
+            return;
          }
       }
+
+      ctx->Driver.Bitmap( ctx, x, y, width, height, &ctx->Unpack, bitmap );
+   }
+#if _HAVE_FULL_GL
+   else if (ctx->RenderMode == GL_FEEDBACK) {
+      FLUSH_CURRENT(ctx, 0);
+      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_BITMAP_TOKEN );
+      _mesa_feedback_vertex( ctx,
+                             ctx->Current.RasterPos,
+                             ctx->Current.RasterColor,
+                             ctx->Current.RasterIndex, 
+                             ctx->Current.RasterTexCoords[0] );
    }
+   else {
+      ASSERT(ctx->RenderMode == GL_SELECT);
+      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
+   }
+#endif
+
+   /* update raster position */
+   ctx->Current.RasterPos[0] += xmove;
+   ctx->Current.RasterPos[1] += ymove;
 }
 
 
 
+#if 0  /* experimental */
 /*
- * Execute glDrawPixels
+ * Execute glDrawDepthPixelsMESA().  This function accepts both a color
+ * image and depth (Z) image.  Rasterization produces fragments with
+ * color and Z taken from these images.  This function is intended for
+ * Z-compositing.  Normally, this operation requires two glDrawPixels
+ * calls with stencil testing.
  */
-void
-_mesa_DrawPixels( GLsizei width, GLsizei height,
-                  GLenum format, GLenum type, const GLvoid *pixels )
+void GLAPIENTRY
+_mesa_DrawDepthPixelsMESA( GLsizei width, GLsizei height,
+                           GLenum colorFormat, GLenum colorType,
+                           const GLvoid *colors,
+                           GLenum depthType, const GLvoid *depths )
 {
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glDrawPixels");
-
-   if (ctx->RenderMode==GL_RENDER) {
-      GLint x, y;
-      if (!pixels || !ctx->Current.RasterPosValid) {
-        return;
-      }
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-      if (ctx->NewState) {
-         gl_update_state(ctx);
-      }
+   if (width < 0 || height < 0) {
+      _mesa_error( ctx, GL_INVALID_VALUE,
+                   "glDrawDepthPixelsMESA(width or height < 0" );
+      return;
+   }
 
-      x = (GLint) (ctx->Current.RasterPos[0] + 0.5F);
-      y = (GLint) (ctx->Current.RasterPos[1] + 0.5F);
+   if (!ctx->Current.RasterPosValid) {
+      return;
+   }
 
-      ctx->OcclusionResult = GL_TRUE;
+   if (ctx->NewState) {
+      _mesa_update_state(ctx);
+   }
 
-      /* see if device driver can do the drawpix */
-      if (ctx->Driver.DrawPixels
-          && (*ctx->Driver.DrawPixels)(ctx, x, y, width, height, format, type,
-                                       &ctx->Unpack, pixels)) {
-         return;
-      }
+   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glDrawDepthPixelsMESA(incomplete framebuffer)");
+      return;
+   }
 
-      switch (format) {
-        case GL_STENCIL_INDEX:
-           draw_stencil_pixels( ctx, x, y, width, height, type, pixels );
-           break;
-        case GL_DEPTH_COMPONENT:
-           draw_depth_pixels( ctx, x, y, width, height, type, pixels );
-           break;
-        case GL_COLOR_INDEX:
-            if (ctx->Visual->RGBAflag)
-               draw_rgba_pixels(ctx, x,y, width, height, format, type, pixels);
-            else
-               draw_index_pixels(ctx, x, y, width, height, type, pixels);
-           break;
-        case GL_RED:
-        case GL_GREEN:
-        case GL_BLUE:
-        case GL_ALPHA:
-        case GL_LUMINANCE:
-        case GL_LUMINANCE_ALPHA:
-        case GL_RGB:
-         case GL_BGR:
-        case GL_RGBA:
-        case GL_BGRA:
-        case GL_ABGR_EXT:
-            draw_rgba_pixels(ctx, x, y, width, height, format, type, pixels);
-           break;
-        default:
-           gl_error( ctx, GL_INVALID_ENUM, "glDrawPixels(format)" );
-            return;
-      }
+   if (ctx->RenderMode == GL_RENDER) {
+      /* Round, to satisfy conformance tests (matches SGI's OpenGL) */
+      GLint x = IROUND(ctx->Current.RasterPos[0]);
+      GLint y = IROUND(ctx->Current.RasterPos[1]);
+      ctx->Driver.DrawDepthPixelsMESA(ctx, x, y, width, height,
+                                      colorFormat, colorType, colors,
+                                      depthType, depths, &ctx->Unpack);
    }
-   else if (ctx->RenderMode==GL_FEEDBACK) {
-      if (ctx->Current.RasterPosValid) {
-         GLfloat color[4];
-        GLfloat texcoord[4], invq;
-        UBYTE_RGBA_TO_FLOAT_RGBA(color, ctx->Current.ByteColor);
-         invq = 1.0F / ctx->Current.Texcoord[0][3];
-         texcoord[0] = ctx->Current.Texcoord[0][0] * invq;
-         texcoord[1] = ctx->Current.Texcoord[0][1] * invq;
-         texcoord[2] = ctx->Current.Texcoord[0][2] * invq;
-         texcoord[3] = ctx->Current.Texcoord[0][3];
-         FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_DRAW_PIXEL_TOKEN );
-         gl_feedback_vertex( ctx,
+   else if (ctx->RenderMode == GL_FEEDBACK) {
+      /* Feedback the current raster pos info */
+      FLUSH_CURRENT( ctx, 0 );
+      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_DRAW_PIXEL_TOKEN );
+      _mesa_feedback_vertex( ctx,
                              ctx->Current.RasterPos,
-                             color, ctx->Current.Index, texcoord );
-      }
+                             ctx->Current.RasterColor,
+                             ctx->Current.RasterIndex,
+                             ctx->Current.RasterTexCoords[0] );
    }
-   else if (ctx->RenderMode==GL_SELECT) {
-      if (ctx->Current.RasterPosValid) {
-         gl_update_hitflag( ctx, ctx->Current.RasterPos[2] );
-      }
+   else {
+      ASSERT(ctx->RenderMode == GL_SELECT);
+      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
    }
 }
 
+#endif