s_copypix.c revision e6c6b1c147f5079a1608234294e2b6cd29dd2a64
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, GL_FLOAT, 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, GL_FLOAT, 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 * This isn't terribly efficient.  If a driver really has combined
426 * depth/stencil buffers the driver should implement an optimized
427 * CopyPixels function.
428 */
429static void
430copy_depth_stencil_pixels(struct gl_context *ctx,
431                          const GLint srcX, const GLint srcY,
432                          const GLint width, const GLint height,
433                          const GLint destX, const GLint destY)
434{
435   struct gl_renderbuffer *stencilReadRb, *depthReadRb, *depthDrawRb;
436   GLint sy, dy, stepy;
437   GLint j;
438   GLubyte *tempStencilImage = NULL, *stencilPtr = NULL;
439   GLfloat *tempDepthImage = NULL, *depthPtr = NULL;
440   const GLfloat depthScale = ctx->DrawBuffer->_DepthMaxF;
441   const GLuint stencilMask = ctx->Stencil.WriteMask[0];
442   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
443   const GLboolean scaleOrBias
444      = ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0;
445   GLint overlapping;
446
447   depthDrawRb = ctx->DrawBuffer->_DepthBuffer;
448   depthReadRb = ctx->ReadBuffer->_DepthBuffer;
449   stencilReadRb = ctx->ReadBuffer->Attachment[BUFFER_STENCIL].Renderbuffer;
450
451   ASSERT(depthDrawRb);
452   ASSERT(depthReadRb);
453   ASSERT(stencilReadRb);
454
455   if (ctx->DrawBuffer == ctx->ReadBuffer) {
456      overlapping = regions_overlap(srcX, srcY, destX, destY, width, height,
457                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
458   }
459   else {
460      overlapping = GL_FALSE;
461   }
462
463   /* Determine if copy should be bottom-to-top or top-to-bottom */
464   if (!overlapping && srcY < destY) {
465      /* top-down  max-to-min */
466      sy = srcY + height - 1;
467      dy = destY + height - 1;
468      stepy = -1;
469   }
470   else {
471      /* bottom-up  min-to-max */
472      sy = srcY;
473      dy = destY;
474      stepy = 1;
475   }
476
477   if (overlapping) {
478      GLint ssy = sy;
479
480      if (stencilMask != 0x0) {
481         tempStencilImage
482            = (GLubyte *) malloc(width * height * sizeof(GLubyte));
483         if (!tempStencilImage) {
484            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
485            return;
486         }
487
488         /* get copy of stencil pixels */
489         stencilPtr = tempStencilImage;
490         for (j = 0; j < height; j++, ssy += stepy) {
491            _swrast_read_stencil_span(ctx, stencilReadRb,
492                                      width, srcX, ssy, stencilPtr);
493            stencilPtr += width;
494         }
495         stencilPtr = tempStencilImage;
496      }
497
498      if (ctx->Depth.Mask) {
499         tempDepthImage
500            = (GLfloat *) malloc(width * height * sizeof(GLfloat));
501         if (!tempDepthImage) {
502            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
503            free(tempStencilImage);
504            return;
505         }
506
507         /* get copy of depth pixels */
508         depthPtr = tempDepthImage;
509         for (j = 0; j < height; j++, ssy += stepy) {
510            _swrast_read_depth_span_float(ctx, depthReadRb,
511                                          width, srcX, ssy, depthPtr);
512            depthPtr += width;
513         }
514         depthPtr = tempDepthImage;
515      }
516   }
517
518   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
519      if (stencilMask != 0x0) {
520         GLubyte stencil[MAX_WIDTH];
521
522         /* Get stencil values */
523         if (overlapping) {
524            memcpy(stencil, stencilPtr, width * sizeof(GLubyte));
525            stencilPtr += width;
526         }
527         else {
528            _swrast_read_stencil_span(ctx, stencilReadRb,
529                                      width, srcX, sy, stencil);
530         }
531
532         _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
533
534         /* Write values */
535         if (zoom) {
536            _swrast_write_zoomed_stencil_span(ctx, destX, destY, width,
537                                              destX, dy, stencil);
538         }
539         else {
540            _swrast_write_stencil_span( ctx, width, destX, dy, stencil );
541         }
542      }
543
544      if (ctx->Depth.Mask) {
545         GLfloat depth[MAX_WIDTH];
546         GLuint zVals32[MAX_WIDTH];
547         GLushort zVals16[MAX_WIDTH];
548         GLvoid *zVals;
549         GLuint zBytes;
550
551         /* get depth values */
552         if (overlapping) {
553            memcpy(depth, depthPtr, width * sizeof(GLfloat));
554            depthPtr += width;
555         }
556         else {
557            _swrast_read_depth_span_float(ctx, depthReadRb,
558                                          width, srcX, sy, depth);
559         }
560
561         /* scale & bias */
562         if (scaleOrBias) {
563            _mesa_scale_and_bias_depth(ctx, width, depth);
564         }
565         /* convert to integer Z values */
566         if (depthDrawRb->DataType == GL_UNSIGNED_SHORT) {
567            GLint k;
568            for (k = 0; k < width; k++)
569               zVals16[k] = (GLushort) (depth[k] * depthScale);
570            zVals = zVals16;
571            zBytes = 2;
572         }
573         else {
574            GLint k;
575            for (k = 0; k < width; k++)
576               zVals32[k] = (GLuint) (depth[k] * depthScale);
577            zVals = zVals32;
578            zBytes = 4;
579         }
580
581         /* Write values */
582         if (zoom) {
583            _swrast_write_zoomed_z_span(ctx, destX, destY, width,
584                                        destX, dy, zVals);
585         }
586         else {
587            _swrast_put_row(ctx, depthDrawRb, width, destX, dy, zVals, zBytes);
588         }
589      }
590   }
591
592   if (tempStencilImage)
593      free(tempStencilImage);
594
595   if (tempDepthImage)
596      free(tempDepthImage);
597}
598
599
600
601/**
602 * Try to do a fast copy pixels with memcpy.
603 * \return GL_TRUE if successful, GL_FALSE otherwise.
604 */
605static GLboolean
606fast_copy_pixels(struct gl_context *ctx,
607                 GLint srcX, GLint srcY, GLsizei width, GLsizei height,
608                 GLint dstX, GLint dstY, GLenum type)
609{
610   struct gl_framebuffer *srcFb = ctx->ReadBuffer;
611   struct gl_framebuffer *dstFb = ctx->DrawBuffer;
612   struct gl_renderbuffer *srcRb, *dstRb;
613   GLint row;
614   GLuint pixelBytes, widthInBytes;
615   GLubyte *srcMap, *dstMap;
616   GLint srcRowStride, dstRowStride;
617
618   if (SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
619       ctx->Pixel.ZoomX != 1.0F ||
620       ctx->Pixel.ZoomY != 1.0F ||
621       ctx->_ImageTransferState) {
622      /* can't handle these */
623      return GL_FALSE;
624   }
625
626   if (type == GL_COLOR) {
627      if (dstFb->_NumColorDrawBuffers != 1)
628         return GL_FALSE;
629      srcRb = srcFb->_ColorReadBuffer;
630      dstRb = dstFb->_ColorDrawBuffers[0];
631   }
632   else if (type == GL_STENCIL) {
633      srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer;
634      dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer;
635   }
636   else if (type == GL_DEPTH) {
637      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
638      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
639   }
640   else {
641      ASSERT(type == GL_DEPTH_STENCIL_EXT);
642      /* XXX correct? */
643      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
644      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
645   }
646
647   /* src and dst renderbuffers must be same format */
648   if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) {
649      return GL_FALSE;
650   }
651
652   if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) {
653      /* can't handle packed depth+stencil here */
654      if (_mesa_is_format_packed_depth_stencil(srcRb->Format) ||
655          _mesa_is_format_packed_depth_stencil(dstRb->Format))
656         return GL_FALSE;
657   }
658   else if (type == GL_DEPTH_STENCIL) {
659      /* can't handle separate depth/stencil buffers */
660      if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer ||
661          dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer)
662         return GL_FALSE;
663   }
664
665   /* clipping not supported */
666   if (srcX < 0 || srcX + width > (GLint) srcFb->Width ||
667       srcY < 0 || srcY + height > (GLint) srcFb->Height ||
668       dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax ||
669       dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) {
670      return GL_FALSE;
671   }
672
673   pixelBytes = _mesa_get_format_bytes(srcRb->Format);
674   widthInBytes = width * pixelBytes;
675
676   if (srcRb == dstRb) {
677      /* map whole buffer for read/write */
678      /* XXX we could be clever and just map the union region of the
679       * source and dest rects.
680       */
681      GLubyte *map;
682      GLint rowStride;
683
684      ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
685                                  srcRb->Width, srcRb->Height,
686                                  GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
687                                  &map, &rowStride);
688      if (!srcMap) {
689         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
690         return GL_TRUE; /* don't retry with slow path */
691      }
692
693      srcMap = map + srcY * rowStride + srcX * pixelBytes;
694      dstMap = map + dstY * rowStride + dstX * pixelBytes;
695
696      /* this handles overlapping copies */
697      if (srcY < dstY) {
698         /* copy in reverse (top->down) order */
699         srcMap += rowStride * (height - 1);
700         dstMap += rowStride * (height - 1);
701         srcRowStride = -rowStride;
702         dstRowStride = -rowStride;
703      }
704      else {
705         /* copy in normal (bottom->up) order */
706         srcRowStride = rowStride;
707         dstRowStride = rowStride;
708      }
709   }
710   else {
711      /* different src/dst buffers */
712      ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
713                                  width, height,
714                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride);
715      if (!srcMap) {
716         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
717         return GL_TRUE; /* don't retry with slow path */
718      }
719      ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
720                                  width, height,
721                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
722      if (!dstMap) {
723         ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
724         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
725         return GL_TRUE; /* don't retry with slow path */
726      }
727   }
728
729   for (row = 0; row < height; row++) {
730      /* memmove() in case of overlap */
731      memmove(dstMap, srcMap, widthInBytes);
732      dstMap += dstRowStride;
733      srcMap += srcRowStride;
734   }
735
736   ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
737   if (dstRb != srcRb) {
738      ctx->Driver.UnmapRenderbuffer(ctx, dstRb);
739   }
740
741   return GL_TRUE;
742}
743
744
745/**
746 * Do software-based glCopyPixels.
747 * By time we get here, all parameters will have been error-checked.
748 */
749void
750_swrast_CopyPixels( struct gl_context *ctx,
751		    GLint srcx, GLint srcy, GLsizei width, GLsizei height,
752		    GLint destx, GLint desty, GLenum type )
753{
754   SWcontext *swrast = SWRAST_CONTEXT(ctx);
755
756   if (!_mesa_check_conditional_render(ctx))
757      return; /* don't copy */
758
759   if (swrast->NewState)
760      _swrast_validate_derived( ctx );
761
762   if (fast_copy_pixels(ctx, srcx, srcy, width, height, destx, desty, type)) {
763      /* all done */
764      return;
765   }
766
767   swrast_render_start(ctx);
768
769   switch (type) {
770   case GL_COLOR:
771      copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
772      break;
773   case GL_DEPTH:
774      copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
775      break;
776   case GL_STENCIL:
777      copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
778      break;
779   case GL_DEPTH_STENCIL_EXT:
780      copy_depth_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
781      break;
782   default:
783      _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
784   }
785
786   swrast_render_finish(ctx);
787}
788