s_copypix.c revision 267fb178844d3f17503dd0f921791f3ab059c4e7
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.1
4 *
5 * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26#include "main/glheader.h"
27#include "main/context.h"
28#include "main/colormac.h"
29#include "main/condrender.h"
30#include "main/macros.h"
31#include "main/pixeltransfer.h"
32#include "main/imports.h"
33
34#include "s_context.h"
35#include "s_depth.h"
36#include "s_span.h"
37#include "s_stencil.h"
38#include "s_zoom.h"
39
40
41
42/**
43 * Determine if there's overlap in an image copy.
44 * This test also compensates for the fact that copies are done from
45 * bottom to top and overlaps can sometimes be handled correctly
46 * without making a temporary image copy.
47 * \return GL_TRUE if the regions overlap, GL_FALSE otherwise.
48 */
49static GLboolean
50regions_overlap(GLint srcx, GLint srcy,
51                GLint dstx, GLint dsty,
52                GLint width, GLint height,
53                GLfloat zoomX, GLfloat zoomY)
54{
55   if (zoomX == 1.0 && zoomY == 1.0) {
56      /* no zoom */
57      if (srcx >= dstx + width || (srcx + width <= dstx)) {
58         return GL_FALSE;
59      }
60      else if (srcy < dsty) { /* this is OK */
61         return GL_FALSE;
62      }
63      else if (srcy > dsty + height) {
64         return GL_FALSE;
65      }
66      else {
67         return GL_TRUE;
68      }
69   }
70   else {
71      /* add one pixel of slop when zooming, just to be safe */
72      if (srcx > (dstx + ((zoomX > 0.0F) ? (width * zoomX + 1.0F) : 0.0F))) {
73         /* src is completely right of dest */
74         return GL_FALSE;
75      }
76      else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) {
77         /* src is completely left of dest */
78         return GL_FALSE;
79      }
80      else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) {
81         /* src is completely below dest */
82         return GL_FALSE;
83      }
84      else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) {
85         /* src is completely above dest */
86         return GL_FALSE;
87      }
88      else {
89         return GL_TRUE;
90      }
91   }
92}
93
94
95/**
96 * RGBA copypixels
97 */
98static void
99copy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy,
100                 GLint width, GLint height, GLint destx, GLint desty)
101{
102   GLfloat *tmpImage, *p;
103   GLint sy, dy, stepy, row;
104   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
105   GLint overlapping;
106   GLuint transferOps = ctx->_ImageTransferState;
107   SWspan span;
108
109   if (!ctx->ReadBuffer->_ColorReadBuffer) {
110      /* no readbuffer - OK */
111      return;
112   }
113
114   if (ctx->DrawBuffer == ctx->ReadBuffer) {
115      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
116                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
117   }
118   else {
119      overlapping = GL_FALSE;
120   }
121
122   /* Determine if copy should be done bottom-to-top or top-to-bottom */
123   if (!overlapping && srcy < desty) {
124      /* top-down  max-to-min */
125      sy = srcy + height - 1;
126      dy = desty + height - 1;
127      stepy = -1;
128   }
129   else {
130      /* bottom-up  min-to-max */
131      sy = srcy;
132      dy = desty;
133      stepy = 1;
134   }
135
136   INIT_SPAN(span, GL_BITMAP);
137   _swrast_span_default_attribs(ctx, &span);
138   span.arrayMask = SPAN_RGBA;
139   span.arrayAttribs = FRAG_BIT_COL0; /* we'll fill in COL0 attrib values */
140
141   if (overlapping) {
142      tmpImage = (GLfloat *) malloc(width * height * sizeof(GLfloat) * 4);
143      if (!tmpImage) {
144         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
145         return;
146      }
147      /* read the source image as RGBA/float */
148      p = tmpImage;
149      for (row = 0; row < height; row++) {
150         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
151                                 width, srcx, sy + row, p );
152         p += width * 4;
153      }
154      p = tmpImage;
155   }
156   else {
157      tmpImage = NULL;  /* silence compiler warnings */
158      p = NULL;
159   }
160
161   ASSERT(width < MAX_WIDTH);
162
163   for (row = 0; row < height; row++, sy += stepy, dy += stepy) {
164      GLvoid *rgba = span.array->attribs[FRAG_ATTRIB_COL0];
165
166      /* Get row/span of source pixels */
167      if (overlapping) {
168         /* get from buffered image */
169         memcpy(rgba, p, width * sizeof(GLfloat) * 4);
170         p += width * 4;
171      }
172      else {
173         /* get from framebuffer */
174         _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
175                                 width, srcx, sy, rgba );
176      }
177
178      if (transferOps) {
179         _mesa_apply_rgba_transfer_ops(ctx, transferOps, width,
180                                       (GLfloat (*)[4]) rgba);
181      }
182
183      /* Write color span */
184      span.x = destx;
185      span.y = dy;
186      span.end = width;
187      span.array->ChanType = GL_FLOAT;
188      if (zoom) {
189         _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba);
190      }
191      else {
192         _swrast_write_rgba_span(ctx, &span);
193      }
194   }
195
196   span.array->ChanType = CHAN_TYPE; /* restore */
197
198   if (overlapping)
199      free(tmpImage);
200}
201
202
203/**
204 * Convert floating point Z values to integer Z values with pixel transfer's
205 * Z scale and bias.
206 */
207static void
208scale_and_bias_z(struct gl_context *ctx, GLuint width,
209                 const GLfloat depth[], GLuint z[])
210{
211   const GLuint depthMax = ctx->DrawBuffer->_DepthMax;
212   GLuint i;
213
214   if (depthMax <= 0xffffff &&
215       ctx->Pixel.DepthScale == 1.0 &&
216       ctx->Pixel.DepthBias == 0.0) {
217      /* no scale or bias and no clamping and no worry of overflow */
218      const GLfloat depthMaxF = ctx->DrawBuffer->_DepthMaxF;
219      for (i = 0; i < width; i++) {
220         z[i] = (GLuint) (depth[i] * depthMaxF);
221      }
222   }
223   else {
224      /* need to be careful with overflow */
225      const GLdouble depthMaxF = ctx->DrawBuffer->_DepthMaxF;
226      for (i = 0; i < width; i++) {
227         GLdouble d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias;
228         d = CLAMP(d, 0.0, 1.0) * depthMaxF;
229         if (d >= depthMaxF)
230            z[i] = depthMax;
231         else
232            z[i] = (GLuint) d;
233      }
234   }
235}
236
237
238
239/*
240 * TODO: Optimize!!!!
241 */
242static void
243copy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
244                   GLint width, GLint height,
245                   GLint destx, GLint desty )
246{
247   struct gl_framebuffer *fb = ctx->ReadBuffer;
248   struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
249   GLfloat *p, *tmpImage;
250   GLint sy, dy, stepy;
251   GLint j;
252   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
253   GLint overlapping;
254   SWspan span;
255
256   if (!readRb) {
257      /* no readbuffer - OK */
258      return;
259   }
260
261   INIT_SPAN(span, GL_BITMAP);
262   _swrast_span_default_attribs(ctx, &span);
263   span.arrayMask = SPAN_Z;
264
265   if (ctx->DrawBuffer == ctx->ReadBuffer) {
266      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
267                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
268   }
269   else {
270      overlapping = GL_FALSE;
271   }
272
273   /* Determine if copy should be bottom-to-top or top-to-bottom */
274   if (!overlapping && srcy < desty) {
275      /* top-down  max-to-min */
276      sy = srcy + height - 1;
277      dy = desty + height - 1;
278      stepy = -1;
279   }
280   else {
281      /* bottom-up  min-to-max */
282      sy = srcy;
283      dy = desty;
284      stepy = 1;
285   }
286
287   if (overlapping) {
288      GLint ssy = sy;
289      tmpImage = (GLfloat *) malloc(width * height * sizeof(GLfloat));
290      if (!tmpImage) {
291         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
292         return;
293      }
294      p = tmpImage;
295      for (j = 0; j < height; j++, ssy += stepy) {
296         _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p);
297         p += width;
298      }
299      p = tmpImage;
300   }
301   else {
302      tmpImage = NULL;  /* silence compiler warning */
303      p = NULL;
304   }
305
306   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
307      GLfloat depth[MAX_WIDTH];
308      /* get depth values */
309      if (overlapping) {
310         memcpy(depth, p, width * sizeof(GLfloat));
311         p += width;
312      }
313      else {
314         _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth);
315      }
316
317      /* apply scale and bias */
318      scale_and_bias_z(ctx, width, depth, span.array->z);
319
320      /* write depth values */
321      span.x = destx;
322      span.y = dy;
323      span.end = width;
324      if (zoom)
325         _swrast_write_zoomed_depth_span(ctx, destx, desty, &span);
326      else
327         _swrast_write_rgba_span(ctx, &span);
328   }
329
330   if (overlapping)
331      free(tmpImage);
332}
333
334
335
336static void
337copy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
338                     GLint width, GLint height,
339                     GLint destx, GLint desty )
340{
341   struct gl_framebuffer *fb = ctx->ReadBuffer;
342   struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
343   GLint sy, dy, stepy;
344   GLint j;
345   GLubyte *p, *tmpImage;
346   const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
347   GLint overlapping;
348
349   if (!rb) {
350      /* no readbuffer - OK */
351      return;
352   }
353
354   if (ctx->DrawBuffer == ctx->ReadBuffer) {
355      overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
356                                    ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
357   }
358   else {
359      overlapping = GL_FALSE;
360   }
361
362   /* Determine if copy should be bottom-to-top or top-to-bottom */
363   if (!overlapping && srcy < desty) {
364      /* top-down  max-to-min */
365      sy = srcy + height - 1;
366      dy = desty + height - 1;
367      stepy = -1;
368   }
369   else {
370      /* bottom-up  min-to-max */
371      sy = srcy;
372      dy = desty;
373      stepy = 1;
374   }
375
376   if (overlapping) {
377      GLint ssy = sy;
378      tmpImage = (GLubyte *) malloc(width * height * sizeof(GLubyte));
379      if (!tmpImage) {
380         _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
381         return;
382      }
383      p = tmpImage;
384      for (j = 0; j < height; j++, ssy += stepy) {
385         _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p );
386         p += width;
387      }
388      p = tmpImage;
389   }
390   else {
391      tmpImage = NULL;  /* silence compiler warning */
392      p = NULL;
393   }
394
395   for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
396      GLubyte stencil[MAX_WIDTH];
397
398      /* Get stencil values */
399      if (overlapping) {
400         memcpy(stencil, p, width * sizeof(GLubyte));
401         p += width;
402      }
403      else {
404         _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil );
405      }
406
407      _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
408
409      /* Write stencil values */
410      if (zoom) {
411         _swrast_write_zoomed_stencil_span(ctx, destx, desty, width,
412                                           destx, dy, stencil);
413      }
414      else {
415         _swrast_write_stencil_span( ctx, width, destx, dy, stencil );
416      }
417   }
418
419   if (overlapping)
420      free(tmpImage);
421}
422
423
424/**
425 * Try to do a fast 1:1 blit with memcpy.
426 * \return GL_TRUE if successful, GL_FALSE otherwise.
427 */
428GLboolean
429swrast_fast_copy_pixels(struct gl_context *ctx,
430			GLint srcX, GLint srcY, GLsizei width, GLsizei height,
431			GLint dstX, GLint dstY, GLenum type)
432{
433   struct gl_framebuffer *srcFb = ctx->ReadBuffer;
434   struct gl_framebuffer *dstFb = ctx->DrawBuffer;
435   struct gl_renderbuffer *srcRb, *dstRb;
436   GLint row;
437   GLuint pixelBytes, widthInBytes;
438   GLubyte *srcMap, *dstMap;
439   GLint srcRowStride, dstRowStride;
440
441   if (type == GL_COLOR) {
442      if (dstFb->_NumColorDrawBuffers != 1)
443         return GL_FALSE;
444      srcRb = srcFb->_ColorReadBuffer;
445      dstRb = dstFb->_ColorDrawBuffers[0];
446   }
447   else if (type == GL_STENCIL) {
448      srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer;
449      dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer;
450   }
451   else if (type == GL_DEPTH) {
452      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
453      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
454   }
455   else {
456      ASSERT(type == GL_DEPTH_STENCIL_EXT);
457      /* XXX correct? */
458      srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
459      dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
460   }
461
462   /* src and dst renderbuffers must be same format */
463   if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) {
464      return GL_FALSE;
465   }
466
467   if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) {
468      /* can't handle packed depth+stencil here */
469      if (_mesa_is_format_packed_depth_stencil(srcRb->Format) ||
470          _mesa_is_format_packed_depth_stencil(dstRb->Format))
471         return GL_FALSE;
472   }
473   else if (type == GL_DEPTH_STENCIL) {
474      /* can't handle separate depth/stencil buffers */
475      if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer ||
476          dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer)
477         return GL_FALSE;
478   }
479
480   /* clipping not supported */
481   if (srcX < 0 || srcX + width > (GLint) srcFb->Width ||
482       srcY < 0 || srcY + height > (GLint) srcFb->Height ||
483       dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax ||
484       dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) {
485      return GL_FALSE;
486   }
487
488   pixelBytes = _mesa_get_format_bytes(srcRb->Format);
489   widthInBytes = width * pixelBytes;
490
491   if (srcRb == dstRb) {
492      /* map whole buffer for read/write */
493      /* XXX we could be clever and just map the union region of the
494       * source and dest rects.
495       */
496      GLubyte *map;
497      GLint rowStride;
498
499      ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
500                                  srcRb->Width, srcRb->Height,
501                                  GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
502                                  &map, &rowStride);
503      if (!map) {
504         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
505         return GL_TRUE; /* don't retry with slow path */
506      }
507
508      srcMap = map + srcY * rowStride + srcX * pixelBytes;
509      dstMap = map + dstY * rowStride + dstX * pixelBytes;
510
511      /* this handles overlapping copies */
512      if (srcY < dstY) {
513         /* copy in reverse (top->down) order */
514         srcMap += rowStride * (height - 1);
515         dstMap += rowStride * (height - 1);
516         srcRowStride = -rowStride;
517         dstRowStride = -rowStride;
518      }
519      else {
520         /* copy in normal (bottom->up) order */
521         srcRowStride = rowStride;
522         dstRowStride = rowStride;
523      }
524   }
525   else {
526      /* different src/dst buffers */
527      ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
528                                  width, height,
529                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride);
530      if (!srcMap) {
531         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
532         return GL_TRUE; /* don't retry with slow path */
533      }
534      ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
535                                  width, height,
536                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
537      if (!dstMap) {
538         ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
539         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
540         return GL_TRUE; /* don't retry with slow path */
541      }
542   }
543
544   for (row = 0; row < height; row++) {
545      /* memmove() in case of overlap */
546      memmove(dstMap, srcMap, widthInBytes);
547      dstMap += dstRowStride;
548      srcMap += srcRowStride;
549   }
550
551   ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
552   if (dstRb != srcRb) {
553      ctx->Driver.UnmapRenderbuffer(ctx, dstRb);
554   }
555
556   return GL_TRUE;
557}
558
559
560/**
561 * Do software-based glCopyPixels.
562 * By time we get here, all parameters will have been error-checked.
563 */
564void
565_swrast_CopyPixels( struct gl_context *ctx,
566		    GLint srcx, GLint srcy, GLsizei width, GLsizei height,
567		    GLint destx, GLint desty, GLenum type )
568{
569   SWcontext *swrast = SWRAST_CONTEXT(ctx);
570
571   if (!_mesa_check_conditional_render(ctx))
572      return; /* don't copy */
573
574   if (swrast->NewState)
575      _swrast_validate_derived( ctx );
576
577   if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
578	 ctx->Pixel.ZoomX != 1.0F ||
579	 ctx->Pixel.ZoomY != 1.0F ||
580	 ctx->_ImageTransferState) &&
581       swrast_fast_copy_pixels(ctx, srcx, srcy, width, height, destx, desty,
582			       type)) {
583      /* all done */
584      return;
585   }
586
587   swrast_render_start(ctx);
588
589   switch (type) {
590   case GL_COLOR:
591      copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
592      break;
593   case GL_DEPTH:
594      copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
595      break;
596   case GL_STENCIL:
597      copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
598      break;
599   case GL_DEPTH_STENCIL_EXT:
600      /* Copy buffers separately (if the fast copy path wasn't taken) */
601      copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty);
602      copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
603      break;
604   default:
605      _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
606   }
607
608   swrast_render_finish(ctx);
609}
610