1 /* 2 * Mesa 3-D graphics library 3 * Version: 7.1 4 * 5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 6 * 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: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 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. 23 */ 24 25 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" 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.0 && zoomY == 1.0) { 56 /* no zoom */ 57 if (srcx >= dstx + width || (srcx + width <= dstx)) { 58 return GL_FALSE; 59 } 60 else if (srcy < dsty) { /* this is OK */ 61 return GL_FALSE; 62 } 63 else if (srcy > dsty + height) { 64 return GL_FALSE; 65 } 66 else { 67 return GL_TRUE; 68 } 69 } 70 else { 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 */ 74 return GL_FALSE; 75 } 76 else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) { 77 /* src is completely left of dest */ 78 return GL_FALSE; 79 } 80 else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) { 81 /* src is completely below dest */ 82 return GL_FALSE; 83 } 84 else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) { 85 /* src is completely above dest */ 86 return GL_FALSE; 87 } 88 else { 89 return GL_TRUE; 90 } 91 } 92 } 93 94 95 /** 96 * RGBA copypixels 97 */ 98 static void 99 copy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy, 100 GLint width, GLint height, GLint destx, GLint desty) 101 { 102 GLfloat *tmpImage, *p; 103 GLint sy, dy, stepy, row; 104 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 105 GLint overlapping; 106 GLuint transferOps = ctx->_ImageTransferState; 107 SWspan span; 108 109 if (!ctx->ReadBuffer->_ColorReadBuffer) { 110 /* no readbuffer - OK */ 111 return; 112 } 113 114 if (ctx->DrawBuffer == ctx->ReadBuffer) { 115 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 116 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 117 } 118 else { 119 overlapping = GL_FALSE; 120 } 121 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; 127 stepy = -1; 128 } 129 else { 130 /* bottom-up min-to-max */ 131 sy = srcy; 132 dy = desty; 133 stepy = 1; 134 } 135 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 */ 140 141 if (overlapping) { 142 tmpImage = (GLfloat *) malloc(width * height * sizeof(GLfloat) * 4); 143 if (!tmpImage) { 144 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 145 return; 146 } 147 /* read the source image as RGBA/float */ 148 p = tmpImage; 149 for (row = 0; row < height; row++) { 150 _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer, 151 width, srcx, sy + row, p ); 152 p += width * 4; 153 } 154 p = tmpImage; 155 } 156 else { 157 tmpImage = NULL; /* silence compiler warnings */ 158 p = NULL; 159 } 160 161 ASSERT(width < SWRAST_MAX_WIDTH); 162 163 for (row = 0; row < height; row++, sy += stepy, dy += stepy) { 164 GLvoid *rgba = span.array->attribs[FRAG_ATTRIB_COL0]; 165 166 /* Get row/span of source pixels */ 167 if (overlapping) { 168 /* get from buffered image */ 169 memcpy(rgba, p, width * sizeof(GLfloat) * 4); 170 p += width * 4; 171 } 172 else { 173 /* get from framebuffer */ 174 _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer, 175 width, srcx, sy, rgba ); 176 } 177 178 if (transferOps) { 179 _mesa_apply_rgba_transfer_ops(ctx, transferOps, width, 180 (GLfloat (*)[4]) rgba); 181 } 182 183 /* Write color span */ 184 span.x = destx; 185 span.y = dy; 186 span.end = width; 187 span.array->ChanType = GL_FLOAT; 188 if (zoom) { 189 _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba); 190 } 191 else { 192 _swrast_write_rgba_span(ctx, &span); 193 } 194 } 195 196 span.array->ChanType = CHAN_TYPE; /* restore */ 197 198 if (overlapping) 199 free(tmpImage); 200 } 201 202 203 /** 204 * Convert floating point Z values to integer Z values with pixel transfer's 205 * Z scale and bias. 206 */ 207 static void 208 scale_and_bias_z(struct gl_context *ctx, GLuint width, 209 const GLfloat depth[], GLuint z[]) 210 { 211 const GLuint depthMax = ctx->DrawBuffer->_DepthMax; 212 GLuint i; 213 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); 221 } 222 } 223 else { 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; 229 if (d >= depthMaxF) 230 z[i] = depthMax; 231 else 232 z[i] = (GLuint) d; 233 } 234 } 235 } 236 237 238 239 /* 240 * TODO: Optimize!!!! 241 */ 242 static void 243 copy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 244 GLint width, GLint height, 245 GLint destx, GLint desty ) 246 { 247 struct gl_framebuffer *fb = ctx->ReadBuffer; 248 struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 249 GLfloat *p, *tmpImage, *depth; 250 GLint sy, dy, stepy; 251 GLint j; 252 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 253 GLint overlapping; 254 SWspan span; 255 256 if (!readRb) { 257 /* no readbuffer - OK */ 258 return; 259 } 260 261 INIT_SPAN(span, GL_BITMAP); 262 _swrast_span_default_attribs(ctx, &span); 263 span.arrayMask = SPAN_Z; 264 265 if (ctx->DrawBuffer == ctx->ReadBuffer) { 266 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 267 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 268 } 269 else { 270 overlapping = GL_FALSE; 271 } 272 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; 278 stepy = -1; 279 } 280 else { 281 /* bottom-up min-to-max */ 282 sy = srcy; 283 dy = desty; 284 stepy = 1; 285 } 286 287 if (overlapping) { 288 GLint ssy = sy; 289 tmpImage = (GLfloat *) malloc(width * height * sizeof(GLfloat)); 290 if (!tmpImage) { 291 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 292 return; 293 } 294 p = tmpImage; 295 for (j = 0; j < height; j++, ssy += stepy) { 296 _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p); 297 p += width; 298 } 299 p = tmpImage; 300 } 301 else { 302 tmpImage = NULL; /* silence compiler warning */ 303 p = NULL; 304 } 305 306 depth = (GLfloat *) malloc(width * sizeof(GLfloat)); 307 if (!depth) { 308 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()"); 309 goto end; 310 } 311 312 for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 313 /* get depth values */ 314 if (overlapping) { 315 memcpy(depth, p, width * sizeof(GLfloat)); 316 p += width; 317 } 318 else { 319 _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth); 320 } 321 322 /* apply scale and bias */ 323 scale_and_bias_z(ctx, width, depth, span.array->z); 324 325 /* write depth values */ 326 span.x = destx; 327 span.y = dy; 328 span.end = width; 329 if (zoom) 330 _swrast_write_zoomed_depth_span(ctx, destx, desty, &span); 331 else 332 _swrast_write_rgba_span(ctx, &span); 333 } 334 335 free(depth); 336 337 end: 338 if (overlapping) 339 free(tmpImage); 340 } 341 342 343 344 static void 345 copy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 346 GLint width, GLint height, 347 GLint destx, GLint desty ) 348 { 349 struct gl_framebuffer *fb = ctx->ReadBuffer; 350 struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 351 GLint sy, dy, stepy; 352 GLint j; 353 GLubyte *p, *tmpImage, *stencil; 354 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 355 GLint overlapping; 356 357 if (!rb) { 358 /* no readbuffer - OK */ 359 return; 360 } 361 362 if (ctx->DrawBuffer == ctx->ReadBuffer) { 363 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 364 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 365 } 366 else { 367 overlapping = GL_FALSE; 368 } 369 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; 375 stepy = -1; 376 } 377 else { 378 /* bottom-up min-to-max */ 379 sy = srcy; 380 dy = desty; 381 stepy = 1; 382 } 383 384 if (overlapping) { 385 GLint ssy = sy; 386 tmpImage = (GLubyte *) malloc(width * height * sizeof(GLubyte)); 387 if (!tmpImage) { 388 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 389 return; 390 } 391 p = tmpImage; 392 for (j = 0; j < height; j++, ssy += stepy) { 393 _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p ); 394 p += width; 395 } 396 p = tmpImage; 397 } 398 else { 399 tmpImage = NULL; /* silence compiler warning */ 400 p = NULL; 401 } 402 403 stencil = (GLubyte *) malloc(width * sizeof(GLubyte)); 404 if (!stencil) { 405 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()"); 406 goto end; 407 } 408 409 for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 410 /* Get stencil values */ 411 if (overlapping) { 412 memcpy(stencil, p, width * sizeof(GLubyte)); 413 p += width; 414 } 415 else { 416 _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil ); 417 } 418 419 _mesa_apply_stencil_transfer_ops(ctx, width, stencil); 420 421 /* Write stencil values */ 422 if (zoom) { 423 _swrast_write_zoomed_stencil_span(ctx, destx, desty, width, 424 destx, dy, stencil); 425 } 426 else { 427 _swrast_write_stencil_span( ctx, width, destx, dy, stencil ); 428 } 429 } 430 431 free(stencil); 432 433 end: 434 if (overlapping) 435 free(tmpImage); 436 } 437 438 439 /** 440 * Try to do a fast 1:1 blit with memcpy. 441 * \return GL_TRUE if successful, GL_FALSE otherwise. 442 */ 443 GLboolean 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) 447 { 448 struct gl_framebuffer *srcFb = ctx->ReadBuffer; 449 struct gl_framebuffer *dstFb = ctx->DrawBuffer; 450 struct gl_renderbuffer *srcRb, *dstRb; 451 GLint row; 452 GLuint pixelBytes, widthInBytes; 453 GLubyte *srcMap, *dstMap; 454 GLint srcRowStride, dstRowStride; 455 456 if (type == GL_COLOR) { 457 if (dstFb->_NumColorDrawBuffers != 1) 458 return GL_FALSE; 459 srcRb = srcFb->_ColorReadBuffer; 460 dstRb = dstFb->_ColorDrawBuffers[0]; 461 } 462 else if (type == GL_STENCIL) { 463 srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer; 464 dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer; 465 } 466 else if (type == GL_DEPTH) { 467 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 468 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 469 } 470 else { 471 ASSERT(type == GL_DEPTH_STENCIL_EXT); 472 /* XXX correct? */ 473 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 474 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 475 } 476 477 /* src and dst renderbuffers must be same format */ 478 if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) { 479 return GL_FALSE; 480 } 481 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)) 486 return GL_FALSE; 487 } 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) 492 return GL_FALSE; 493 } 494 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) { 500 return GL_FALSE; 501 } 502 503 pixelBytes = _mesa_get_format_bytes(srcRb->Format); 504 widthInBytes = width * pixelBytes; 505 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. 510 */ 511 GLubyte *map; 512 GLint rowStride; 513 514 ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0, 515 srcRb->Width, srcRb->Height, 516 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 517 &map, &rowStride); 518 if (!map) { 519 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 520 return GL_TRUE; /* don't retry with slow path */ 521 } 522 523 srcMap = map + srcY * rowStride + srcX * pixelBytes; 524 dstMap = map + dstY * rowStride + dstX * pixelBytes; 525 526 /* this handles overlapping copies */ 527 if (srcY < dstY) { 528 /* copy in reverse (top->down) order */ 529 srcMap += rowStride * (height - 1); 530 dstMap += rowStride * (height - 1); 531 srcRowStride = -rowStride; 532 dstRowStride = -rowStride; 533 } 534 else { 535 /* copy in normal (bottom->up) order */ 536 srcRowStride = rowStride; 537 dstRowStride = rowStride; 538 } 539 } 540 else { 541 /* different src/dst buffers */ 542 ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY, 543 width, height, 544 GL_MAP_READ_BIT, &srcMap, &srcRowStride); 545 if (!srcMap) { 546 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 547 return GL_TRUE; /* don't retry with slow path */ 548 } 549 ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY, 550 width, height, 551 GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); 552 if (!dstMap) { 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 */ 556 } 557 } 558 559 for (row = 0; row < height; row++) { 560 /* memmove() in case of overlap */ 561 memmove(dstMap, srcMap, widthInBytes); 562 dstMap += dstRowStride; 563 srcMap += srcRowStride; 564 } 565 566 ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 567 if (dstRb != srcRb) { 568 ctx->Driver.UnmapRenderbuffer(ctx, dstRb); 569 } 570 571 return GL_TRUE; 572 } 573 574 575 /** 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. 579 */ 580 static struct gl_renderbuffer * 581 map_readbuffer(struct gl_context *ctx, GLenum type) 582 { 583 struct gl_framebuffer *fb = ctx->ReadBuffer; 584 struct gl_renderbuffer *rb; 585 struct swrast_renderbuffer *srb; 586 587 switch (type) { 588 case GL_COLOR: 589 rb = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer; 590 break; 591 case GL_DEPTH: 592 case GL_DEPTH_STENCIL: 593 rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 594 break; 595 case GL_STENCIL: 596 rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 597 break; 598 default: 599 return NULL; 600 } 601 602 srb = swrast_renderbuffer(rb); 603 604 if (!srb || srb->Map) { 605 /* no buffer, or buffer is mapped already, we're done */ 606 return NULL; 607 } 608 609 ctx->Driver.MapRenderbuffer(ctx, rb, 610 0, 0, rb->Width, rb->Height, 611 GL_MAP_READ_BIT, 612 &srb->Map, &srb->RowStride); 613 614 return rb; 615 } 616 617 618 /** 619 * Do software-based glCopyPixels. 620 * By time we get here, all parameters will have been error-checked. 621 */ 622 void 623 _swrast_CopyPixels( struct gl_context *ctx, 624 GLint srcx, GLint srcy, GLsizei width, GLsizei height, 625 GLint destx, GLint desty, GLenum type ) 626 { 627 SWcontext *swrast = SWRAST_CONTEXT(ctx); 628 struct gl_renderbuffer *rb; 629 630 if (!_mesa_check_conditional_render(ctx)) 631 return; /* don't copy */ 632 633 if (swrast->NewState) 634 _swrast_validate_derived( ctx ); 635 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, 641 type)) { 642 /* all done */ 643 return; 644 } 645 646 swrast_render_start(ctx); 647 rb = map_readbuffer(ctx, type); 648 649 switch (type) { 650 case GL_COLOR: 651 copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty ); 652 break; 653 case GL_DEPTH: 654 copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty ); 655 break; 656 case GL_STENCIL: 657 copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty ); 658 break; 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); 663 break; 664 default: 665 _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels"); 666 } 667 668 swrast_render_finish(ctx); 669 670 if (rb) { 671 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 672 ctx->Driver.UnmapRenderbuffer(ctx, rb); 673 srb->Map = NULL; 674 } 675 } 676