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 < 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 */
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, *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
337end:
338   if (overlapping)
339      free(tmpImage);
340}
341
342
343
344static void
345copy_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
433end:
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 */
443GLboolean
444swrast_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 */
580static struct gl_renderbuffer *
581map_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 */
622void
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