• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Mesa 3-D graphics library
3  * Version:  7.5
4  *
5  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
6  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 
27 /**
28  * \file image.c
29  * Image handling.
30  */
31 
32 
33 #include "glheader.h"
34 #include "colormac.h"
35 #include "glformats.h"
36 #include "image.h"
37 #include "imports.h"
38 #include "macros.h"
39 #include "mfeatures.h"
40 #include "mtypes.h"
41 
42 
43 
44 /**
45  * Flip the order of the 2 bytes in each word in the given array.
46  *
47  * \param p array.
48  * \param n number of words.
49  */
50 void
_mesa_swap2(GLushort * p,GLuint n)51 _mesa_swap2( GLushort *p, GLuint n )
52 {
53    GLuint i;
54    for (i = 0; i < n; i++) {
55       p[i] = (p[i] >> 8) | ((p[i] << 8) & 0xff00);
56    }
57 }
58 
59 
60 
61 /*
62  * Flip the order of the 4 bytes in each word in the given array.
63  */
64 void
_mesa_swap4(GLuint * p,GLuint n)65 _mesa_swap4( GLuint *p, GLuint n )
66 {
67    GLuint i, a, b;
68    for (i = 0; i < n; i++) {
69       b = p[i];
70       a =  (b >> 24)
71 	| ((b >> 8) & 0xff00)
72 	| ((b << 8) & 0xff0000)
73 	| ((b << 24) & 0xff000000);
74       p[i] = a;
75    }
76 }
77 
78 
79 /**
80  * Return the byte offset of a specific pixel in an image (1D, 2D or 3D).
81  *
82  * Pixel unpacking/packing parameters are observed according to \p packing.
83  *
84  * \param dimensions either 1, 2 or 3 to indicate dimensionality of image
85  * \param packing  the pixelstore attributes
86  * \param width  the image width
87  * \param height  the image height
88  * \param format  the pixel format (must be validated beforehand)
89  * \param type  the pixel data type (must be validated beforehand)
90  * \param img  which image in the volume (0 for 1D or 2D images)
91  * \param row  row of pixel in the image (0 for 1D images)
92  * \param column column of pixel in the image
93  *
94  * \return offset of pixel.
95  *
96  * \sa gl_pixelstore_attrib.
97  */
98 GLintptr
_mesa_image_offset(GLuint dimensions,const struct gl_pixelstore_attrib * packing,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint img,GLint row,GLint column)99 _mesa_image_offset( GLuint dimensions,
100                     const struct gl_pixelstore_attrib *packing,
101                     GLsizei width, GLsizei height,
102                     GLenum format, GLenum type,
103                     GLint img, GLint row, GLint column )
104 {
105    GLint alignment;        /* 1, 2 or 4 */
106    GLint pixels_per_row;
107    GLint rows_per_image;
108    GLint skiprows;
109    GLint skippixels;
110    GLint skipimages;       /* for 3-D volume images */
111    GLintptr offset;
112 
113    ASSERT(dimensions >= 1 && dimensions <= 3);
114 
115    alignment = packing->Alignment;
116    if (packing->RowLength > 0) {
117       pixels_per_row = packing->RowLength;
118    }
119    else {
120       pixels_per_row = width;
121    }
122    if (packing->ImageHeight > 0) {
123       rows_per_image = packing->ImageHeight;
124    }
125    else {
126       rows_per_image = height;
127    }
128 
129    skippixels = packing->SkipPixels;
130    /* Note: SKIP_ROWS _is_ used for 1D images */
131    skiprows = packing->SkipRows;
132    /* Note: SKIP_IMAGES is only used for 3D images */
133    skipimages = (dimensions == 3) ? packing->SkipImages : 0;
134 
135    if (type == GL_BITMAP) {
136       /* BITMAP data */
137       GLint bytes_per_row;
138       GLint bytes_per_image;
139       /* components per pixel for color or stencil index: */
140       const GLint comp_per_pixel = 1;
141 
142       /* The pixel type and format should have been error checked earlier */
143       assert(format == GL_COLOR_INDEX || format == GL_STENCIL_INDEX);
144 
145       bytes_per_row = alignment
146                     * CEILING( comp_per_pixel*pixels_per_row, 8*alignment );
147 
148       bytes_per_image = bytes_per_row * rows_per_image;
149 
150       offset = (skipimages + img) * bytes_per_image
151                  + (skiprows + row) * bytes_per_row
152                  + (skippixels + column) / 8;
153    }
154    else {
155       /* Non-BITMAP data */
156       GLint bytes_per_pixel, bytes_per_row, remainder, bytes_per_image;
157       GLint topOfImage;
158 
159       bytes_per_pixel = _mesa_bytes_per_pixel( format, type );
160 
161       /* The pixel type and format should have been error checked earlier */
162       assert(bytes_per_pixel > 0);
163 
164       bytes_per_row = pixels_per_row * bytes_per_pixel;
165       remainder = bytes_per_row % alignment;
166       if (remainder > 0)
167          bytes_per_row += (alignment - remainder);
168 
169       ASSERT(bytes_per_row % alignment == 0);
170 
171       bytes_per_image = bytes_per_row * rows_per_image;
172 
173       if (packing->Invert) {
174          /* set pixel_addr to the last row */
175          topOfImage = bytes_per_row * (height - 1);
176          bytes_per_row = -bytes_per_row;
177       }
178       else {
179          topOfImage = 0;
180       }
181 
182       /* compute final pixel address */
183       offset = (skipimages + img) * bytes_per_image
184                  + topOfImage
185                  + (skiprows + row) * bytes_per_row
186                  + (skippixels + column) * bytes_per_pixel;
187    }
188 
189    return offset;
190 }
191 
192 
193 /**
194  * Return the address of a specific pixel in an image (1D, 2D or 3D).
195  *
196  * Pixel unpacking/packing parameters are observed according to \p packing.
197  *
198  * \param dimensions either 1, 2 or 3 to indicate dimensionality of image
199  * \param packing  the pixelstore attributes
200  * \param image  starting address of image data
201  * \param width  the image width
202  * \param height  the image height
203  * \param format  the pixel format (must be validated beforehand)
204  * \param type  the pixel data type (must be validated beforehand)
205  * \param img  which image in the volume (0 for 1D or 2D images)
206  * \param row  row of pixel in the image (0 for 1D images)
207  * \param column column of pixel in the image
208  *
209  * \return address of pixel.
210  *
211  * \sa gl_pixelstore_attrib.
212  */
213 GLvoid *
_mesa_image_address(GLuint dimensions,const struct gl_pixelstore_attrib * packing,const GLvoid * image,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint img,GLint row,GLint column)214 _mesa_image_address( GLuint dimensions,
215                      const struct gl_pixelstore_attrib *packing,
216                      const GLvoid *image,
217                      GLsizei width, GLsizei height,
218                      GLenum format, GLenum type,
219                      GLint img, GLint row, GLint column )
220 {
221    const GLubyte *addr = (const GLubyte *) image;
222 
223    addr += _mesa_image_offset(dimensions, packing, width, height,
224                               format, type, img, row, column);
225 
226    return (GLvoid *) addr;
227 }
228 
229 
230 GLvoid *
_mesa_image_address1d(const struct gl_pixelstore_attrib * packing,const GLvoid * image,GLsizei width,GLenum format,GLenum type,GLint column)231 _mesa_image_address1d( const struct gl_pixelstore_attrib *packing,
232                        const GLvoid *image,
233                        GLsizei width,
234                        GLenum format, GLenum type,
235                        GLint column )
236 {
237    return _mesa_image_address(1, packing, image, width, 1,
238                               format, type, 0, 0, column);
239 }
240 
241 
242 GLvoid *
_mesa_image_address2d(const struct gl_pixelstore_attrib * packing,const GLvoid * image,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint row,GLint column)243 _mesa_image_address2d( const struct gl_pixelstore_attrib *packing,
244                        const GLvoid *image,
245                        GLsizei width, GLsizei height,
246                        GLenum format, GLenum type,
247                        GLint row, GLint column )
248 {
249    return _mesa_image_address(2, packing, image, width, height,
250                               format, type, 0, row, column);
251 }
252 
253 
254 GLvoid *
_mesa_image_address3d(const struct gl_pixelstore_attrib * packing,const GLvoid * image,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint img,GLint row,GLint column)255 _mesa_image_address3d( const struct gl_pixelstore_attrib *packing,
256                        const GLvoid *image,
257                        GLsizei width, GLsizei height,
258                        GLenum format, GLenum type,
259                        GLint img, GLint row, GLint column )
260 {
261    return _mesa_image_address(3, packing, image, width, height,
262                               format, type, img, row, column);
263 }
264 
265 
266 
267 /**
268  * Compute the stride (in bytes) between image rows.
269  *
270  * \param packing the pixelstore attributes
271  * \param width image width.
272  * \param format pixel format.
273  * \param type pixel data type.
274  *
275  * \return the stride in bytes for the given parameters, or -1 if error
276  */
277 GLint
_mesa_image_row_stride(const struct gl_pixelstore_attrib * packing,GLint width,GLenum format,GLenum type)278 _mesa_image_row_stride( const struct gl_pixelstore_attrib *packing,
279                         GLint width, GLenum format, GLenum type )
280 {
281    GLint bytesPerRow, remainder;
282 
283    ASSERT(packing);
284 
285    if (type == GL_BITMAP) {
286       if (packing->RowLength == 0) {
287          bytesPerRow = (width + 7) / 8;
288       }
289       else {
290          bytesPerRow = (packing->RowLength + 7) / 8;
291       }
292    }
293    else {
294       /* Non-BITMAP data */
295       const GLint bytesPerPixel = _mesa_bytes_per_pixel(format, type);
296       if (bytesPerPixel <= 0)
297          return -1;  /* error */
298       if (packing->RowLength == 0) {
299          bytesPerRow = bytesPerPixel * width;
300       }
301       else {
302          bytesPerRow = bytesPerPixel * packing->RowLength;
303       }
304    }
305 
306    remainder = bytesPerRow % packing->Alignment;
307    if (remainder > 0) {
308       bytesPerRow += (packing->Alignment - remainder);
309    }
310 
311    if (packing->Invert) {
312       /* negate the bytes per row (negative row stride) */
313       bytesPerRow = -bytesPerRow;
314    }
315 
316    return bytesPerRow;
317 }
318 
319 
320 /*
321  * Compute the stride between images in a 3D texture (in bytes) for the given
322  * pixel packing parameters and image width, format and type.
323  */
324 GLint
_mesa_image_image_stride(const struct gl_pixelstore_attrib * packing,GLint width,GLint height,GLenum format,GLenum type)325 _mesa_image_image_stride( const struct gl_pixelstore_attrib *packing,
326                           GLint width, GLint height,
327                           GLenum format, GLenum type )
328 {
329    GLint bytesPerRow, bytesPerImage, remainder;
330 
331    ASSERT(packing);
332 
333    if (type == GL_BITMAP) {
334       if (packing->RowLength == 0) {
335          bytesPerRow = (width + 7) / 8;
336       }
337       else {
338          bytesPerRow = (packing->RowLength + 7) / 8;
339       }
340    }
341    else {
342       const GLint bytesPerPixel = _mesa_bytes_per_pixel(format, type);
343 
344       if (bytesPerPixel <= 0)
345          return -1;  /* error */
346       if (packing->RowLength == 0) {
347          bytesPerRow = bytesPerPixel * width;
348       }
349       else {
350          bytesPerRow = bytesPerPixel * packing->RowLength;
351       }
352    }
353 
354    remainder = bytesPerRow % packing->Alignment;
355    if (remainder > 0)
356       bytesPerRow += (packing->Alignment - remainder);
357 
358    if (packing->ImageHeight == 0)
359       bytesPerImage = bytesPerRow * height;
360    else
361       bytesPerImage = bytesPerRow * packing->ImageHeight;
362 
363    return bytesPerImage;
364 }
365 
366 
367 
368 /**
369  * "Expand" a bitmap from 1-bit per pixel to 8-bits per pixel.
370  * This is typically used to convert a bitmap into a GLubyte/pixel texture.
371  * "On" bits will set texels to \p onValue.
372  * "Off" bits will not modify texels.
373  * \param width  src bitmap width in pixels
374  * \param height  src bitmap height in pixels
375  * \param unpack  bitmap unpacking state
376  * \param bitmap  the src bitmap data
377  * \param destBuffer  start of dest buffer
378  * \param destStride  row stride in dest buffer
379  * \param onValue  if bit is 1, set destBuffer pixel to this value
380  */
381 void
_mesa_expand_bitmap(GLsizei width,GLsizei height,const struct gl_pixelstore_attrib * unpack,const GLubyte * bitmap,GLubyte * destBuffer,GLint destStride,GLubyte onValue)382 _mesa_expand_bitmap(GLsizei width, GLsizei height,
383                     const struct gl_pixelstore_attrib *unpack,
384                     const GLubyte *bitmap,
385                     GLubyte *destBuffer, GLint destStride,
386                     GLubyte onValue)
387 {
388    const GLubyte *srcRow = (const GLubyte *)
389       _mesa_image_address2d(unpack, bitmap, width, height,
390                             GL_COLOR_INDEX, GL_BITMAP, 0, 0);
391    const GLint srcStride = _mesa_image_row_stride(unpack, width,
392                                                   GL_COLOR_INDEX, GL_BITMAP);
393    GLint row, col;
394 
395 #define SET_PIXEL(COL, ROW) \
396    destBuffer[(ROW) * destStride + (COL)] = onValue;
397 
398    for (row = 0; row < height; row++) {
399       const GLubyte *src = srcRow;
400 
401       if (unpack->LsbFirst) {
402          /* Lsb first */
403          GLubyte mask = 1U << (unpack->SkipPixels & 0x7);
404          for (col = 0; col < width; col++) {
405 
406             if (*src & mask) {
407                SET_PIXEL(col, row);
408             }
409 
410             if (mask == 128U) {
411                src++;
412                mask = 1U;
413             }
414             else {
415                mask = mask << 1;
416             }
417          }
418 
419          /* get ready for next row */
420          if (mask != 1)
421             src++;
422       }
423       else {
424          /* Msb first */
425          GLubyte mask = 128U >> (unpack->SkipPixels & 0x7);
426          for (col = 0; col < width; col++) {
427 
428             if (*src & mask) {
429                SET_PIXEL(col, row);
430             }
431 
432             if (mask == 1U) {
433                src++;
434                mask = 128U;
435             }
436             else {
437                mask = mask >> 1;
438             }
439          }
440 
441          /* get ready for next row */
442          if (mask != 128)
443             src++;
444       }
445 
446       srcRow += srcStride;
447    } /* row */
448 
449 #undef SET_PIXEL
450 }
451 
452 
453 
454 
455 /**
456  * Convert an array of RGBA colors from one datatype to another.
457  * NOTE: src may equal dst.  In that case, we use a temporary buffer.
458  */
459 void
_mesa_convert_colors(GLenum srcType,const GLvoid * src,GLenum dstType,GLvoid * dst,GLuint count,const GLubyte mask[])460 _mesa_convert_colors(GLenum srcType, const GLvoid *src,
461                      GLenum dstType, GLvoid *dst,
462                      GLuint count, const GLubyte mask[])
463 {
464    GLuint *tempBuffer;
465    const GLboolean useTemp = (src == dst);
466 
467    tempBuffer = malloc(count * MAX_PIXEL_BYTES);
468    if (!tempBuffer)
469       return;
470 
471    ASSERT(srcType != dstType);
472 
473    switch (srcType) {
474    case GL_UNSIGNED_BYTE:
475       if (dstType == GL_UNSIGNED_SHORT) {
476          const GLubyte (*src1)[4] = (const GLubyte (*)[4]) src;
477          GLushort (*dst2)[4] = (GLushort (*)[4]) (useTemp ? tempBuffer : dst);
478          GLuint i;
479          for (i = 0; i < count; i++) {
480             if (!mask || mask[i]) {
481                dst2[i][RCOMP] = UBYTE_TO_USHORT(src1[i][RCOMP]);
482                dst2[i][GCOMP] = UBYTE_TO_USHORT(src1[i][GCOMP]);
483                dst2[i][BCOMP] = UBYTE_TO_USHORT(src1[i][BCOMP]);
484                dst2[i][ACOMP] = UBYTE_TO_USHORT(src1[i][ACOMP]);
485             }
486          }
487          if (useTemp)
488             memcpy(dst, tempBuffer, count * 4 * sizeof(GLushort));
489       }
490       else {
491          const GLubyte (*src1)[4] = (const GLubyte (*)[4]) src;
492          GLfloat (*dst4)[4] = (GLfloat (*)[4]) (useTemp ? tempBuffer : dst);
493          GLuint i;
494          ASSERT(dstType == GL_FLOAT);
495          for (i = 0; i < count; i++) {
496             if (!mask || mask[i]) {
497                dst4[i][RCOMP] = UBYTE_TO_FLOAT(src1[i][RCOMP]);
498                dst4[i][GCOMP] = UBYTE_TO_FLOAT(src1[i][GCOMP]);
499                dst4[i][BCOMP] = UBYTE_TO_FLOAT(src1[i][BCOMP]);
500                dst4[i][ACOMP] = UBYTE_TO_FLOAT(src1[i][ACOMP]);
501             }
502          }
503          if (useTemp)
504             memcpy(dst, tempBuffer, count * 4 * sizeof(GLfloat));
505       }
506       break;
507    case GL_UNSIGNED_SHORT:
508       if (dstType == GL_UNSIGNED_BYTE) {
509          const GLushort (*src2)[4] = (const GLushort (*)[4]) src;
510          GLubyte (*dst1)[4] = (GLubyte (*)[4]) (useTemp ? tempBuffer : dst);
511          GLuint i;
512          for (i = 0; i < count; i++) {
513             if (!mask || mask[i]) {
514                dst1[i][RCOMP] = USHORT_TO_UBYTE(src2[i][RCOMP]);
515                dst1[i][GCOMP] = USHORT_TO_UBYTE(src2[i][GCOMP]);
516                dst1[i][BCOMP] = USHORT_TO_UBYTE(src2[i][BCOMP]);
517                dst1[i][ACOMP] = USHORT_TO_UBYTE(src2[i][ACOMP]);
518             }
519          }
520          if (useTemp)
521             memcpy(dst, tempBuffer, count * 4 * sizeof(GLubyte));
522       }
523       else {
524          const GLushort (*src2)[4] = (const GLushort (*)[4]) src;
525          GLfloat (*dst4)[4] = (GLfloat (*)[4]) (useTemp ? tempBuffer : dst);
526          GLuint i;
527          ASSERT(dstType == GL_FLOAT);
528          for (i = 0; i < count; i++) {
529             if (!mask || mask[i]) {
530                dst4[i][RCOMP] = USHORT_TO_FLOAT(src2[i][RCOMP]);
531                dst4[i][GCOMP] = USHORT_TO_FLOAT(src2[i][GCOMP]);
532                dst4[i][BCOMP] = USHORT_TO_FLOAT(src2[i][BCOMP]);
533                dst4[i][ACOMP] = USHORT_TO_FLOAT(src2[i][ACOMP]);
534             }
535          }
536          if (useTemp)
537             memcpy(dst, tempBuffer, count * 4 * sizeof(GLfloat));
538       }
539       break;
540    case GL_FLOAT:
541       if (dstType == GL_UNSIGNED_BYTE) {
542          const GLfloat (*src4)[4] = (const GLfloat (*)[4]) src;
543          GLubyte (*dst1)[4] = (GLubyte (*)[4]) (useTemp ? tempBuffer : dst);
544          GLuint i;
545          for (i = 0; i < count; i++) {
546             if (!mask || mask[i])
547                _mesa_unclamped_float_rgba_to_ubyte(dst1[i], src4[i]);
548          }
549          if (useTemp)
550             memcpy(dst, tempBuffer, count * 4 * sizeof(GLubyte));
551       }
552       else {
553          const GLfloat (*src4)[4] = (const GLfloat (*)[4]) src;
554          GLushort (*dst2)[4] = (GLushort (*)[4]) (useTemp ? tempBuffer : dst);
555          GLuint i;
556          ASSERT(dstType == GL_UNSIGNED_SHORT);
557          for (i = 0; i < count; i++) {
558             if (!mask || mask[i]) {
559                UNCLAMPED_FLOAT_TO_USHORT(dst2[i][RCOMP], src4[i][RCOMP]);
560                UNCLAMPED_FLOAT_TO_USHORT(dst2[i][GCOMP], src4[i][GCOMP]);
561                UNCLAMPED_FLOAT_TO_USHORT(dst2[i][BCOMP], src4[i][BCOMP]);
562                UNCLAMPED_FLOAT_TO_USHORT(dst2[i][ACOMP], src4[i][ACOMP]);
563             }
564          }
565          if (useTemp)
566             memcpy(dst, tempBuffer, count * 4 * sizeof(GLushort));
567       }
568       break;
569    default:
570       _mesa_problem(NULL, "Invalid datatype in _mesa_convert_colors");
571    }
572 
573    free(tempBuffer);
574 }
575 
576 
577 
578 
579 /**
580  * Perform basic clipping for glDrawPixels.  The image's position and size
581  * and the unpack SkipPixels and SkipRows are adjusted so that the image
582  * region is entirely within the window and scissor bounds.
583  * NOTE: this will only work when glPixelZoom is (1, 1) or (1, -1).
584  * If Pixel.ZoomY is -1, *destY will be changed to be the first row which
585  * we'll actually write.  Beforehand, *destY-1 is the first drawing row.
586  *
587  * \return  GL_TRUE if image is ready for drawing or
588  *          GL_FALSE if image was completely clipped away (draw nothing)
589  */
590 GLboolean
_mesa_clip_drawpixels(const struct gl_context * ctx,GLint * destX,GLint * destY,GLsizei * width,GLsizei * height,struct gl_pixelstore_attrib * unpack)591 _mesa_clip_drawpixels(const struct gl_context *ctx,
592                       GLint *destX, GLint *destY,
593                       GLsizei *width, GLsizei *height,
594                       struct gl_pixelstore_attrib *unpack)
595 {
596    const struct gl_framebuffer *buffer = ctx->DrawBuffer;
597 
598    if (unpack->RowLength == 0) {
599       unpack->RowLength = *width;
600    }
601 
602    ASSERT(ctx->Pixel.ZoomX == 1.0F);
603    ASSERT(ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F);
604 
605    /* left clipping */
606    if (*destX < buffer->_Xmin) {
607       unpack->SkipPixels += (buffer->_Xmin - *destX);
608       *width -= (buffer->_Xmin - *destX);
609       *destX = buffer->_Xmin;
610    }
611    /* right clipping */
612    if (*destX + *width > buffer->_Xmax)
613       *width -= (*destX + *width - buffer->_Xmax);
614 
615    if (*width <= 0)
616       return GL_FALSE;
617 
618    if (ctx->Pixel.ZoomY == 1.0F) {
619       /* bottom clipping */
620       if (*destY < buffer->_Ymin) {
621          unpack->SkipRows += (buffer->_Ymin - *destY);
622          *height -= (buffer->_Ymin - *destY);
623          *destY = buffer->_Ymin;
624       }
625       /* top clipping */
626       if (*destY + *height > buffer->_Ymax)
627          *height -= (*destY + *height - buffer->_Ymax);
628    }
629    else { /* upside down */
630       /* top clipping */
631       if (*destY > buffer->_Ymax) {
632          unpack->SkipRows += (*destY - buffer->_Ymax);
633          *height -= (*destY - buffer->_Ymax);
634          *destY = buffer->_Ymax;
635       }
636       /* bottom clipping */
637       if (*destY - *height < buffer->_Ymin)
638          *height -= (buffer->_Ymin - (*destY - *height));
639       /* adjust destY so it's the first row to write to */
640       (*destY)--;
641    }
642 
643    if (*height <= 0)
644       return GL_FALSE;
645 
646    return GL_TRUE;
647 }
648 
649 
650 /**
651  * Perform clipping for glReadPixels.  The image's window position
652  * and size, and the pack skipPixels, skipRows and rowLength are adjusted
653  * so that the image region is entirely within the window bounds.
654  * Note: this is different from _mesa_clip_drawpixels() in that the
655  * scissor box is ignored, and we use the bounds of the current readbuffer
656  * surface.
657  *
658  * \return  GL_TRUE if region to read is in bounds
659  *          GL_FALSE if region is completely out of bounds (nothing to read)
660  */
661 GLboolean
_mesa_clip_readpixels(const struct gl_context * ctx,GLint * srcX,GLint * srcY,GLsizei * width,GLsizei * height,struct gl_pixelstore_attrib * pack)662 _mesa_clip_readpixels(const struct gl_context *ctx,
663                       GLint *srcX, GLint *srcY,
664                       GLsizei *width, GLsizei *height,
665                       struct gl_pixelstore_attrib *pack)
666 {
667    const struct gl_framebuffer *buffer = ctx->ReadBuffer;
668 
669    if (pack->RowLength == 0) {
670       pack->RowLength = *width;
671    }
672 
673    /* left clipping */
674    if (*srcX < 0) {
675       pack->SkipPixels += (0 - *srcX);
676       *width -= (0 - *srcX);
677       *srcX = 0;
678    }
679    /* right clipping */
680    if (*srcX + *width > (GLsizei) buffer->Width)
681       *width -= (*srcX + *width - buffer->Width);
682 
683    if (*width <= 0)
684       return GL_FALSE;
685 
686    /* bottom clipping */
687    if (*srcY < 0) {
688       pack->SkipRows += (0 - *srcY);
689       *height -= (0 - *srcY);
690       *srcY = 0;
691    }
692    /* top clipping */
693    if (*srcY + *height > (GLsizei) buffer->Height)
694       *height -= (*srcY + *height - buffer->Height);
695 
696    if (*height <= 0)
697       return GL_FALSE;
698 
699    return GL_TRUE;
700 }
701 
702 
703 /**
704  * Do clipping for a glCopyTexSubImage call.
705  * The framebuffer source region might extend outside the framebuffer
706  * bounds.  Clip the source region against the framebuffer bounds and
707  * adjust the texture/dest position and size accordingly.
708  *
709  * \return GL_FALSE if region is totally clipped, GL_TRUE otherwise.
710  */
711 GLboolean
_mesa_clip_copytexsubimage(const struct gl_context * ctx,GLint * destX,GLint * destY,GLint * srcX,GLint * srcY,GLsizei * width,GLsizei * height)712 _mesa_clip_copytexsubimage(const struct gl_context *ctx,
713                            GLint *destX, GLint *destY,
714                            GLint *srcX, GLint *srcY,
715                            GLsizei *width, GLsizei *height)
716 {
717    const struct gl_framebuffer *fb = ctx->ReadBuffer;
718    const GLint srcX0 = *srcX, srcY0 = *srcY;
719 
720    if (_mesa_clip_to_region(0, 0, fb->Width, fb->Height,
721                             srcX, srcY, width, height)) {
722       *destX = *destX + *srcX - srcX0;
723       *destY = *destY + *srcY - srcY0;
724 
725       return GL_TRUE;
726    }
727    else {
728       return GL_FALSE;
729    }
730 }
731 
732 
733 
734 /**
735  * Clip the rectangle defined by (x, y, width, height) against the bounds
736  * specified by [xmin, xmax) and [ymin, ymax).
737  * \return GL_FALSE if rect is totally clipped, GL_TRUE otherwise.
738  */
739 GLboolean
_mesa_clip_to_region(GLint xmin,GLint ymin,GLint xmax,GLint ymax,GLint * x,GLint * y,GLsizei * width,GLsizei * height)740 _mesa_clip_to_region(GLint xmin, GLint ymin,
741                      GLint xmax, GLint ymax,
742                      GLint *x, GLint *y,
743                      GLsizei *width, GLsizei *height )
744 {
745    /* left clipping */
746    if (*x < xmin) {
747       *width -= (xmin - *x);
748       *x = xmin;
749    }
750 
751    /* right clipping */
752    if (*x + *width > xmax)
753       *width -= (*x + *width - xmax);
754 
755    if (*width <= 0)
756       return GL_FALSE;
757 
758    /* bottom (or top) clipping */
759    if (*y < ymin) {
760       *height -= (ymin - *y);
761       *y = ymin;
762    }
763 
764    /* top (or bottom) clipping */
765    if (*y + *height > ymax)
766       *height -= (*y + *height - ymax);
767 
768    if (*height <= 0)
769       return GL_FALSE;
770 
771    return GL_TRUE;
772 }
773 
774 
775 /**
776  * Clip dst coords against Xmax (or Ymax).
777  */
778 static inline void
clip_right_or_top(GLint * srcX0,GLint * srcX1,GLint * dstX0,GLint * dstX1,GLint maxValue)779 clip_right_or_top(GLint *srcX0, GLint *srcX1,
780                   GLint *dstX0, GLint *dstX1,
781                   GLint maxValue)
782 {
783    GLfloat t, bias;
784 
785    if (*dstX1 > maxValue) {
786       /* X1 outside right edge */
787       ASSERT(*dstX0 < maxValue); /* X0 should be inside right edge */
788       t = (GLfloat) (maxValue - *dstX0) / (GLfloat) (*dstX1 - *dstX0);
789       /* chop off [t, 1] part */
790       ASSERT(t >= 0.0 && t <= 1.0);
791       *dstX1 = maxValue;
792       bias = (*srcX0 < *srcX1) ? 0.5F : -0.5F;
793       *srcX1 = *srcX0 + (GLint) (t * (*srcX1 - *srcX0) + bias);
794    }
795    else if (*dstX0 > maxValue) {
796       /* X0 outside right edge */
797       ASSERT(*dstX1 < maxValue); /* X1 should be inside right edge */
798       t = (GLfloat) (maxValue - *dstX1) / (GLfloat) (*dstX0 - *dstX1);
799       /* chop off [t, 1] part */
800       ASSERT(t >= 0.0 && t <= 1.0);
801       *dstX0 = maxValue;
802       bias = (*srcX0 < *srcX1) ? -0.5F : 0.5F;
803       *srcX0 = *srcX1 + (GLint) (t * (*srcX0 - *srcX1) + bias);
804    }
805 }
806 
807 
808 /**
809  * Clip dst coords against Xmin (or Ymin).
810  */
811 static inline void
clip_left_or_bottom(GLint * srcX0,GLint * srcX1,GLint * dstX0,GLint * dstX1,GLint minValue)812 clip_left_or_bottom(GLint *srcX0, GLint *srcX1,
813                     GLint *dstX0, GLint *dstX1,
814                     GLint minValue)
815 {
816    GLfloat t, bias;
817 
818    if (*dstX0 < minValue) {
819       /* X0 outside left edge */
820       ASSERT(*dstX1 > minValue); /* X1 should be inside left edge */
821       t = (GLfloat) (minValue - *dstX0) / (GLfloat) (*dstX1 - *dstX0);
822       /* chop off [0, t] part */
823       ASSERT(t >= 0.0 && t <= 1.0);
824       *dstX0 = minValue;
825       bias = (*srcX0 < *srcX1) ? 0.5F : -0.5F; /* flipped??? */
826       *srcX0 = *srcX0 + (GLint) (t * (*srcX1 - *srcX0) + bias);
827    }
828    else if (*dstX1 < minValue) {
829       /* X1 outside left edge */
830       ASSERT(*dstX0 > minValue); /* X0 should be inside left edge */
831       t = (GLfloat) (minValue - *dstX1) / (GLfloat) (*dstX0 - *dstX1);
832       /* chop off [0, t] part */
833       ASSERT(t >= 0.0 && t <= 1.0);
834       *dstX1 = minValue;
835       bias = (*srcX0 < *srcX1) ? 0.5F : -0.5F;
836       *srcX1 = *srcX1 + (GLint) (t * (*srcX0 - *srcX1) + bias);
837    }
838 }
839 
840 
841 /**
842  * Do clipping of blit src/dest rectangles.
843  * The dest rect is clipped against both the buffer bounds and scissor bounds.
844  * The src rect is just clipped against the buffer bounds.
845  *
846  * When either the src or dest rect is clipped, the other is also clipped
847  * proportionately!
848  *
849  * Note that X0 need not be less than X1 (same for Y) for either the source
850  * and dest rects.  That makes the clipping a little trickier.
851  *
852  * \return GL_TRUE if anything is left to draw, GL_FALSE if totally clipped
853  */
854 GLboolean
_mesa_clip_blit(struct gl_context * ctx,GLint * srcX0,GLint * srcY0,GLint * srcX1,GLint * srcY1,GLint * dstX0,GLint * dstY0,GLint * dstX1,GLint * dstY1)855 _mesa_clip_blit(struct gl_context *ctx,
856                 GLint *srcX0, GLint *srcY0, GLint *srcX1, GLint *srcY1,
857                 GLint *dstX0, GLint *dstY0, GLint *dstX1, GLint *dstY1)
858 {
859    const GLint srcXmin = 0;
860    const GLint srcXmax = ctx->ReadBuffer->Width;
861    const GLint srcYmin = 0;
862    const GLint srcYmax = ctx->ReadBuffer->Height;
863 
864    /* these include scissor bounds */
865    const GLint dstXmin = ctx->DrawBuffer->_Xmin;
866    const GLint dstXmax = ctx->DrawBuffer->_Xmax;
867    const GLint dstYmin = ctx->DrawBuffer->_Ymin;
868    const GLint dstYmax = ctx->DrawBuffer->_Ymax;
869 
870    /*
871    printf("PreClipX:  src: %d .. %d  dst: %d .. %d\n",
872           *srcX0, *srcX1, *dstX0, *dstX1);
873    printf("PreClipY:  src: %d .. %d  dst: %d .. %d\n",
874           *srcY0, *srcY1, *dstY0, *dstY1);
875    */
876 
877    /* trivial rejection tests */
878    if (*dstX0 == *dstX1)
879       return GL_FALSE; /* no width */
880    if (*dstX0 <= dstXmin && *dstX1 <= dstXmin)
881       return GL_FALSE; /* totally out (left) of bounds */
882    if (*dstX0 >= dstXmax && *dstX1 >= dstXmax)
883       return GL_FALSE; /* totally out (right) of bounds */
884 
885    if (*dstY0 == *dstY1)
886       return GL_FALSE;
887    if (*dstY0 <= dstYmin && *dstY1 <= dstYmin)
888       return GL_FALSE;
889    if (*dstY0 >= dstYmax && *dstY1 >= dstYmax)
890       return GL_FALSE;
891 
892    if (*srcX0 == *srcX1)
893       return GL_FALSE;
894    if (*srcX0 <= srcXmin && *srcX1 <= srcXmin)
895       return GL_FALSE;
896    if (*srcX0 >= srcXmax && *srcX1 >= srcXmax)
897       return GL_FALSE;
898 
899    if (*srcY0 == *srcY1)
900       return GL_FALSE;
901    if (*srcY0 <= srcYmin && *srcY1 <= srcYmin)
902       return GL_FALSE;
903    if (*srcY0 >= srcYmax && *srcY1 >= srcYmax)
904       return GL_FALSE;
905 
906    /*
907     * dest clip
908     */
909    clip_right_or_top(srcX0, srcX1, dstX0, dstX1, dstXmax);
910    clip_right_or_top(srcY0, srcY1, dstY0, dstY1, dstYmax);
911    clip_left_or_bottom(srcX0, srcX1, dstX0, dstX1, dstXmin);
912    clip_left_or_bottom(srcY0, srcY1, dstY0, dstY1, dstYmin);
913 
914    /*
915     * src clip (just swap src/dst values from above)
916     */
917    clip_right_or_top(dstX0, dstX1, srcX0, srcX1, srcXmax);
918    clip_right_or_top(dstY0, dstY1, srcY0, srcY1, srcYmax);
919    clip_left_or_bottom(dstX0, dstX1, srcX0, srcX1, srcXmin);
920    clip_left_or_bottom(dstY0, dstY1, srcY0, srcY1, srcYmin);
921 
922    /*
923    printf("PostClipX: src: %d .. %d  dst: %d .. %d\n",
924           *srcX0, *srcX1, *dstX0, *dstX1);
925    printf("PostClipY: src: %d .. %d  dst: %d .. %d\n",
926           *srcY0, *srcY1, *dstY0, *dstY1);
927    */
928 
929    ASSERT(*dstX0 >= dstXmin);
930    ASSERT(*dstX0 <= dstXmax);
931    ASSERT(*dstX1 >= dstXmin);
932    ASSERT(*dstX1 <= dstXmax);
933 
934    ASSERT(*dstY0 >= dstYmin);
935    ASSERT(*dstY0 <= dstYmax);
936    ASSERT(*dstY1 >= dstYmin);
937    ASSERT(*dstY1 <= dstYmax);
938 
939    ASSERT(*srcX0 >= srcXmin);
940    ASSERT(*srcX0 <= srcXmax);
941    ASSERT(*srcX1 >= srcXmin);
942    ASSERT(*srcX1 <= srcXmax);
943 
944    ASSERT(*srcY0 >= srcYmin);
945    ASSERT(*srcY0 <= srcYmax);
946    ASSERT(*srcY1 >= srcYmin);
947    ASSERT(*srcY1 <= srcYmax);
948 
949    return GL_TRUE;
950 }
951