1 /* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 26 #include "main/glheader.h" 27 #include "main/context.h" 28 #include "main/condrender.h" 29 #include "main/macros.h" 30 #include "main/blit.h" 31 #include "main/pixeltransfer.h" 32 #include "main/imports.h" 33 34 #include "s_context.h" 35 #include "s_depth.h" 36 #include "s_span.h" 37 #include "s_stencil.h" 38 #include "s_zoom.h" 39 40 41 42 /** 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. 48 */ 49 static GLboolean 50 regions_overlap(GLint srcx, GLint srcy, 51 GLint dstx, GLint dsty, 52 GLint width, GLint height, 53 GLfloat zoomX, GLfloat zoomY) 54 { 55 if (zoomX == 1.0F && zoomY == 1.0F) { 56 return _mesa_regions_overlap(srcx, srcy, srcx + width, srcy + height, 57 dstx, dsty, dstx + width, dsty + height); 58 } 59 else { 60 /* add one pixel of slop when zooming, just to be safe */ 61 if (srcx > (dstx + ((zoomX > 0.0F) ? (width * zoomX + 1.0F) : 0.0F))) { 62 /* src is completely right of dest */ 63 return GL_FALSE; 64 } 65 else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) { 66 /* src is completely left of dest */ 67 return GL_FALSE; 68 } 69 else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) { 70 /* src is completely below dest */ 71 return GL_FALSE; 72 } 73 else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) { 74 /* src is completely above dest */ 75 return GL_FALSE; 76 } 77 else { 78 return GL_TRUE; 79 } 80 } 81 } 82 83 84 /** 85 * RGBA copypixels 86 */ 87 static void 88 copy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, 89 GLint width, GLint height, GLint destx, GLint desty) 90 { 91 GLfloat *tmpImage, *p; 92 GLint sy, dy, stepy, row; 93 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 94 GLint overlapping; 95 GLuint transferOps = ctx->_ImageTransferState; 96 SWspan span; 97 98 if (!ctx->ReadBuffer->_ColorReadBuffer) { 99 /* no readbuffer - OK */ 100 return; 101 } 102 103 if (ctx->DrawBuffer == ctx->ReadBuffer) { 104 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 105 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 106 } 107 else { 108 overlapping = GL_FALSE; 109 } 110 111 /* Determine if copy should be done bottom-to-top or top-to-bottom */ 112 if (!overlapping && srcy < desty) { 113 /* top-down max-to-min */ 114 sy = srcy + height - 1; 115 dy = desty + height - 1; 116 stepy = -1; 117 } 118 else { 119 /* bottom-up min-to-max */ 120 sy = srcy; 121 dy = desty; 122 stepy = 1; 123 } 124 125 INIT_SPAN(span, GL_BITMAP); 126 _swrast_span_default_attribs(ctx, &span); 127 span.arrayMask = SPAN_RGBA; 128 span.arrayAttribs = VARYING_BIT_COL0; /* we'll fill in COL0 attrib values */ 129 130 if (overlapping) { 131 tmpImage = malloc(width * height * sizeof(GLfloat) * 4); 132 if (!tmpImage) { 133 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 134 return; 135 } 136 /* read the source image as RGBA/float */ 137 p = tmpImage; 138 for (row = 0; row < height; row++) { 139 _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer, 140 width, srcx, sy + row, p ); 141 p += width * 4; 142 } 143 p = tmpImage; 144 } 145 else { 146 tmpImage = NULL; /* silence compiler warnings */ 147 p = NULL; 148 } 149 150 assert(width < SWRAST_MAX_WIDTH); 151 152 for (row = 0; row < height; row++, sy += stepy, dy += stepy) { 153 GLvoid *rgba = span.array->attribs[VARYING_SLOT_COL0]; 154 155 /* Get row/span of source pixels */ 156 if (overlapping) { 157 /* get from buffered image */ 158 memcpy(rgba, p, width * sizeof(GLfloat) * 4); 159 p += width * 4; 160 } 161 else { 162 /* get from framebuffer */ 163 _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer, 164 width, srcx, sy, rgba ); 165 } 166 167 if (transferOps) { 168 _mesa_apply_rgba_transfer_ops(ctx, transferOps, width, 169 (GLfloat (*)[4]) rgba); 170 } 171 172 /* Write color span */ 173 span.x = destx; 174 span.y = dy; 175 span.end = width; 176 span.array->ChanType = GL_FLOAT; 177 if (zoom) { 178 _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba); 179 } 180 else { 181 _swrast_write_rgba_span(ctx, &span); 182 } 183 } 184 185 span.array->ChanType = CHAN_TYPE; /* restore */ 186 187 if (overlapping) 188 free(tmpImage); 189 } 190 191 192 /** 193 * Convert floating point Z values to integer Z values with pixel transfer's 194 * Z scale and bias. 195 */ 196 static void 197 scale_and_bias_z(struct gl_context *ctx, GLuint width, 198 const GLfloat depth[], GLuint z[]) 199 { 200 const GLuint depthMax = ctx->DrawBuffer->_DepthMax; 201 GLuint i; 202 203 if (depthMax <= 0xffffff && 204 ctx->Pixel.DepthScale == 1.0F && 205 ctx->Pixel.DepthBias == 0.0F) { 206 /* no scale or bias and no clamping and no worry of overflow */ 207 const GLfloat depthMaxF = ctx->DrawBuffer->_DepthMaxF; 208 for (i = 0; i < width; i++) { 209 z[i] = (GLuint) (depth[i] * depthMaxF); 210 } 211 } 212 else { 213 /* need to be careful with overflow */ 214 const GLdouble depthMaxF = ctx->DrawBuffer->_DepthMaxF; 215 for (i = 0; i < width; i++) { 216 GLdouble d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias; 217 d = CLAMP(d, 0.0, 1.0) * depthMaxF; 218 if (d >= depthMaxF) 219 z[i] = depthMax; 220 else 221 z[i] = (GLuint) d; 222 } 223 } 224 } 225 226 227 228 /* 229 * TODO: Optimize!!!! 230 */ 231 static void 232 copy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 233 GLint width, GLint height, 234 GLint destx, GLint desty ) 235 { 236 struct gl_framebuffer *fb = ctx->ReadBuffer; 237 struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 238 GLfloat *p, *tmpImage, *depth; 239 GLint sy, dy, stepy; 240 GLint j; 241 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 242 GLint overlapping; 243 SWspan span; 244 245 if (!readRb) { 246 /* no readbuffer - OK */ 247 return; 248 } 249 250 INIT_SPAN(span, GL_BITMAP); 251 _swrast_span_default_attribs(ctx, &span); 252 span.arrayMask = SPAN_Z; 253 254 if (ctx->DrawBuffer == ctx->ReadBuffer) { 255 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 256 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 257 } 258 else { 259 overlapping = GL_FALSE; 260 } 261 262 /* Determine if copy should be bottom-to-top or top-to-bottom */ 263 if (!overlapping && srcy < desty) { 264 /* top-down max-to-min */ 265 sy = srcy + height - 1; 266 dy = desty + height - 1; 267 stepy = -1; 268 } 269 else { 270 /* bottom-up min-to-max */ 271 sy = srcy; 272 dy = desty; 273 stepy = 1; 274 } 275 276 if (overlapping) { 277 GLint ssy = sy; 278 tmpImage = malloc(width * height * sizeof(GLfloat)); 279 if (!tmpImage) { 280 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 281 return; 282 } 283 p = tmpImage; 284 for (j = 0; j < height; j++, ssy += stepy) { 285 _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p); 286 p += width; 287 } 288 p = tmpImage; 289 } 290 else { 291 tmpImage = NULL; /* silence compiler warning */ 292 p = NULL; 293 } 294 295 depth = malloc(width * sizeof(GLfloat)); 296 if (!depth) { 297 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()"); 298 goto end; 299 } 300 301 for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 302 /* get depth values */ 303 if (overlapping) { 304 memcpy(depth, p, width * sizeof(GLfloat)); 305 p += width; 306 } 307 else { 308 _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth); 309 } 310 311 /* apply scale and bias */ 312 scale_and_bias_z(ctx, width, depth, span.array->z); 313 314 /* write depth values */ 315 span.x = destx; 316 span.y = dy; 317 span.end = width; 318 if (zoom) 319 _swrast_write_zoomed_depth_span(ctx, destx, desty, &span); 320 else 321 _swrast_write_rgba_span(ctx, &span); 322 } 323 324 free(depth); 325 326 end: 327 if (overlapping) 328 free(tmpImage); 329 } 330 331 332 333 static void 334 copy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 335 GLint width, GLint height, 336 GLint destx, GLint desty ) 337 { 338 struct gl_framebuffer *fb = ctx->ReadBuffer; 339 struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 340 GLint sy, dy, stepy; 341 GLint j; 342 GLubyte *p, *tmpImage, *stencil; 343 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 344 GLint overlapping; 345 346 if (!rb) { 347 /* no readbuffer - OK */ 348 return; 349 } 350 351 if (ctx->DrawBuffer == ctx->ReadBuffer) { 352 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 353 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 354 } 355 else { 356 overlapping = GL_FALSE; 357 } 358 359 /* Determine if copy should be bottom-to-top or top-to-bottom */ 360 if (!overlapping && srcy < desty) { 361 /* top-down max-to-min */ 362 sy = srcy + height - 1; 363 dy = desty + height - 1; 364 stepy = -1; 365 } 366 else { 367 /* bottom-up min-to-max */ 368 sy = srcy; 369 dy = desty; 370 stepy = 1; 371 } 372 373 if (overlapping) { 374 GLint ssy = sy; 375 tmpImage = malloc(width * height * sizeof(GLubyte)); 376 if (!tmpImage) { 377 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 378 return; 379 } 380 p = tmpImage; 381 for (j = 0; j < height; j++, ssy += stepy) { 382 _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p ); 383 p += width; 384 } 385 p = tmpImage; 386 } 387 else { 388 tmpImage = NULL; /* silence compiler warning */ 389 p = NULL; 390 } 391 392 stencil = malloc(width * sizeof(GLubyte)); 393 if (!stencil) { 394 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()"); 395 goto end; 396 } 397 398 for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 399 /* Get stencil values */ 400 if (overlapping) { 401 memcpy(stencil, p, width * sizeof(GLubyte)); 402 p += width; 403 } 404 else { 405 _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil ); 406 } 407 408 _mesa_apply_stencil_transfer_ops(ctx, width, stencil); 409 410 /* Write stencil values */ 411 if (zoom) { 412 _swrast_write_zoomed_stencil_span(ctx, destx, desty, width, 413 destx, dy, stencil); 414 } 415 else { 416 _swrast_write_stencil_span( ctx, width, destx, dy, stencil ); 417 } 418 } 419 420 free(stencil); 421 422 end: 423 if (overlapping) 424 free(tmpImage); 425 } 426 427 428 /** 429 * Try to do a fast 1:1 blit with memcpy. 430 * \return GL_TRUE if successful, GL_FALSE otherwise. 431 */ 432 GLboolean 433 swrast_fast_copy_pixels(struct gl_context *ctx, 434 struct gl_framebuffer *srcFb, 435 struct gl_framebuffer *dstFb, 436 GLint srcX, GLint srcY, GLsizei width, GLsizei height, 437 GLint dstX, GLint dstY, GLenum type) 438 { 439 struct gl_renderbuffer *srcRb, *dstRb; 440 GLint row; 441 GLuint pixelBytes, widthInBytes; 442 GLubyte *srcMap, *dstMap; 443 GLint srcRowStride, dstRowStride; 444 445 if (type == GL_COLOR) { 446 if (dstFb->_NumColorDrawBuffers != 1) 447 return GL_FALSE; 448 srcRb = srcFb->_ColorReadBuffer; 449 dstRb = dstFb->_ColorDrawBuffers[0]; 450 } 451 else if (type == GL_STENCIL) { 452 srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer; 453 dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer; 454 } 455 else if (type == GL_DEPTH) { 456 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 457 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 458 } 459 else { 460 assert(type == GL_DEPTH_STENCIL_EXT); 461 /* XXX correct? */ 462 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 463 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 464 } 465 466 /* src and dst renderbuffers must be same format */ 467 if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) { 468 return GL_FALSE; 469 } 470 471 if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) { 472 /* can't handle packed depth+stencil here */ 473 if (_mesa_is_format_packed_depth_stencil(srcRb->Format) || 474 _mesa_is_format_packed_depth_stencil(dstRb->Format)) 475 return GL_FALSE; 476 } 477 else if (type == GL_DEPTH_STENCIL) { 478 /* can't handle separate depth/stencil buffers */ 479 if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer || 480 dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer) 481 return GL_FALSE; 482 } 483 484 /* clipping not supported */ 485 if (srcX < 0 || srcX + width > (GLint) srcFb->Width || 486 srcY < 0 || srcY + height > (GLint) srcFb->Height || 487 dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax || 488 dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) { 489 return GL_FALSE; 490 } 491 492 pixelBytes = _mesa_get_format_bytes(srcRb->Format); 493 widthInBytes = width * pixelBytes; 494 495 if (srcRb == dstRb) { 496 /* map whole buffer for read/write */ 497 /* XXX we could be clever and just map the union region of the 498 * source and dest rects. 499 */ 500 GLubyte *map; 501 GLint rowStride; 502 503 ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0, 504 srcRb->Width, srcRb->Height, 505 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 506 &map, &rowStride); 507 if (!map) { 508 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 509 return GL_TRUE; /* don't retry with slow path */ 510 } 511 512 srcMap = map + srcY * rowStride + srcX * pixelBytes; 513 dstMap = map + dstY * rowStride + dstX * pixelBytes; 514 515 /* this handles overlapping copies */ 516 if (srcY < dstY) { 517 /* copy in reverse (top->down) order */ 518 srcMap += rowStride * (height - 1); 519 dstMap += rowStride * (height - 1); 520 srcRowStride = -rowStride; 521 dstRowStride = -rowStride; 522 } 523 else { 524 /* copy in normal (bottom->up) order */ 525 srcRowStride = rowStride; 526 dstRowStride = rowStride; 527 } 528 } 529 else { 530 /* different src/dst buffers */ 531 ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY, 532 width, height, 533 GL_MAP_READ_BIT, &srcMap, &srcRowStride); 534 if (!srcMap) { 535 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 536 return GL_TRUE; /* don't retry with slow path */ 537 } 538 ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY, 539 width, height, 540 GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); 541 if (!dstMap) { 542 ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 543 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 544 return GL_TRUE; /* don't retry with slow path */ 545 } 546 } 547 548 for (row = 0; row < height; row++) { 549 /* memmove() in case of overlap */ 550 memmove(dstMap, srcMap, widthInBytes); 551 dstMap += dstRowStride; 552 srcMap += srcRowStride; 553 } 554 555 ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 556 if (dstRb != srcRb) { 557 ctx->Driver.UnmapRenderbuffer(ctx, dstRb); 558 } 559 560 return GL_TRUE; 561 } 562 563 564 /** 565 * Find/map the renderbuffer that we'll be reading from. 566 * The swrast_render_start() function only maps the drawing buffers, 567 * not the read buffer. 568 */ 569 static struct gl_renderbuffer * 570 map_readbuffer(struct gl_context *ctx, GLenum type) 571 { 572 struct gl_framebuffer *fb = ctx->ReadBuffer; 573 struct gl_renderbuffer *rb; 574 struct swrast_renderbuffer *srb; 575 576 switch (type) { 577 case GL_COLOR: 578 rb = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer; 579 break; 580 case GL_DEPTH: 581 case GL_DEPTH_STENCIL: 582 rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 583 break; 584 case GL_STENCIL: 585 rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 586 break; 587 default: 588 return NULL; 589 } 590 591 srb = swrast_renderbuffer(rb); 592 593 if (!srb || srb->Map) { 594 /* no buffer, or buffer is mapped already, we're done */ 595 return NULL; 596 } 597 598 ctx->Driver.MapRenderbuffer(ctx, rb, 599 0, 0, rb->Width, rb->Height, 600 GL_MAP_READ_BIT, 601 &srb->Map, &srb->RowStride); 602 603 return rb; 604 } 605 606 607 /** 608 * Do software-based glCopyPixels. 609 * By time we get here, all parameters will have been error-checked. 610 */ 611 void 612 _swrast_CopyPixels(struct gl_context *ctx, 613 GLint srcx, GLint srcy, GLsizei width, GLsizei height, 614 GLint destx, GLint desty, GLenum type) 615 { 616 SWcontext *swrast = SWRAST_CONTEXT(ctx); 617 struct gl_renderbuffer *rb; 618 619 if (!_mesa_check_conditional_render(ctx)) 620 return; /* don't copy */ 621 622 if (swrast->NewState) 623 _swrast_validate_derived( ctx ); 624 625 if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 || 626 ctx->Pixel.ZoomX != 1.0F || 627 ctx->Pixel.ZoomY != 1.0F || 628 ctx->_ImageTransferState) && 629 swrast_fast_copy_pixels(ctx, ctx->ReadBuffer, ctx->DrawBuffer, 630 srcx, srcy, width, height, destx, desty, 631 type)) { 632 /* all done */ 633 return; 634 } 635 636 swrast_render_start(ctx); 637 rb = map_readbuffer(ctx, type); 638 639 switch (type) { 640 case GL_COLOR: 641 copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty ); 642 break; 643 case GL_DEPTH: 644 copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty ); 645 break; 646 case GL_STENCIL: 647 copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty ); 648 break; 649 case GL_DEPTH_STENCIL_EXT: 650 /* Copy buffers separately (if the fast copy path wasn't taken) */ 651 copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty); 652 copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty); 653 break; 654 default: 655 _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels"); 656 } 657 658 swrast_render_finish(ctx); 659 660 if (rb) { 661 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 662 ctx->Driver.UnmapRenderbuffer(ctx, rb); 663 srb->Map = NULL; 664 } 665 } 666