s_copypix.c revision 267fb178844d3f17503dd0f921791f3ab059c4e7
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 */ 49static GLboolean 50regions_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 */ 98static void 99copy_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 < 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 */ 207static void 208scale_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 */ 242static void 243copy_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; 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 for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 307 GLfloat depth[MAX_WIDTH]; 308 /* get depth values */ 309 if (overlapping) { 310 memcpy(depth, p, width * sizeof(GLfloat)); 311 p += width; 312 } 313 else { 314 _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth); 315 } 316 317 /* apply scale and bias */ 318 scale_and_bias_z(ctx, width, depth, span.array->z); 319 320 /* write depth values */ 321 span.x = destx; 322 span.y = dy; 323 span.end = width; 324 if (zoom) 325 _swrast_write_zoomed_depth_span(ctx, destx, desty, &span); 326 else 327 _swrast_write_rgba_span(ctx, &span); 328 } 329 330 if (overlapping) 331 free(tmpImage); 332} 333 334 335 336static void 337copy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy, 338 GLint width, GLint height, 339 GLint destx, GLint desty ) 340{ 341 struct gl_framebuffer *fb = ctx->ReadBuffer; 342 struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 343 GLint sy, dy, stepy; 344 GLint j; 345 GLubyte *p, *tmpImage; 346 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F; 347 GLint overlapping; 348 349 if (!rb) { 350 /* no readbuffer - OK */ 351 return; 352 } 353 354 if (ctx->DrawBuffer == ctx->ReadBuffer) { 355 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height, 356 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY); 357 } 358 else { 359 overlapping = GL_FALSE; 360 } 361 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; 367 stepy = -1; 368 } 369 else { 370 /* bottom-up min-to-max */ 371 sy = srcy; 372 dy = desty; 373 stepy = 1; 374 } 375 376 if (overlapping) { 377 GLint ssy = sy; 378 tmpImage = (GLubyte *) malloc(width * height * sizeof(GLubyte)); 379 if (!tmpImage) { 380 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" ); 381 return; 382 } 383 p = tmpImage; 384 for (j = 0; j < height; j++, ssy += stepy) { 385 _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p ); 386 p += width; 387 } 388 p = tmpImage; 389 } 390 else { 391 tmpImage = NULL; /* silence compiler warning */ 392 p = NULL; 393 } 394 395 for (j = 0; j < height; j++, sy += stepy, dy += stepy) { 396 GLubyte stencil[MAX_WIDTH]; 397 398 /* Get stencil values */ 399 if (overlapping) { 400 memcpy(stencil, p, width * sizeof(GLubyte)); 401 p += width; 402 } 403 else { 404 _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil ); 405 } 406 407 _mesa_apply_stencil_transfer_ops(ctx, width, stencil); 408 409 /* Write stencil values */ 410 if (zoom) { 411 _swrast_write_zoomed_stencil_span(ctx, destx, desty, width, 412 destx, dy, stencil); 413 } 414 else { 415 _swrast_write_stencil_span( ctx, width, destx, dy, stencil ); 416 } 417 } 418 419 if (overlapping) 420 free(tmpImage); 421} 422 423 424/** 425 * Try to do a fast 1:1 blit with memcpy. 426 * \return GL_TRUE if successful, GL_FALSE otherwise. 427 */ 428GLboolean 429swrast_fast_copy_pixels(struct gl_context *ctx, 430 GLint srcX, GLint srcY, GLsizei width, GLsizei height, 431 GLint dstX, GLint dstY, GLenum type) 432{ 433 struct gl_framebuffer *srcFb = ctx->ReadBuffer; 434 struct gl_framebuffer *dstFb = ctx->DrawBuffer; 435 struct gl_renderbuffer *srcRb, *dstRb; 436 GLint row; 437 GLuint pixelBytes, widthInBytes; 438 GLubyte *srcMap, *dstMap; 439 GLint srcRowStride, dstRowStride; 440 441 if (type == GL_COLOR) { 442 if (dstFb->_NumColorDrawBuffers != 1) 443 return GL_FALSE; 444 srcRb = srcFb->_ColorReadBuffer; 445 dstRb = dstFb->_ColorDrawBuffers[0]; 446 } 447 else if (type == GL_STENCIL) { 448 srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer; 449 dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer; 450 } 451 else if (type == GL_DEPTH) { 452 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 453 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 454 } 455 else { 456 ASSERT(type == GL_DEPTH_STENCIL_EXT); 457 /* XXX correct? */ 458 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer; 459 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer; 460 } 461 462 /* src and dst renderbuffers must be same format */ 463 if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) { 464 return GL_FALSE; 465 } 466 467 if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) { 468 /* can't handle packed depth+stencil here */ 469 if (_mesa_is_format_packed_depth_stencil(srcRb->Format) || 470 _mesa_is_format_packed_depth_stencil(dstRb->Format)) 471 return GL_FALSE; 472 } 473 else if (type == GL_DEPTH_STENCIL) { 474 /* can't handle separate depth/stencil buffers */ 475 if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer || 476 dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer) 477 return GL_FALSE; 478 } 479 480 /* clipping not supported */ 481 if (srcX < 0 || srcX + width > (GLint) srcFb->Width || 482 srcY < 0 || srcY + height > (GLint) srcFb->Height || 483 dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax || 484 dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) { 485 return GL_FALSE; 486 } 487 488 pixelBytes = _mesa_get_format_bytes(srcRb->Format); 489 widthInBytes = width * pixelBytes; 490 491 if (srcRb == dstRb) { 492 /* map whole buffer for read/write */ 493 /* XXX we could be clever and just map the union region of the 494 * source and dest rects. 495 */ 496 GLubyte *map; 497 GLint rowStride; 498 499 ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0, 500 srcRb->Width, srcRb->Height, 501 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 502 &map, &rowStride); 503 if (!map) { 504 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 505 return GL_TRUE; /* don't retry with slow path */ 506 } 507 508 srcMap = map + srcY * rowStride + srcX * pixelBytes; 509 dstMap = map + dstY * rowStride + dstX * pixelBytes; 510 511 /* this handles overlapping copies */ 512 if (srcY < dstY) { 513 /* copy in reverse (top->down) order */ 514 srcMap += rowStride * (height - 1); 515 dstMap += rowStride * (height - 1); 516 srcRowStride = -rowStride; 517 dstRowStride = -rowStride; 518 } 519 else { 520 /* copy in normal (bottom->up) order */ 521 srcRowStride = rowStride; 522 dstRowStride = rowStride; 523 } 524 } 525 else { 526 /* different src/dst buffers */ 527 ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY, 528 width, height, 529 GL_MAP_READ_BIT, &srcMap, &srcRowStride); 530 if (!srcMap) { 531 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 532 return GL_TRUE; /* don't retry with slow path */ 533 } 534 ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY, 535 width, height, 536 GL_MAP_WRITE_BIT, &dstMap, &dstRowStride); 537 if (!dstMap) { 538 ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 539 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels"); 540 return GL_TRUE; /* don't retry with slow path */ 541 } 542 } 543 544 for (row = 0; row < height; row++) { 545 /* memmove() in case of overlap */ 546 memmove(dstMap, srcMap, widthInBytes); 547 dstMap += dstRowStride; 548 srcMap += srcRowStride; 549 } 550 551 ctx->Driver.UnmapRenderbuffer(ctx, srcRb); 552 if (dstRb != srcRb) { 553 ctx->Driver.UnmapRenderbuffer(ctx, dstRb); 554 } 555 556 return GL_TRUE; 557} 558 559 560/** 561 * Do software-based glCopyPixels. 562 * By time we get here, all parameters will have been error-checked. 563 */ 564void 565_swrast_CopyPixels( struct gl_context *ctx, 566 GLint srcx, GLint srcy, GLsizei width, GLsizei height, 567 GLint destx, GLint desty, GLenum type ) 568{ 569 SWcontext *swrast = SWRAST_CONTEXT(ctx); 570 571 if (!_mesa_check_conditional_render(ctx)) 572 return; /* don't copy */ 573 574 if (swrast->NewState) 575 _swrast_validate_derived( ctx ); 576 577 if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 || 578 ctx->Pixel.ZoomX != 1.0F || 579 ctx->Pixel.ZoomY != 1.0F || 580 ctx->_ImageTransferState) && 581 swrast_fast_copy_pixels(ctx, srcx, srcy, width, height, destx, desty, 582 type)) { 583 /* all done */ 584 return; 585 } 586 587 swrast_render_start(ctx); 588 589 switch (type) { 590 case GL_COLOR: 591 copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty ); 592 break; 593 case GL_DEPTH: 594 copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty ); 595 break; 596 case GL_STENCIL: 597 copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty ); 598 break; 599 case GL_DEPTH_STENCIL_EXT: 600 /* Copy buffers separately (if the fast copy path wasn't taken) */ 601 copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty); 602 copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty); 603 break; 604 default: 605 _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels"); 606 } 607 608 swrast_render_finish(ctx); 609} 610