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, GL_FLOAT, p );
157 tmpImage = NULL; /* silence compiler warnings */
161 ASSERT(width < 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, GL_FLOAT, 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->_DepthBuffer;
249 GLfloat *p, *tmpImage;
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 for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
307 GLfloat depth[MAX_WIDTH];
308 /* get depth values */
310 memcpy(depth, p, width * sizeof(GLfloat));
314 _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth);
317 /* apply scale and bias */
318 scale_and_bias_z(ctx, width, depth, span.array->z);
320 /* write depth values */
325 _swrast_write_zoomed_depth_span(ctx, destx, desty, &span);
327 _swrast_write_rgba_span(ctx, &span);
337 copy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
338 GLint width, GLint height,
339 GLint destx, GLint desty )
341 struct gl_framebuffer *fb = ctx->ReadBuffer;
342 struct gl_renderbuffer *rb = fb->_StencilBuffer;
345 GLstencil *p, *tmpImage;
346 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
350 /* no readbuffer - OK */
354 if (ctx->DrawBuffer == ctx->ReadBuffer) {
355 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
356 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
359 overlapping = GL_FALSE;
362 /* Determine if copy should be bottom-to-top or top-to-bottom */
363 if (!overlapping && srcy < desty) {
364 /* top-down max-to-min */
365 sy = srcy + height - 1;
366 dy = desty + height - 1;
370 /* bottom-up min-to-max */
378 tmpImage = (GLstencil *) malloc(width * height * sizeof(GLstencil));
380 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
384 for (j = 0; j < height; j++, ssy += stepy) {
385 _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p );
391 tmpImage = NULL; /* silence compiler warning */
395 for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
396 GLstencil stencil[MAX_WIDTH];
398 /* Get stencil values */
400 memcpy(stencil, p, width * sizeof(GLstencil));
404 _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil );
407 _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
409 /* Write stencil values */
411 _swrast_write_zoomed_stencil_span(ctx, destx, desty, width,
415 _swrast_write_stencil_span( ctx, width, destx, dy, stencil );
425 * This isn't terribly efficient. If a driver really has combined
426 * depth/stencil buffers the driver should implement an optimized
427 * CopyPixels function.
430 copy_depth_stencil_pixels(struct gl_context *ctx,
431 const GLint srcX, const GLint srcY,
432 const GLint width, const GLint height,
433 const GLint destX, const GLint destY)
435 struct gl_renderbuffer *stencilReadRb, *depthReadRb, *depthDrawRb;
438 GLstencil *tempStencilImage = NULL, *stencilPtr = NULL;
439 GLfloat *tempDepthImage = NULL, *depthPtr = NULL;
440 const GLfloat depthScale = ctx->DrawBuffer->_DepthMaxF;
441 const GLuint stencilMask = ctx->Stencil.WriteMask[0];
442 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
443 const GLboolean scaleOrBias
444 = ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0;
447 depthDrawRb = ctx->DrawBuffer->_DepthBuffer;
448 depthReadRb = ctx->ReadBuffer->_DepthBuffer;
449 stencilReadRb = ctx->ReadBuffer->_StencilBuffer;
453 ASSERT(stencilReadRb);
455 if (ctx->DrawBuffer == ctx->ReadBuffer) {
456 overlapping = regions_overlap(srcX, srcY, destX, destY, width, height,
457 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
460 overlapping = GL_FALSE;
463 /* Determine if copy should be bottom-to-top or top-to-bottom */
464 if (!overlapping && srcY < destY) {
465 /* top-down max-to-min */
466 sy = srcY + height - 1;
467 dy = destY + height - 1;
471 /* bottom-up min-to-max */
480 if (stencilMask != 0x0) {
482 = (GLstencil *) malloc(width * height * sizeof(GLstencil));
483 if (!tempStencilImage) {
484 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
488 /* get copy of stencil pixels */
489 stencilPtr = tempStencilImage;
490 for (j = 0; j < height; j++, ssy += stepy) {
491 _swrast_read_stencil_span(ctx, stencilReadRb,
492 width, srcX, ssy, stencilPtr);
495 stencilPtr = tempStencilImage;
498 if (ctx->Depth.Mask) {
500 = (GLfloat *) malloc(width * height * sizeof(GLfloat));
501 if (!tempDepthImage) {
502 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
503 free(tempStencilImage);
507 /* get copy of depth pixels */
508 depthPtr = tempDepthImage;
509 for (j = 0; j < height; j++, ssy += stepy) {
510 _swrast_read_depth_span_float(ctx, depthReadRb,
511 width, srcX, ssy, depthPtr);
514 depthPtr = tempDepthImage;
518 for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
519 if (stencilMask != 0x0) {
520 GLstencil stencil[MAX_WIDTH];
522 /* Get stencil values */
524 memcpy(stencil, stencilPtr, width * sizeof(GLstencil));
528 _swrast_read_stencil_span(ctx, stencilReadRb,
529 width, srcX, sy, stencil);
532 _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
536 _swrast_write_zoomed_stencil_span(ctx, destX, destY, width,
540 _swrast_write_stencil_span( ctx, width, destX, dy, stencil );
544 if (ctx->Depth.Mask) {
545 GLfloat depth[MAX_WIDTH];
546 GLuint zVals32[MAX_WIDTH];
547 GLushort zVals16[MAX_WIDTH];
551 /* get depth values */
553 memcpy(depth, depthPtr, width * sizeof(GLfloat));
557 _swrast_read_depth_span_float(ctx, depthReadRb,
558 width, srcX, sy, depth);
563 _mesa_scale_and_bias_depth(ctx, width, depth);
565 /* convert to integer Z values */
566 if (depthDrawRb->DataType == GL_UNSIGNED_SHORT) {
568 for (k = 0; k < width; k++)
569 zVals16[k] = (GLushort) (depth[k] * depthScale);
575 for (k = 0; k < width; k++)
576 zVals32[k] = (GLuint) (depth[k] * depthScale);
583 _swrast_write_zoomed_z_span(ctx, destX, destY, width,
587 _swrast_put_row(ctx, depthDrawRb, width, destX, dy, zVals, zBytes);
592 if (tempStencilImage)
593 free(tempStencilImage);
596 free(tempDepthImage);
602 * Try to do a fast copy pixels.
605 fast_copy_pixels(struct gl_context *ctx,
606 GLint srcX, GLint srcY, GLsizei width, GLsizei height,
607 GLint dstX, GLint dstY, GLenum type)
609 struct gl_framebuffer *srcFb = ctx->ReadBuffer;
610 struct gl_framebuffer *dstFb = ctx->DrawBuffer;
611 struct gl_renderbuffer *srcRb, *dstRb;
614 if (SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
615 ctx->Pixel.ZoomX != 1.0F ||
616 ctx->Pixel.ZoomY != 1.0F ||
617 ctx->_ImageTransferState) {
618 /* can't handle these */
622 if (type == GL_COLOR) {
623 if (dstFb->_NumColorDrawBuffers != 1)
625 srcRb = srcFb->_ColorReadBuffer;
626 dstRb = dstFb->_ColorDrawBuffers[0];
628 else if (type == GL_STENCIL) {
629 srcRb = srcFb->_StencilBuffer;
630 dstRb = dstFb->_StencilBuffer;
632 else if (type == GL_DEPTH) {
633 srcRb = srcFb->_DepthBuffer;
634 dstRb = dstFb->_DepthBuffer;
637 ASSERT(type == GL_DEPTH_STENCIL_EXT);
639 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
640 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
643 /* src and dst renderbuffers must be same format and type */
644 if (!srcRb || !dstRb ||
645 srcRb->DataType != dstRb->DataType ||
646 srcRb->_BaseFormat != dstRb->_BaseFormat) {
650 /* clipping not supported */
651 if (srcX < 0 || srcX + width > (GLint) srcFb->Width ||
652 srcY < 0 || srcY + height > (GLint) srcFb->Height ||
653 dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax ||
654 dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) {
658 /* overlapping src/dst doesn't matter, just determine Y direction */
660 /* top-down max-to-min */
661 srcY = srcY + height - 1;
662 dstY = dstY + height - 1;
666 /* bottom-up min-to-max */
670 for (row = 0; row < height; row++) {
671 GLuint temp[MAX_WIDTH][4];
672 srcRb->GetRow(ctx, srcRb, width, srcX, srcY, temp);
673 dstRb->PutRow(ctx, dstRb, width, dstX, dstY, temp, NULL);
683 * Do software-based glCopyPixels.
684 * By time we get here, all parameters will have been error-checked.
687 _swrast_CopyPixels( struct gl_context *ctx,
688 GLint srcx, GLint srcy, GLsizei width, GLsizei height,
689 GLint destx, GLint desty, GLenum type )
691 SWcontext *swrast = SWRAST_CONTEXT(ctx);
692 swrast_render_start(ctx);
694 if (!_mesa_check_conditional_render(ctx))
695 return; /* don't copy */
697 if (swrast->NewState)
698 _swrast_validate_derived( ctx );
700 if (!fast_copy_pixels(ctx, srcx, srcy, width, height, destx, desty, type)) {
703 copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
706 copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
709 copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
711 case GL_DEPTH_STENCIL_EXT:
712 copy_depth_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
715 _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
719 swrast_render_finish(ctx);