mesa: refactor: move glReadPixels code into new readpix.c file
[mesa.git] / src / mesa / main / api_validate.c
index 2706c705fc12ea54f4d619c4184b951042203e9e..5a199939760fac3e2b5ac2cef1dc91b9a9b447ee 100644 (file)
@@ -1,11 +1,8 @@
-
-/* $Id: api_validate.c,v 1.2 2001/01/02 21:40:57 brianp Exp $ */
-
 /*
  * Mesa 3-D graphics library
- * Version:  3.5
+ * Version:  7.1
  *
- * Copyright (C) 1999-2000  Brian Paul   All Rights Reserved.
+ * Copyright (C) 1999-2007  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"),
 #include "glheader.h"
 #include "api_validate.h"
 #include "context.h"
+#include "imports.h"
 #include "mtypes.h"
 #include "state.h"
 
+
+/**
+ * Find the max index in the given element/index buffer
+ */
+static GLuint
+max_buffer_index(GLcontext *ctx, GLuint count, GLenum type,
+                 const void *indices,
+                 struct gl_buffer_object *elementBuf)
+{
+   const GLubyte *map = NULL;
+   GLuint max = 0;
+   GLint i;
+
+   if (elementBuf->Name) {
+      /* elements are in a user-defined buffer object.  need to map it */
+      map = ctx->Driver.MapBuffer(ctx,
+                                  GL_ELEMENT_ARRAY_BUFFER_ARB,
+                                  GL_READ_ONLY,
+                                  elementBuf);
+      /* Actual address is the sum of pointers */
+      indices = (const GLvoid *) ADD_POINTERS(map, (const GLubyte *) indices);
+   }
+
+   if (type == GL_UNSIGNED_INT) {
+      for (i = 0; i < count; i++)
+         if (((GLuint *) indices)[i] > max)
+            max = ((GLuint *) indices)[i];
+   }
+   else if (type == GL_UNSIGNED_SHORT) {
+      for (i = 0; i < count; i++)
+         if (((GLushort *) indices)[i] > max)
+            max = ((GLushort *) indices)[i];
+   }
+   else {
+      ASSERT(type == GL_UNSIGNED_BYTE);
+      for (i = 0; i < count; i++)
+         if (((GLubyte *) indices)[i] > max)
+            max = ((GLubyte *) indices)[i];
+   }
+
+   if (map) {
+      ctx->Driver.UnmapBuffer(ctx,
+                              GL_ELEMENT_ARRAY_BUFFER_ARB,
+                              ctx->Array.ElementArrayBufferObj);
+   }
+
+   return max;
+}
+
+
 GLboolean
 _mesa_validate_DrawElements(GLcontext *ctx,
-                           GLenum mode, GLsizei count, GLenum type, 
+                           GLenum mode, GLsizei count, GLenum type,
                            const GLvoid *indices)
 {
-   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx,  GL_FALSE); 
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx,  GL_FALSE);
 
    if (count <= 0) {
       if (count < 0)
-        gl_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" );
+        _mesa_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" );
       return GL_FALSE;
    }
 
-   if (mode < 0 || 
-       mode > GL_POLYGON) {
-      gl_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)" );
+   if (mode > GL_POLYGON) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(mode)" );
       return GL_FALSE;
    }
 
-   if (type != GL_UNSIGNED_INT && 
-       type != GL_UNSIGNED_BYTE && 
+   if (type != GL_UNSIGNED_INT &&
+       type != GL_UNSIGNED_BYTE &&
        type != GL_UNSIGNED_SHORT)
    {
-      gl_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
+      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
       return GL_FALSE;
    }
 
    if (ctx->NewState)
-      gl_update_state( ctx );
+      _mesa_update_state(ctx);
 
-   if (!ctx->Array.Vertex.Enabled)
+   /* Always need vertex positions, unless a vertex program is in use */
+   if (!ctx->VertexProgram._Current &&
+       !ctx->Array.ArrayObj->Vertex.Enabled &&
+       !ctx->Array.ArrayObj->VertexAttrib[0].Enabled)
       return GL_FALSE;
 
+   /* Vertex buffer object tests */
+   if (ctx->Array.ElementArrayBufferObj->Name) {
+      /* use indices in the buffer object */
+      GLuint indexBytes;
+
+      if (type == GL_UNSIGNED_INT) {
+         indexBytes = count * sizeof(GLuint);
+      }
+      else if (type == GL_UNSIGNED_BYTE) {
+         indexBytes = count * sizeof(GLubyte);
+      }
+      else {
+         ASSERT(type == GL_UNSIGNED_SHORT);
+         indexBytes = count * sizeof(GLushort);
+      }
+
+      /* make sure count doesn't go outside buffer bounds */
+      if (indexBytes > ctx->Array.ElementArrayBufferObj->Size) {
+         _mesa_warning(ctx, "glDrawElements index out of buffer bounds");
+         return GL_FALSE;
+      }
+   }
+   else {
+      /* not using a VBO */
+      if (!indices)
+         return GL_FALSE;
+   }
+
+   if (ctx->Const.CheckArrayBounds) {
+      /* find max array index */
+      GLuint max = max_buffer_index(ctx, count, type, indices,
+                                    ctx->Array.ElementArrayBufferObj);
+      if (max >= ctx->Array._MaxElement) {
+         /* the max element is out of bounds of one or more enabled arrays */
+         return GL_FALSE;
+      }
+   }
+
    return GL_TRUE;
 }
 
 
 GLboolean
-_mesa_validate_DrawRangeElements(GLcontext *ctx, GLenum mode, 
-                                GLuint start, GLuint end, 
-                                GLsizei count, GLenum type, 
+_mesa_validate_DrawRangeElements(GLcontext *ctx, GLenum mode,
+                                GLuint start, GLuint end,
+                                GLsizei count, GLenum type,
                                 const GLvoid *indices)
 {
-   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
 
    if (count <= 0) {
       if (count < 0)
-        gl_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" );
+        _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(count)" );
       return GL_FALSE;
    }
 
-   if (mode < 0 || mode > GL_POLYGON) {
-      gl_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)" );
+   if (mode > GL_POLYGON) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(mode)" );
       return GL_FALSE;
    }
 
    if (end < start) {
-      gl_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(end<start)");
+      _mesa_error(ctx, GL_INVALID_VALUE, "glDrawRangeElements(end<start)");
       return GL_FALSE;
    }
 
-   if (type != GL_UNSIGNED_INT && 
-       type != GL_UNSIGNED_BYTE && 
-       type != GL_UNSIGNED_SHORT)
-   {
-      gl_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
+   if (type != GL_UNSIGNED_INT &&
+       type != GL_UNSIGNED_BYTE &&
+       type != GL_UNSIGNED_SHORT) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawRangeElements(type)" );
       return GL_FALSE;
    }
 
    if (ctx->NewState)
-      gl_update_state( ctx );
+      _mesa_update_state(ctx);
 
-   if (!ctx->Array.Vertex.Enabled)
+   /* Always need vertex positions, unless a vertex program is in use */
+   if (!ctx->VertexProgram._Current &&
+       !ctx->Array.ArrayObj->Vertex.Enabled &&
+       !ctx->Array.ArrayObj->VertexAttrib[0].Enabled)
       return GL_FALSE;
 
+   /* Vertex buffer object tests */
+   if (ctx->Array.ElementArrayBufferObj->Name) {
+      /* use indices in the buffer object */
+      GLuint indexBytes;
+
+      if (type == GL_UNSIGNED_INT) {
+         indexBytes = count * sizeof(GLuint);
+      }
+      else if (type == GL_UNSIGNED_BYTE) {
+         indexBytes = count * sizeof(GLubyte);
+      }
+      else {
+         ASSERT(type == GL_UNSIGNED_SHORT);
+         indexBytes = count * sizeof(GLushort);
+      }
+
+      /* make sure count doesn't go outside buffer bounds */
+      if (indexBytes > ctx->Array.ElementArrayBufferObj->Size) {
+         _mesa_warning(ctx, "glDrawRangeElements index out of buffer bounds");
+         return GL_FALSE;
+      }
+   }
+   else {
+      /* not using a VBO */
+      if (!indices)
+         return GL_FALSE;
+   }
+
+   if (ctx->Const.CheckArrayBounds) {
+      GLuint max = max_buffer_index(ctx, count, type, indices,
+                                    ctx->Array.ElementArrayBufferObj);
+      if (max >= ctx->Array._MaxElement) {
+         /* the max element is out of bounds of one or more enabled arrays */
+         return GL_FALSE;
+      }
+   }
+
    return GL_TRUE;
 }
 
 
-
+/**
+ * Called from the tnl module to error check the function parameters and
+ * verify that we really can draw something.
+ */
 GLboolean
-_mesa_validate_DrawArrays(GLcontext *ctx, 
+_mesa_validate_DrawArrays(GLcontext *ctx,
                          GLenum mode, GLint start, GLsizei count)
 {
-   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); 
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
 
-   if (count<0) {
-      gl_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count)" );
+   if (count <= 0) {
+      if (count < 0)
+         _mesa_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count)" );
       return GL_FALSE;
    }
 
-   if (mode < 0 || mode > GL_POLYGON) {
-      gl_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)" );
+   if (mode > GL_POLYGON) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glDrawArrays(mode)" );
       return GL_FALSE;
    }
 
    if (ctx->NewState)
-      gl_update_state( ctx );
+      _mesa_update_state(ctx);
 
-   if (!ctx->Array.Vertex.Enabled) 
+   /* Always need vertex positions, unless a vertex program is in use */
+   if (!ctx->VertexProgram._Current &&
+       !ctx->Array.ArrayObj->Vertex.Enabled &&
+       !ctx->Array.ArrayObj->VertexAttrib[0].Enabled)
       return GL_FALSE;
 
+   if (ctx->Const.CheckArrayBounds) {
+      if (start + count > (GLint) ctx->Array._MaxElement)
+         return GL_FALSE;
+   }
+
    return GL_TRUE;
 }
-