2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
26 #include "main/glheader.h"
27 #include "main/context.h"
28 #include "main/colormac.h"
29 #include "main/condrender.h"
30 #include "main/macros.h"
31 #include "main/pixeltransfer.h"
32 #include "main/imports.h"
34 #include "s_context.h"
37 #include "s_stencil.h"
43 * Determine if there's overlap in an image copy.
44 * This test also compensates for the fact that copies are done from
45 * bottom to top and overlaps can sometimes be handled correctly
46 * without making a temporary image copy.
47 * \return GL_TRUE if the regions overlap, GL_FALSE otherwise.
50 regions_overlap(GLint srcx
, GLint srcy
,
51 GLint dstx
, GLint dsty
,
52 GLint width
, GLint height
,
53 GLfloat zoomX
, GLfloat zoomY
)
55 if (zoomX
== 1.0 && zoomY
== 1.0) {
57 if (srcx
>= dstx
+ width
|| (srcx
+ width
<= dstx
)) {
60 else if (srcy
< dsty
) { /* this is OK */
63 else if (srcy
> dsty
+ height
) {
71 /* add one pixel of slop when zooming, just to be safe */
72 if (srcx
> (dstx
+ ((zoomX
> 0.0F
) ? (width
* zoomX
+ 1.0F
) : 0.0F
))) {
73 /* src is completely right of dest */
76 else if (srcx
+ width
+ 1.0F
< dstx
+ ((zoomX
> 0.0F
) ? 0.0F
: (width
* zoomX
))) {
77 /* src is completely left of dest */
80 else if ((srcy
< dsty
) && (srcy
+ height
< dsty
+ (height
* zoomY
))) {
81 /* src is completely below dest */
84 else if ((srcy
> dsty
) && (srcy
+ height
> dsty
+ (height
* zoomY
))) {
85 /* src is completely above dest */
99 copy_rgba_pixels(struct gl_context
*ctx
, GLint srcx
, GLint srcy
,
100 GLint width
, GLint height
, GLint destx
, GLint desty
)
102 GLfloat
*tmpImage
, *p
;
103 GLint sy
, dy
, stepy
, row
;
104 const GLboolean zoom
= ctx
->Pixel
.ZoomX
!= 1.0F
|| ctx
->Pixel
.ZoomY
!= 1.0F
;
106 GLuint transferOps
= ctx
->_ImageTransferState
;
109 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
110 /* no readbuffer - OK */
114 if (ctx
->DrawBuffer
== ctx
->ReadBuffer
) {
115 overlapping
= regions_overlap(srcx
, srcy
, destx
, desty
, width
, height
,
116 ctx
->Pixel
.ZoomX
, ctx
->Pixel
.ZoomY
);
119 overlapping
= GL_FALSE
;
122 /* Determine if copy should be done bottom-to-top or top-to-bottom */
123 if (!overlapping
&& srcy
< desty
) {
124 /* top-down max-to-min */
125 sy
= srcy
+ height
- 1;
126 dy
= desty
+ height
- 1;
130 /* bottom-up min-to-max */
136 INIT_SPAN(span
, GL_BITMAP
);
137 _swrast_span_default_attribs(ctx
, &span
);
138 span
.arrayMask
= SPAN_RGBA
;
139 span
.arrayAttribs
= FRAG_BIT_COL0
; /* we'll fill in COL0 attrib values */
142 tmpImage
= (GLfloat
*) malloc(width
* height
* sizeof(GLfloat
) * 4);
144 _mesa_error( ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels" );
147 /* read the source image as RGBA/float */
149 for (row
= 0; row
< height
; row
++) {
150 _swrast_read_rgba_span( ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
,
151 width
, srcx
, sy
+ row
, p
);
157 tmpImage
= NULL
; /* silence compiler warnings */
161 ASSERT(width
< SWRAST_MAX_WIDTH
);
163 for (row
= 0; row
< height
; row
++, sy
+= stepy
, dy
+= stepy
) {
164 GLvoid
*rgba
= span
.array
->attribs
[FRAG_ATTRIB_COL0
];
166 /* Get row/span of source pixels */
168 /* get from buffered image */
169 memcpy(rgba
, p
, width
* sizeof(GLfloat
) * 4);
173 /* get from framebuffer */
174 _swrast_read_rgba_span( ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
,
175 width
, srcx
, sy
, rgba
);
179 _mesa_apply_rgba_transfer_ops(ctx
, transferOps
, width
,
180 (GLfloat (*)[4]) rgba
);
183 /* Write color span */
187 span
.array
->ChanType
= GL_FLOAT
;
189 _swrast_write_zoomed_rgba_span(ctx
, destx
, desty
, &span
, rgba
);
192 _swrast_write_rgba_span(ctx
, &span
);
196 span
.array
->ChanType
= CHAN_TYPE
; /* restore */
204 * Convert floating point Z values to integer Z values with pixel transfer's
208 scale_and_bias_z(struct gl_context
*ctx
, GLuint width
,
209 const GLfloat depth
[], GLuint z
[])
211 const GLuint depthMax
= ctx
->DrawBuffer
->_DepthMax
;
214 if (depthMax
<= 0xffffff &&
215 ctx
->Pixel
.DepthScale
== 1.0 &&
216 ctx
->Pixel
.DepthBias
== 0.0) {
217 /* no scale or bias and no clamping and no worry of overflow */
218 const GLfloat depthMaxF
= ctx
->DrawBuffer
->_DepthMaxF
;
219 for (i
= 0; i
< width
; i
++) {
220 z
[i
] = (GLuint
) (depth
[i
] * depthMaxF
);
224 /* need to be careful with overflow */
225 const GLdouble depthMaxF
= ctx
->DrawBuffer
->_DepthMaxF
;
226 for (i
= 0; i
< width
; i
++) {
227 GLdouble d
= depth
[i
] * ctx
->Pixel
.DepthScale
+ ctx
->Pixel
.DepthBias
;
228 d
= CLAMP(d
, 0.0, 1.0) * depthMaxF
;
243 copy_depth_pixels( struct gl_context
*ctx
, GLint srcx
, GLint srcy
,
244 GLint width
, GLint height
,
245 GLint destx
, GLint desty
)
247 struct gl_framebuffer
*fb
= ctx
->ReadBuffer
;
248 struct gl_renderbuffer
*readRb
= fb
->Attachment
[BUFFER_DEPTH
].Renderbuffer
;
249 GLfloat
*p
, *tmpImage
, *depth
;
252 const GLboolean zoom
= ctx
->Pixel
.ZoomX
!= 1.0F
|| ctx
->Pixel
.ZoomY
!= 1.0F
;
257 /* no readbuffer - OK */
261 INIT_SPAN(span
, GL_BITMAP
);
262 _swrast_span_default_attribs(ctx
, &span
);
263 span
.arrayMask
= SPAN_Z
;
265 if (ctx
->DrawBuffer
== ctx
->ReadBuffer
) {
266 overlapping
= regions_overlap(srcx
, srcy
, destx
, desty
, width
, height
,
267 ctx
->Pixel
.ZoomX
, ctx
->Pixel
.ZoomY
);
270 overlapping
= GL_FALSE
;
273 /* Determine if copy should be bottom-to-top or top-to-bottom */
274 if (!overlapping
&& srcy
< desty
) {
275 /* top-down max-to-min */
276 sy
= srcy
+ height
- 1;
277 dy
= desty
+ height
- 1;
281 /* bottom-up min-to-max */
289 tmpImage
= (GLfloat
*) malloc(width
* height
* sizeof(GLfloat
));
291 _mesa_error( ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels" );
295 for (j
= 0; j
< height
; j
++, ssy
+= stepy
) {
296 _swrast_read_depth_span_float(ctx
, readRb
, width
, srcx
, ssy
, p
);
302 tmpImage
= NULL
; /* silence compiler warning */
306 depth
= (GLfloat
*) malloc(width
* sizeof(GLfloat
));
308 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels()");
312 for (j
= 0; j
< height
; j
++, sy
+= stepy
, dy
+= stepy
) {
313 /* get depth values */
315 memcpy(depth
, p
, width
* sizeof(GLfloat
));
319 _swrast_read_depth_span_float(ctx
, readRb
, width
, srcx
, sy
, depth
);
322 /* apply scale and bias */
323 scale_and_bias_z(ctx
, width
, depth
, span
.array
->z
);
325 /* write depth values */
330 _swrast_write_zoomed_depth_span(ctx
, destx
, desty
, &span
);
332 _swrast_write_rgba_span(ctx
, &span
);
345 copy_stencil_pixels( struct gl_context
*ctx
, GLint srcx
, GLint srcy
,
346 GLint width
, GLint height
,
347 GLint destx
, GLint desty
)
349 struct gl_framebuffer
*fb
= ctx
->ReadBuffer
;
350 struct gl_renderbuffer
*rb
= fb
->Attachment
[BUFFER_STENCIL
].Renderbuffer
;
353 GLubyte
*p
, *tmpImage
, *stencil
;
354 const GLboolean zoom
= ctx
->Pixel
.ZoomX
!= 1.0F
|| ctx
->Pixel
.ZoomY
!= 1.0F
;
358 /* no readbuffer - OK */
362 if (ctx
->DrawBuffer
== ctx
->ReadBuffer
) {
363 overlapping
= regions_overlap(srcx
, srcy
, destx
, desty
, width
, height
,
364 ctx
->Pixel
.ZoomX
, ctx
->Pixel
.ZoomY
);
367 overlapping
= GL_FALSE
;
370 /* Determine if copy should be bottom-to-top or top-to-bottom */
371 if (!overlapping
&& srcy
< desty
) {
372 /* top-down max-to-min */
373 sy
= srcy
+ height
- 1;
374 dy
= desty
+ height
- 1;
378 /* bottom-up min-to-max */
386 tmpImage
= (GLubyte
*) malloc(width
* height
* sizeof(GLubyte
));
388 _mesa_error( ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels" );
392 for (j
= 0; j
< height
; j
++, ssy
+= stepy
) {
393 _swrast_read_stencil_span( ctx
, rb
, width
, srcx
, ssy
, p
);
399 tmpImage
= NULL
; /* silence compiler warning */
403 stencil
= (GLubyte
*) malloc(width
* sizeof(GLubyte
));
405 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels()");
409 for (j
= 0; j
< height
; j
++, sy
+= stepy
, dy
+= stepy
) {
410 /* Get stencil values */
412 memcpy(stencil
, p
, width
* sizeof(GLubyte
));
416 _swrast_read_stencil_span( ctx
, rb
, width
, srcx
, sy
, stencil
);
419 _mesa_apply_stencil_transfer_ops(ctx
, width
, stencil
);
421 /* Write stencil values */
423 _swrast_write_zoomed_stencil_span(ctx
, destx
, desty
, width
,
427 _swrast_write_stencil_span( ctx
, width
, destx
, dy
, stencil
);
440 * Try to do a fast 1:1 blit with memcpy.
441 * \return GL_TRUE if successful, GL_FALSE otherwise.
444 swrast_fast_copy_pixels(struct gl_context
*ctx
,
445 GLint srcX
, GLint srcY
, GLsizei width
, GLsizei height
,
446 GLint dstX
, GLint dstY
, GLenum type
)
448 struct gl_framebuffer
*srcFb
= ctx
->ReadBuffer
;
449 struct gl_framebuffer
*dstFb
= ctx
->DrawBuffer
;
450 struct gl_renderbuffer
*srcRb
, *dstRb
;
452 GLuint pixelBytes
, widthInBytes
;
453 GLubyte
*srcMap
, *dstMap
;
454 GLint srcRowStride
, dstRowStride
;
456 if (type
== GL_COLOR
) {
457 if (dstFb
->_NumColorDrawBuffers
!= 1)
459 srcRb
= srcFb
->_ColorReadBuffer
;
460 dstRb
= dstFb
->_ColorDrawBuffers
[0];
462 else if (type
== GL_STENCIL
) {
463 srcRb
= srcFb
->Attachment
[BUFFER_STENCIL
].Renderbuffer
;
464 dstRb
= dstFb
->Attachment
[BUFFER_STENCIL
].Renderbuffer
;
466 else if (type
== GL_DEPTH
) {
467 srcRb
= srcFb
->Attachment
[BUFFER_DEPTH
].Renderbuffer
;
468 dstRb
= dstFb
->Attachment
[BUFFER_DEPTH
].Renderbuffer
;
471 ASSERT(type
== GL_DEPTH_STENCIL_EXT
);
473 srcRb
= srcFb
->Attachment
[BUFFER_DEPTH
].Renderbuffer
;
474 dstRb
= dstFb
->Attachment
[BUFFER_DEPTH
].Renderbuffer
;
477 /* src and dst renderbuffers must be same format */
478 if (!srcRb
|| !dstRb
|| srcRb
->Format
!= dstRb
->Format
) {
482 if (type
== GL_STENCIL
|| type
== GL_DEPTH_COMPONENT
) {
483 /* can't handle packed depth+stencil here */
484 if (_mesa_is_format_packed_depth_stencil(srcRb
->Format
) ||
485 _mesa_is_format_packed_depth_stencil(dstRb
->Format
))
488 else if (type
== GL_DEPTH_STENCIL
) {
489 /* can't handle separate depth/stencil buffers */
490 if (srcRb
!= srcFb
->Attachment
[BUFFER_STENCIL
].Renderbuffer
||
491 dstRb
!= dstFb
->Attachment
[BUFFER_STENCIL
].Renderbuffer
)
495 /* clipping not supported */
496 if (srcX
< 0 || srcX
+ width
> (GLint
) srcFb
->Width
||
497 srcY
< 0 || srcY
+ height
> (GLint
) srcFb
->Height
||
498 dstX
< dstFb
->_Xmin
|| dstX
+ width
> dstFb
->_Xmax
||
499 dstY
< dstFb
->_Ymin
|| dstY
+ height
> dstFb
->_Ymax
) {
503 pixelBytes
= _mesa_get_format_bytes(srcRb
->Format
);
504 widthInBytes
= width
* pixelBytes
;
506 if (srcRb
== dstRb
) {
507 /* map whole buffer for read/write */
508 /* XXX we could be clever and just map the union region of the
509 * source and dest rects.
514 ctx
->Driver
.MapRenderbuffer(ctx
, srcRb
, 0, 0,
515 srcRb
->Width
, srcRb
->Height
,
516 GL_MAP_READ_BIT
| GL_MAP_WRITE_BIT
,
519 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels");
520 return GL_TRUE
; /* don't retry with slow path */
523 srcMap
= map
+ srcY
* rowStride
+ srcX
* pixelBytes
;
524 dstMap
= map
+ dstY
* rowStride
+ dstX
* pixelBytes
;
526 /* this handles overlapping copies */
528 /* copy in reverse (top->down) order */
529 srcMap
+= rowStride
* (height
- 1);
530 dstMap
+= rowStride
* (height
- 1);
531 srcRowStride
= -rowStride
;
532 dstRowStride
= -rowStride
;
535 /* copy in normal (bottom->up) order */
536 srcRowStride
= rowStride
;
537 dstRowStride
= rowStride
;
541 /* different src/dst buffers */
542 ctx
->Driver
.MapRenderbuffer(ctx
, srcRb
, srcX
, srcY
,
544 GL_MAP_READ_BIT
, &srcMap
, &srcRowStride
);
546 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels");
547 return GL_TRUE
; /* don't retry with slow path */
549 ctx
->Driver
.MapRenderbuffer(ctx
, dstRb
, dstX
, dstY
,
551 GL_MAP_WRITE_BIT
, &dstMap
, &dstRowStride
);
553 ctx
->Driver
.UnmapRenderbuffer(ctx
, srcRb
);
554 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glCopyPixels");
555 return GL_TRUE
; /* don't retry with slow path */
559 for (row
= 0; row
< height
; row
++) {
560 /* memmove() in case of overlap */
561 memmove(dstMap
, srcMap
, widthInBytes
);
562 dstMap
+= dstRowStride
;
563 srcMap
+= srcRowStride
;
566 ctx
->Driver
.UnmapRenderbuffer(ctx
, srcRb
);
567 if (dstRb
!= srcRb
) {
568 ctx
->Driver
.UnmapRenderbuffer(ctx
, dstRb
);
576 * Find/map the renderbuffer that we'll be reading from.
577 * The swrast_render_start() function only maps the drawing buffers,
578 * not the read buffer.
580 static struct gl_renderbuffer
*
581 map_readbuffer(struct gl_context
*ctx
, GLenum type
)
583 struct gl_framebuffer
*fb
= ctx
->ReadBuffer
;
584 struct gl_renderbuffer
*rb
;
585 struct swrast_renderbuffer
*srb
;
589 rb
= fb
->Attachment
[fb
->_ColorReadBufferIndex
].Renderbuffer
;
592 case GL_DEPTH_STENCIL
:
593 rb
= fb
->Attachment
[BUFFER_DEPTH
].Renderbuffer
;
596 rb
= fb
->Attachment
[BUFFER_STENCIL
].Renderbuffer
;
602 srb
= swrast_renderbuffer(rb
);
604 if (!srb
|| srb
->Map
) {
605 /* no buffer, or buffer is mapped already, we're done */
609 ctx
->Driver
.MapRenderbuffer(ctx
, rb
,
610 0, 0, rb
->Width
, rb
->Height
,
612 &srb
->Map
, &srb
->RowStride
);
619 * Do software-based glCopyPixels.
620 * By time we get here, all parameters will have been error-checked.
623 _swrast_CopyPixels( struct gl_context
*ctx
,
624 GLint srcx
, GLint srcy
, GLsizei width
, GLsizei height
,
625 GLint destx
, GLint desty
, GLenum type
)
627 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
628 struct gl_renderbuffer
*rb
;
630 if (!_mesa_check_conditional_render(ctx
))
631 return; /* don't copy */
633 if (swrast
->NewState
)
634 _swrast_validate_derived( ctx
);
636 if (!(SWRAST_CONTEXT(ctx
)->_RasterMask
!= 0x0 ||
637 ctx
->Pixel
.ZoomX
!= 1.0F
||
638 ctx
->Pixel
.ZoomY
!= 1.0F
||
639 ctx
->_ImageTransferState
) &&
640 swrast_fast_copy_pixels(ctx
, srcx
, srcy
, width
, height
, destx
, desty
,
646 swrast_render_start(ctx
);
647 rb
= map_readbuffer(ctx
, type
);
651 copy_rgba_pixels( ctx
, srcx
, srcy
, width
, height
, destx
, desty
);
654 copy_depth_pixels( ctx
, srcx
, srcy
, width
, height
, destx
, desty
);
657 copy_stencil_pixels( ctx
, srcx
, srcy
, width
, height
, destx
, desty
);
659 case GL_DEPTH_STENCIL_EXT
:
660 /* Copy buffers separately (if the fast copy path wasn't taken) */
661 copy_depth_pixels(ctx
, srcx
, srcy
, width
, height
, destx
, desty
);
662 copy_stencil_pixels(ctx
, srcx
, srcy
, width
, height
, destx
, desty
);
665 _mesa_problem(ctx
, "unexpected type in _swrast_CopyPixels");
668 swrast_render_finish(ctx
);
671 struct swrast_renderbuffer
*srb
= swrast_renderbuffer(rb
);
672 ctx
->Driver
.UnmapRenderbuffer(ctx
, rb
);