| /* |
| * Mesa 3-D graphics library |
| * 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 |
| * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
| * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #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 "state.h" |
| |
| |
| /** |
| * 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 |
| error_check_format_type(GLcontext *ctx, GLenum format, GLenum type, |
| GLboolean drawing) |
| { |
| const char *readDraw = drawing ? "Draw" : "Read"; |
| |
| 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; |
| } |
| |
| /* 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; |
| } |
| |
| /* 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; |
| } |
| if (!drawing && !_mesa_dest_buffer_exists(ctx, GL_COLOR)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glReadPixels(no color buffer)"); |
| return GL_TRUE; |
| } |
| 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; |
| } |
| if (!drawing && !_mesa_dest_buffer_exists(ctx, GL_COLOR)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glReadPixels(no color buffer)"); |
| return GL_TRUE; |
| } |
| 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; |
| } |
| 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; |
| } |
| 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; |
| } |
| 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; |
| } |
| |
| /* no errors */ |
| return GL_FALSE; |
| } |
| |
| |
| |
| #if _HAVE_FULL_GL |
| |
| /* |
| * Execute glDrawPixels |
| */ |
| void GLAPIENTRY |
| _mesa_DrawPixels( GLsizei width, GLsizei height, |
| GLenum format, GLenum type, const GLvoid *pixels ) |
| { |
| 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; |
| } |
| |
| 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. */ |
| } |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_CopyPixels( GLint srcx, GLint srcy, GLsizei width, GLsizei height, |
| GLenum 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; |
| } |
| |
| if (width < 0 || height < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "glCopyPixels(width or height < 0)"); |
| return; |
| } |
| |
| 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 (!_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 */ |
| |
| |
| |
| void GLAPIENTRY |
| _mesa_ReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, |
| GLenum format, GLenum type, GLvoid *pixels ) |
| { |
| 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; |
| } |
| |
| if (ctx->NewState) |
| _mesa_update_state(ctx); |
| |
| if (error_check_format_type(ctx, format, type, GL_FALSE)) { |
| /* found an error */ |
| return; |
| } |
| |
| if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { |
| _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, |
| "glReadPixels(incomplete framebuffer)" ); |
| return; |
| } |
| |
| if (!_mesa_source_buffer_exists(ctx, format)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(no readbuffer)"); |
| return; |
| } |
| |
| 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; |
| } |
| |
| 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); |
| } |
| |
| |
| |
| void GLAPIENTRY |
| _mesa_Bitmap( GLsizei width, GLsizei height, |
| GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, |
| const GLubyte *bitmap ) |
| { |
| 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; |
| } |
| |
| if (!ctx->Current.RasterPosValid) { |
| return; /* do nothing */ |
| } |
| |
| if (ctx->NewState) { |
| _mesa_update_state(ctx); |
| } |
| |
| if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glBitmap (invalid fragment program)"); |
| return; |
| } |
| |
| 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; |
| } |
| 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 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 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); |
| |
| if (width < 0 || height < 0) { |
| _mesa_error( ctx, GL_INVALID_VALUE, |
| "glDrawDepthPixelsMESA(width or height < 0" ); |
| return; |
| } |
| |
| if (!ctx->Current.RasterPosValid) { |
| return; |
| } |
| |
| if (ctx->NewState) { |
| _mesa_update_state(ctx); |
| } |
| |
| if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { |
| _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, |
| "glDrawDepthPixelsMESA(incomplete framebuffer)"); |
| 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) { |
| /* 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. */ |
| } |
| } |
| |
| #endif |