17117f1b4Smrg/* 27117f1b4Smrg * Mesa 3-D graphics library 37117f1b4Smrg * 47117f1b4Smrg * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 57117f1b4Smrg * 67117f1b4Smrg * Permission is hereby granted, free of charge, to any person obtaining a 77117f1b4Smrg * copy of this software and associated documentation files (the "Software"), 87117f1b4Smrg * to deal in the Software without restriction, including without limitation 97117f1b4Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 107117f1b4Smrg * and/or sell copies of the Software, and to permit persons to whom the 117117f1b4Smrg * Software is furnished to do so, subject to the following conditions: 127117f1b4Smrg * 137117f1b4Smrg * The above copyright notice and this permission notice shall be included 147117f1b4Smrg * in all copies or substantial portions of the Software. 157117f1b4Smrg * 167117f1b4Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 177117f1b4Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 187117f1b4Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE. 237117f1b4Smrg */ 247117f1b4Smrg 257117f1b4Smrg 26c1f859d4Smrg#include "main/glheader.h" 27c1f859d4Smrg#include "main/context.h" 28cdc920a0Smrg#include "main/condrender.h" 29c1f859d4Smrg#include "main/macros.h" 3001e04c3fSmrg#include "main/blit.h" 313464ebd5Sriastradh#include "main/pixeltransfer.h" 327ec681f3Smrg 337117f1b4Smrg 347117f1b4Smrg#include "s_context.h" 357117f1b4Smrg#include "s_depth.h" 367117f1b4Smrg#include "s_span.h" 377117f1b4Smrg#include "s_stencil.h" 387117f1b4Smrg#include "s_zoom.h" 397117f1b4Smrg 407117f1b4Smrg 417117f1b4Smrg 427117f1b4Smrg/** 437117f1b4Smrg * Determine if there's overlap in an image copy. 447117f1b4Smrg * This test also compensates for the fact that copies are done from 457117f1b4Smrg * bottom to top and overlaps can sometimes be handled correctly 467117f1b4Smrg * without making a temporary image copy. 477117f1b4Smrg * \return GL_TRUE if the regions overlap, GL_FALSE otherwise. 487117f1b4Smrg */ 497117f1b4Smrgstatic GLboolean 507117f1b4Smrgregions_overlap(GLint srcx, GLint srcy, 517117f1b4Smrg GLint dstx, GLint dsty, 527117f1b4Smrg GLint width, GLint height, 537117f1b4Smrg GLfloat zoomX, GLfloat zoomY) 547117f1b4Smrg{ 5501e04c3fSmrg if (zoomX == 1.0F && zoomY == 1.0F) { 5601e04c3fSmrg return _mesa_regions_overlap(srcx, srcy, srcx + width, srcy + height, 5701e04c3fSmrg dstx, dsty, dstx + width, dsty + height); 587117f1b4Smrg } 597117f1b4Smrg else { 607117f1b4Smrg /* add one pixel of slop when zooming, just to be safe */ 617117f1b4Smrg if (srcx > (dstx + ((zoomX > 0.0F) ? (width * zoomX + 1.0F) : 0.0F))) { 627117f1b4Smrg /* src is completely right of dest */ 637117f1b4Smrg return GL_FALSE; 647117f1b4Smrg } 657117f1b4Smrg else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) { 667117f1b4Smrg /* src is completely left of dest */ 677117f1b4Smrg return GL_FALSE; 687117f1b4Smrg } 697117f1b4Smrg else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) { 707117f1b4Smrg /* src is completely below dest */ 717117f1b4Smrg return GL_FALSE; 727117f1b4Smrg } 737117f1b4Smrg else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) { 747117f1b4Smrg /* src is completely above dest */ 757117f1b4Smrg return GL_FALSE; 767117f1b4Smrg } 777117f1b4Smrg else { 787117f1b4Smrg return GL_TRUE; 797117f1b4Smrg } 807117f1b4Smrg } 817117f1b4Smrg} 827117f1b4Smrg 837117f1b4Smrg 847117f1b4Smrg/** 857117f1b4Smrg * RGBA copypixels 867117f1b4Smrg */ 877117f1b4Smrgstatic void 883464ebd5Sriastradhcopy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, 897117f1b4Smrg GLint width, GLint height, GLint destx, GLint desty) 907117f1b4Smrg{ 917117f1b4Smrg GLfloat *tmpImage, *p; 927117f1b4Smrg GLint sy, dy, stepy, row; 937117f1b4Smrg const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 947117f1b4Smrg GLint overlapping; 957117f1b4Smrg GLuint transferOps = ctx->_ImageTransferState; 967117f1b4Smrg SWspan span; 977117f1b4Smrg 987117f1b4Smrg if (!ctx->ReadBuffer->_ColorReadBuffer) { 997117f1b4Smrg /* no readbuffer - OK */ 1007117f1b4Smrg return; 1017117f1b4Smrg } 1027117f1b4Smrg 1037117f1b4Smrg if (ctx->DrawBuffer == ctx->ReadBuffer) { 1047117f1b4Smrg overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 1057117f1b4Smrg ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 1067117f1b4Smrg } 1077117f1b4Smrg else { 1087117f1b4Smrg overlapping = GL_FALSE; 1097117f1b4Smrg } 1107117f1b4Smrg 1117117f1b4Smrg /* Determine if copy should be done bottom-to-top or top-to-bottom */ 1127117f1b4Smrg if (!overlapping && srcy < desty) { 1137117f1b4Smrg /* top-down max-to-min */ 1147117f1b4Smrg sy = srcy + height - 1; 1157117f1b4Smrg dy = desty + height - 1; 1167117f1b4Smrg stepy = -1; 1177117f1b4Smrg } 1187117f1b4Smrg else { 1197117f1b4Smrg /* bottom-up min-to-max */ 1207117f1b4Smrg sy = srcy; 1217117f1b4Smrg dy = desty; 1227117f1b4Smrg stepy = 1; 1237117f1b4Smrg } 1247117f1b4Smrg 125c1f859d4Smrg INIT_SPAN(span, GL_BITMAP); 126c1f859d4Smrg _swrast_span_default_attribs(ctx, &span); 127c1f859d4Smrg span.arrayMask = SPAN_RGBA; 128af69d88dSmrg span.arrayAttribs = VARYING_BIT_COL0; /* we'll fill in COL0 attrib values */ 1297117f1b4Smrg 1307117f1b4Smrg if (overlapping) { 131af69d88dSmrg tmpImage = malloc(width * height * sizeof(GLfloat) * 4); 1327117f1b4Smrg if (!tmpImage) { 1337117f1b4Smrg _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 1347117f1b4Smrg return; 1357117f1b4Smrg } 1367117f1b4Smrg /* read the source image as RGBA/float */ 1377117f1b4Smrg p = tmpImage; 1387117f1b4Smrg for (row = 0; row < height; row++) { 1397117f1b4Smrg _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer, 140af69d88dSmrg width, srcx, sy + row, p ); 1417117f1b4Smrg p += width * 4; 1427117f1b4Smrg } 1437117f1b4Smrg p = tmpImage; 1447117f1b4Smrg } 1457117f1b4Smrg else { 1467117f1b4Smrg tmpImage = NULL; /* silence compiler warnings */ 1477117f1b4Smrg p = NULL; 1487117f1b4Smrg } 1497117f1b4Smrg 15001e04c3fSmrg assert(width < SWRAST_MAX_WIDTH); 1517117f1b4Smrg 1527117f1b4Smrg for (row = 0; row < height; row++, sy += stepy, dy += stepy) { 153af69d88dSmrg GLvoid *rgba = span.array->attribs[VARYING_SLOT_COL0]; 1547117f1b4Smrg 1557117f1b4Smrg /* Get row/span of source pixels */ 1567117f1b4Smrg if (overlapping) { 1577117f1b4Smrg /* get from buffered image */ 158cdc920a0Smrg memcpy(rgba, p, width * sizeof(GLfloat) * 4); 1597117f1b4Smrg p += width * 4; 1607117f1b4Smrg } 1617117f1b4Smrg else { 1627117f1b4Smrg /* get from framebuffer */ 1637117f1b4Smrg _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer, 164af69d88dSmrg width, srcx, sy, rgba ); 1657117f1b4Smrg } 1667117f1b4Smrg 1677117f1b4Smrg if (transferOps) { 1687117f1b4Smrg _mesa_apply_rgba_transfer_ops(ctx, transferOps, width, 1697117f1b4Smrg (GLfloat (*)[4]) rgba); 1707117f1b4Smrg } 1717117f1b4Smrg 1727117f1b4Smrg /* Write color span */ 1737117f1b4Smrg span.x = destx; 1747117f1b4Smrg span.y = dy; 1757117f1b4Smrg span.end = width; 1767117f1b4Smrg span.array->ChanType = GL_FLOAT; 1777117f1b4Smrg if (zoom) { 1787117f1b4Smrg _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba); 1797117f1b4Smrg } 1807117f1b4Smrg else { 1817117f1b4Smrg _swrast_write_rgba_span(ctx, &span); 1827117f1b4Smrg } 1837117f1b4Smrg } 1847117f1b4Smrg 1857117f1b4Smrg span.array->ChanType = CHAN_TYPE; /* restore */ 1867117f1b4Smrg 1877117f1b4Smrg if (overlapping) 188cdc920a0Smrg free(tmpImage); 1897117f1b4Smrg} 1907117f1b4Smrg 1917117f1b4Smrg 1927117f1b4Smrg/** 1937117f1b4Smrg * Convert floating point Z values to integer Z values with pixel transfer's 1947117f1b4Smrg * Z scale and bias. 1957117f1b4Smrg */ 1967117f1b4Smrgstatic void 1973464ebd5Sriastradhscale_and_bias_z(struct gl_context *ctx, GLuint width, 1987117f1b4Smrg const GLfloat depth[], GLuint z[]) 1997117f1b4Smrg{ 2007117f1b4Smrg const GLuint depthMax = ctx->DrawBuffer->_DepthMax; 2017117f1b4Smrg GLuint i; 2027117f1b4Smrg 2037117f1b4Smrg if (depthMax <= 0xffffff && 20401e04c3fSmrg ctx->Pixel.DepthScale == 1.0F && 20501e04c3fSmrg ctx->Pixel.DepthBias == 0.0F) { 2067117f1b4Smrg /* no scale or bias and no clamping and no worry of overflow */ 2077117f1b4Smrg const GLfloat depthMaxF = ctx->DrawBuffer->_DepthMaxF; 2087117f1b4Smrg for (i = 0; i < width; i++) { 2097117f1b4Smrg z[i] = (GLuint) (depth[i] * depthMaxF); 2107117f1b4Smrg } 2117117f1b4Smrg } 2127117f1b4Smrg else { 2137117f1b4Smrg /* need to be careful with overflow */ 2147117f1b4Smrg const GLdouble depthMaxF = ctx->DrawBuffer->_DepthMaxF; 2157117f1b4Smrg for (i = 0; i < width; i++) { 2167117f1b4Smrg GLdouble d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias; 2177ec681f3Smrg d = SATURATE(d) * depthMaxF; 2187117f1b4Smrg if (d >= depthMaxF) 2197117f1b4Smrg z[i] = depthMax; 2207117f1b4Smrg else 2217117f1b4Smrg z[i] = (GLuint) d; 2227117f1b4Smrg } 2237117f1b4Smrg } 2247117f1b4Smrg} 2257117f1b4Smrg 2267117f1b4Smrg 2277117f1b4Smrg 2287117f1b4Smrg/* 2297117f1b4Smrg * TODO: Optimize!!!! 2307117f1b4Smrg */ 2317117f1b4Smrgstatic void 2323464ebd5Sriastradhcopy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 2337117f1b4Smrg GLint width, GLint height, 2347117f1b4Smrg GLint destx, GLint desty ) 2357117f1b4Smrg{ 2367117f1b4Smrg struct gl_framebuffer *fb = ctx->ReadBuffer; 237af69d88dSmrg struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 238af69d88dSmrg GLfloat *p, *tmpImage, *depth; 2397117f1b4Smrg GLint sy, dy, stepy; 2407117f1b4Smrg GLint j; 2417117f1b4Smrg const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 2427117f1b4Smrg GLint overlapping; 2437117f1b4Smrg SWspan span; 2447117f1b4Smrg 2457117f1b4Smrg if (!readRb) { 2467117f1b4Smrg /* no readbuffer - OK */ 2477117f1b4Smrg return; 2487117f1b4Smrg } 2497117f1b4Smrg 250c1f859d4Smrg INIT_SPAN(span, GL_BITMAP); 251c1f859d4Smrg _swrast_span_default_attribs(ctx, &span); 252c1f859d4Smrg span.arrayMask = SPAN_Z; 2537117f1b4Smrg 2547117f1b4Smrg if (ctx->DrawBuffer == ctx->ReadBuffer) { 2557117f1b4Smrg overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 2567117f1b4Smrg ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 2577117f1b4Smrg } 2587117f1b4Smrg else { 2597117f1b4Smrg overlapping = GL_FALSE; 2607117f1b4Smrg } 2617117f1b4Smrg 2627117f1b4Smrg /* Determine if copy should be bottom-to-top or top-to-bottom */ 2637117f1b4Smrg if (!overlapping && srcy < desty) { 2647117f1b4Smrg /* top-down max-to-min */ 2657117f1b4Smrg sy = srcy + height - 1; 2667117f1b4Smrg dy = desty + height - 1; 2677117f1b4Smrg stepy = -1; 2687117f1b4Smrg } 2697117f1b4Smrg else { 2707117f1b4Smrg /* bottom-up min-to-max */ 2717117f1b4Smrg sy = srcy; 2727117f1b4Smrg dy = desty; 2737117f1b4Smrg stepy = 1; 2747117f1b4Smrg } 2757117f1b4Smrg 2767117f1b4Smrg if (overlapping) { 2777117f1b4Smrg GLint ssy = sy; 278af69d88dSmrg tmpImage = malloc(width * height * sizeof(GLfloat)); 2797117f1b4Smrg if (!tmpImage) { 2807117f1b4Smrg _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 2817117f1b4Smrg return; 2827117f1b4Smrg } 2837117f1b4Smrg p = tmpImage; 2847117f1b4Smrg for (j = 0; j < height; j++, ssy += stepy) { 2857117f1b4Smrg _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p); 2867117f1b4Smrg p += width; 2877117f1b4Smrg } 2887117f1b4Smrg p = tmpImage; 2897117f1b4Smrg } 2907117f1b4Smrg else { 2917117f1b4Smrg tmpImage = NULL; /* silence compiler warning */ 2927117f1b4Smrg p = NULL; 2937117f1b4Smrg } 2947117f1b4Smrg 295af69d88dSmrg depth = malloc(width * sizeof(GLfloat)); 296af69d88dSmrg if (!depth) { 297af69d88dSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()"); 298af69d88dSmrg goto end; 299af69d88dSmrg } 300af69d88dSmrg 3017117f1b4Smrg for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 3027117f1b4Smrg /* get depth values */ 3037117f1b4Smrg if (overlapping) { 304cdc920a0Smrg memcpy(depth, p, width * sizeof(GLfloat)); 3057117f1b4Smrg p += width; 3067117f1b4Smrg } 3077117f1b4Smrg else { 3087117f1b4Smrg _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth); 3097117f1b4Smrg } 3107117f1b4Smrg 3117117f1b4Smrg /* apply scale and bias */ 3127117f1b4Smrg scale_and_bias_z(ctx, width, depth, span.array->z); 3137117f1b4Smrg 3147117f1b4Smrg /* write depth values */ 3157117f1b4Smrg span.x = destx; 3167117f1b4Smrg span.y = dy; 3177117f1b4Smrg span.end = width; 318cdc920a0Smrg if (zoom) 319cdc920a0Smrg _swrast_write_zoomed_depth_span(ctx, destx, desty, &span); 320cdc920a0Smrg else 321cdc920a0Smrg _swrast_write_rgba_span(ctx, &span); 3227117f1b4Smrg } 3237117f1b4Smrg 324af69d88dSmrg free(depth); 325af69d88dSmrg 326af69d88dSmrgend: 3277117f1b4Smrg if (overlapping) 328cdc920a0Smrg free(tmpImage); 3297117f1b4Smrg} 3307117f1b4Smrg 3317117f1b4Smrg 3327117f1b4Smrg 3337117f1b4Smrgstatic void 3343464ebd5Sriastradhcopy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 3357117f1b4Smrg GLint width, GLint height, 3367117f1b4Smrg GLint destx, GLint desty ) 3377117f1b4Smrg{ 3387117f1b4Smrg struct gl_framebuffer *fb = ctx->ReadBuffer; 339af69d88dSmrg struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 3407117f1b4Smrg GLint sy, dy, stepy; 3417117f1b4Smrg GLint j; 342af69d88dSmrg GLubyte *p, *tmpImage, *stencil; 3437117f1b4Smrg const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 3447117f1b4Smrg GLint overlapping; 3457117f1b4Smrg 3467117f1b4Smrg if (!rb) { 3477117f1b4Smrg /* no readbuffer - OK */ 3487117f1b4Smrg return; 3497117f1b4Smrg } 3507117f1b4Smrg 3517117f1b4Smrg if (ctx->DrawBuffer == ctx->ReadBuffer) { 3527117f1b4Smrg overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 3537117f1b4Smrg ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 3547117f1b4Smrg } 3557117f1b4Smrg else { 3567117f1b4Smrg overlapping = GL_FALSE; 3577117f1b4Smrg } 3587117f1b4Smrg 3597117f1b4Smrg /* Determine if copy should be bottom-to-top or top-to-bottom */ 3607117f1b4Smrg if (!overlapping && srcy < desty) { 3617117f1b4Smrg /* top-down max-to-min */ 3627117f1b4Smrg sy = srcy + height - 1; 3637117f1b4Smrg dy = desty + height - 1; 3647117f1b4Smrg stepy = -1; 3657117f1b4Smrg } 3667117f1b4Smrg else { 3677117f1b4Smrg /* bottom-up min-to-max */ 3687117f1b4Smrg sy = srcy; 3697117f1b4Smrg dy = desty; 3707117f1b4Smrg stepy = 1; 3717117f1b4Smrg } 3727117f1b4Smrg 3737117f1b4Smrg if (overlapping) { 3747117f1b4Smrg GLint ssy = sy; 375af69d88dSmrg tmpImage = malloc(width * height * sizeof(GLubyte)); 3767117f1b4Smrg if (!tmpImage) { 3777117f1b4Smrg _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 3787117f1b4Smrg return; 3797117f1b4Smrg } 3807117f1b4Smrg p = tmpImage; 3817117f1b4Smrg for (j = 0; j < height; j++, ssy += stepy) { 3827117f1b4Smrg _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p ); 3837117f1b4Smrg p += width; 3847117f1b4Smrg } 3857117f1b4Smrg p = tmpImage; 3867117f1b4Smrg } 3877117f1b4Smrg else { 3887117f1b4Smrg tmpImage = NULL; /* silence compiler warning */ 3897117f1b4Smrg p = NULL; 3907117f1b4Smrg } 3917117f1b4Smrg 392af69d88dSmrg stencil = malloc(width * sizeof(GLubyte)); 393af69d88dSmrg if (!stencil) { 394af69d88dSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()"); 395af69d88dSmrg goto end; 396af69d88dSmrg } 3977117f1b4Smrg 398af69d88dSmrg for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 3997117f1b4Smrg /* Get stencil values */ 4007117f1b4Smrg if (overlapping) { 401af69d88dSmrg memcpy(stencil, p, width * sizeof(GLubyte)); 4027117f1b4Smrg p += width; 4037117f1b4Smrg } 4047117f1b4Smrg else { 4057117f1b4Smrg _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil ); 4067117f1b4Smrg } 4077117f1b4Smrg 4087117f1b4Smrg _mesa_apply_stencil_transfer_ops(ctx, width, stencil); 4097117f1b4Smrg 4107117f1b4Smrg /* Write stencil values */ 4117117f1b4Smrg if (zoom) { 4127117f1b4Smrg _swrast_write_zoomed_stencil_span(ctx, destx, desty, width, 4137117f1b4Smrg destx, dy, stencil); 4147117f1b4Smrg } 4157117f1b4Smrg else { 4167117f1b4Smrg _swrast_write_stencil_span( ctx, width, destx, dy, stencil ); 4177117f1b4Smrg } 4187117f1b4Smrg } 4197117f1b4Smrg 420af69d88dSmrg free(stencil); 421af69d88dSmrg 422af69d88dSmrgend: 4237117f1b4Smrg if (overlapping) 424cdc920a0Smrg free(tmpImage); 4257117f1b4Smrg} 4267117f1b4Smrg 4277117f1b4Smrg 4287117f1b4Smrg/** 429af69d88dSmrg * Try to do a fast 1:1 blit with memcpy. 430af69d88dSmrg * \return GL_TRUE if successful, GL_FALSE otherwise. 4317117f1b4Smrg */ 432af69d88dSmrgGLboolean 433af69d88dSmrgswrast_fast_copy_pixels(struct gl_context *ctx, 43401e04c3fSmrg struct gl_framebuffer *srcFb, 43501e04c3fSmrg struct gl_framebuffer *dstFb, 43601e04c3fSmrg GLint srcX, GLint srcY, GLsizei width, GLsizei height, 43701e04c3fSmrg GLint dstX, GLint dstY, GLenum type) 4387117f1b4Smrg{ 4397117f1b4Smrg struct gl_renderbuffer *srcRb, *dstRb; 440af69d88dSmrg GLint row; 441af69d88dSmrg GLuint pixelBytes, widthInBytes; 442af69d88dSmrg GLubyte *srcMap, *dstMap; 443af69d88dSmrg GLint srcRowStride, dstRowStride; 4447117f1b4Smrg 4457117f1b4Smrg if (type == GL_COLOR) { 446c1f859d4Smrg if (dstFb->_NumColorDrawBuffers != 1) 4477117f1b4Smrg return GL_FALSE; 4487117f1b4Smrg srcRb = srcFb->_ColorReadBuffer; 449c1f859d4Smrg dstRb = dstFb->_ColorDrawBuffers[0]; 4507117f1b4Smrg } 4517117f1b4Smrg else if (type == GL_STENCIL) { 452af69d88dSmrg srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer; 453af69d88dSmrg dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer; 4547117f1b4Smrg } 4557117f1b4Smrg else if (type == GL_DEPTH) { 456af69d88dSmrg srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 457af69d88dSmrg dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 4587117f1b4Smrg } 4597117f1b4Smrg else { 46001e04c3fSmrg assert(type == GL_DEPTH_STENCIL_EXT); 4617117f1b4Smrg /* XXX correct? */ 4627117f1b4Smrg srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 4637117f1b4Smrg dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 4647117f1b4Smrg } 4657117f1b4Smrg 466af69d88dSmrg /* src and dst renderbuffers must be same format */ 467af69d88dSmrg if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) { 4687117f1b4Smrg return GL_FALSE; 4697117f1b4Smrg } 4707117f1b4Smrg 471af69d88dSmrg if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) { 472af69d88dSmrg /* can't handle packed depth+stencil here */ 473af69d88dSmrg if (_mesa_is_format_packed_depth_stencil(srcRb->Format) || 474af69d88dSmrg _mesa_is_format_packed_depth_stencil(dstRb->Format)) 475af69d88dSmrg return GL_FALSE; 476af69d88dSmrg } 477af69d88dSmrg else if (type == GL_DEPTH_STENCIL) { 478af69d88dSmrg /* can't handle separate depth/stencil buffers */ 479af69d88dSmrg if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer || 480af69d88dSmrg dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer) 481af69d88dSmrg return GL_FALSE; 482af69d88dSmrg } 483af69d88dSmrg 4847117f1b4Smrg /* clipping not supported */ 4857117f1b4Smrg if (srcX < 0 || srcX + width > (GLint) srcFb->Width || 4867117f1b4Smrg srcY < 0 || srcY + height > (GLint) srcFb->Height || 4877117f1b4Smrg dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax || 4887117f1b4Smrg dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) { 4897117f1b4Smrg return GL_FALSE; 4907117f1b4Smrg } 4917117f1b4Smrg 492af69d88dSmrg pixelBytes = _mesa_get_format_bytes(srcRb->Format); 493af69d88dSmrg widthInBytes = width * pixelBytes; 494af69d88dSmrg 495af69d88dSmrg if (srcRb == dstRb) { 496af69d88dSmrg /* map whole buffer for read/write */ 497af69d88dSmrg /* XXX we could be clever and just map the union region of the 498af69d88dSmrg * source and dest rects. 499af69d88dSmrg */ 500af69d88dSmrg GLubyte *map; 501af69d88dSmrg GLint rowStride; 502af69d88dSmrg 503af69d88dSmrg ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0, 504af69d88dSmrg srcRb->Width, srcRb->Height, 505af69d88dSmrg GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 50601e04c3fSmrg &map, &rowStride, srcFb->FlipY); 507af69d88dSmrg if (!map) { 508af69d88dSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 509af69d88dSmrg return GL_TRUE; /* don't retry with slow path */ 510af69d88dSmrg } 511af69d88dSmrg 512af69d88dSmrg srcMap = map + srcY * rowStride + srcX * pixelBytes; 513af69d88dSmrg dstMap = map + dstY * rowStride + dstX * pixelBytes; 514af69d88dSmrg 515af69d88dSmrg /* this handles overlapping copies */ 516af69d88dSmrg if (srcY < dstY) { 517af69d88dSmrg /* copy in reverse (top->down) order */ 518af69d88dSmrg srcMap += rowStride * (height - 1); 519af69d88dSmrg dstMap += rowStride * (height - 1); 520af69d88dSmrg srcRowStride = -rowStride; 521af69d88dSmrg dstRowStride = -rowStride; 522af69d88dSmrg } 523af69d88dSmrg else { 524af69d88dSmrg /* copy in normal (bottom->up) order */ 525af69d88dSmrg srcRowStride = rowStride; 526af69d88dSmrg dstRowStride = rowStride; 527af69d88dSmrg } 5287117f1b4Smrg } 5297117f1b4Smrg else { 530af69d88dSmrg /* different src/dst buffers */ 531af69d88dSmrg ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY, 532af69d88dSmrg width, height, 53301e04c3fSmrg GL_MAP_READ_BIT, &srcMap, &srcRowStride, 53401e04c3fSmrg srcFb->FlipY); 535af69d88dSmrg if (!srcMap) { 536af69d88dSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 537af69d88dSmrg return GL_TRUE; /* don't retry with slow path */ 538af69d88dSmrg } 539af69d88dSmrg ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY, 540af69d88dSmrg width, height, 54101e04c3fSmrg GL_MAP_WRITE_BIT, &dstMap, &dstRowStride, 54201e04c3fSmrg dstFb->FlipY); 543af69d88dSmrg if (!dstMap) { 544af69d88dSmrg ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 545af69d88dSmrg _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 546af69d88dSmrg return GL_TRUE; /* don't retry with slow path */ 547af69d88dSmrg } 5487117f1b4Smrg } 5497117f1b4Smrg 5507117f1b4Smrg for (row = 0; row < height; row++) { 551af69d88dSmrg /* memmove() in case of overlap */ 552af69d88dSmrg memmove(dstMap, srcMap, widthInBytes); 553af69d88dSmrg dstMap += dstRowStride; 554af69d88dSmrg srcMap += srcRowStride; 555af69d88dSmrg } 556af69d88dSmrg 557af69d88dSmrg ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 558af69d88dSmrg if (dstRb != srcRb) { 559af69d88dSmrg ctx->Driver.UnmapRenderbuffer(ctx, dstRb); 5607117f1b4Smrg } 5617117f1b4Smrg 5627117f1b4Smrg return GL_TRUE; 5637117f1b4Smrg} 5647117f1b4Smrg 5657117f1b4Smrg 566af69d88dSmrg/** 567af69d88dSmrg * Find/map the renderbuffer that we'll be reading from. 568af69d88dSmrg * The swrast_render_start() function only maps the drawing buffers, 569af69d88dSmrg * not the read buffer. 570af69d88dSmrg */ 571af69d88dSmrgstatic struct gl_renderbuffer * 572af69d88dSmrgmap_readbuffer(struct gl_context *ctx, GLenum type) 573af69d88dSmrg{ 574af69d88dSmrg struct gl_framebuffer *fb = ctx->ReadBuffer; 575af69d88dSmrg struct gl_renderbuffer *rb; 576af69d88dSmrg struct swrast_renderbuffer *srb; 577af69d88dSmrg 578af69d88dSmrg switch (type) { 579af69d88dSmrg case GL_COLOR: 580af69d88dSmrg rb = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer; 581af69d88dSmrg break; 582af69d88dSmrg case GL_DEPTH: 583af69d88dSmrg case GL_DEPTH_STENCIL: 584af69d88dSmrg rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 585af69d88dSmrg break; 586af69d88dSmrg case GL_STENCIL: 587af69d88dSmrg rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 588af69d88dSmrg break; 589af69d88dSmrg default: 590af69d88dSmrg return NULL; 591af69d88dSmrg } 592af69d88dSmrg 593af69d88dSmrg srb = swrast_renderbuffer(rb); 594af69d88dSmrg 595af69d88dSmrg if (!srb || srb->Map) { 596af69d88dSmrg /* no buffer, or buffer is mapped already, we're done */ 597af69d88dSmrg return NULL; 598af69d88dSmrg } 599af69d88dSmrg 600af69d88dSmrg ctx->Driver.MapRenderbuffer(ctx, rb, 601af69d88dSmrg 0, 0, rb->Width, rb->Height, 602af69d88dSmrg GL_MAP_READ_BIT, 60301e04c3fSmrg &srb->Map, &srb->RowStride, 60401e04c3fSmrg fb->FlipY); 605af69d88dSmrg 606af69d88dSmrg return rb; 607af69d88dSmrg} 608af69d88dSmrg 609af69d88dSmrg 6107117f1b4Smrg/** 6117117f1b4Smrg * Do software-based glCopyPixels. 6127117f1b4Smrg * By time we get here, all parameters will have been error-checked. 6137117f1b4Smrg */ 6147117f1b4Smrgvoid 61501e04c3fSmrg_swrast_CopyPixels(struct gl_context *ctx, 61601e04c3fSmrg GLint srcx, GLint srcy, GLsizei width, GLsizei height, 61701e04c3fSmrg GLint destx, GLint desty, GLenum type) 6187117f1b4Smrg{ 6197117f1b4Smrg SWcontext *swrast = SWRAST_CONTEXT(ctx); 620af69d88dSmrg struct gl_renderbuffer *rb; 6217ec681f3Smrg 622cdc920a0Smrg if (!_mesa_check_conditional_render(ctx)) 623cdc920a0Smrg return; /* don't copy */ 624cdc920a0Smrg 6257117f1b4Smrg if (swrast->NewState) 6267117f1b4Smrg _swrast_validate_derived( ctx ); 6277117f1b4Smrg 628af69d88dSmrg if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 || 62901e04c3fSmrg ctx->Pixel.ZoomX != 1.0F || 63001e04c3fSmrg ctx->Pixel.ZoomY != 1.0F || 63101e04c3fSmrg ctx->_ImageTransferState) && 63201e04c3fSmrg swrast_fast_copy_pixels(ctx, ctx->ReadBuffer, ctx->DrawBuffer, 63301e04c3fSmrg srcx, srcy, width, height, destx, desty, 63401e04c3fSmrg type)) { 635af69d88dSmrg /* all done */ 636af69d88dSmrg return; 637af69d88dSmrg } 638af69d88dSmrg 639af69d88dSmrg swrast_render_start(ctx); 640af69d88dSmrg rb = map_readbuffer(ctx, type); 641af69d88dSmrg 642af69d88dSmrg switch (type) { 643af69d88dSmrg case GL_COLOR: 644af69d88dSmrg copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty ); 645af69d88dSmrg break; 646af69d88dSmrg case GL_DEPTH: 647af69d88dSmrg copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty ); 648af69d88dSmrg break; 649af69d88dSmrg case GL_STENCIL: 650af69d88dSmrg copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty ); 651af69d88dSmrg break; 652af69d88dSmrg case GL_DEPTH_STENCIL_EXT: 653af69d88dSmrg /* Copy buffers separately (if the fast copy path wasn't taken) */ 654af69d88dSmrg copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty); 655af69d88dSmrg copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty); 656af69d88dSmrg break; 657af69d88dSmrg default: 658af69d88dSmrg _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels"); 6597117f1b4Smrg } 6607117f1b4Smrg 6614a49301eSmrg swrast_render_finish(ctx); 662af69d88dSmrg 663af69d88dSmrg if (rb) { 664af69d88dSmrg struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 665af69d88dSmrg ctx->Driver.UnmapRenderbuffer(ctx, rb); 666af69d88dSmrg srb->Map = NULL; 667af69d88dSmrg } 6687117f1b4Smrg} 669