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
regions_overlap(GLint srcx,GLint srcy,GLint dstx,GLint dsty,GLint width,GLint height,GLfloat zoomX,GLfloat zoomY)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
copy_rgba_pixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLint width,GLint height,GLint destx,GLint desty)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
scale_and_bias_z(struct gl_context * ctx,GLuint width,const GLfloat depth[],GLuint z[])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
copy_depth_pixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLint width,GLint height,GLint destx,GLint desty)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
copy_stencil_pixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLint width,GLint height,GLint destx,GLint desty)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
swrast_fast_copy_pixels(struct gl_context * ctx,GLint srcX,GLint srcY,GLsizei width,GLsizei height,GLint dstX,GLint dstY,GLenum type)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 *
map_readbuffer(struct gl_context * ctx,GLenum type)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
_swrast_CopyPixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLsizei width,GLsizei height,GLint destx,GLint desty,GLenum type)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