cf419c328d65e570b0a351e2c54917c713e367dc
[mesa.git] / src / mesa / swrast / s_copypix.c
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 }