1848b8605Smrg/*
2848b8605Smrg * Mesa 3-D graphics library
3848b8605Smrg *
4848b8605Smrg * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
5848b8605Smrg *
6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7848b8605Smrg * copy of this software and associated documentation files (the "Software"),
8848b8605Smrg * to deal in the Software without restriction, including without limitation
9848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the
11848b8605Smrg * Software is furnished to do so, subject to the following conditions:
12848b8605Smrg *
13848b8605Smrg * The above copyright notice and this permission notice shall be included
14848b8605Smrg * in all copies or substantial portions of the Software.
15848b8605Smrg *
16848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20848b8605Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22848b8605Smrg * OTHER DEALINGS IN THE SOFTWARE.
23848b8605Smrg */
24848b8605Smrg
25848b8605Smrg
26848b8605Smrg#include "main/glheader.h"
27848b8605Smrg#include "main/context.h"
28848b8605Smrg#include "main/condrender.h"
29848b8605Smrg#include "main/macros.h"
30b8e80941Smrg#include "main/blit.h"
31848b8605Smrg#include "main/pixeltransfer.h"
32848b8605Smrg#include "main/imports.h"
33848b8605Smrg
34848b8605Smrg#include "s_context.h"
35848b8605Smrg#include "s_depth.h"
36848b8605Smrg#include "s_span.h"
37848b8605Smrg#include "s_stencil.h"
38848b8605Smrg#include "s_zoom.h"
39848b8605Smrg
40848b8605Smrg
41848b8605Smrg
42848b8605Smrg/**
43848b8605Smrg * Determine if there's overlap in an image copy.
44848b8605Smrg * This test also compensates for the fact that copies are done from
45848b8605Smrg * bottom to top and overlaps can sometimes be handled correctly
46848b8605Smrg * without making a temporary image copy.
47848b8605Smrg * \return GL_TRUE if the regions overlap, GL_FALSE otherwise.
48848b8605Smrg */
49848b8605Smrgstatic GLboolean
50848b8605Smrgregions_overlap(GLint srcx, GLint srcy,
51848b8605Smrg                GLint dstx, GLint dsty,
52848b8605Smrg                GLint width, GLint height,
53848b8605Smrg                GLfloat zoomX, GLfloat zoomY)
54848b8605Smrg{
55b8e80941Smrg   if (zoomX == 1.0F && zoomY == 1.0F) {
56b8e80941Smrg      return _mesa_regions_overlap(srcx, srcy, srcx + width, srcy + height,
57b8e80941Smrg                                   dstx, dsty, dstx + width, dsty + height);
58848b8605Smrg   }
59848b8605Smrg   else {
60848b8605Smrg      /* add one pixel of slop when zooming, just to be safe */
61848b8605Smrg      if (srcx > (dstx + ((zoomX > 0.0F) ? (width * zoomX + 1.0F) : 0.0F))) {
62848b8605Smrg         /* src is completely right of dest */
63848b8605Smrg         return GL_FALSE;
64848b8605Smrg      }
65848b8605Smrg      else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) {
66848b8605Smrg         /* src is completely left of dest */
67848b8605Smrg         return GL_FALSE;
68848b8605Smrg      }
69848b8605Smrg      else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) {
70848b8605Smrg         /* src is completely below dest */
71848b8605Smrg         return GL_FALSE;
72848b8605Smrg      }
73848b8605Smrg      else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) {
74848b8605Smrg         /* src is completely above dest */
75848b8605Smrg         return GL_FALSE;
76848b8605Smrg      }
77848b8605Smrg      else {
78848b8605Smrg         return GL_TRUE;
79848b8605Smrg      }
80848b8605Smrg   }
81848b8605Smrg}
82848b8605Smrg
83848b8605Smrg
84848b8605Smrg/**
85848b8605Smrg * RGBA copypixels
86848b8605Smrg */
87848b8605Smrgstatic void
88848b8605Smrgcopy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy,
89848b8605Smrg                 GLint width, GLint height, GLint destx, GLint desty)
90848b8605Smrg{
91848b8605Smrg   GLfloat *tmpImage, *p;
92848b8605Smrg   GLint sy, dy, stepy, row;
93848b8605Smrg   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
94848b8605Smrg   GLint overlapping;
95848b8605Smrg   GLuint transferOps = ctx->_ImageTransferState;
96848b8605Smrg   SWspan span;
97848b8605Smrg
98848b8605Smrg   if (!ctx->ReadBuffer->_ColorReadBuffer) {
99848b8605Smrg      /* no readbuffer - OK */
100848b8605Smrg      return;
101848b8605Smrg   }
102848b8605Smrg
103848b8605Smrg   if (ctx->DrawBuffer == ctx->ReadBuffer) {
104848b8605Smrg      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
105848b8605Smrg                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
106848b8605Smrg   }
107848b8605Smrg   else {
108848b8605Smrg      overlapping = GL_FALSE;
109848b8605Smrg   }
110848b8605Smrg
111848b8605Smrg   /* Determine if copy should be done bottom-to-top or top-to-bottom */
112848b8605Smrg   if (!overlapping && srcy < desty) {
113848b8605Smrg      /* top-down  max-to-min */
114848b8605Smrg      sy = srcy + height - 1;
115848b8605Smrg      dy = desty + height - 1;
116848b8605Smrg      stepy = -1;
117848b8605Smrg   }
118848b8605Smrg   else {
119848b8605Smrg      /* bottom-up  min-to-max */
120848b8605Smrg      sy = srcy;
121848b8605Smrg      dy = desty;
122848b8605Smrg      stepy = 1;
123848b8605Smrg   }
124848b8605Smrg
125848b8605Smrg   INIT_SPAN(span, GL_BITMAP);
126848b8605Smrg   _swrast_span_default_attribs(ctx, &span);
127848b8605Smrg   span.arrayMask = SPAN_RGBA;
128848b8605Smrg   span.arrayAttribs = VARYING_BIT_COL0; /* we'll fill in COL0 attrib values */
129848b8605Smrg
130848b8605Smrg   if (overlapping) {
131848b8605Smrg      tmpImage = malloc(width * height * sizeof(GLfloat) * 4);
132848b8605Smrg      if (!tmpImage) {
133848b8605Smrg         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
134848b8605Smrg         return;
135848b8605Smrg      }
136848b8605Smrg      /* read the source image as RGBA/float */
137848b8605Smrg      p = tmpImage;
138848b8605Smrg      for (row = 0; row < height; row++) {
139848b8605Smrg         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
140848b8605Smrg                                 width, srcx, sy + row, p );
141848b8605Smrg         p += width * 4;
142848b8605Smrg      }
143848b8605Smrg      p = tmpImage;
144848b8605Smrg   }
145848b8605Smrg   else {
146848b8605Smrg      tmpImage = NULL;  /* silence compiler warnings */
147848b8605Smrg      p = NULL;
148848b8605Smrg   }
149848b8605Smrg
150b8e80941Smrg   assert(width < SWRAST_MAX_WIDTH);
151848b8605Smrg
152848b8605Smrg   for (row = 0; row < height; row++, sy += stepy, dy += stepy) {
153848b8605Smrg      GLvoid *rgba = span.array->attribs[VARYING_SLOT_COL0];
154848b8605Smrg
155848b8605Smrg      /* Get row/span of source pixels */
156848b8605Smrg      if (overlapping) {
157848b8605Smrg         /* get from buffered image */
158848b8605Smrg         memcpy(rgba, p, width * sizeof(GLfloat) * 4);
159848b8605Smrg         p += width * 4;
160848b8605Smrg      }
161848b8605Smrg      else {
162848b8605Smrg         /* get from framebuffer */
163848b8605Smrg         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
164848b8605Smrg                                 width, srcx, sy, rgba );
165848b8605Smrg      }
166848b8605Smrg
167848b8605Smrg      if (transferOps) {
168848b8605Smrg         _mesa_apply_rgba_transfer_ops(ctx, transferOps, width,
169848b8605Smrg                                       (GLfloat (*)[4]) rgba);
170848b8605Smrg      }
171848b8605Smrg
172848b8605Smrg      /* Write color span */
173848b8605Smrg      span.x = destx;
174848b8605Smrg      span.y = dy;
175848b8605Smrg      span.end = width;
176848b8605Smrg      span.array->ChanType = GL_FLOAT;
177848b8605Smrg      if (zoom) {
178848b8605Smrg         _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba);
179848b8605Smrg      }
180848b8605Smrg      else {
181848b8605Smrg         _swrast_write_rgba_span(ctx, &span);
182848b8605Smrg      }
183848b8605Smrg   }
184848b8605Smrg
185848b8605Smrg   span.array->ChanType = CHAN_TYPE; /* restore */
186848b8605Smrg
187848b8605Smrg   if (overlapping)
188848b8605Smrg      free(tmpImage);
189848b8605Smrg}
190848b8605Smrg
191848b8605Smrg
192848b8605Smrg/**
193848b8605Smrg * Convert floating point Z values to integer Z values with pixel transfer's
194848b8605Smrg * Z scale and bias.
195848b8605Smrg */
196848b8605Smrgstatic void
197848b8605Smrgscale_and_bias_z(struct gl_context *ctx, GLuint width,
198848b8605Smrg                 const GLfloat depth[], GLuint z[])
199848b8605Smrg{
200848b8605Smrg   const GLuint depthMax = ctx->DrawBuffer->_DepthMax;
201848b8605Smrg   GLuint i;
202848b8605Smrg
203848b8605Smrg   if (depthMax <= 0xffffff &&
204b8e80941Smrg       ctx->Pixel.DepthScale == 1.0F &&
205b8e80941Smrg       ctx->Pixel.DepthBias == 0.0F) {
206848b8605Smrg      /* no scale or bias and no clamping and no worry of overflow */
207848b8605Smrg      const GLfloat depthMaxF = ctx->DrawBuffer->_DepthMaxF;
208848b8605Smrg      for (i = 0; i < width; i++) {
209848b8605Smrg         z[i] = (GLuint) (depth[i] * depthMaxF);
210848b8605Smrg      }
211848b8605Smrg   }
212848b8605Smrg   else {
213848b8605Smrg      /* need to be careful with overflow */
214848b8605Smrg      const GLdouble depthMaxF = ctx->DrawBuffer->_DepthMaxF;
215848b8605Smrg      for (i = 0; i < width; i++) {
216848b8605Smrg         GLdouble d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias;
217848b8605Smrg         d = CLAMP(d, 0.0, 1.0) * depthMaxF;
218848b8605Smrg         if (d >= depthMaxF)
219848b8605Smrg            z[i] = depthMax;
220848b8605Smrg         else
221848b8605Smrg            z[i] = (GLuint) d;
222848b8605Smrg      }
223848b8605Smrg   }
224848b8605Smrg}
225848b8605Smrg
226848b8605Smrg
227848b8605Smrg
228848b8605Smrg/*
229848b8605Smrg * TODO: Optimize!!!!
230848b8605Smrg */
231848b8605Smrgstatic void
232848b8605Smrgcopy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
233848b8605Smrg                   GLint width, GLint height,
234848b8605Smrg                   GLint destx, GLint desty )
235848b8605Smrg{
236848b8605Smrg   struct gl_framebuffer *fb = ctx->ReadBuffer;
237848b8605Smrg   struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
238848b8605Smrg   GLfloat *p, *tmpImage, *depth;
239848b8605Smrg   GLint sy, dy, stepy;
240848b8605Smrg   GLint j;
241848b8605Smrg   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
242848b8605Smrg   GLint overlapping;
243848b8605Smrg   SWspan span;
244848b8605Smrg
245848b8605Smrg   if (!readRb) {
246848b8605Smrg      /* no readbuffer - OK */
247848b8605Smrg      return;
248848b8605Smrg   }
249848b8605Smrg
250848b8605Smrg   INIT_SPAN(span, GL_BITMAP);
251848b8605Smrg   _swrast_span_default_attribs(ctx, &span);
252848b8605Smrg   span.arrayMask = SPAN_Z;
253848b8605Smrg
254848b8605Smrg   if (ctx->DrawBuffer == ctx->ReadBuffer) {
255848b8605Smrg      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
256848b8605Smrg                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
257848b8605Smrg   }
258848b8605Smrg   else {
259848b8605Smrg      overlapping = GL_FALSE;
260848b8605Smrg   }
261848b8605Smrg
262848b8605Smrg   /* Determine if copy should be bottom-to-top or top-to-bottom */
263848b8605Smrg   if (!overlapping && srcy < desty) {
264848b8605Smrg      /* top-down  max-to-min */
265848b8605Smrg      sy = srcy + height - 1;
266848b8605Smrg      dy = desty + height - 1;
267848b8605Smrg      stepy = -1;
268848b8605Smrg   }
269848b8605Smrg   else {
270848b8605Smrg      /* bottom-up  min-to-max */
271848b8605Smrg      sy = srcy;
272848b8605Smrg      dy = desty;
273848b8605Smrg      stepy = 1;
274848b8605Smrg   }
275848b8605Smrg
276848b8605Smrg   if (overlapping) {
277848b8605Smrg      GLint ssy = sy;
278848b8605Smrg      tmpImage = malloc(width * height * sizeof(GLfloat));
279848b8605Smrg      if (!tmpImage) {
280848b8605Smrg         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
281848b8605Smrg         return;
282848b8605Smrg      }
283848b8605Smrg      p = tmpImage;
284848b8605Smrg      for (j = 0; j < height; j++, ssy += stepy) {
285848b8605Smrg         _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p);
286848b8605Smrg         p += width;
287848b8605Smrg      }
288848b8605Smrg      p = tmpImage;
289848b8605Smrg   }
290848b8605Smrg   else {
291848b8605Smrg      tmpImage = NULL;  /* silence compiler warning */
292848b8605Smrg      p = NULL;
293848b8605Smrg   }
294848b8605Smrg
295848b8605Smrg   depth = malloc(width * sizeof(GLfloat));
296848b8605Smrg   if (!depth) {
297848b8605Smrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()");
298848b8605Smrg      goto end;
299848b8605Smrg   }
300848b8605Smrg
301848b8605Smrg   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
302848b8605Smrg      /* get depth values */
303848b8605Smrg      if (overlapping) {
304848b8605Smrg         memcpy(depth, p, width * sizeof(GLfloat));
305848b8605Smrg         p += width;
306848b8605Smrg      }
307848b8605Smrg      else {
308848b8605Smrg         _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth);
309848b8605Smrg      }
310848b8605Smrg
311848b8605Smrg      /* apply scale and bias */
312848b8605Smrg      scale_and_bias_z(ctx, width, depth, span.array->z);
313848b8605Smrg
314848b8605Smrg      /* write depth values */
315848b8605Smrg      span.x = destx;
316848b8605Smrg      span.y = dy;
317848b8605Smrg      span.end = width;
318848b8605Smrg      if (zoom)
319848b8605Smrg         _swrast_write_zoomed_depth_span(ctx, destx, desty, &span);
320848b8605Smrg      else
321848b8605Smrg         _swrast_write_rgba_span(ctx, &span);
322848b8605Smrg   }
323848b8605Smrg
324848b8605Smrg   free(depth);
325848b8605Smrg
326848b8605Smrgend:
327848b8605Smrg   if (overlapping)
328848b8605Smrg      free(tmpImage);
329848b8605Smrg}
330848b8605Smrg
331848b8605Smrg
332848b8605Smrg
333848b8605Smrgstatic void
334848b8605Smrgcopy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
335848b8605Smrg                     GLint width, GLint height,
336848b8605Smrg                     GLint destx, GLint desty )
337848b8605Smrg{
338848b8605Smrg   struct gl_framebuffer *fb = ctx->ReadBuffer;
339848b8605Smrg   struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
340848b8605Smrg   GLint sy, dy, stepy;
341848b8605Smrg   GLint j;
342848b8605Smrg   GLubyte *p, *tmpImage, *stencil;
343848b8605Smrg   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
344848b8605Smrg   GLint overlapping;
345848b8605Smrg
346848b8605Smrg   if (!rb) {
347848b8605Smrg      /* no readbuffer - OK */
348848b8605Smrg      return;
349848b8605Smrg   }
350848b8605Smrg
351848b8605Smrg   if (ctx->DrawBuffer == ctx->ReadBuffer) {
352848b8605Smrg      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
353848b8605Smrg                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
354848b8605Smrg   }
355848b8605Smrg   else {
356848b8605Smrg      overlapping = GL_FALSE;
357848b8605Smrg   }
358848b8605Smrg
359848b8605Smrg   /* Determine if copy should be bottom-to-top or top-to-bottom */
360848b8605Smrg   if (!overlapping && srcy < desty) {
361848b8605Smrg      /* top-down  max-to-min */
362848b8605Smrg      sy = srcy + height - 1;
363848b8605Smrg      dy = desty + height - 1;
364848b8605Smrg      stepy = -1;
365848b8605Smrg   }
366848b8605Smrg   else {
367848b8605Smrg      /* bottom-up  min-to-max */
368848b8605Smrg      sy = srcy;
369848b8605Smrg      dy = desty;
370848b8605Smrg      stepy = 1;
371848b8605Smrg   }
372848b8605Smrg
373848b8605Smrg   if (overlapping) {
374848b8605Smrg      GLint ssy = sy;
375848b8605Smrg      tmpImage = malloc(width * height * sizeof(GLubyte));
376848b8605Smrg      if (!tmpImage) {
377848b8605Smrg         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
378848b8605Smrg         return;
379848b8605Smrg      }
380848b8605Smrg      p = tmpImage;
381848b8605Smrg      for (j = 0; j < height; j++, ssy += stepy) {
382848b8605Smrg         _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p );
383848b8605Smrg         p += width;
384848b8605Smrg      }
385848b8605Smrg      p = tmpImage;
386848b8605Smrg   }
387848b8605Smrg   else {
388848b8605Smrg      tmpImage = NULL;  /* silence compiler warning */
389848b8605Smrg      p = NULL;
390848b8605Smrg   }
391848b8605Smrg
392848b8605Smrg   stencil = malloc(width * sizeof(GLubyte));
393848b8605Smrg   if (!stencil) {
394848b8605Smrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()");
395848b8605Smrg      goto end;
396848b8605Smrg   }
397848b8605Smrg
398848b8605Smrg   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
399848b8605Smrg      /* Get stencil values */
400848b8605Smrg      if (overlapping) {
401848b8605Smrg         memcpy(stencil, p, width * sizeof(GLubyte));
402848b8605Smrg         p += width;
403848b8605Smrg      }
404848b8605Smrg      else {
405848b8605Smrg         _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil );
406848b8605Smrg      }
407848b8605Smrg
408848b8605Smrg      _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
409848b8605Smrg
410848b8605Smrg      /* Write stencil values */
411848b8605Smrg      if (zoom) {
412848b8605Smrg         _swrast_write_zoomed_stencil_span(ctx, destx, desty, width,
413848b8605Smrg                                           destx, dy, stencil);
414848b8605Smrg      }
415848b8605Smrg      else {
416848b8605Smrg         _swrast_write_stencil_span( ctx, width, destx, dy, stencil );
417848b8605Smrg      }
418848b8605Smrg   }
419848b8605Smrg
420848b8605Smrg   free(stencil);
421848b8605Smrg
422848b8605Smrgend:
423848b8605Smrg   if (overlapping)
424848b8605Smrg      free(tmpImage);
425848b8605Smrg}
426848b8605Smrg
427848b8605Smrg
428848b8605Smrg/**
429848b8605Smrg * Try to do a fast 1:1 blit with memcpy.
430848b8605Smrg * \return GL_TRUE if successful, GL_FALSE otherwise.
431848b8605Smrg */
432848b8605SmrgGLboolean
433848b8605Smrgswrast_fast_copy_pixels(struct gl_context *ctx,
434b8e80941Smrg                        struct gl_framebuffer *srcFb,
435b8e80941Smrg                        struct gl_framebuffer *dstFb,
436b8e80941Smrg                        GLint srcX, GLint srcY, GLsizei width, GLsizei height,
437b8e80941Smrg                        GLint dstX, GLint dstY, GLenum type)
438848b8605Smrg{
439848b8605Smrg   struct gl_renderbuffer *srcRb, *dstRb;
440848b8605Smrg   GLint row;
441848b8605Smrg   GLuint pixelBytes, widthInBytes;
442848b8605Smrg   GLubyte *srcMap, *dstMap;
443848b8605Smrg   GLint srcRowStride, dstRowStride;
444848b8605Smrg
445848b8605Smrg   if (type == GL_COLOR) {
446848b8605Smrg      if (dstFb->_NumColorDrawBuffers != 1)
447848b8605Smrg         return GL_FALSE;
448848b8605Smrg      srcRb = srcFb->_ColorReadBuffer;
449848b8605Smrg      dstRb = dstFb->_ColorDrawBuffers[0];
450848b8605Smrg   }
451848b8605Smrg   else if (type == GL_STENCIL) {
452848b8605Smrg      srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer;
453848b8605Smrg      dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer;
454848b8605Smrg   }
455848b8605Smrg   else if (type == GL_DEPTH) {
456848b8605Smrg      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
457848b8605Smrg      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
458848b8605Smrg   }
459848b8605Smrg   else {
460b8e80941Smrg      assert(type == GL_DEPTH_STENCIL_EXT);
461848b8605Smrg      /* XXX correct? */
462848b8605Smrg      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
463848b8605Smrg      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
464848b8605Smrg   }
465848b8605Smrg
466848b8605Smrg   /* src and dst renderbuffers must be same format */
467848b8605Smrg   if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) {
468848b8605Smrg      return GL_FALSE;
469848b8605Smrg   }
470848b8605Smrg
471848b8605Smrg   if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) {
472848b8605Smrg      /* can't handle packed depth+stencil here */
473848b8605Smrg      if (_mesa_is_format_packed_depth_stencil(srcRb->Format) ||
474848b8605Smrg          _mesa_is_format_packed_depth_stencil(dstRb->Format))
475848b8605Smrg         return GL_FALSE;
476848b8605Smrg   }
477848b8605Smrg   else if (type == GL_DEPTH_STENCIL) {
478848b8605Smrg      /* can't handle separate depth/stencil buffers */
479848b8605Smrg      if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer ||
480848b8605Smrg          dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer)
481848b8605Smrg         return GL_FALSE;
482848b8605Smrg   }
483848b8605Smrg
484848b8605Smrg   /* clipping not supported */
485848b8605Smrg   if (srcX < 0 || srcX + width > (GLint) srcFb->Width ||
486848b8605Smrg       srcY < 0 || srcY + height > (GLint) srcFb->Height ||
487848b8605Smrg       dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax ||
488848b8605Smrg       dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) {
489848b8605Smrg      return GL_FALSE;
490848b8605Smrg   }
491848b8605Smrg
492848b8605Smrg   pixelBytes = _mesa_get_format_bytes(srcRb->Format);
493848b8605Smrg   widthInBytes = width * pixelBytes;
494848b8605Smrg
495848b8605Smrg   if (srcRb == dstRb) {
496848b8605Smrg      /* map whole buffer for read/write */
497848b8605Smrg      /* XXX we could be clever and just map the union region of the
498848b8605Smrg       * source and dest rects.
499848b8605Smrg       */
500848b8605Smrg      GLubyte *map;
501848b8605Smrg      GLint rowStride;
502848b8605Smrg
503848b8605Smrg      ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
504848b8605Smrg                                  srcRb->Width, srcRb->Height,
505848b8605Smrg                                  GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
506b8e80941Smrg                                  &map, &rowStride, srcFb->FlipY);
507848b8605Smrg      if (!map) {
508848b8605Smrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
509848b8605Smrg         return GL_TRUE; /* don't retry with slow path */
510848b8605Smrg      }
511848b8605Smrg
512848b8605Smrg      srcMap = map + srcY * rowStride + srcX * pixelBytes;
513848b8605Smrg      dstMap = map + dstY * rowStride + dstX * pixelBytes;
514848b8605Smrg
515848b8605Smrg      /* this handles overlapping copies */
516848b8605Smrg      if (srcY < dstY) {
517848b8605Smrg         /* copy in reverse (top->down) order */
518848b8605Smrg         srcMap += rowStride * (height - 1);
519848b8605Smrg         dstMap += rowStride * (height - 1);
520848b8605Smrg         srcRowStride = -rowStride;
521848b8605Smrg         dstRowStride = -rowStride;
522848b8605Smrg      }
523848b8605Smrg      else {
524848b8605Smrg         /* copy in normal (bottom->up) order */
525848b8605Smrg         srcRowStride = rowStride;
526848b8605Smrg         dstRowStride = rowStride;
527848b8605Smrg      }
528848b8605Smrg   }
529848b8605Smrg   else {
530848b8605Smrg      /* different src/dst buffers */
531848b8605Smrg      ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
532848b8605Smrg                                  width, height,
533b8e80941Smrg                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride,
534b8e80941Smrg                                  srcFb->FlipY);
535848b8605Smrg      if (!srcMap) {
536848b8605Smrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
537848b8605Smrg         return GL_TRUE; /* don't retry with slow path */
538848b8605Smrg      }
539848b8605Smrg      ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
540848b8605Smrg                                  width, height,
541b8e80941Smrg                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
542b8e80941Smrg                                  dstFb->FlipY);
543848b8605Smrg      if (!dstMap) {
544848b8605Smrg         ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
545848b8605Smrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
546848b8605Smrg         return GL_TRUE; /* don't retry with slow path */
547848b8605Smrg      }
548848b8605Smrg   }
549848b8605Smrg
550848b8605Smrg   for (row = 0; row < height; row++) {
551848b8605Smrg      /* memmove() in case of overlap */
552848b8605Smrg      memmove(dstMap, srcMap, widthInBytes);
553848b8605Smrg      dstMap += dstRowStride;
554848b8605Smrg      srcMap += srcRowStride;
555848b8605Smrg   }
556848b8605Smrg
557848b8605Smrg   ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
558848b8605Smrg   if (dstRb != srcRb) {
559848b8605Smrg      ctx->Driver.UnmapRenderbuffer(ctx, dstRb);
560848b8605Smrg   }
561848b8605Smrg
562848b8605Smrg   return GL_TRUE;
563848b8605Smrg}
564848b8605Smrg
565848b8605Smrg
566848b8605Smrg/**
567848b8605Smrg * Find/map the renderbuffer that we'll be reading from.
568848b8605Smrg * The swrast_render_start() function only maps the drawing buffers,
569848b8605Smrg * not the read buffer.
570848b8605Smrg */
571848b8605Smrgstatic struct gl_renderbuffer *
572848b8605Smrgmap_readbuffer(struct gl_context *ctx, GLenum type)
573848b8605Smrg{
574848b8605Smrg   struct gl_framebuffer *fb = ctx->ReadBuffer;
575848b8605Smrg   struct gl_renderbuffer *rb;
576848b8605Smrg   struct swrast_renderbuffer *srb;
577848b8605Smrg
578848b8605Smrg   switch (type) {
579848b8605Smrg   case GL_COLOR:
580848b8605Smrg      rb = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer;
581848b8605Smrg      break;
582848b8605Smrg   case GL_DEPTH:
583848b8605Smrg   case GL_DEPTH_STENCIL:
584848b8605Smrg      rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
585848b8605Smrg      break;
586848b8605Smrg   case GL_STENCIL:
587848b8605Smrg      rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
588848b8605Smrg      break;
589848b8605Smrg   default:
590848b8605Smrg      return NULL;
591848b8605Smrg   }
592848b8605Smrg
593848b8605Smrg   srb = swrast_renderbuffer(rb);
594848b8605Smrg
595848b8605Smrg   if (!srb || srb->Map) {
596848b8605Smrg      /* no buffer, or buffer is mapped already, we're done */
597848b8605Smrg      return NULL;
598848b8605Smrg   }
599848b8605Smrg
600848b8605Smrg   ctx->Driver.MapRenderbuffer(ctx, rb,
601848b8605Smrg                               0, 0, rb->Width, rb->Height,
602848b8605Smrg                               GL_MAP_READ_BIT,
603b8e80941Smrg                               &srb->Map, &srb->RowStride,
604b8e80941Smrg                               fb->FlipY);
605848b8605Smrg
606848b8605Smrg   return rb;
607848b8605Smrg}
608848b8605Smrg
609848b8605Smrg
610848b8605Smrg/**
611848b8605Smrg * Do software-based glCopyPixels.
612848b8605Smrg * By time we get here, all parameters will have been error-checked.
613848b8605Smrg */
614848b8605Smrgvoid
615b8e80941Smrg_swrast_CopyPixels(struct gl_context *ctx,
616b8e80941Smrg                   GLint srcx, GLint srcy, GLsizei width, GLsizei height,
617b8e80941Smrg                   GLint destx, GLint desty, GLenum type)
618848b8605Smrg{
619848b8605Smrg   SWcontext *swrast = SWRAST_CONTEXT(ctx);
620848b8605Smrg   struct gl_renderbuffer *rb;
621848b8605Smrg
622848b8605Smrg   if (!_mesa_check_conditional_render(ctx))
623848b8605Smrg      return; /* don't copy */
624848b8605Smrg
625848b8605Smrg   if (swrast->NewState)
626848b8605Smrg      _swrast_validate_derived( ctx );
627848b8605Smrg
628848b8605Smrg   if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
629b8e80941Smrg       ctx->Pixel.ZoomX != 1.0F ||
630b8e80941Smrg       ctx->Pixel.ZoomY != 1.0F ||
631b8e80941Smrg       ctx->_ImageTransferState) &&
632b8e80941Smrg      swrast_fast_copy_pixels(ctx, ctx->ReadBuffer, ctx->DrawBuffer,
633b8e80941Smrg                              srcx, srcy, width, height, destx, desty,
634b8e80941Smrg                              type)) {
635848b8605Smrg      /* all done */
636848b8605Smrg      return;
637848b8605Smrg   }
638848b8605Smrg
639848b8605Smrg   swrast_render_start(ctx);
640848b8605Smrg   rb = map_readbuffer(ctx, type);
641848b8605Smrg
642848b8605Smrg   switch (type) {
643848b8605Smrg   case GL_COLOR:
644848b8605Smrg      copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
645848b8605Smrg      break;
646848b8605Smrg   case GL_DEPTH:
647848b8605Smrg      copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
648848b8605Smrg      break;
649848b8605Smrg   case GL_STENCIL:
650848b8605Smrg      copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
651848b8605Smrg      break;
652848b8605Smrg   case GL_DEPTH_STENCIL_EXT:
653848b8605Smrg      /* Copy buffers separately (if the fast copy path wasn't taken) */
654848b8605Smrg      copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty);
655848b8605Smrg      copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
656848b8605Smrg      break;
657848b8605Smrg   default:
658848b8605Smrg      _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
659848b8605Smrg   }
660848b8605Smrg
661848b8605Smrg   swrast_render_finish(ctx);
662848b8605Smrg
663848b8605Smrg   if (rb) {
664848b8605Smrg      struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
665848b8605Smrg      ctx->Driver.UnmapRenderbuffer(ctx, rb);
666848b8605Smrg      srb->Map = NULL;
667848b8605Smrg   }
668848b8605Smrg}
669