1848b8605Smrg/*
2848b8605Smrg * Mesa 3-D graphics library
3848b8605Smrg *
4848b8605Smrg * Copyright (C) 1999-2006  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/condrender.h"
28848b8605Smrg#include "main/image.h"
29848b8605Smrg#include "main/macros.h"
30848b8605Smrg#include "main/format_unpack.h"
31848b8605Smrg#include "main/format_pack.h"
32848b8605Smrg#include "main/condrender.h"
33848b8605Smrg#include "s_context.h"
34848b8605Smrg
35848b8605Smrg
36848b8605Smrg#define ABS(X)   ((X) < 0 ? -(X) : (X))
37848b8605Smrg
38848b8605Smrg
39848b8605Smrg/**
40848b8605Smrg * Generate a row resampler function for GL_NEAREST mode.
41848b8605Smrg */
42848b8605Smrg#define RESAMPLE(NAME, PIXELTYPE, SIZE)			\
43848b8605Smrgstatic void						\
44848b8605SmrgNAME(GLint srcWidth, GLint dstWidth,			\
45848b8605Smrg     const GLvoid *srcBuffer, GLvoid *dstBuffer,	\
46848b8605Smrg     GLboolean flip)					\
47848b8605Smrg{							\
48848b8605Smrg   const PIXELTYPE *src = (const PIXELTYPE *) srcBuffer;\
49848b8605Smrg   PIXELTYPE *dst = (PIXELTYPE *) dstBuffer;		\
50848b8605Smrg   GLint dstCol;					\
51848b8605Smrg							\
52848b8605Smrg   if (flip) {						\
53848b8605Smrg      for (dstCol = 0; dstCol < dstWidth; dstCol++) {	\
54848b8605Smrg         GLint srcCol = (dstCol * srcWidth) / dstWidth;	\
55b8e80941Smrg         assert(srcCol >= 0);				\
56b8e80941Smrg         assert(srcCol < srcWidth);			\
57848b8605Smrg         srcCol = srcWidth - 1 - srcCol; /* flip */	\
58848b8605Smrg         if (SIZE == 1) {				\
59848b8605Smrg            dst[dstCol] = src[srcCol];			\
60848b8605Smrg         }						\
61848b8605Smrg         else if (SIZE == 2) {				\
62848b8605Smrg            dst[dstCol*2+0] = src[srcCol*2+0];		\
63848b8605Smrg            dst[dstCol*2+1] = src[srcCol*2+1];		\
64848b8605Smrg         }						\
65848b8605Smrg         else if (SIZE == 4) {				\
66848b8605Smrg            dst[dstCol*4+0] = src[srcCol*4+0];		\
67848b8605Smrg            dst[dstCol*4+1] = src[srcCol*4+1];		\
68848b8605Smrg            dst[dstCol*4+2] = src[srcCol*4+2];		\
69848b8605Smrg            dst[dstCol*4+3] = src[srcCol*4+3];		\
70848b8605Smrg         }						\
71848b8605Smrg      }							\
72848b8605Smrg   }							\
73848b8605Smrg   else {						\
74848b8605Smrg      for (dstCol = 0; dstCol < dstWidth; dstCol++) {	\
75848b8605Smrg         GLint srcCol = (dstCol * srcWidth) / dstWidth;	\
76b8e80941Smrg         assert(srcCol >= 0);				\
77b8e80941Smrg         assert(srcCol < srcWidth);			\
78848b8605Smrg         if (SIZE == 1) {				\
79848b8605Smrg            dst[dstCol] = src[srcCol];			\
80848b8605Smrg         }						\
81848b8605Smrg         else if (SIZE == 2) {				\
82848b8605Smrg            dst[dstCol*2+0] = src[srcCol*2+0];		\
83848b8605Smrg            dst[dstCol*2+1] = src[srcCol*2+1];		\
84848b8605Smrg         }						\
85848b8605Smrg         else if (SIZE == 4) {				\
86848b8605Smrg            dst[dstCol*4+0] = src[srcCol*4+0];		\
87848b8605Smrg            dst[dstCol*4+1] = src[srcCol*4+1];		\
88848b8605Smrg            dst[dstCol*4+2] = src[srcCol*4+2];		\
89848b8605Smrg            dst[dstCol*4+3] = src[srcCol*4+3];		\
90848b8605Smrg         }						\
91848b8605Smrg      }							\
92848b8605Smrg   }							\
93848b8605Smrg}
94848b8605Smrg
95848b8605Smrg/**
96848b8605Smrg * Resamplers for 1, 2, 4, 8 and 16-byte pixels.
97848b8605Smrg */
98848b8605SmrgRESAMPLE(resample_row_1, GLubyte, 1)
99848b8605SmrgRESAMPLE(resample_row_2, GLushort, 1)
100848b8605SmrgRESAMPLE(resample_row_4, GLuint, 1)
101848b8605SmrgRESAMPLE(resample_row_8, GLuint, 2)
102848b8605SmrgRESAMPLE(resample_row_16, GLuint, 4)
103848b8605Smrg
104848b8605Smrg
105848b8605Smrg/**
106848b8605Smrg * Blit color, depth or stencil with GL_NEAREST filtering.
107848b8605Smrg */
108848b8605Smrgstatic void
109848b8605Smrgblit_nearest(struct gl_context *ctx,
110b8e80941Smrg             struct gl_framebuffer *readFb,
111b8e80941Smrg             struct gl_framebuffer *drawFb,
112848b8605Smrg             GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
113848b8605Smrg             GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
114848b8605Smrg             GLbitfield buffer)
115848b8605Smrg{
116848b8605Smrg   struct gl_renderbuffer *readRb, *drawRb = NULL;
117848b8605Smrg   struct gl_renderbuffer_attachment *readAtt = NULL, *drawAtt = NULL;
118848b8605Smrg   GLuint numDrawBuffers = 0;
119848b8605Smrg   GLuint i;
120848b8605Smrg
121848b8605Smrg   const GLint srcWidth = ABS(srcX1 - srcX0);
122848b8605Smrg   const GLint dstWidth = ABS(dstX1 - dstX0);
123848b8605Smrg   const GLint srcHeight = ABS(srcY1 - srcY0);
124848b8605Smrg   const GLint dstHeight = ABS(dstY1 - dstY0);
125848b8605Smrg
126848b8605Smrg   const GLint srcXpos = MIN2(srcX0, srcX1);
127848b8605Smrg   const GLint srcYpos = MIN2(srcY0, srcY1);
128848b8605Smrg   const GLint dstXpos = MIN2(dstX0, dstX1);
129848b8605Smrg   const GLint dstYpos = MIN2(dstY0, dstY1);
130848b8605Smrg
131848b8605Smrg   const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
132848b8605Smrg   const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
133848b8605Smrg   enum mode {
134848b8605Smrg      DIRECT,
135848b8605Smrg      UNPACK_RGBA_FLOAT,
136848b8605Smrg      UNPACK_Z_FLOAT,
137848b8605Smrg      UNPACK_Z_INT,
138848b8605Smrg      UNPACK_S,
139848b8605Smrg   } mode = DIRECT;
140848b8605Smrg   GLubyte *srcMap, *dstMap;
141848b8605Smrg   GLint srcRowStride, dstRowStride;
142848b8605Smrg   GLint dstRow;
143848b8605Smrg
144848b8605Smrg   GLint pixelSize = 0;
145848b8605Smrg   GLvoid *srcBuffer, *dstBuffer;
146848b8605Smrg   GLint prevY = -1;
147848b8605Smrg
148848b8605Smrg   typedef void (*resample_func)(GLint srcWidth, GLint dstWidth,
149848b8605Smrg                                 const GLvoid *srcBuffer, GLvoid *dstBuffer,
150848b8605Smrg                                 GLboolean flip);
151848b8605Smrg   resample_func resampleRow;
152848b8605Smrg
153848b8605Smrg   switch (buffer) {
154848b8605Smrg   case GL_COLOR_BUFFER_BIT:
155848b8605Smrg      readAtt = &readFb->Attachment[readFb->_ColorReadBufferIndex];
156848b8605Smrg      readRb = readFb->_ColorReadBuffer;
157848b8605Smrg      numDrawBuffers = drawFb->_NumColorDrawBuffers;
158848b8605Smrg      break;
159848b8605Smrg   case GL_DEPTH_BUFFER_BIT:
160848b8605Smrg      readAtt = &readFb->Attachment[BUFFER_DEPTH];
161848b8605Smrg      drawAtt = &drawFb->Attachment[BUFFER_DEPTH];
162848b8605Smrg      readRb = readAtt->Renderbuffer;
163848b8605Smrg      drawRb = drawAtt->Renderbuffer;
164848b8605Smrg      numDrawBuffers = 1;
165848b8605Smrg
166848b8605Smrg      /* Note that for depth/stencil, the formats of src/dst must match.  By
167848b8605Smrg       * using the core helpers for pack/unpack, we avoid needing to handle
168848b8605Smrg       * masking for things like DEPTH copies of Z24S8.
169848b8605Smrg       */
170848b8605Smrg      if (readRb->Format == MESA_FORMAT_Z_FLOAT32 ||
171848b8605Smrg	  readRb->Format == MESA_FORMAT_Z32_FLOAT_S8X24_UINT) {
172848b8605Smrg	 mode = UNPACK_Z_FLOAT;
173848b8605Smrg      } else {
174848b8605Smrg	 mode = UNPACK_Z_INT;
175848b8605Smrg      }
176848b8605Smrg      pixelSize = 4;
177848b8605Smrg      break;
178848b8605Smrg   case GL_STENCIL_BUFFER_BIT:
179848b8605Smrg      readAtt = &readFb->Attachment[BUFFER_STENCIL];
180848b8605Smrg      drawAtt = &drawFb->Attachment[BUFFER_STENCIL];
181848b8605Smrg      readRb = readAtt->Renderbuffer;
182848b8605Smrg      drawRb = drawAtt->Renderbuffer;
183848b8605Smrg      numDrawBuffers = 1;
184848b8605Smrg      mode = UNPACK_S;
185848b8605Smrg      pixelSize = 1;
186848b8605Smrg      break;
187848b8605Smrg   default:
188848b8605Smrg      _mesa_problem(ctx, "unexpected buffer in blit_nearest()");
189848b8605Smrg      return;
190848b8605Smrg   }
191848b8605Smrg
192848b8605Smrg   /* allocate the src/dst row buffers */
193848b8605Smrg   srcBuffer = malloc(MAX_PIXEL_BYTES * srcWidth);
194848b8605Smrg   dstBuffer = malloc(MAX_PIXEL_BYTES * dstWidth);
195848b8605Smrg   if (!srcBuffer || !dstBuffer)
196848b8605Smrg      goto fail_no_memory;
197848b8605Smrg
198848b8605Smrg   /* Blit to all the draw buffers */
199848b8605Smrg   for (i = 0; i < numDrawBuffers; i++) {
200848b8605Smrg      if (buffer == GL_COLOR_BUFFER_BIT) {
201b8e80941Smrg         gl_buffer_index idx = drawFb->_ColorDrawBufferIndexes[i];
202b8e80941Smrg         if (idx == BUFFER_NONE)
203848b8605Smrg            continue;
204848b8605Smrg         drawAtt = &drawFb->Attachment[idx];
205848b8605Smrg         drawRb = drawAtt->Renderbuffer;
206848b8605Smrg
207848b8605Smrg         if (!drawRb)
208848b8605Smrg            continue;
209848b8605Smrg
210848b8605Smrg         if (readRb->Format == drawRb->Format) {
211848b8605Smrg            mode = DIRECT;
212848b8605Smrg            pixelSize = _mesa_get_format_bytes(readRb->Format);
213848b8605Smrg         } else {
214848b8605Smrg            mode = UNPACK_RGBA_FLOAT;
215848b8605Smrg            pixelSize = 16;
216848b8605Smrg         }
217848b8605Smrg      }
218848b8605Smrg
219848b8605Smrg      /* choose row resampler */
220848b8605Smrg      switch (pixelSize) {
221848b8605Smrg      case 1:
222848b8605Smrg         resampleRow = resample_row_1;
223848b8605Smrg         break;
224848b8605Smrg      case 2:
225848b8605Smrg         resampleRow = resample_row_2;
226848b8605Smrg         break;
227848b8605Smrg      case 4:
228848b8605Smrg         resampleRow = resample_row_4;
229848b8605Smrg         break;
230848b8605Smrg      case 8:
231848b8605Smrg         resampleRow = resample_row_8;
232848b8605Smrg         break;
233848b8605Smrg      case 16:
234848b8605Smrg         resampleRow = resample_row_16;
235848b8605Smrg         break;
236848b8605Smrg      default:
237848b8605Smrg         _mesa_problem(ctx, "unexpected pixel size (%d) in blit_nearest",
238848b8605Smrg                       pixelSize);
239848b8605Smrg         goto fail;
240848b8605Smrg      }
241848b8605Smrg
242848b8605Smrg      if ((readRb == drawRb) ||
243848b8605Smrg          (readAtt->Texture && drawAtt->Texture &&
244848b8605Smrg           (readAtt->Texture == drawAtt->Texture))) {
245848b8605Smrg         /* map whole buffer for read/write */
246848b8605Smrg         /* XXX we could be clever and just map the union region of the
247848b8605Smrg          * source and dest rects.
248848b8605Smrg          */
249848b8605Smrg         GLubyte *map;
250848b8605Smrg         GLint rowStride;
251848b8605Smrg         GLint formatSize = _mesa_get_format_bytes(readRb->Format);
252848b8605Smrg
253848b8605Smrg         ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0,
254848b8605Smrg                                     readRb->Width, readRb->Height,
255848b8605Smrg                                     GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
256b8e80941Smrg                                     &map, &rowStride, readFb->FlipY);
257848b8605Smrg         if (!map) {
258848b8605Smrg            goto fail_no_memory;
259848b8605Smrg         }
260848b8605Smrg
261848b8605Smrg         srcMap = map + srcYpos * rowStride + srcXpos * formatSize;
262848b8605Smrg         dstMap = map + dstYpos * rowStride + dstXpos * formatSize;
263848b8605Smrg
264848b8605Smrg         /* this handles overlapping copies */
265848b8605Smrg         if (srcY0 < dstY0) {
266848b8605Smrg            /* copy in reverse (top->down) order */
267848b8605Smrg            srcMap += rowStride * (readRb->Height - 1);
268848b8605Smrg            dstMap += rowStride * (readRb->Height - 1);
269848b8605Smrg            srcRowStride = -rowStride;
270848b8605Smrg            dstRowStride = -rowStride;
271848b8605Smrg         }
272848b8605Smrg         else {
273848b8605Smrg            /* copy in normal (bottom->up) order */
274848b8605Smrg            srcRowStride = rowStride;
275848b8605Smrg            dstRowStride = rowStride;
276848b8605Smrg         }
277848b8605Smrg      }
278848b8605Smrg      else {
279848b8605Smrg         /* different src/dst buffers */
280848b8605Smrg         ctx->Driver.MapRenderbuffer(ctx, readRb,
281848b8605Smrg                                     srcXpos, srcYpos,
282848b8605Smrg                                     srcWidth, srcHeight,
283b8e80941Smrg                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride,
284b8e80941Smrg                                     readFb->FlipY);
285848b8605Smrg         if (!srcMap) {
286848b8605Smrg            goto fail_no_memory;
287848b8605Smrg         }
288848b8605Smrg         ctx->Driver.MapRenderbuffer(ctx, drawRb,
289848b8605Smrg                                     dstXpos, dstYpos,
290848b8605Smrg                                     dstWidth, dstHeight,
291b8e80941Smrg                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
292b8e80941Smrg                                     drawFb->FlipY);
293848b8605Smrg         if (!dstMap) {
294848b8605Smrg            ctx->Driver.UnmapRenderbuffer(ctx, readRb);
295848b8605Smrg            goto fail_no_memory;
296848b8605Smrg         }
297848b8605Smrg      }
298848b8605Smrg
299848b8605Smrg      for (dstRow = 0; dstRow < dstHeight; dstRow++) {
300848b8605Smrg         GLfloat srcRowF = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F;
301848b8605Smrg         GLint srcRow = IROUND(srcRowF);
302848b8605Smrg         GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
303848b8605Smrg
304b8e80941Smrg         assert(srcRow >= 0);
305b8e80941Smrg         assert(srcRow < srcHeight);
306848b8605Smrg
307848b8605Smrg         if (invertY) {
308848b8605Smrg            srcRow = srcHeight - 1 - srcRow;
309848b8605Smrg         }
310848b8605Smrg
311848b8605Smrg         /* get pixel row from source and resample to match dest width */
312848b8605Smrg         if (prevY != srcRow) {
313848b8605Smrg            GLubyte *srcRowStart = srcMap + srcRowStride * srcRow;
314848b8605Smrg
315848b8605Smrg            switch (mode) {
316848b8605Smrg            case DIRECT:
317848b8605Smrg               memcpy(srcBuffer, srcRowStart, pixelSize * srcWidth);
318848b8605Smrg               break;
319848b8605Smrg            case UNPACK_RGBA_FLOAT:
320848b8605Smrg               _mesa_unpack_rgba_row(readRb->Format, srcWidth, srcRowStart,
321848b8605Smrg                                     srcBuffer);
322848b8605Smrg               break;
323848b8605Smrg            case UNPACK_Z_FLOAT:
324848b8605Smrg               _mesa_unpack_float_z_row(readRb->Format, srcWidth, srcRowStart,
325848b8605Smrg                                        srcBuffer);
326848b8605Smrg               break;
327848b8605Smrg            case UNPACK_Z_INT:
328848b8605Smrg               _mesa_unpack_uint_z_row(readRb->Format, srcWidth, srcRowStart,
329848b8605Smrg                                       srcBuffer);
330848b8605Smrg               break;
331848b8605Smrg            case UNPACK_S:
332848b8605Smrg               _mesa_unpack_ubyte_stencil_row(readRb->Format, srcWidth,
333848b8605Smrg                                              srcRowStart, srcBuffer);
334848b8605Smrg               break;
335848b8605Smrg            }
336848b8605Smrg
337848b8605Smrg            (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX);
338848b8605Smrg            prevY = srcRow;
339848b8605Smrg         }
340848b8605Smrg
341848b8605Smrg         /* store pixel row in destination */
342848b8605Smrg         switch (mode) {
343848b8605Smrg         case DIRECT:
344848b8605Smrg            memcpy(dstRowStart, dstBuffer, pixelSize * dstWidth);
345848b8605Smrg            break;
346848b8605Smrg         case UNPACK_RGBA_FLOAT:
347848b8605Smrg            _mesa_pack_float_rgba_row(drawRb->Format, dstWidth, dstBuffer,
348848b8605Smrg                                      dstRowStart);
349848b8605Smrg            break;
350848b8605Smrg         case UNPACK_Z_FLOAT:
351848b8605Smrg            _mesa_pack_float_z_row(drawRb->Format, dstWidth, dstBuffer,
352848b8605Smrg                                   dstRowStart);
353848b8605Smrg            break;
354848b8605Smrg         case UNPACK_Z_INT:
355848b8605Smrg            _mesa_pack_uint_z_row(drawRb->Format, dstWidth, dstBuffer,
356848b8605Smrg                                  dstRowStart);
357848b8605Smrg            break;
358848b8605Smrg         case UNPACK_S:
359848b8605Smrg            _mesa_pack_ubyte_stencil_row(drawRb->Format, dstWidth, dstBuffer,
360848b8605Smrg                                         dstRowStart);
361848b8605Smrg            break;
362848b8605Smrg         }
363848b8605Smrg      }
364848b8605Smrg
365848b8605Smrg      ctx->Driver.UnmapRenderbuffer(ctx, readRb);
366848b8605Smrg      if (drawRb != readRb) {
367848b8605Smrg         ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
368848b8605Smrg      }
369848b8605Smrg   }
370848b8605Smrg
371848b8605Smrgfail:
372848b8605Smrg   free(srcBuffer);
373848b8605Smrg   free(dstBuffer);
374848b8605Smrg   return;
375848b8605Smrg
376848b8605Smrgfail_no_memory:
377848b8605Smrg   free(srcBuffer);
378848b8605Smrg   free(dstBuffer);
379848b8605Smrg   _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBuffer");
380848b8605Smrg}
381848b8605Smrg
382848b8605Smrg
383848b8605Smrg
384848b8605Smrg#define LERP(T, A, B)  ( (A) + (T) * ((B) - (A)) )
385848b8605Smrg
386848b8605Smrgstatic inline GLfloat
387848b8605Smrglerp_2d(GLfloat a, GLfloat b,
388848b8605Smrg        GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
389848b8605Smrg{
390848b8605Smrg   const GLfloat temp0 = LERP(a, v00, v10);
391848b8605Smrg   const GLfloat temp1 = LERP(a, v01, v11);
392848b8605Smrg   return LERP(b, temp0, temp1);
393848b8605Smrg}
394848b8605Smrg
395848b8605Smrg
396848b8605Smrg/**
397848b8605Smrg * Bilinear interpolation of two source rows.
398848b8605Smrg * GLubyte pixels.
399848b8605Smrg */
400848b8605Smrgstatic void
401848b8605Smrgresample_linear_row_ub(GLint srcWidth, GLint dstWidth,
402848b8605Smrg                       const GLvoid *srcBuffer0, const GLvoid *srcBuffer1,
403848b8605Smrg                       GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight)
404848b8605Smrg{
405848b8605Smrg   const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0;
406848b8605Smrg   const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1;
407848b8605Smrg   GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer;
408848b8605Smrg   GLint dstCol;
409848b8605Smrg
410848b8605Smrg   for (dstCol = 0; dstCol < dstWidth; dstCol++) {
411848b8605Smrg      const GLfloat srcCol = (dstCol + 0.5F) / dstWidth * srcWidth - 0.5F;
412848b8605Smrg      GLint srcCol0 = MAX2(0, IFLOOR(srcCol));
413848b8605Smrg      GLint srcCol1 = srcCol0 + 1;
414848b8605Smrg      GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
415848b8605Smrg      GLfloat red, green, blue, alpha;
416848b8605Smrg
417b8e80941Smrg      assert(srcCol0 < srcWidth);
418b8e80941Smrg      assert(srcCol1 <= srcWidth);
419848b8605Smrg
420848b8605Smrg      if (srcCol1 == srcWidth) {
421848b8605Smrg         /* last column fudge */
422848b8605Smrg         srcCol1--;
423848b8605Smrg         colWeight = 0.0;
424848b8605Smrg      }
425848b8605Smrg
426848b8605Smrg      if (flip) {
427848b8605Smrg         srcCol0 = srcWidth - 1 - srcCol0;
428848b8605Smrg         srcCol1 = srcWidth - 1 - srcCol1;
429848b8605Smrg      }
430848b8605Smrg
431848b8605Smrg      red = lerp_2d(colWeight, rowWeight,
432848b8605Smrg                    srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP],
433848b8605Smrg                    srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]);
434848b8605Smrg      green = lerp_2d(colWeight, rowWeight,
435848b8605Smrg                    srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP],
436848b8605Smrg                    srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]);
437848b8605Smrg      blue = lerp_2d(colWeight, rowWeight,
438848b8605Smrg                    srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP],
439848b8605Smrg                    srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]);
440848b8605Smrg      alpha = lerp_2d(colWeight, rowWeight,
441848b8605Smrg                    srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
442848b8605Smrg                    srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
443848b8605Smrg
444848b8605Smrg      dstColor[dstCol][RCOMP] = IFLOOR(red);
445848b8605Smrg      dstColor[dstCol][GCOMP] = IFLOOR(green);
446848b8605Smrg      dstColor[dstCol][BCOMP] = IFLOOR(blue);
447848b8605Smrg      dstColor[dstCol][ACOMP] = IFLOOR(alpha);
448848b8605Smrg   }
449848b8605Smrg}
450848b8605Smrg
451848b8605Smrg
452848b8605Smrg/**
453848b8605Smrg * Bilinear interpolation of two source rows.  floating point pixels.
454848b8605Smrg */
455848b8605Smrgstatic void
456848b8605Smrgresample_linear_row_float(GLint srcWidth, GLint dstWidth,
457848b8605Smrg                          const GLvoid *srcBuffer0, const GLvoid *srcBuffer1,
458848b8605Smrg                          GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight)
459848b8605Smrg{
460848b8605Smrg   const GLfloat (*srcColor0)[4] = (const GLfloat (*)[4]) srcBuffer0;
461848b8605Smrg   const GLfloat (*srcColor1)[4] = (const GLfloat (*)[4]) srcBuffer1;
462848b8605Smrg   GLfloat (*dstColor)[4] = (GLfloat (*)[4]) dstBuffer;
463848b8605Smrg   GLint dstCol;
464848b8605Smrg
465848b8605Smrg   for (dstCol = 0; dstCol < dstWidth; dstCol++) {
466848b8605Smrg      const GLfloat srcCol = (dstCol + 0.5F) / dstWidth * srcWidth - 0.5F;
467848b8605Smrg      GLint srcCol0 = MAX2(0, IFLOOR(srcCol));
468848b8605Smrg      GLint srcCol1 = srcCol0 + 1;
469848b8605Smrg      GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
470848b8605Smrg      GLfloat red, green, blue, alpha;
471848b8605Smrg
472b8e80941Smrg      assert(srcCol0 < srcWidth);
473b8e80941Smrg      assert(srcCol1 <= srcWidth);
474848b8605Smrg
475848b8605Smrg      if (srcCol1 == srcWidth) {
476848b8605Smrg         /* last column fudge */
477848b8605Smrg         srcCol1--;
478848b8605Smrg         colWeight = 0.0;
479848b8605Smrg      }
480848b8605Smrg
481848b8605Smrg      if (flip) {
482848b8605Smrg         srcCol0 = srcWidth - 1 - srcCol0;
483848b8605Smrg         srcCol1 = srcWidth - 1 - srcCol1;
484848b8605Smrg      }
485848b8605Smrg
486848b8605Smrg      red = lerp_2d(colWeight, rowWeight,
487848b8605Smrg                    srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP],
488848b8605Smrg                    srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]);
489848b8605Smrg      green = lerp_2d(colWeight, rowWeight,
490848b8605Smrg                    srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP],
491848b8605Smrg                    srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]);
492848b8605Smrg      blue = lerp_2d(colWeight, rowWeight,
493848b8605Smrg                    srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP],
494848b8605Smrg                    srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]);
495848b8605Smrg      alpha = lerp_2d(colWeight, rowWeight,
496848b8605Smrg                    srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
497848b8605Smrg                    srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
498848b8605Smrg
499848b8605Smrg      dstColor[dstCol][RCOMP] = red;
500848b8605Smrg      dstColor[dstCol][GCOMP] = green;
501848b8605Smrg      dstColor[dstCol][BCOMP] = blue;
502848b8605Smrg      dstColor[dstCol][ACOMP] = alpha;
503848b8605Smrg   }
504848b8605Smrg}
505848b8605Smrg
506848b8605Smrg
507848b8605Smrg
508848b8605Smrg/**
509848b8605Smrg * Bilinear filtered blit (color only, non-integer values).
510848b8605Smrg */
511848b8605Smrgstatic void
512848b8605Smrgblit_linear(struct gl_context *ctx,
513b8e80941Smrg            struct gl_framebuffer *readFb,
514b8e80941Smrg            struct gl_framebuffer *drawFb,
515848b8605Smrg            GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
516848b8605Smrg            GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
517848b8605Smrg{
518848b8605Smrg   struct gl_renderbuffer *readRb = readFb->_ColorReadBuffer;
519848b8605Smrg   struct gl_renderbuffer_attachment *readAtt =
520848b8605Smrg      &readFb->Attachment[readFb->_ColorReadBufferIndex];
521848b8605Smrg
522848b8605Smrg   const GLint srcWidth = ABS(srcX1 - srcX0);
523848b8605Smrg   const GLint dstWidth = ABS(dstX1 - dstX0);
524848b8605Smrg   const GLint srcHeight = ABS(srcY1 - srcY0);
525848b8605Smrg   const GLint dstHeight = ABS(dstY1 - dstY0);
526848b8605Smrg
527848b8605Smrg   const GLint srcXpos = MIN2(srcX0, srcX1);
528848b8605Smrg   const GLint srcYpos = MIN2(srcY0, srcY1);
529848b8605Smrg   const GLint dstXpos = MIN2(dstX0, dstX1);
530848b8605Smrg   const GLint dstYpos = MIN2(dstY0, dstY1);
531848b8605Smrg
532848b8605Smrg   const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
533848b8605Smrg   const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
534848b8605Smrg
535848b8605Smrg   GLint dstRow;
536848b8605Smrg
537848b8605Smrg   GLint pixelSize;
538848b8605Smrg   GLvoid *srcBuffer0, *srcBuffer1;
539848b8605Smrg   GLint srcBufferY0 = -1, srcBufferY1 = -1;
540848b8605Smrg   GLvoid *dstBuffer;
541848b8605Smrg
542848b8605Smrg   mesa_format readFormat = _mesa_get_srgb_format_linear(readRb->Format);
543848b8605Smrg   GLuint bpp = _mesa_get_format_bytes(readFormat);
544848b8605Smrg
545848b8605Smrg   GLenum pixelType;
546848b8605Smrg
547848b8605Smrg   GLubyte *srcMap, *dstMap;
548848b8605Smrg   GLint srcRowStride, dstRowStride;
549848b8605Smrg   GLuint i;
550848b8605Smrg
551848b8605Smrg
552848b8605Smrg   /* Determine datatype for resampling */
553848b8605Smrg   if (_mesa_get_format_max_bits(readFormat) == 8 &&
554848b8605Smrg       _mesa_get_format_datatype(readFormat) == GL_UNSIGNED_NORMALIZED) {
555848b8605Smrg      pixelType = GL_UNSIGNED_BYTE;
556848b8605Smrg      pixelSize = 4 * sizeof(GLubyte);
557848b8605Smrg   }
558848b8605Smrg   else {
559848b8605Smrg      pixelType = GL_FLOAT;
560848b8605Smrg      pixelSize = 4 * sizeof(GLfloat);
561848b8605Smrg   }
562848b8605Smrg
563848b8605Smrg   /* Allocate the src/dst row buffers.
564848b8605Smrg    * Keep two adjacent src rows around for bilinear sampling.
565848b8605Smrg    */
566848b8605Smrg   srcBuffer0 = malloc(pixelSize * srcWidth);
567848b8605Smrg   srcBuffer1 = malloc(pixelSize * srcWidth);
568848b8605Smrg   dstBuffer = malloc(pixelSize * dstWidth);
569848b8605Smrg   if (!srcBuffer0 || !srcBuffer1 || !dstBuffer) {
570848b8605Smrg      goto fail_no_memory;
571848b8605Smrg   }
572848b8605Smrg
573848b8605Smrg   for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) {
574b8e80941Smrg      gl_buffer_index idx = drawFb->_ColorDrawBufferIndexes[i];
575848b8605Smrg      struct gl_renderbuffer_attachment *drawAtt;
576848b8605Smrg      struct gl_renderbuffer *drawRb;
577848b8605Smrg      mesa_format drawFormat;
578848b8605Smrg
579b8e80941Smrg      if (idx == BUFFER_NONE)
580848b8605Smrg         continue;
581848b8605Smrg
582848b8605Smrg      drawAtt = &drawFb->Attachment[idx];
583848b8605Smrg      drawRb = drawAtt->Renderbuffer;
584848b8605Smrg      if (!drawRb)
585848b8605Smrg         continue;
586848b8605Smrg
587848b8605Smrg      drawFormat = _mesa_get_srgb_format_linear(drawRb->Format);
588848b8605Smrg
589848b8605Smrg      /*
590848b8605Smrg       * Map src / dst renderbuffers
591848b8605Smrg       */
592848b8605Smrg      if ((readRb == drawRb) ||
593848b8605Smrg          (readAtt->Texture && drawAtt->Texture &&
594848b8605Smrg           (readAtt->Texture == drawAtt->Texture))) {
595848b8605Smrg         /* map whole buffer for read/write */
596848b8605Smrg         ctx->Driver.MapRenderbuffer(ctx, readRb,
597848b8605Smrg                                     0, 0, readRb->Width, readRb->Height,
598848b8605Smrg                                     GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
599b8e80941Smrg                                     &srcMap, &srcRowStride,
600b8e80941Smrg                                     readFb->FlipY);
601848b8605Smrg         if (!srcMap) {
602848b8605Smrg            goto fail_no_memory;
603848b8605Smrg         }
604848b8605Smrg
605848b8605Smrg         dstMap = srcMap;
606848b8605Smrg         dstRowStride = srcRowStride;
607848b8605Smrg      }
608848b8605Smrg      else {
609848b8605Smrg         /* different src/dst buffers */
610848b8605Smrg         /* XXX with a bit of work we could just map the regions to be
611848b8605Smrg          * read/written instead of the whole buffers.
612848b8605Smrg          */
613848b8605Smrg         ctx->Driver.MapRenderbuffer(ctx, readRb,
614848b8605Smrg                                     0, 0, readRb->Width, readRb->Height,
615b8e80941Smrg                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride,
616b8e80941Smrg                                     readFb->FlipY);
617848b8605Smrg         if (!srcMap) {
618848b8605Smrg            goto fail_no_memory;
619848b8605Smrg         }
620848b8605Smrg         ctx->Driver.MapRenderbuffer(ctx, drawRb,
621848b8605Smrg                                     0, 0, drawRb->Width, drawRb->Height,
622b8e80941Smrg                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
623b8e80941Smrg                                     drawFb->FlipY);
624848b8605Smrg         if (!dstMap) {
625848b8605Smrg            ctx->Driver.UnmapRenderbuffer(ctx, readRb);
626848b8605Smrg            goto fail_no_memory;
627848b8605Smrg         }
628848b8605Smrg      }
629848b8605Smrg
630848b8605Smrg      for (dstRow = 0; dstRow < dstHeight; dstRow++) {
631848b8605Smrg         const GLint dstY = dstYpos + dstRow;
632848b8605Smrg         GLfloat srcRow = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F;
633848b8605Smrg         GLint srcRow0 = MAX2(0, IFLOOR(srcRow));
634848b8605Smrg         GLint srcRow1 = srcRow0 + 1;
635848b8605Smrg         GLfloat rowWeight = srcRow - srcRow0; /* fractional part of srcRow */
636848b8605Smrg
637848b8605Smrg         if (srcRow1 == srcHeight) {
638848b8605Smrg            /* last row fudge */
639848b8605Smrg            srcRow1 = srcRow0;
640848b8605Smrg            rowWeight = 0.0;
641848b8605Smrg         }
642848b8605Smrg
643848b8605Smrg         if (invertY) {
644848b8605Smrg            srcRow0 = srcHeight - 1 - srcRow0;
645848b8605Smrg            srcRow1 = srcHeight - 1 - srcRow1;
646848b8605Smrg         }
647848b8605Smrg
648848b8605Smrg         srcY0 = srcYpos + srcRow0;
649848b8605Smrg         srcY1 = srcYpos + srcRow1;
650848b8605Smrg
651848b8605Smrg         /* get the two source rows */
652848b8605Smrg         if (srcY0 == srcBufferY0 && srcY1 == srcBufferY1) {
653848b8605Smrg            /* use same source row buffers again */
654848b8605Smrg         }
655848b8605Smrg         else if (srcY0 == srcBufferY1) {
656848b8605Smrg            /* move buffer1 into buffer0 by swapping pointers */
657848b8605Smrg            GLvoid *tmp = srcBuffer0;
658848b8605Smrg            srcBuffer0 = srcBuffer1;
659848b8605Smrg            srcBuffer1 = tmp;
660848b8605Smrg            /* get y1 row */
661848b8605Smrg            {
662848b8605Smrg               GLubyte *src = srcMap + srcY1 * srcRowStride + srcXpos * bpp;
663848b8605Smrg               if (pixelType == GL_UNSIGNED_BYTE) {
664848b8605Smrg                  _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
665848b8605Smrg                                              src, srcBuffer1);
666848b8605Smrg               }
667848b8605Smrg               else {
668848b8605Smrg                  _mesa_unpack_rgba_row(readFormat, srcWidth,
669848b8605Smrg                                        src, srcBuffer1);
670848b8605Smrg               }
671848b8605Smrg            }
672848b8605Smrg            srcBufferY0 = srcY0;
673848b8605Smrg            srcBufferY1 = srcY1;
674848b8605Smrg         }
675848b8605Smrg         else {
676848b8605Smrg            /* get both new rows */
677848b8605Smrg            {
678848b8605Smrg               GLubyte *src0 = srcMap + srcY0 * srcRowStride + srcXpos * bpp;
679848b8605Smrg               GLubyte *src1 = srcMap + srcY1 * srcRowStride + srcXpos * bpp;
680848b8605Smrg               if (pixelType == GL_UNSIGNED_BYTE) {
681848b8605Smrg                  _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
682848b8605Smrg                                              src0, srcBuffer0);
683848b8605Smrg                  _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
684848b8605Smrg                                              src1, srcBuffer1);
685848b8605Smrg               }
686848b8605Smrg               else {
687848b8605Smrg                  _mesa_unpack_rgba_row(readFormat, srcWidth, src0, srcBuffer0);
688848b8605Smrg                  _mesa_unpack_rgba_row(readFormat, srcWidth, src1, srcBuffer1);
689848b8605Smrg               }
690848b8605Smrg            }
691848b8605Smrg            srcBufferY0 = srcY0;
692848b8605Smrg            srcBufferY1 = srcY1;
693848b8605Smrg         }
694848b8605Smrg
695848b8605Smrg         if (pixelType == GL_UNSIGNED_BYTE) {
696848b8605Smrg            resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
697848b8605Smrg                                   dstBuffer, invertX, rowWeight);
698848b8605Smrg         }
699848b8605Smrg         else {
700848b8605Smrg            resample_linear_row_float(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
701848b8605Smrg                                      dstBuffer, invertX, rowWeight);
702848b8605Smrg         }
703848b8605Smrg
704848b8605Smrg         /* store pixel row in destination */
705848b8605Smrg         {
706848b8605Smrg            GLubyte *dst = dstMap + dstY * dstRowStride + dstXpos * bpp;
707848b8605Smrg            if (pixelType == GL_UNSIGNED_BYTE) {
708848b8605Smrg               _mesa_pack_ubyte_rgba_row(drawFormat, dstWidth, dstBuffer, dst);
709848b8605Smrg            }
710848b8605Smrg            else {
711848b8605Smrg               _mesa_pack_float_rgba_row(drawFormat, dstWidth, dstBuffer, dst);
712848b8605Smrg            }
713848b8605Smrg         }
714848b8605Smrg      }
715848b8605Smrg
716848b8605Smrg      ctx->Driver.UnmapRenderbuffer(ctx, readRb);
717848b8605Smrg      if (drawRb != readRb) {
718848b8605Smrg         ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
719848b8605Smrg      }
720848b8605Smrg   }
721848b8605Smrg
722848b8605Smrg   free(srcBuffer0);
723848b8605Smrg   free(srcBuffer1);
724848b8605Smrg   free(dstBuffer);
725848b8605Smrg   return;
726848b8605Smrg
727848b8605Smrgfail_no_memory:
728848b8605Smrg   free(srcBuffer0);
729848b8605Smrg   free(srcBuffer1);
730848b8605Smrg   free(dstBuffer);
731848b8605Smrg   _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
732848b8605Smrg}
733848b8605Smrg
734848b8605Smrg
735848b8605Smrg
736848b8605Smrg/**
737848b8605Smrg * Software fallback for glBlitFramebufferEXT().
738848b8605Smrg */
739848b8605Smrgvoid
740848b8605Smrg_swrast_BlitFramebuffer(struct gl_context *ctx,
741b8e80941Smrg                        struct gl_framebuffer *readFb,
742b8e80941Smrg                        struct gl_framebuffer *drawFb,
743848b8605Smrg                        GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
744848b8605Smrg                        GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
745848b8605Smrg                        GLbitfield mask, GLenum filter)
746848b8605Smrg{
747848b8605Smrg   static const GLbitfield buffers[3] = {
748848b8605Smrg      GL_COLOR_BUFFER_BIT,
749848b8605Smrg      GL_DEPTH_BUFFER_BIT,
750848b8605Smrg      GL_STENCIL_BUFFER_BIT
751848b8605Smrg   };
752848b8605Smrg   static const GLenum buffer_enums[3] = {
753848b8605Smrg      GL_COLOR,
754848b8605Smrg      GL_DEPTH,
755848b8605Smrg      GL_STENCIL,
756848b8605Smrg   };
757848b8605Smrg   GLint i;
758848b8605Smrg
759848b8605Smrg   /* Page 679 of OpenGL 4.4 spec says:
760848b8605Smrg    *    "Added BlitFramebuffer to commands affected by conditional rendering in
761848b8605Smrg    *     section 10.10 (Bug 9562)."
762848b8605Smrg    */
763848b8605Smrg   if (!_mesa_check_conditional_render(ctx))
764848b8605Smrg      return; /* Do not blit */
765848b8605Smrg
766b8e80941Smrg   if (!_mesa_clip_blit(ctx, readFb, drawFb, &srcX0, &srcY0, &srcX1, &srcY1,
767848b8605Smrg                        &dstX0, &dstY0, &dstX1, &dstY1)) {
768848b8605Smrg      return;
769848b8605Smrg   }
770848b8605Smrg
771848b8605Smrg   if (SWRAST_CONTEXT(ctx)->NewState)
772848b8605Smrg      _swrast_validate_derived(ctx);
773848b8605Smrg
774848b8605Smrg   /* First, try covering whatever buffers possible using the fast 1:1 copy
775848b8605Smrg    * path.
776848b8605Smrg    */
777848b8605Smrg   if (srcX1 - srcX0 == dstX1 - dstX0 &&
778848b8605Smrg       srcY1 - srcY0 == dstY1 - dstY0 &&
779848b8605Smrg       srcX0 < srcX1 &&
780848b8605Smrg       srcY0 < srcY1 &&
781848b8605Smrg       dstX0 < dstX1 &&
782848b8605Smrg       dstY0 < dstY1) {
783848b8605Smrg      for (i = 0; i < 3; i++) {
784848b8605Smrg         if (mask & buffers[i]) {
785b8e80941Smrg            if (swrast_fast_copy_pixels(ctx,
786b8e80941Smrg                                        readFb, drawFb,
787b8e80941Smrg                                        srcX0, srcY0,
788b8e80941Smrg                                        srcX1 - srcX0, srcY1 - srcY0,
789b8e80941Smrg                                        dstX0, dstY0,
790b8e80941Smrg                                        buffer_enums[i])) {
791b8e80941Smrg               mask &= ~buffers[i];
792b8e80941Smrg            }
793b8e80941Smrg         }
794848b8605Smrg      }
795848b8605Smrg
796848b8605Smrg      if (!mask)
797b8e80941Smrg         return;
798848b8605Smrg   }
799848b8605Smrg
800848b8605Smrg   if (filter == GL_NEAREST) {
801848b8605Smrg      for (i = 0; i < 3; i++) {
802b8e80941Smrg          if (mask & buffers[i]) {
803b8e80941Smrg             blit_nearest(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1,
804b8e80941Smrg                          dstX0, dstY0, dstX1, dstY1, buffers[i]);
805b8e80941Smrg          }
806848b8605Smrg      }
807848b8605Smrg   }
808848b8605Smrg   else {
809b8e80941Smrg      assert(filter == GL_LINEAR);
810848b8605Smrg      if (mask & GL_COLOR_BUFFER_BIT) {  /* depth/stencil not allowed */
811b8e80941Smrg         blit_linear(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1,
812b8e80941Smrg                     dstX0, dstY0, dstX1, dstY1);
813848b8605Smrg      }
814848b8605Smrg   }
815848b8605Smrg
816848b8605Smrg}
817