s_copypix.c revision e4b2356c07d31fbeeabb13b2fb47db703b473080
1/*
2 * Mesa 3-D graphics library
3 * Version:  6.3
4 *
5 * Copyright (C) 1999-2004  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 "glheader.h"
27#include "context.h"
28#include "colormac.h"
29#include "convolve.h"
30#include "histogram.h"
31#include "image.h"
32#include "macros.h"
33#include "imports.h"
34#include "pixel.h"
35
36#include "s_context.h"
37#include "s_depth.h"
38#include "s_pixeltex.h"
39#include "s_span.h"
40#include "s_stencil.h"
41#include "s_texture.h"
42#include "s_zoom.h"
43
44
45
46/*
47 * Determine if there's overlap in an image copy.
48 * This test also compensates for the fact that copies are done from
49 * bottom to top and overlaps can sometimes be handled correctly
50 * without making a temporary image copy.
51 */
52static GLboolean
53regions_overlap(GLint srcx, GLint srcy,
54                GLint dstx, GLint dsty,
55                GLint width, GLint height,
56                GLfloat zoomX, GLfloat zoomY)
57{
58   if (zoomX == 1.0 && zoomY == 1.0) {
59      /* no zoom */
60      if (srcx >= dstx + width || (srcx + width <= dstx)) {
61         return GL_FALSE;
62      }
63      else if (srcy < dsty) { /* this is OK */
64         return GL_FALSE;
65      }
66      else if (srcy > dsty + height) {
67         return GL_FALSE;
68      }
69      else {
70         return GL_TRUE;
71      }
72   }
73   else {
74      /* add one pixel of slop when zooming, just to be safe */
75      if ((srcx > dstx + (width * zoomX) + 1) || (srcx + width + 1 < dstx)) {
76         return GL_FALSE;
77      }
78      else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) {
79         return GL_FALSE;
80      }
81      else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) {
82         return GL_FALSE;
83      }
84      else {
85         return GL_TRUE;
86      }
87   }
88}
89
90
91/**
92 * Convert GLfloat[n][4] colors to GLchan[n][4].
93 * XXX maybe move into image.c
94 */
95static void
96float_span_to_chan(GLuint n, CONST GLfloat in[][4], GLchan out[][4])
97{
98   GLuint i;
99   for (i = 0; i < n; i++) {
100      UNCLAMPED_FLOAT_TO_CHAN(out[i][RCOMP], in[i][RCOMP]);
101      UNCLAMPED_FLOAT_TO_CHAN(out[i][GCOMP], in[i][GCOMP]);
102      UNCLAMPED_FLOAT_TO_CHAN(out[i][BCOMP], in[i][BCOMP]);
103      UNCLAMPED_FLOAT_TO_CHAN(out[i][ACOMP], in[i][ACOMP]);
104   }
105}
106
107
108/**
109 * Convert GLchan[n][4] colors to GLfloat[n][4].
110 * XXX maybe move into image.c
111 */
112static void
113chan_span_to_float(GLuint n, CONST GLchan in[][4], GLfloat out[][4])
114{
115   GLuint i;
116   for (i = 0; i < n; i++) {
117      out[i][RCOMP] = CHAN_TO_FLOAT(in[i][RCOMP]);
118      out[i][GCOMP] = CHAN_TO_FLOAT(in[i][GCOMP]);
119      out[i][BCOMP] = CHAN_TO_FLOAT(in[i][BCOMP]);
120      out[i][ACOMP] = CHAN_TO_FLOAT(in[i][ACOMP]);
121   }
122}
123
124
125
126/*
127 * RGBA copypixels with convolution.
128 */
129static void
130copy_conv_rgba_pixels(GLcontext *ctx, GLint srcx, GLint srcy,
131                      GLint width, GLint height, GLint destx, GLint desty)
132{
133   SWcontext *swrast = SWRAST_CONTEXT(ctx);
134   struct gl_renderbuffer *drawRb = NULL;
135   GLboolean quick_draw;
136   GLint row;
137   GLboolean changeBuffer;
138   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
139   const GLuint transferOps = ctx->_ImageTransferState;
140   GLfloat *dest, *tmpImage, *convImage;
141   struct sw_span span;
142
143   INIT_SPAN(span, GL_BITMAP, 0, 0, SPAN_RGBA);
144
145   if (ctx->Depth.Test)
146      _swrast_span_default_z(ctx, &span);
147   if (ctx->Fog.Enabled)
148      _swrast_span_default_fog(ctx, &span);
149
150
151   if (SWRAST_CONTEXT(ctx)->_RasterMask == 0
152       && !zoom
153       && destx >= 0
154       && destx + width <= (GLint) ctx->DrawBuffer->Width) {
155      quick_draw = GL_TRUE;
156      drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0][0];
157   }
158   else {
159      quick_draw = GL_FALSE;
160   }
161
162   /* If read and draw buffer are different we must do buffer switching */
163   changeBuffer = ctx->Pixel.ReadBuffer != ctx->Color.DrawBuffer[0]
164               || ctx->DrawBuffer != ctx->ReadBuffer;
165
166
167   /* allocate space for GLfloat image */
168   tmpImage = (GLfloat *) MALLOC(width * height * 4 * sizeof(GLfloat));
169   if (!tmpImage) {
170      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
171      return;
172   }
173   convImage = (GLfloat *) MALLOC(width * height * 4 * sizeof(GLfloat));
174   if (!convImage) {
175      FREE(tmpImage);
176      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
177      return;
178   }
179
180   if (changeBuffer) {
181      /* choose the read buffer */
182      _swrast_use_read_buffer(ctx);
183   }
184
185   /* read source image */
186   dest = tmpImage;
187   for (row = 0; row < height; row++) {
188      GLchan rgba[MAX_WIDTH][4];
189      /* Read GLchan and convert to GLfloat */
190      _swrast_read_rgba_span(ctx, ctx->ReadBuffer->_ColorReadBuffer,
191                             width, srcx, srcy + row, rgba);
192      chan_span_to_float(width, (CONST GLchan (*)[4]) rgba,
193                         (GLfloat (*)[4]) dest);
194      dest += 4 * width;
195   }
196
197   if (changeBuffer) {
198      /* restore default src/dst buffer */
199      _swrast_use_draw_buffer(ctx);
200   }
201
202   /* do the image transfer ops which preceed convolution */
203   for (row = 0; row < height; row++) {
204      GLfloat (*rgba)[4] = (GLfloat (*)[4]) (tmpImage + row * width * 4);
205      _mesa_apply_rgba_transfer_ops(ctx,
206                                    transferOps & IMAGE_PRE_CONVOLUTION_BITS,
207                                    width, rgba);
208   }
209
210   /* do convolution */
211   if (ctx->Pixel.Convolution2DEnabled) {
212      _mesa_convolve_2d_image(ctx, &width, &height, tmpImage, convImage);
213   }
214   else {
215      ASSERT(ctx->Pixel.Separable2DEnabled);
216      _mesa_convolve_sep_image(ctx, &width, &height, tmpImage, convImage);
217   }
218   FREE(tmpImage);
219
220   /* do remaining post-convolution image transfer ops */
221   for (row = 0; row < height; row++) {
222      GLfloat (*rgba)[4] = (GLfloat (*)[4]) (convImage + row * width * 4);
223      _mesa_apply_rgba_transfer_ops(ctx,
224                                    transferOps & IMAGE_POST_CONVOLUTION_BITS,
225                                    width, rgba);
226   }
227
228   /* write the new image */
229   for (row = 0; row < height; row++) {
230      const GLfloat *src = convImage + row * width * 4;
231      GLint dy;
232
233      /* convert floats back to chan */
234      float_span_to_chan(width, (const GLfloat (*)[4]) src, span.array->rgba);
235
236      if (ctx->Pixel.PixelTextureEnabled && ctx->Texture._EnabledUnits) {
237         span.end = width;
238         _swrast_pixel_texture(ctx, &span);
239      }
240
241      /* write row to framebuffer */
242
243      dy = desty + row;
244      if (quick_draw && dy >= 0 && dy < (GLint) ctx->DrawBuffer->Height) {
245#if OLD_RENDERBUFFER
246         if (swrast->Driver.WriteRGBASpan)
247            swrast->Driver.WriteRGBASpan(ctx, drawRb, width, destx, dy,
248		       (const GLchan (*)[4]) span.array->rgba, NULL);
249         else
250#endif
251            drawRb->PutRow(ctx, drawRb, width, destx, dy,
252                           span.array->rgba, NULL);
253      }
254      else if (zoom) {
255         span.x = destx;
256         span.y = dy;
257         span.end = width;
258         _swrast_write_zoomed_rgba_span(ctx, &span,
259                                     (CONST GLchan (*)[4])span.array->rgba,
260                                     desty, 0);
261      }
262      else {
263         span.x = destx;
264         span.y = dy;
265         span.end = width;
266         _swrast_write_rgba_span(ctx, &span);
267      }
268   }
269
270   FREE(convImage);
271}
272
273
274/*
275 * RGBA copypixels
276 */
277static void
278copy_rgba_pixels(GLcontext *ctx, GLint srcx, GLint srcy,
279                 GLint width, GLint height, GLint destx, GLint desty)
280{
281   SWcontext *swrast = SWRAST_CONTEXT(ctx);
282   struct gl_renderbuffer *drawRb;
283   GLchan *tmpImage,*p;
284   GLboolean quick_draw;
285   GLint sy, dy, stepy, j;
286   GLboolean changeBuffer;
287   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
288   GLint overlapping;
289   const GLuint transferOps = ctx->_ImageTransferState;
290   struct sw_span span;
291
292   if (!ctx->ReadBuffer->_ColorReadBuffer) {
293      /* no readbuffer - OK */
294      return;
295   }
296
297   INIT_SPAN(span, GL_BITMAP, 0, 0, SPAN_RGBA);
298
299   if (ctx->Pixel.Convolution2DEnabled || ctx->Pixel.Separable2DEnabled) {
300      copy_conv_rgba_pixels(ctx, srcx, srcy, width, height, destx, desty);
301      return;
302   }
303
304   /* Determine if copy should be done bottom-to-top or top-to-bottom */
305   if (srcy < desty) {
306      /* top-down  max-to-min */
307      sy = srcy + height - 1;
308      dy = desty + height - 1;
309      stepy = -1;
310   }
311   else {
312      /* bottom-up  min-to-max */
313      sy = srcy;
314      dy = desty;
315      stepy = 1;
316   }
317
318   if (ctx->DrawBuffer == ctx->ReadBuffer) {
319      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
320                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
321   }
322   else {
323      overlapping = GL_FALSE;
324   }
325
326   if (ctx->Depth.Test)
327      _swrast_span_default_z(ctx, &span);
328   if (ctx->Fog.Enabled)
329      _swrast_span_default_fog(ctx, &span);
330
331   if (SWRAST_CONTEXT(ctx)->_RasterMask == 0
332       && !zoom
333       && destx >= 0
334       && destx + width <= (GLint) ctx->DrawBuffer->Width) {
335      quick_draw = GL_TRUE;
336      drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0][0];
337   }
338   else {
339      quick_draw = GL_FALSE;
340      drawRb = NULL;
341   }
342
343   /* If read and draw buffer are different we must do buffer switching */
344   changeBuffer = ctx->Pixel.ReadBuffer != ctx->Color.DrawBuffer[0]
345                  || ctx->DrawBuffer != ctx->ReadBuffer;
346
347   if (overlapping) {
348      GLint ssy = sy;
349      tmpImage = (GLchan *) MALLOC(width * height * sizeof(GLchan) * 4);
350      if (!tmpImage) {
351         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
352         return;
353      }
354      /* setup source */
355      if (changeBuffer)
356         _swrast_use_read_buffer(ctx);
357      /* read the source image */
358      p = tmpImage;
359      for (j = 0; j < height; j++, ssy += stepy) {
360         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
361                                 width, srcx, ssy, (GLchan (*)[4]) p );
362         p += width * 4;
363      }
364      p = tmpImage;
365      /* restore dest */
366      if (changeBuffer) {
367         _swrast_use_draw_buffer(ctx);
368         changeBuffer = GL_FALSE;
369      }
370   }
371   else {
372      tmpImage = NULL;  /* silence compiler warnings */
373      p = NULL;
374   }
375
376   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
377      /* Get source pixels */
378      if (overlapping) {
379         /* get from buffered image */
380         ASSERT(width < MAX_WIDTH);
381         MEMCPY(span.array->rgba, p, width * sizeof(GLchan) * 4);
382         p += width * 4;
383      }
384      else {
385         /* get from framebuffer */
386         if (changeBuffer)
387            _swrast_use_read_buffer(ctx);
388         ASSERT(width < MAX_WIDTH);
389         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
390                                 width, srcx, sy, span.array->rgba );
391         if (changeBuffer)
392            _swrast_use_draw_buffer(ctx);
393      }
394
395      if (transferOps) {
396         DEFMARRAY(GLfloat, rgbaFloat, MAX_WIDTH, 4);  /* mac 32k limitation */
397         CHECKARRAY(rgbaFloat, return);
398
399         /* convert to float, transfer, convert back to chan */
400         chan_span_to_float(width, (CONST GLchan (*)[4]) span.array->rgba,
401                            rgbaFloat);
402         _mesa_apply_rgba_transfer_ops(ctx, transferOps, width, rgbaFloat);
403         float_span_to_chan(width, (CONST GLfloat (*)[4]) rgbaFloat,
404                            span.array->rgba);
405
406         UNDEFARRAY(rgbaFloat);  /* mac 32k limitation */
407      }
408
409      if (ctx->Pixel.PixelTextureEnabled && ctx->Texture._EnabledUnits) {
410         span.end = width;
411         _swrast_pixel_texture(ctx, &span);
412      }
413
414      /* Write color span */
415      if (quick_draw && dy >= 0 && dy < (GLint) ctx->DrawBuffer->Height) {
416#if OLD_RENDERBUFFER
417         if (swrast->Driver.WriteRGBASpan)
418            swrast->Driver.WriteRGBASpan(ctx, drawRb, width, destx, dy,
419                                 (const GLchan (*)[4])span.array->rgba, NULL);
420         else
421#endif
422            drawRb->PutRow(ctx, drawRb, width, destx, dy, span.array->rgba, NULL);
423
424      }
425      else if (zoom) {
426         span.x = destx;
427         span.y = dy;
428         span.end = width;
429         _swrast_write_zoomed_rgba_span(ctx, &span,
430                                     (CONST GLchan (*)[4]) span.array->rgba,
431                                     desty, 0);
432      }
433      else {
434         span.x = destx;
435         span.y = dy;
436         span.end = width;
437         _swrast_write_rgba_span(ctx, &span);
438      }
439   }
440
441   if (overlapping)
442      FREE(tmpImage);
443}
444
445
446static void
447copy_ci_pixels( GLcontext *ctx, GLint srcx, GLint srcy,
448                GLint width, GLint height,
449                GLint destx, GLint desty )
450{
451   GLuint *tmpImage,*p;
452   GLint sy, dy, stepy;
453   GLint j;
454   GLboolean changeBuffer;
455   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
456   const GLboolean shift_or_offset = ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset;
457   GLint overlapping;
458   struct sw_span span;
459
460   if (!ctx->ReadBuffer->_ColorReadBuffer) {
461      /* no readbuffer - OK */
462      return;
463   }
464
465   INIT_SPAN(span, GL_BITMAP, 0, 0, SPAN_INDEX);
466
467   /* Determine if copy should be bottom-to-top or top-to-bottom */
468   if (srcy<desty) {
469      /* top-down  max-to-min */
470      sy = srcy + height - 1;
471      dy = desty + height - 1;
472      stepy = -1;
473   }
474   else {
475      /* bottom-up  min-to-max */
476      sy = srcy;
477      dy = desty;
478      stepy = 1;
479   }
480
481   if (ctx->DrawBuffer == ctx->ReadBuffer) {
482      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
483                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
484   }
485   else {
486      overlapping = GL_FALSE;
487   }
488
489   if (ctx->Depth.Test)
490      _swrast_span_default_z(ctx, &span);
491   if (ctx->Fog.Enabled)
492      _swrast_span_default_fog(ctx, &span);
493
494   /* If read and draw buffer are different we must do buffer switching */
495   changeBuffer = ctx->Pixel.ReadBuffer != ctx->Color.DrawBuffer[0]
496               || ctx->DrawBuffer != ctx->ReadBuffer;
497
498   if (overlapping) {
499      GLint ssy = sy;
500      tmpImage = (GLuint *) MALLOC(width * height * sizeof(GLuint));
501      if (!tmpImage) {
502         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
503         return;
504      }
505      /* setup source */
506      if (changeBuffer)
507         _swrast_use_read_buffer(ctx);
508      /* read the image */
509      p = tmpImage;
510      for (j = 0; j < height; j++, ssy += stepy) {
511         _swrast_read_index_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
512                                  width, srcx, ssy, p );
513         p += width;
514      }
515      p = tmpImage;
516      /* restore to draw buffer */
517      if (changeBuffer) {
518         _swrast_use_draw_buffer(ctx);
519         changeBuffer = GL_FALSE;
520      }
521   }
522   else {
523      tmpImage = NULL;  /* silence compiler warning */
524      p = NULL;
525   }
526
527   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
528      /* Get color indexes */
529      if (overlapping) {
530         MEMCPY(span.array->index, p, width * sizeof(GLuint));
531         p += width;
532      }
533      else {
534         if (changeBuffer)
535            _swrast_use_read_buffer(ctx);
536         _swrast_read_index_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
537                                  width, srcx, sy, span.array->index );
538         if (changeBuffer)
539            _swrast_use_draw_buffer(ctx);
540      }
541
542      /* Apply shift, offset, look-up table */
543      if (shift_or_offset) {
544         _mesa_shift_and_offset_ci( ctx, width, span.array->index );
545      }
546      if (ctx->Pixel.MapColorFlag) {
547         _mesa_map_ci( ctx, width, span.array->index );
548      }
549
550      /* write color indexes */
551      span.x = destx;
552      span.y = dy;
553      span.end = width;
554      if (zoom)
555         _swrast_write_zoomed_index_span(ctx, &span, desty, 0);
556      else
557         _swrast_write_index_span(ctx, &span);
558   }
559
560   if (overlapping)
561      FREE(tmpImage);
562}
563
564
565
566/*
567 * TODO: Optimize!!!!
568 */
569static void
570copy_depth_pixels( GLcontext *ctx, GLint srcx, GLint srcy,
571                   GLint width, GLint height,
572                   GLint destx, GLint desty )
573{
574   const GLfloat depthMax = ctx->DrawBuffer->_DepthMaxF;
575   struct gl_renderbuffer *readRb
576      = ctx->ReadBuffer->Attachment[BUFFER_DEPTH].Renderbuffer;
577   GLfloat *p, *tmpImage;
578   GLint sy, dy, stepy;
579   GLint i, j;
580   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
581   GLint overlapping;
582   struct sw_span span;
583
584   if (!readRb) {
585      /* no readbuffer - OK */
586      return;
587   }
588
589   INIT_SPAN(span, GL_BITMAP, 0, 0, SPAN_Z);
590
591   if (!ctx->Visual.depthBits) {
592      _mesa_error( ctx, GL_INVALID_OPERATION, "glCopyPixels" );
593      return;
594   }
595
596   /* Determine if copy should be bottom-to-top or top-to-bottom */
597   if (srcy<desty) {
598      /* top-down  max-to-min */
599      sy = srcy + height - 1;
600      dy = desty + height - 1;
601      stepy = -1;
602   }
603   else {
604      /* bottom-up  min-to-max */
605      sy = srcy;
606      dy = desty;
607      stepy = 1;
608   }
609
610   if (ctx->DrawBuffer == ctx->ReadBuffer) {
611      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
612                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
613   }
614   else {
615      overlapping = GL_FALSE;
616   }
617
618   _swrast_span_default_color(ctx, &span);
619   if (ctx->Fog.Enabled)
620      _swrast_span_default_fog(ctx, &span);
621
622   if (overlapping) {
623      GLint ssy = sy;
624      tmpImage = (GLfloat *) MALLOC(width * height * sizeof(GLfloat));
625      if (!tmpImage) {
626         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
627         return;
628      }
629      p = tmpImage;
630      for (j = 0; j < height; j++, ssy += stepy) {
631         _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p);
632         p += width;
633      }
634      p = tmpImage;
635   }
636   else {
637      tmpImage = NULL;  /* silence compiler warning */
638      p = NULL;
639   }
640
641   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
642      GLfloat depth[MAX_WIDTH];
643      float sum = 0;
644      /* get depth values */
645      if (overlapping) {
646         MEMCPY(depth, p, width * sizeof(GLfloat));
647         p += width;
648      }
649      else {
650         _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth);
651      }
652
653      /* apply scale and bias */
654      for (i = 0; i < width; i++) {
655         GLfloat d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias;
656         sum += d;
657         span.array->z[i] = (GLdepth) (CLAMP(d, 0.0F, 1.0F) * depthMax);
658      }
659
660      /* write depth values */
661      span.x = destx;
662      span.y = dy;
663      span.end = width;
664      if (ctx->Visual.rgbMode) {
665         if (zoom)
666            _swrast_write_zoomed_rgba_span( ctx, &span,
667                            (const GLchan (*)[4])span.array->rgba, desty, 0 );
668         else
669            _swrast_write_rgba_span(ctx, &span);
670      }
671      else {
672         if (zoom)
673            _swrast_write_zoomed_index_span( ctx, &span, desty, 0 );
674         else
675            _swrast_write_index_span(ctx, &span);
676      }
677   }
678
679   if (overlapping)
680      FREE(tmpImage);
681}
682
683
684
685static void
686copy_stencil_pixels( GLcontext *ctx, GLint srcx, GLint srcy,
687                     GLint width, GLint height,
688                     GLint destx, GLint desty )
689{
690   struct gl_renderbuffer *rb
691      = ctx->ReadBuffer->Attachment[BUFFER_STENCIL].Renderbuffer;
692   GLint sy, dy, stepy;
693   GLint j;
694   GLstencil *p, *tmpImage;
695   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
696   const GLboolean shift_or_offset = ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset;
697   GLint overlapping;
698
699   if (!ctx->Visual.stencilBits) {
700      _mesa_error( ctx, GL_INVALID_OPERATION, "glCopyPixels" );
701      return;
702   }
703
704   if (!rb) {
705      /* no readbuffer - OK */
706      return;
707   }
708
709   /* Determine if copy should be bottom-to-top or top-to-bottom */
710   if (srcy < desty) {
711      /* top-down  max-to-min */
712      sy = srcy + height - 1;
713      dy = desty + height - 1;
714      stepy = -1;
715   }
716   else {
717      /* bottom-up  min-to-max */
718      sy = srcy;
719      dy = desty;
720      stepy = 1;
721   }
722
723   if (ctx->DrawBuffer == ctx->ReadBuffer) {
724      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
725                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
726   }
727   else {
728      overlapping = GL_FALSE;
729   }
730
731   if (overlapping) {
732      GLint ssy = sy;
733      tmpImage = (GLstencil *) MALLOC(width * height * sizeof(GLstencil));
734      if (!tmpImage) {
735         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
736         return;
737      }
738      p = tmpImage;
739      for (j = 0; j < height; j++, ssy += stepy) {
740         _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p );
741         p += width;
742      }
743      p = tmpImage;
744   }
745   else {
746      tmpImage = NULL;  /* silence compiler warning */
747      p = NULL;
748   }
749
750   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
751      GLstencil stencil[MAX_WIDTH];
752
753      /* Get stencil values */
754      if (overlapping) {
755         MEMCPY(stencil, p, width * sizeof(GLstencil));
756         p += width;
757      }
758      else {
759         _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil );
760      }
761
762      /* Apply shift, offset, look-up table */
763      if (shift_or_offset) {
764         _mesa_shift_and_offset_stencil( ctx, width, stencil );
765      }
766      if (ctx->Pixel.MapStencilFlag) {
767         _mesa_map_stencil( ctx, width, stencil );
768      }
769
770      /* Write stencil values */
771      if (zoom) {
772         _swrast_write_zoomed_stencil_span( ctx, width, destx, dy,
773                                          stencil, desty, 0 );
774      }
775      else {
776         _swrast_write_stencil_span( ctx, width, destx, dy, stencil );
777      }
778   }
779
780   if (overlapping)
781      FREE(tmpImage);
782}
783
784
785
786void
787_swrast_CopyPixels( GLcontext *ctx,
788		    GLint srcx, GLint srcy, GLsizei width, GLsizei height,
789		    GLint destx, GLint desty,
790		    GLenum type )
791{
792   SWcontext *swrast = SWRAST_CONTEXT(ctx);
793   RENDER_START(swrast,ctx);
794
795   if (swrast->NewState)
796      _swrast_validate_derived( ctx );
797
798   if (type == GL_COLOR && ctx->Visual.rgbMode) {
799      copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
800   }
801   else if (type == GL_COLOR && !ctx->Visual.rgbMode) {
802      copy_ci_pixels( ctx, srcx, srcy, width, height, destx, desty );
803   }
804   else if (type == GL_DEPTH) {
805      copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
806   }
807   else if (type == GL_STENCIL) {
808      copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
809   }
810   else {
811      _mesa_error( ctx, GL_INVALID_ENUM, "glCopyPixels" );
812   }
813
814   RENDER_FINISH(swrast,ctx);
815}
816