drawpix.c revision 5208867f12abd4b13c517e8cd006afde6fadbed8
1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5
4 *
5 * Copyright (C) 1999-2005  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include "glheader.h"
26#include "imports.h"
27#include "context.h"
28#include "drawpix.h"
29#include "feedback.h"
30#include "image.h"
31#include "state.h"
32
33
34/**
35 * Do error checking of the format/type parameters to glReadPixels and
36 * glDrawPixels.
37 * \param drawing if GL_TRUE do checking for DrawPixels, else do checking
38 *                for ReadPixels.
39 * \return GL_TRUE if error detected, GL_FALSE if no errors
40 */
41static GLboolean
42error_check_format_type(GLcontext *ctx, GLenum format, GLenum type,
43                        GLboolean drawing)
44{
45   const char *readDraw = drawing ? "Draw" : "Read";
46
47   /* basic combinations test */
48   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
49      _mesa_error(ctx, GL_INVALID_ENUM,
50                  "gl%sPixels(format or type)", readDraw);
51      return GL_TRUE;
52   }
53
54   /* additional checks */
55   switch (format) {
56   case GL_RED:
57   case GL_GREEN:
58   case GL_BLUE:
59   case GL_ALPHA:
60   case GL_LUMINANCE:
61   case GL_LUMINANCE_ALPHA:
62   case GL_RGB:
63   case GL_BGR:
64   case GL_RGBA:
65   case GL_BGRA:
66   case GL_ABGR_EXT:
67      if (drawing && !ctx->Visual.rgbMode) {
68         _mesa_error(ctx, GL_INVALID_OPERATION,
69                   "glDrawPixels(drawing RGB pixels into color index buffer)");
70         return GL_TRUE;
71      }
72      break;
73   case GL_COLOR_INDEX:
74      if (!drawing && ctx->Visual.rgbMode) {
75         _mesa_error(ctx, GL_INVALID_OPERATION,
76                    "glReadPixels(reading color index format from RGB buffer");
77         return GL_TRUE;
78      }
79      break;
80   case GL_STENCIL_INDEX:
81      if (ctx->DrawBuffer->Visual.stencilBits == 0) {
82         _mesa_error(ctx, GL_INVALID_OPERATION,
83                     "gl%sPixels(no stencil buffer)", readDraw);
84         return GL_TRUE;
85      }
86      break;
87   case GL_DEPTH_COMPONENT:
88      if (ctx->DrawBuffer->Visual.depthBits == 0) {
89         _mesa_error(ctx, GL_INVALID_OPERATION,
90                     "gl%sPixels(no depth buffer)", readDraw);
91         return GL_TRUE;
92      }
93      break;
94   case GL_DEPTH_STENCIL_EXT:
95      if (!ctx->Extensions.EXT_packed_depth_stencil ||
96          type != GL_UNSIGNED_INT_24_8_EXT) {
97         _mesa_error(ctx, GL_INVALID_ENUM, "gl%sPixels(type)", readDraw);
98         return GL_TRUE;
99      }
100      if (ctx->DrawBuffer->Visual.depthBits == 0 ||
101          ctx->ReadBuffer->Visual.depthBits == 0 ||
102          ctx->DrawBuffer->Visual.stencilBits == 0 ||
103          ctx->ReadBuffer->Visual.stencilBits == 0) {
104         _mesa_error(ctx, GL_INVALID_OPERATION,
105                     "gl%sPixels(no depth or stencil buffer)", readDraw);
106         return GL_TRUE;
107      }
108      break;
109   default:
110      /* this should have been caught in _mesa_is_legal_format_type() */
111      _mesa_problem(ctx, "unexpected format in _mesa_%sPixels", readDraw);
112      return GL_TRUE;
113   }
114
115   /* XXX might have to move this to the top of the function */
116   if (type == GL_UNSIGNED_INT_24_8_EXT && format != GL_DEPTH_STENCIL_EXT) {
117      _mesa_error(ctx, GL_INVALID_OPERATION,
118                  "gl%sPixels(format is not GL_DEPTH_STENCIL_EXT)", readDraw);
119      return GL_TRUE;
120   }
121
122   /* no errors */
123   return GL_FALSE;
124}
125
126
127
128#if _HAVE_FULL_GL
129
130/*
131 * Execute glDrawPixels
132 */
133void GLAPIENTRY
134_mesa_DrawPixels( GLsizei width, GLsizei height,
135                  GLenum format, GLenum type, const GLvoid *pixels )
136{
137   GET_CURRENT_CONTEXT(ctx);
138   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
139
140   if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
141      _mesa_error(ctx, GL_INVALID_OPERATION,
142                  "glDrawPixels (invalid fragment program)");
143      return;
144   }
145
146   if (width < 0 || height < 0) {
147      _mesa_error( ctx, GL_INVALID_VALUE, "glDrawPixels(width or height < 0" );
148      return;
149   }
150
151   if (error_check_format_type(ctx, format, type, GL_TRUE)) {
152      /* found an error */
153      return;
154   }
155
156   if (ctx->NewState) {
157      _mesa_update_state(ctx);
158   }
159
160   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
161      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
162                  "glDrawPixels(incomplete framebuffer)" );
163      return;
164   }
165
166   if (!ctx->Current.RasterPosValid) {
167      return;
168   }
169
170   if (ctx->RenderMode == GL_RENDER) {
171      /* Round, to satisfy conformance tests (matches SGI's OpenGL) */
172      GLint x = IROUND(ctx->Current.RasterPos[0]);
173      GLint y = IROUND(ctx->Current.RasterPos[1]);
174      ctx->Driver.DrawPixels(ctx, x, y, width, height, format, type,
175			     &ctx->Unpack, pixels);
176   }
177   else if (ctx->RenderMode == GL_FEEDBACK) {
178      /* Feedback the current raster pos info */
179      FLUSH_CURRENT( ctx, 0 );
180      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_DRAW_PIXEL_TOKEN );
181      _mesa_feedback_vertex( ctx,
182                             ctx->Current.RasterPos,
183                             ctx->Current.RasterColor,
184                             ctx->Current.RasterIndex,
185                             ctx->Current.RasterTexCoords[0] );
186   }
187   else {
188      ASSERT(ctx->RenderMode == GL_SELECT);
189      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
190   }
191}
192
193
194void GLAPIENTRY
195_mesa_CopyPixels( GLint srcx, GLint srcy, GLsizei width, GLsizei height,
196                  GLenum type )
197{
198   GET_CURRENT_CONTEXT(ctx);
199   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
200
201   if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
202      _mesa_error(ctx, GL_INVALID_OPERATION,
203                  "glCopyPixels (invalid fragment program)");
204      return;
205   }
206
207   if (width < 0 || height < 0) {
208      _mesa_error(ctx, GL_INVALID_VALUE, "glCopyPixels(width or height < 0)");
209      return;
210   }
211
212   switch (type) {
213   case GL_COLOR:
214      /* OK */
215      break;
216   case GL_DEPTH:
217      if (ctx->DrawBuffer->Visual.depthBits == 0 ||
218          ctx->ReadBuffer->Visual.depthBits == 0) {
219         _mesa_error(ctx, GL_INVALID_OPERATION,
220                     "glCopyPixels(no depth buffer)");
221         return;
222      }
223      break;
224   case GL_STENCIL:
225      if (ctx->DrawBuffer->Visual.stencilBits == 0 ||
226          ctx->ReadBuffer->Visual.stencilBits == 0) {
227         _mesa_error(ctx, GL_INVALID_OPERATION,
228                     "glCopyPixels(no stencil buffer)");
229         return;
230      }
231      break;
232   case GL_DEPTH_STENCIL_EXT:
233      if (!ctx->Extensions.EXT_packed_depth_stencil) {
234         _mesa_error(ctx, GL_INVALID_ENUM, "glCopyPixels");
235         return;
236      }
237      if (ctx->DrawBuffer->Visual.depthBits == 0 ||
238          ctx->ReadBuffer->Visual.depthBits == 0 ||
239          ctx->DrawBuffer->Visual.stencilBits == 0 ||
240          ctx->ReadBuffer->Visual.stencilBits == 0) {
241         _mesa_error(ctx, GL_INVALID_OPERATION,
242                     "glCopyPixels(no depth or stencil buffer)");
243         return;
244      }
245      break;
246   default:
247      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyPixels");
248      return;
249   }
250
251   if (ctx->NewState) {
252      _mesa_update_state(ctx);
253   }
254
255   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
256       ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
257      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
258                  "glCopyPixels(incomplete framebuffer)" );
259      return;
260   }
261
262   if (!ctx->Current.RasterPosValid) {
263      return;
264   }
265
266   if (ctx->RenderMode == GL_RENDER) {
267      /* Round to satisfy conformance tests (matches SGI's OpenGL) */
268      GLint destx = IROUND(ctx->Current.RasterPos[0]);
269      GLint desty = IROUND(ctx->Current.RasterPos[1]);
270      ctx->Driver.CopyPixels( ctx, srcx, srcy, width, height, destx, desty,
271			      type );
272   }
273   else if (ctx->RenderMode == GL_FEEDBACK) {
274      FLUSH_CURRENT( ctx, 0 );
275      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_COPY_PIXEL_TOKEN );
276      _mesa_feedback_vertex( ctx,
277                             ctx->Current.RasterPos,
278                             ctx->Current.RasterColor,
279                             ctx->Current.RasterIndex,
280                             ctx->Current.RasterTexCoords[0] );
281   }
282   else {
283      ASSERT(ctx->RenderMode == GL_SELECT);
284      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
285   }
286}
287
288#endif /* _HAVE_FULL_GL */
289
290
291
292void GLAPIENTRY
293_mesa_ReadPixels( GLint x, GLint y, GLsizei width, GLsizei height,
294		  GLenum format, GLenum type, GLvoid *pixels )
295{
296   GET_CURRENT_CONTEXT(ctx);
297   const struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer;
298   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
299
300   if (width < 0 || height < 0) {
301      _mesa_error( ctx, GL_INVALID_VALUE,
302                   "glReadPixels(width=%d height=%d)", width, height );
303      return;
304   }
305
306   if (error_check_format_type(ctx, format, type, GL_FALSE)) {
307      /* found an error */
308      return;
309   }
310
311   if (ctx->NewState)
312      _mesa_update_state(ctx);
313
314   if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
315      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
316                  "glReadPixels(incomplete framebuffer)" );
317      return;
318   }
319
320   if (!rb) {
321      _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(no readbuffer)");
322      return;
323   }
324
325   ctx->Driver.ReadPixels(ctx, x, y, width, height,
326			  format, type, &ctx->Pack, pixels);
327}
328
329
330
331void GLAPIENTRY
332_mesa_Bitmap( GLsizei width, GLsizei height,
333              GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove,
334              const GLubyte *bitmap )
335{
336   GET_CURRENT_CONTEXT(ctx);
337   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
338
339   if (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled) {
340      _mesa_error(ctx, GL_INVALID_OPERATION,
341                  "glBitmap (invalid fragment program)");
342      return;
343   }
344
345   if (width < 0 || height < 0) {
346      _mesa_error( ctx, GL_INVALID_VALUE, "glBitmap(width or height < 0)" );
347      return;
348   }
349
350   if (!ctx->Current.RasterPosValid) {
351      return;    /* do nothing */
352   }
353
354   if (ctx->NewState) {
355      _mesa_update_state(ctx);
356   }
357
358   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
359      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
360                  "glBitmap(incomplete framebuffer)");
361      return;
362   }
363
364   if (ctx->RenderMode == GL_RENDER) {
365      /* Truncate, to satisfy conformance tests (matches SGI's OpenGL). */
366      GLint x = IFLOOR(ctx->Current.RasterPos[0] - xorig);
367      GLint y = IFLOOR(ctx->Current.RasterPos[1] - yorig);
368      ctx->Driver.Bitmap( ctx, x, y, width, height, &ctx->Unpack, bitmap );
369   }
370#if _HAVE_FULL_GL
371   else if (ctx->RenderMode == GL_FEEDBACK) {
372      FLUSH_CURRENT(ctx, 0);
373      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_BITMAP_TOKEN );
374      _mesa_feedback_vertex( ctx,
375                             ctx->Current.RasterPos,
376                             ctx->Current.RasterColor,
377                             ctx->Current.RasterIndex,
378                             ctx->Current.RasterTexCoords[0] );
379   }
380   else {
381      ASSERT(ctx->RenderMode == GL_SELECT);
382      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
383   }
384#endif
385
386   /* update raster position */
387   ctx->Current.RasterPos[0] += xmove;
388   ctx->Current.RasterPos[1] += ymove;
389}
390
391
392
393#if 0  /* experimental */
394/*
395 * Execute glDrawDepthPixelsMESA().  This function accepts both a color
396 * image and depth (Z) image.  Rasterization produces fragments with
397 * color and Z taken from these images.  This function is intended for
398 * Z-compositing.  Normally, this operation requires two glDrawPixels
399 * calls with stencil testing.
400 */
401void GLAPIENTRY
402_mesa_DrawDepthPixelsMESA( GLsizei width, GLsizei height,
403                           GLenum colorFormat, GLenum colorType,
404                           const GLvoid *colors,
405                           GLenum depthType, const GLvoid *depths )
406{
407   GET_CURRENT_CONTEXT(ctx);
408   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
409
410   if (width < 0 || height < 0) {
411      _mesa_error( ctx, GL_INVALID_VALUE,
412                   "glDrawDepthPixelsMESA(width or height < 0" );
413      return;
414   }
415
416   if (!ctx->Current.RasterPosValid) {
417      return;
418   }
419
420   if (ctx->NewState) {
421      _mesa_update_state(ctx);
422   }
423
424   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
425      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
426                  "glDrawDepthPixelsMESA(incomplete framebuffer)");
427      return;
428   }
429
430   if (ctx->RenderMode == GL_RENDER) {
431      /* Round, to satisfy conformance tests (matches SGI's OpenGL) */
432      GLint x = IROUND(ctx->Current.RasterPos[0]);
433      GLint y = IROUND(ctx->Current.RasterPos[1]);
434      ctx->Driver.DrawDepthPixelsMESA(ctx, x, y, width, height,
435                                      colorFormat, colorType, colors,
436                                      depthType, depths, &ctx->Unpack);
437   }
438   else if (ctx->RenderMode == GL_FEEDBACK) {
439      /* Feedback the current raster pos info */
440      FLUSH_CURRENT( ctx, 0 );
441      FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_DRAW_PIXEL_TOKEN );
442      _mesa_feedback_vertex( ctx,
443                             ctx->Current.RasterPos,
444                             ctx->Current.RasterColor,
445                             ctx->Current.RasterIndex,
446                             ctx->Current.RasterTexCoords[0] );
447   }
448   else {
449      ASSERT(ctx->RenderMode == GL_SELECT);
450      /* Do nothing.  See OpenGL Spec, Appendix B, Corollary 6. */
451   }
452}
453
454#endif
455