17117f1b4Smrg/*
27117f1b4Smrg * Mesa 3-D graphics library
37117f1b4Smrg *
47117f1b4Smrg * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
57117f1b4Smrg *
67117f1b4Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77117f1b4Smrg * copy of this software and associated documentation files (the "Software"),
87117f1b4Smrg * to deal in the Software without restriction, including without limitation
97117f1b4Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
107117f1b4Smrg * and/or sell copies of the Software, and to permit persons to whom the
117117f1b4Smrg * Software is furnished to do so, subject to the following conditions:
127117f1b4Smrg *
137117f1b4Smrg * The above copyright notice and this permission notice shall be included
147117f1b4Smrg * in all copies or substantial portions of the Software.
157117f1b4Smrg *
167117f1b4Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177117f1b4Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187117f1b4Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE.
237117f1b4Smrg */
247117f1b4Smrg
257117f1b4Smrg
26c1f859d4Smrg#include "main/glheader.h"
27c1f859d4Smrg#include "main/context.h"
28cdc920a0Smrg#include "main/condrender.h"
29c1f859d4Smrg#include "main/macros.h"
3001e04c3fSmrg#include "main/blit.h"
313464ebd5Sriastradh#include "main/pixeltransfer.h"
327ec681f3Smrg
337117f1b4Smrg
347117f1b4Smrg#include "s_context.h"
357117f1b4Smrg#include "s_depth.h"
367117f1b4Smrg#include "s_span.h"
377117f1b4Smrg#include "s_stencil.h"
387117f1b4Smrg#include "s_zoom.h"
397117f1b4Smrg
407117f1b4Smrg
417117f1b4Smrg
427117f1b4Smrg/**
437117f1b4Smrg * Determine if there's overlap in an image copy.
447117f1b4Smrg * This test also compensates for the fact that copies are done from
457117f1b4Smrg * bottom to top and overlaps can sometimes be handled correctly
467117f1b4Smrg * without making a temporary image copy.
477117f1b4Smrg * \return GL_TRUE if the regions overlap, GL_FALSE otherwise.
487117f1b4Smrg */
497117f1b4Smrgstatic GLboolean
507117f1b4Smrgregions_overlap(GLint srcx, GLint srcy,
517117f1b4Smrg                GLint dstx, GLint dsty,
527117f1b4Smrg                GLint width, GLint height,
537117f1b4Smrg                GLfloat zoomX, GLfloat zoomY)
547117f1b4Smrg{
5501e04c3fSmrg   if (zoomX == 1.0F && zoomY == 1.0F) {
5601e04c3fSmrg      return _mesa_regions_overlap(srcx, srcy, srcx + width, srcy + height,
5701e04c3fSmrg                                   dstx, dsty, dstx + width, dsty + height);
587117f1b4Smrg   }
597117f1b4Smrg   else {
607117f1b4Smrg      /* add one pixel of slop when zooming, just to be safe */
617117f1b4Smrg      if (srcx > (dstx + ((zoomX > 0.0F) ? (width * zoomX + 1.0F) : 0.0F))) {
627117f1b4Smrg         /* src is completely right of dest */
637117f1b4Smrg         return GL_FALSE;
647117f1b4Smrg      }
657117f1b4Smrg      else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) {
667117f1b4Smrg         /* src is completely left of dest */
677117f1b4Smrg         return GL_FALSE;
687117f1b4Smrg      }
697117f1b4Smrg      else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) {
707117f1b4Smrg         /* src is completely below dest */
717117f1b4Smrg         return GL_FALSE;
727117f1b4Smrg      }
737117f1b4Smrg      else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) {
747117f1b4Smrg         /* src is completely above dest */
757117f1b4Smrg         return GL_FALSE;
767117f1b4Smrg      }
777117f1b4Smrg      else {
787117f1b4Smrg         return GL_TRUE;
797117f1b4Smrg      }
807117f1b4Smrg   }
817117f1b4Smrg}
827117f1b4Smrg
837117f1b4Smrg
847117f1b4Smrg/**
857117f1b4Smrg * RGBA copypixels
867117f1b4Smrg */
877117f1b4Smrgstatic void
883464ebd5Sriastradhcopy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy,
897117f1b4Smrg                 GLint width, GLint height, GLint destx, GLint desty)
907117f1b4Smrg{
917117f1b4Smrg   GLfloat *tmpImage, *p;
927117f1b4Smrg   GLint sy, dy, stepy, row;
937117f1b4Smrg   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
947117f1b4Smrg   GLint overlapping;
957117f1b4Smrg   GLuint transferOps = ctx->_ImageTransferState;
967117f1b4Smrg   SWspan span;
977117f1b4Smrg
987117f1b4Smrg   if (!ctx->ReadBuffer->_ColorReadBuffer) {
997117f1b4Smrg      /* no readbuffer - OK */
1007117f1b4Smrg      return;
1017117f1b4Smrg   }
1027117f1b4Smrg
1037117f1b4Smrg   if (ctx->DrawBuffer == ctx->ReadBuffer) {
1047117f1b4Smrg      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
1057117f1b4Smrg                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
1067117f1b4Smrg   }
1077117f1b4Smrg   else {
1087117f1b4Smrg      overlapping = GL_FALSE;
1097117f1b4Smrg   }
1107117f1b4Smrg
1117117f1b4Smrg   /* Determine if copy should be done bottom-to-top or top-to-bottom */
1127117f1b4Smrg   if (!overlapping && srcy < desty) {
1137117f1b4Smrg      /* top-down  max-to-min */
1147117f1b4Smrg      sy = srcy + height - 1;
1157117f1b4Smrg      dy = desty + height - 1;
1167117f1b4Smrg      stepy = -1;
1177117f1b4Smrg   }
1187117f1b4Smrg   else {
1197117f1b4Smrg      /* bottom-up  min-to-max */
1207117f1b4Smrg      sy = srcy;
1217117f1b4Smrg      dy = desty;
1227117f1b4Smrg      stepy = 1;
1237117f1b4Smrg   }
1247117f1b4Smrg
125c1f859d4Smrg   INIT_SPAN(span, GL_BITMAP);
126c1f859d4Smrg   _swrast_span_default_attribs(ctx, &span);
127c1f859d4Smrg   span.arrayMask = SPAN_RGBA;
128af69d88dSmrg   span.arrayAttribs = VARYING_BIT_COL0; /* we'll fill in COL0 attrib values */
1297117f1b4Smrg
1307117f1b4Smrg   if (overlapping) {
131af69d88dSmrg      tmpImage = malloc(width * height * sizeof(GLfloat) * 4);
1327117f1b4Smrg      if (!tmpImage) {
1337117f1b4Smrg         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
1347117f1b4Smrg         return;
1357117f1b4Smrg      }
1367117f1b4Smrg      /* read the source image as RGBA/float */
1377117f1b4Smrg      p = tmpImage;
1387117f1b4Smrg      for (row = 0; row < height; row++) {
1397117f1b4Smrg         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
140af69d88dSmrg                                 width, srcx, sy + row, p );
1417117f1b4Smrg         p += width * 4;
1427117f1b4Smrg      }
1437117f1b4Smrg      p = tmpImage;
1447117f1b4Smrg   }
1457117f1b4Smrg   else {
1467117f1b4Smrg      tmpImage = NULL;  /* silence compiler warnings */
1477117f1b4Smrg      p = NULL;
1487117f1b4Smrg   }
1497117f1b4Smrg
15001e04c3fSmrg   assert(width < SWRAST_MAX_WIDTH);
1517117f1b4Smrg
1527117f1b4Smrg   for (row = 0; row < height; row++, sy += stepy, dy += stepy) {
153af69d88dSmrg      GLvoid *rgba = span.array->attribs[VARYING_SLOT_COL0];
1547117f1b4Smrg
1557117f1b4Smrg      /* Get row/span of source pixels */
1567117f1b4Smrg      if (overlapping) {
1577117f1b4Smrg         /* get from buffered image */
158cdc920a0Smrg         memcpy(rgba, p, width * sizeof(GLfloat) * 4);
1597117f1b4Smrg         p += width * 4;
1607117f1b4Smrg      }
1617117f1b4Smrg      else {
1627117f1b4Smrg         /* get from framebuffer */
1637117f1b4Smrg         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
164af69d88dSmrg                                 width, srcx, sy, rgba );
1657117f1b4Smrg      }
1667117f1b4Smrg
1677117f1b4Smrg      if (transferOps) {
1687117f1b4Smrg         _mesa_apply_rgba_transfer_ops(ctx, transferOps, width,
1697117f1b4Smrg                                       (GLfloat (*)[4]) rgba);
1707117f1b4Smrg      }
1717117f1b4Smrg
1727117f1b4Smrg      /* Write color span */
1737117f1b4Smrg      span.x = destx;
1747117f1b4Smrg      span.y = dy;
1757117f1b4Smrg      span.end = width;
1767117f1b4Smrg      span.array->ChanType = GL_FLOAT;
1777117f1b4Smrg      if (zoom) {
1787117f1b4Smrg         _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba);
1797117f1b4Smrg      }
1807117f1b4Smrg      else {
1817117f1b4Smrg         _swrast_write_rgba_span(ctx, &span);
1827117f1b4Smrg      }
1837117f1b4Smrg   }
1847117f1b4Smrg
1857117f1b4Smrg   span.array->ChanType = CHAN_TYPE; /* restore */
1867117f1b4Smrg
1877117f1b4Smrg   if (overlapping)
188cdc920a0Smrg      free(tmpImage);
1897117f1b4Smrg}
1907117f1b4Smrg
1917117f1b4Smrg
1927117f1b4Smrg/**
1937117f1b4Smrg * Convert floating point Z values to integer Z values with pixel transfer's
1947117f1b4Smrg * Z scale and bias.
1957117f1b4Smrg */
1967117f1b4Smrgstatic void
1973464ebd5Sriastradhscale_and_bias_z(struct gl_context *ctx, GLuint width,
1987117f1b4Smrg                 const GLfloat depth[], GLuint z[])
1997117f1b4Smrg{
2007117f1b4Smrg   const GLuint depthMax = ctx->DrawBuffer->_DepthMax;
2017117f1b4Smrg   GLuint i;
2027117f1b4Smrg
2037117f1b4Smrg   if (depthMax <= 0xffffff &&
20401e04c3fSmrg       ctx->Pixel.DepthScale == 1.0F &&
20501e04c3fSmrg       ctx->Pixel.DepthBias == 0.0F) {
2067117f1b4Smrg      /* no scale or bias and no clamping and no worry of overflow */
2077117f1b4Smrg      const GLfloat depthMaxF = ctx->DrawBuffer->_DepthMaxF;
2087117f1b4Smrg      for (i = 0; i < width; i++) {
2097117f1b4Smrg         z[i] = (GLuint) (depth[i] * depthMaxF);
2107117f1b4Smrg      }
2117117f1b4Smrg   }
2127117f1b4Smrg   else {
2137117f1b4Smrg      /* need to be careful with overflow */
2147117f1b4Smrg      const GLdouble depthMaxF = ctx->DrawBuffer->_DepthMaxF;
2157117f1b4Smrg      for (i = 0; i < width; i++) {
2167117f1b4Smrg         GLdouble d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias;
2177ec681f3Smrg         d = SATURATE(d) * depthMaxF;
2187117f1b4Smrg         if (d >= depthMaxF)
2197117f1b4Smrg            z[i] = depthMax;
2207117f1b4Smrg         else
2217117f1b4Smrg            z[i] = (GLuint) d;
2227117f1b4Smrg      }
2237117f1b4Smrg   }
2247117f1b4Smrg}
2257117f1b4Smrg
2267117f1b4Smrg
2277117f1b4Smrg
2287117f1b4Smrg/*
2297117f1b4Smrg * TODO: Optimize!!!!
2307117f1b4Smrg */
2317117f1b4Smrgstatic void
2323464ebd5Sriastradhcopy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
2337117f1b4Smrg                   GLint width, GLint height,
2347117f1b4Smrg                   GLint destx, GLint desty )
2357117f1b4Smrg{
2367117f1b4Smrg   struct gl_framebuffer *fb = ctx->ReadBuffer;
237af69d88dSmrg   struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
238af69d88dSmrg   GLfloat *p, *tmpImage, *depth;
2397117f1b4Smrg   GLint sy, dy, stepy;
2407117f1b4Smrg   GLint j;
2417117f1b4Smrg   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
2427117f1b4Smrg   GLint overlapping;
2437117f1b4Smrg   SWspan span;
2447117f1b4Smrg
2457117f1b4Smrg   if (!readRb) {
2467117f1b4Smrg      /* no readbuffer - OK */
2477117f1b4Smrg      return;
2487117f1b4Smrg   }
2497117f1b4Smrg
250c1f859d4Smrg   INIT_SPAN(span, GL_BITMAP);
251c1f859d4Smrg   _swrast_span_default_attribs(ctx, &span);
252c1f859d4Smrg   span.arrayMask = SPAN_Z;
2537117f1b4Smrg
2547117f1b4Smrg   if (ctx->DrawBuffer == ctx->ReadBuffer) {
2557117f1b4Smrg      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
2567117f1b4Smrg                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
2577117f1b4Smrg   }
2587117f1b4Smrg   else {
2597117f1b4Smrg      overlapping = GL_FALSE;
2607117f1b4Smrg   }
2617117f1b4Smrg
2627117f1b4Smrg   /* Determine if copy should be bottom-to-top or top-to-bottom */
2637117f1b4Smrg   if (!overlapping && srcy < desty) {
2647117f1b4Smrg      /* top-down  max-to-min */
2657117f1b4Smrg      sy = srcy + height - 1;
2667117f1b4Smrg      dy = desty + height - 1;
2677117f1b4Smrg      stepy = -1;
2687117f1b4Smrg   }
2697117f1b4Smrg   else {
2707117f1b4Smrg      /* bottom-up  min-to-max */
2717117f1b4Smrg      sy = srcy;
2727117f1b4Smrg      dy = desty;
2737117f1b4Smrg      stepy = 1;
2747117f1b4Smrg   }
2757117f1b4Smrg
2767117f1b4Smrg   if (overlapping) {
2777117f1b4Smrg      GLint ssy = sy;
278af69d88dSmrg      tmpImage = malloc(width * height * sizeof(GLfloat));
2797117f1b4Smrg      if (!tmpImage) {
2807117f1b4Smrg         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
2817117f1b4Smrg         return;
2827117f1b4Smrg      }
2837117f1b4Smrg      p = tmpImage;
2847117f1b4Smrg      for (j = 0; j < height; j++, ssy += stepy) {
2857117f1b4Smrg         _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p);
2867117f1b4Smrg         p += width;
2877117f1b4Smrg      }
2887117f1b4Smrg      p = tmpImage;
2897117f1b4Smrg   }
2907117f1b4Smrg   else {
2917117f1b4Smrg      tmpImage = NULL;  /* silence compiler warning */
2927117f1b4Smrg      p = NULL;
2937117f1b4Smrg   }
2947117f1b4Smrg
295af69d88dSmrg   depth = malloc(width * sizeof(GLfloat));
296af69d88dSmrg   if (!depth) {
297af69d88dSmrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()");
298af69d88dSmrg      goto end;
299af69d88dSmrg   }
300af69d88dSmrg
3017117f1b4Smrg   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
3027117f1b4Smrg      /* get depth values */
3037117f1b4Smrg      if (overlapping) {
304cdc920a0Smrg         memcpy(depth, p, width * sizeof(GLfloat));
3057117f1b4Smrg         p += width;
3067117f1b4Smrg      }
3077117f1b4Smrg      else {
3087117f1b4Smrg         _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth);
3097117f1b4Smrg      }
3107117f1b4Smrg
3117117f1b4Smrg      /* apply scale and bias */
3127117f1b4Smrg      scale_and_bias_z(ctx, width, depth, span.array->z);
3137117f1b4Smrg
3147117f1b4Smrg      /* write depth values */
3157117f1b4Smrg      span.x = destx;
3167117f1b4Smrg      span.y = dy;
3177117f1b4Smrg      span.end = width;
318cdc920a0Smrg      if (zoom)
319cdc920a0Smrg         _swrast_write_zoomed_depth_span(ctx, destx, desty, &span);
320cdc920a0Smrg      else
321cdc920a0Smrg         _swrast_write_rgba_span(ctx, &span);
3227117f1b4Smrg   }
3237117f1b4Smrg
324af69d88dSmrg   free(depth);
325af69d88dSmrg
326af69d88dSmrgend:
3277117f1b4Smrg   if (overlapping)
328cdc920a0Smrg      free(tmpImage);
3297117f1b4Smrg}
3307117f1b4Smrg
3317117f1b4Smrg
3327117f1b4Smrg
3337117f1b4Smrgstatic void
3343464ebd5Sriastradhcopy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
3357117f1b4Smrg                     GLint width, GLint height,
3367117f1b4Smrg                     GLint destx, GLint desty )
3377117f1b4Smrg{
3387117f1b4Smrg   struct gl_framebuffer *fb = ctx->ReadBuffer;
339af69d88dSmrg   struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
3407117f1b4Smrg   GLint sy, dy, stepy;
3417117f1b4Smrg   GLint j;
342af69d88dSmrg   GLubyte *p, *tmpImage, *stencil;
3437117f1b4Smrg   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
3447117f1b4Smrg   GLint overlapping;
3457117f1b4Smrg
3467117f1b4Smrg   if (!rb) {
3477117f1b4Smrg      /* no readbuffer - OK */
3487117f1b4Smrg      return;
3497117f1b4Smrg   }
3507117f1b4Smrg
3517117f1b4Smrg   if (ctx->DrawBuffer == ctx->ReadBuffer) {
3527117f1b4Smrg      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
3537117f1b4Smrg                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
3547117f1b4Smrg   }
3557117f1b4Smrg   else {
3567117f1b4Smrg      overlapping = GL_FALSE;
3577117f1b4Smrg   }
3587117f1b4Smrg
3597117f1b4Smrg   /* Determine if copy should be bottom-to-top or top-to-bottom */
3607117f1b4Smrg   if (!overlapping && srcy < desty) {
3617117f1b4Smrg      /* top-down  max-to-min */
3627117f1b4Smrg      sy = srcy + height - 1;
3637117f1b4Smrg      dy = desty + height - 1;
3647117f1b4Smrg      stepy = -1;
3657117f1b4Smrg   }
3667117f1b4Smrg   else {
3677117f1b4Smrg      /* bottom-up  min-to-max */
3687117f1b4Smrg      sy = srcy;
3697117f1b4Smrg      dy = desty;
3707117f1b4Smrg      stepy = 1;
3717117f1b4Smrg   }
3727117f1b4Smrg
3737117f1b4Smrg   if (overlapping) {
3747117f1b4Smrg      GLint ssy = sy;
375af69d88dSmrg      tmpImage = malloc(width * height * sizeof(GLubyte));
3767117f1b4Smrg      if (!tmpImage) {
3777117f1b4Smrg         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
3787117f1b4Smrg         return;
3797117f1b4Smrg      }
3807117f1b4Smrg      p = tmpImage;
3817117f1b4Smrg      for (j = 0; j < height; j++, ssy += stepy) {
3827117f1b4Smrg         _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p );
3837117f1b4Smrg         p += width;
3847117f1b4Smrg      }
3857117f1b4Smrg      p = tmpImage;
3867117f1b4Smrg   }
3877117f1b4Smrg   else {
3887117f1b4Smrg      tmpImage = NULL;  /* silence compiler warning */
3897117f1b4Smrg      p = NULL;
3907117f1b4Smrg   }
3917117f1b4Smrg
392af69d88dSmrg   stencil = malloc(width * sizeof(GLubyte));
393af69d88dSmrg   if (!stencil) {
394af69d88dSmrg      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()");
395af69d88dSmrg      goto end;
396af69d88dSmrg   }
3977117f1b4Smrg
398af69d88dSmrg   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
3997117f1b4Smrg      /* Get stencil values */
4007117f1b4Smrg      if (overlapping) {
401af69d88dSmrg         memcpy(stencil, p, width * sizeof(GLubyte));
4027117f1b4Smrg         p += width;
4037117f1b4Smrg      }
4047117f1b4Smrg      else {
4057117f1b4Smrg         _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil );
4067117f1b4Smrg      }
4077117f1b4Smrg
4087117f1b4Smrg      _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
4097117f1b4Smrg
4107117f1b4Smrg      /* Write stencil values */
4117117f1b4Smrg      if (zoom) {
4127117f1b4Smrg         _swrast_write_zoomed_stencil_span(ctx, destx, desty, width,
4137117f1b4Smrg                                           destx, dy, stencil);
4147117f1b4Smrg      }
4157117f1b4Smrg      else {
4167117f1b4Smrg         _swrast_write_stencil_span( ctx, width, destx, dy, stencil );
4177117f1b4Smrg      }
4187117f1b4Smrg   }
4197117f1b4Smrg
420af69d88dSmrg   free(stencil);
421af69d88dSmrg
422af69d88dSmrgend:
4237117f1b4Smrg   if (overlapping)
424cdc920a0Smrg      free(tmpImage);
4257117f1b4Smrg}
4267117f1b4Smrg
4277117f1b4Smrg
4287117f1b4Smrg/**
429af69d88dSmrg * Try to do a fast 1:1 blit with memcpy.
430af69d88dSmrg * \return GL_TRUE if successful, GL_FALSE otherwise.
4317117f1b4Smrg */
432af69d88dSmrgGLboolean
433af69d88dSmrgswrast_fast_copy_pixels(struct gl_context *ctx,
43401e04c3fSmrg                        struct gl_framebuffer *srcFb,
43501e04c3fSmrg                        struct gl_framebuffer *dstFb,
43601e04c3fSmrg                        GLint srcX, GLint srcY, GLsizei width, GLsizei height,
43701e04c3fSmrg                        GLint dstX, GLint dstY, GLenum type)
4387117f1b4Smrg{
4397117f1b4Smrg   struct gl_renderbuffer *srcRb, *dstRb;
440af69d88dSmrg   GLint row;
441af69d88dSmrg   GLuint pixelBytes, widthInBytes;
442af69d88dSmrg   GLubyte *srcMap, *dstMap;
443af69d88dSmrg   GLint srcRowStride, dstRowStride;
4447117f1b4Smrg
4457117f1b4Smrg   if (type == GL_COLOR) {
446c1f859d4Smrg      if (dstFb->_NumColorDrawBuffers != 1)
4477117f1b4Smrg         return GL_FALSE;
4487117f1b4Smrg      srcRb = srcFb->_ColorReadBuffer;
449c1f859d4Smrg      dstRb = dstFb->_ColorDrawBuffers[0];
4507117f1b4Smrg   }
4517117f1b4Smrg   else if (type == GL_STENCIL) {
452af69d88dSmrg      srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer;
453af69d88dSmrg      dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer;
4547117f1b4Smrg   }
4557117f1b4Smrg   else if (type == GL_DEPTH) {
456af69d88dSmrg      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
457af69d88dSmrg      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
4587117f1b4Smrg   }
4597117f1b4Smrg   else {
46001e04c3fSmrg      assert(type == GL_DEPTH_STENCIL_EXT);
4617117f1b4Smrg      /* XXX correct? */
4627117f1b4Smrg      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
4637117f1b4Smrg      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
4647117f1b4Smrg   }
4657117f1b4Smrg
466af69d88dSmrg   /* src and dst renderbuffers must be same format */
467af69d88dSmrg   if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) {
4687117f1b4Smrg      return GL_FALSE;
4697117f1b4Smrg   }
4707117f1b4Smrg
471af69d88dSmrg   if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) {
472af69d88dSmrg      /* can't handle packed depth+stencil here */
473af69d88dSmrg      if (_mesa_is_format_packed_depth_stencil(srcRb->Format) ||
474af69d88dSmrg          _mesa_is_format_packed_depth_stencil(dstRb->Format))
475af69d88dSmrg         return GL_FALSE;
476af69d88dSmrg   }
477af69d88dSmrg   else if (type == GL_DEPTH_STENCIL) {
478af69d88dSmrg      /* can't handle separate depth/stencil buffers */
479af69d88dSmrg      if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer ||
480af69d88dSmrg          dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer)
481af69d88dSmrg         return GL_FALSE;
482af69d88dSmrg   }
483af69d88dSmrg
4847117f1b4Smrg   /* clipping not supported */
4857117f1b4Smrg   if (srcX < 0 || srcX + width > (GLint) srcFb->Width ||
4867117f1b4Smrg       srcY < 0 || srcY + height > (GLint) srcFb->Height ||
4877117f1b4Smrg       dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax ||
4887117f1b4Smrg       dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) {
4897117f1b4Smrg      return GL_FALSE;
4907117f1b4Smrg   }
4917117f1b4Smrg
492af69d88dSmrg   pixelBytes = _mesa_get_format_bytes(srcRb->Format);
493af69d88dSmrg   widthInBytes = width * pixelBytes;
494af69d88dSmrg
495af69d88dSmrg   if (srcRb == dstRb) {
496af69d88dSmrg      /* map whole buffer for read/write */
497af69d88dSmrg      /* XXX we could be clever and just map the union region of the
498af69d88dSmrg       * source and dest rects.
499af69d88dSmrg       */
500af69d88dSmrg      GLubyte *map;
501af69d88dSmrg      GLint rowStride;
502af69d88dSmrg
503af69d88dSmrg      ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
504af69d88dSmrg                                  srcRb->Width, srcRb->Height,
505af69d88dSmrg                                  GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
50601e04c3fSmrg                                  &map, &rowStride, srcFb->FlipY);
507af69d88dSmrg      if (!map) {
508af69d88dSmrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
509af69d88dSmrg         return GL_TRUE; /* don't retry with slow path */
510af69d88dSmrg      }
511af69d88dSmrg
512af69d88dSmrg      srcMap = map + srcY * rowStride + srcX * pixelBytes;
513af69d88dSmrg      dstMap = map + dstY * rowStride + dstX * pixelBytes;
514af69d88dSmrg
515af69d88dSmrg      /* this handles overlapping copies */
516af69d88dSmrg      if (srcY < dstY) {
517af69d88dSmrg         /* copy in reverse (top->down) order */
518af69d88dSmrg         srcMap += rowStride * (height - 1);
519af69d88dSmrg         dstMap += rowStride * (height - 1);
520af69d88dSmrg         srcRowStride = -rowStride;
521af69d88dSmrg         dstRowStride = -rowStride;
522af69d88dSmrg      }
523af69d88dSmrg      else {
524af69d88dSmrg         /* copy in normal (bottom->up) order */
525af69d88dSmrg         srcRowStride = rowStride;
526af69d88dSmrg         dstRowStride = rowStride;
527af69d88dSmrg      }
5287117f1b4Smrg   }
5297117f1b4Smrg   else {
530af69d88dSmrg      /* different src/dst buffers */
531af69d88dSmrg      ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
532af69d88dSmrg                                  width, height,
53301e04c3fSmrg                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride,
53401e04c3fSmrg                                  srcFb->FlipY);
535af69d88dSmrg      if (!srcMap) {
536af69d88dSmrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
537af69d88dSmrg         return GL_TRUE; /* don't retry with slow path */
538af69d88dSmrg      }
539af69d88dSmrg      ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
540af69d88dSmrg                                  width, height,
54101e04c3fSmrg                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
54201e04c3fSmrg                                  dstFb->FlipY);
543af69d88dSmrg      if (!dstMap) {
544af69d88dSmrg         ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
545af69d88dSmrg         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
546af69d88dSmrg         return GL_TRUE; /* don't retry with slow path */
547af69d88dSmrg      }
5487117f1b4Smrg   }
5497117f1b4Smrg
5507117f1b4Smrg   for (row = 0; row < height; row++) {
551af69d88dSmrg      /* memmove() in case of overlap */
552af69d88dSmrg      memmove(dstMap, srcMap, widthInBytes);
553af69d88dSmrg      dstMap += dstRowStride;
554af69d88dSmrg      srcMap += srcRowStride;
555af69d88dSmrg   }
556af69d88dSmrg
557af69d88dSmrg   ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
558af69d88dSmrg   if (dstRb != srcRb) {
559af69d88dSmrg      ctx->Driver.UnmapRenderbuffer(ctx, dstRb);
5607117f1b4Smrg   }
5617117f1b4Smrg
5627117f1b4Smrg   return GL_TRUE;
5637117f1b4Smrg}
5647117f1b4Smrg
5657117f1b4Smrg
566af69d88dSmrg/**
567af69d88dSmrg * Find/map the renderbuffer that we'll be reading from.
568af69d88dSmrg * The swrast_render_start() function only maps the drawing buffers,
569af69d88dSmrg * not the read buffer.
570af69d88dSmrg */
571af69d88dSmrgstatic struct gl_renderbuffer *
572af69d88dSmrgmap_readbuffer(struct gl_context *ctx, GLenum type)
573af69d88dSmrg{
574af69d88dSmrg   struct gl_framebuffer *fb = ctx->ReadBuffer;
575af69d88dSmrg   struct gl_renderbuffer *rb;
576af69d88dSmrg   struct swrast_renderbuffer *srb;
577af69d88dSmrg
578af69d88dSmrg   switch (type) {
579af69d88dSmrg   case GL_COLOR:
580af69d88dSmrg      rb = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer;
581af69d88dSmrg      break;
582af69d88dSmrg   case GL_DEPTH:
583af69d88dSmrg   case GL_DEPTH_STENCIL:
584af69d88dSmrg      rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
585af69d88dSmrg      break;
586af69d88dSmrg   case GL_STENCIL:
587af69d88dSmrg      rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
588af69d88dSmrg      break;
589af69d88dSmrg   default:
590af69d88dSmrg      return NULL;
591af69d88dSmrg   }
592af69d88dSmrg
593af69d88dSmrg   srb = swrast_renderbuffer(rb);
594af69d88dSmrg
595af69d88dSmrg   if (!srb || srb->Map) {
596af69d88dSmrg      /* no buffer, or buffer is mapped already, we're done */
597af69d88dSmrg      return NULL;
598af69d88dSmrg   }
599af69d88dSmrg
600af69d88dSmrg   ctx->Driver.MapRenderbuffer(ctx, rb,
601af69d88dSmrg                               0, 0, rb->Width, rb->Height,
602af69d88dSmrg                               GL_MAP_READ_BIT,
60301e04c3fSmrg                               &srb->Map, &srb->RowStride,
60401e04c3fSmrg                               fb->FlipY);
605af69d88dSmrg
606af69d88dSmrg   return rb;
607af69d88dSmrg}
608af69d88dSmrg
609af69d88dSmrg
6107117f1b4Smrg/**
6117117f1b4Smrg * Do software-based glCopyPixels.
6127117f1b4Smrg * By time we get here, all parameters will have been error-checked.
6137117f1b4Smrg */
6147117f1b4Smrgvoid
61501e04c3fSmrg_swrast_CopyPixels(struct gl_context *ctx,
61601e04c3fSmrg                   GLint srcx, GLint srcy, GLsizei width, GLsizei height,
61701e04c3fSmrg                   GLint destx, GLint desty, GLenum type)
6187117f1b4Smrg{
6197117f1b4Smrg   SWcontext *swrast = SWRAST_CONTEXT(ctx);
620af69d88dSmrg   struct gl_renderbuffer *rb;
6217ec681f3Smrg
622cdc920a0Smrg   if (!_mesa_check_conditional_render(ctx))
623cdc920a0Smrg      return; /* don't copy */
624cdc920a0Smrg
6257117f1b4Smrg   if (swrast->NewState)
6267117f1b4Smrg      _swrast_validate_derived( ctx );
6277117f1b4Smrg
628af69d88dSmrg   if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
62901e04c3fSmrg       ctx->Pixel.ZoomX != 1.0F ||
63001e04c3fSmrg       ctx->Pixel.ZoomY != 1.0F ||
63101e04c3fSmrg       ctx->_ImageTransferState) &&
63201e04c3fSmrg      swrast_fast_copy_pixels(ctx, ctx->ReadBuffer, ctx->DrawBuffer,
63301e04c3fSmrg                              srcx, srcy, width, height, destx, desty,
63401e04c3fSmrg                              type)) {
635af69d88dSmrg      /* all done */
636af69d88dSmrg      return;
637af69d88dSmrg   }
638af69d88dSmrg
639af69d88dSmrg   swrast_render_start(ctx);
640af69d88dSmrg   rb = map_readbuffer(ctx, type);
641af69d88dSmrg
642af69d88dSmrg   switch (type) {
643af69d88dSmrg   case GL_COLOR:
644af69d88dSmrg      copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
645af69d88dSmrg      break;
646af69d88dSmrg   case GL_DEPTH:
647af69d88dSmrg      copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
648af69d88dSmrg      break;
649af69d88dSmrg   case GL_STENCIL:
650af69d88dSmrg      copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
651af69d88dSmrg      break;
652af69d88dSmrg   case GL_DEPTH_STENCIL_EXT:
653af69d88dSmrg      /* Copy buffers separately (if the fast copy path wasn't taken) */
654af69d88dSmrg      copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty);
655af69d88dSmrg      copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
656af69d88dSmrg      break;
657af69d88dSmrg   default:
658af69d88dSmrg      _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
6597117f1b4Smrg   }
6607117f1b4Smrg
6614a49301eSmrg   swrast_render_finish(ctx);
662af69d88dSmrg
663af69d88dSmrg   if (rb) {
664af69d88dSmrg      struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
665af69d88dSmrg      ctx->Driver.UnmapRenderbuffer(ctx, rb);
666af69d88dSmrg      srb->Map = NULL;
667af69d88dSmrg   }
6687117f1b4Smrg}
669