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 */
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.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 */
87static void
88copy_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 */
196static void
197scale_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 */
231static void
232copy_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
326end:
327   if (overlapping)
328      free(tmpImage);
329}
330
331
332
333static void
334copy_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
422end:
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 */
432GLboolean
433swrast_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 */
569static struct gl_renderbuffer *
570map_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 */
611void
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