1/*
2 * Copyright © 1998 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Keith Packard makes no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_DIX_CONFIG_H
24#include <dix-config.h>
25#endif
26
27#include "mi.h"
28#include "scrnintstr.h"
29#include "gcstruct.h"
30#include "pixmap.h"
31#include "pixmapstr.h"
32#include "windowstr.h"
33
34void
35miCopyRegion (DrawablePtr   pSrcDrawable,
36	      DrawablePtr   pDstDrawable,
37	      GCPtr	    pGC,
38	      RegionPtr	    pDstRegion,
39	      int	    dx,
40	      int	    dy,
41	      miCopyProc    copyProc,
42	      Pixel	    bitPlane,
43	      void	    *closure)
44{
45    int		careful;
46    Bool	reverse;
47    Bool	upsidedown;
48    BoxPtr	pbox;
49    int		nbox;
50    BoxPtr	pboxNew1, pboxNew2, pboxBase, pboxNext, pboxTmp;
51
52    pbox = RegionRects(pDstRegion);
53    nbox = RegionNumRects(pDstRegion);
54
55    /* XXX we have to err on the side of safety when both are windows,
56     * because we don't know if IncludeInferiors is being used.
57     */
58    careful = ((pSrcDrawable == pDstDrawable) ||
59	       ((pSrcDrawable->type == DRAWABLE_WINDOW) &&
60		(pDstDrawable->type == DRAWABLE_WINDOW)));
61
62    pboxNew1 = NULL;
63    pboxNew2 = NULL;
64    if (careful && dy < 0)
65    {
66	upsidedown = TRUE;
67
68	if (nbox > 1)
69	{
70	    /* keep ordering in each band, reverse order of bands */
71	    pboxNew1 = (BoxPtr)malloc(sizeof(BoxRec) * nbox);
72	    if(!pboxNew1)
73		return;
74	    pboxBase = pboxNext = pbox+nbox-1;
75	    while (pboxBase >= pbox)
76	    {
77		while ((pboxNext >= pbox) &&
78		       (pboxBase->y1 == pboxNext->y1))
79		    pboxNext--;
80		pboxTmp = pboxNext+1;
81		while (pboxTmp <= pboxBase)
82		{
83		    *pboxNew1++ = *pboxTmp++;
84		}
85		pboxBase = pboxNext;
86	    }
87	    pboxNew1 -= nbox;
88	    pbox = pboxNew1;
89	}
90    }
91    else
92    {
93	/* walk source top to bottom */
94	upsidedown = FALSE;
95    }
96
97    if (careful && dx < 0)
98    {
99	/* walk source right to left */
100	if (dy <= 0)
101	    reverse = TRUE;
102	else
103	    reverse = FALSE;
104
105	if (nbox > 1)
106	{
107	    /* reverse order of rects in each band */
108	    pboxNew2 = (BoxPtr)malloc(sizeof(BoxRec) * nbox);
109	    if(!pboxNew2)
110	    {
111		free(pboxNew1);
112		return;
113	    }
114	    pboxBase = pboxNext = pbox;
115	    while (pboxBase < pbox+nbox)
116	    {
117		while ((pboxNext < pbox+nbox) &&
118		       (pboxNext->y1 == pboxBase->y1))
119		    pboxNext++;
120		pboxTmp = pboxNext;
121		while (pboxTmp != pboxBase)
122		{
123		    *pboxNew2++ = *--pboxTmp;
124		}
125		pboxBase = pboxNext;
126	    }
127	    pboxNew2 -= nbox;
128	    pbox = pboxNew2;
129	}
130    }
131    else
132    {
133	/* walk source left to right */
134	reverse = FALSE;
135    }
136
137    (*copyProc) (pSrcDrawable,
138		 pDstDrawable,
139		 pGC,
140		 pbox,
141		 nbox,
142		 dx, dy,
143		 reverse, upsidedown, bitPlane, closure);
144
145    free(pboxNew1);
146    free(pboxNew2);
147}
148
149RegionPtr
150miDoCopy (DrawablePtr	pSrcDrawable,
151	  DrawablePtr	pDstDrawable,
152	  GCPtr		pGC,
153	  int		xIn,
154	  int		yIn,
155	  int		widthSrc,
156	  int		heightSrc,
157	  int		xOut,
158	  int		yOut,
159	  miCopyProc	copyProc,
160	  Pixel		bitPlane,
161	  void		*closure)
162{
163    RegionPtr	prgnSrcClip = NULL; /* may be a new region, or just a copy */
164    Bool	freeSrcClip = FALSE;
165    RegionPtr	prgnExposed = NULL;
166    RegionRec	rgnDst;
167    int		dx;
168    int		dy;
169    int		numRects;
170    int         box_x1;
171    int         box_y1;
172    int         box_x2;
173    int         box_y2;
174    Bool	fastSrc = FALSE;    /* for fast clipping with pixmap source */
175    Bool	fastDst = FALSE;    /* for fast clipping with one rect dest */
176    Bool	fastExpose = FALSE; /* for fast exposures with pixmap source */
177
178    /* Short cut for unmapped windows */
179
180    if (pDstDrawable->type == DRAWABLE_WINDOW &&
181	!((WindowPtr)pDstDrawable)->realized)
182    {
183	return NULL;
184    }
185
186    if (pSrcDrawable->pScreen->SourceValidate)
187    {
188	(*pSrcDrawable->pScreen->SourceValidate) (pSrcDrawable, xIn, yIn, widthSrc, heightSrc,
189						  pGC->subWindowMode);
190    }
191
192    /* Compute source clip region */
193    if (pSrcDrawable->type == DRAWABLE_PIXMAP)
194    {
195	if ((pSrcDrawable == pDstDrawable) && (pGC->clientClipType == CT_NONE))
196	    prgnSrcClip = miGetCompositeClip(pGC);
197	else
198	    fastSrc = TRUE;
199    }
200    else
201    {
202	if (pGC->subWindowMode == IncludeInferiors)
203	{
204	    /*
205	     * XFree86 DDX empties the border clip when the
206	     * VT is inactive, make sure the region isn't empty
207	     */
208	    if (!((WindowPtr) pSrcDrawable)->parent &&
209		RegionNotEmpty(&((WindowPtr) pSrcDrawable)->borderClip))
210	    {
211		/*
212		 * special case bitblt from root window in
213		 * IncludeInferiors mode; just like from a pixmap
214		 */
215		fastSrc = TRUE;
216	    }
217	    else if ((pSrcDrawable == pDstDrawable) &&
218		     (pGC->clientClipType == CT_NONE))
219	    {
220		prgnSrcClip = miGetCompositeClip(pGC);
221	    }
222	    else
223	    {
224		prgnSrcClip = NotClippedByChildren((WindowPtr)pSrcDrawable);
225		freeSrcClip = TRUE;
226	    }
227	}
228	else
229	{
230	    prgnSrcClip = &((WindowPtr)pSrcDrawable)->clipList;
231	}
232    }
233
234    xIn += pSrcDrawable->x;
235    yIn += pSrcDrawable->y;
236
237    xOut += pDstDrawable->x;
238    yOut += pDstDrawable->y;
239
240    box_x1 = xIn;
241    box_y1 = yIn;
242    box_x2 = xIn + widthSrc;
243    box_y2 = yIn + heightSrc;
244
245    dx = xIn - xOut;
246    dy = yIn - yOut;
247
248    /* Don't create a source region if we are doing a fast clip */
249    if (fastSrc)
250    {
251	RegionPtr cclip;
252
253	fastExpose = TRUE;
254	/*
255	 * clip the source; if regions extend beyond the source size,
256 	 * make sure exposure events get sent
257	 */
258	if (box_x1 < pSrcDrawable->x)
259	{
260	    box_x1 = pSrcDrawable->x;
261	    fastExpose = FALSE;
262	}
263	if (box_y1 < pSrcDrawable->y)
264	{
265	    box_y1 = pSrcDrawable->y;
266	    fastExpose = FALSE;
267	}
268	if (box_x2 > pSrcDrawable->x + (int) pSrcDrawable->width)
269	{
270	    box_x2 = pSrcDrawable->x + (int) pSrcDrawable->width;
271	    fastExpose = FALSE;
272	}
273	if (box_y2 > pSrcDrawable->y + (int) pSrcDrawable->height)
274	{
275	    box_y2 = pSrcDrawable->y + (int) pSrcDrawable->height;
276	    fastExpose = FALSE;
277	}
278
279	/* Translate and clip the dst to the destination composite clip */
280        box_x1 -= dx;
281        box_x2 -= dx;
282        box_y1 -= dy;
283        box_y2 -= dy;
284
285	/* If the destination composite clip is one rectangle we can
286	   do the clip directly.  Otherwise we have to create a full
287	   blown region and call intersect */
288
289	cclip = miGetCompositeClip(pGC);
290        if (RegionNumRects(cclip) == 1)
291        {
292	    BoxPtr pBox = RegionRects(cclip);
293
294	    if (box_x1 < pBox->x1) box_x1 = pBox->x1;
295	    if (box_x2 > pBox->x2) box_x2 = pBox->x2;
296	    if (box_y1 < pBox->y1) box_y1 = pBox->y1;
297	    if (box_y2 > pBox->y2) box_y2 = pBox->y2;
298	    fastDst = TRUE;
299	}
300    }
301
302    /* Check to see if the region is empty */
303    if (box_x1 >= box_x2 || box_y1 >= box_y2)
304    {
305	RegionNull(&rgnDst);
306    }
307    else
308    {
309        BoxRec	box;
310	box.x1 = box_x1;
311	box.y1 = box_y1;
312	box.x2 = box_x2;
313	box.y2 = box_y2;
314	RegionInit(&rgnDst, &box, 1);
315    }
316
317    /* Clip against complex source if needed */
318    if (!fastSrc)
319    {
320	RegionIntersect(&rgnDst, &rgnDst, prgnSrcClip);
321	RegionTranslate(&rgnDst, -dx, -dy);
322    }
323
324    /* Clip against complex dest if needed */
325    if (!fastDst)
326    {
327	RegionIntersect(&rgnDst, &rgnDst,
328			 miGetCompositeClip(pGC));
329    }
330
331    /* Do bit blitting */
332    numRects = RegionNumRects(&rgnDst);
333    if (numRects && widthSrc && heightSrc)
334	miCopyRegion (pSrcDrawable, pDstDrawable, pGC,
335		      &rgnDst, dx, dy, copyProc, bitPlane, closure);
336
337    /* Pixmap sources generate a NoExposed (we return NULL to do this) */
338    if (!fastExpose && pGC->fExpose)
339	prgnExposed = miHandleExposures(pSrcDrawable, pDstDrawable, pGC,
340					xIn - pSrcDrawable->x,
341					yIn - pSrcDrawable->y,
342					widthSrc, heightSrc,
343					xOut - pDstDrawable->x,
344					yOut - pDstDrawable->y,
345					(unsigned long) bitPlane);
346    RegionUninit(&rgnDst);
347    if (freeSrcClip)
348	RegionDestroy(prgnSrcClip);
349    return prgnExposed;
350}
351