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 
34 void
miCopyRegion(DrawablePtr pSrcDrawable,DrawablePtr pDstDrawable,GCPtr pGC,RegionPtr pDstRegion,int dx,int dy,miCopyProc copyProc,Pixel bitPlane,void * closure)35 miCopyRegion(DrawablePtr pSrcDrawable,
36              DrawablePtr pDstDrawable,
37              GCPtr pGC,
38              RegionPtr pDstRegion,
39              int dx, int dy, miCopyProc copyProc, Pixel bitPlane, void *closure)
40 {
41     int careful;
42     Bool reverse;
43     Bool upsidedown;
44     BoxPtr pbox;
45     int nbox;
46     BoxPtr pboxNew1, pboxNew2, pboxBase, pboxNext, pboxTmp;
47 
48     pbox = RegionRects(pDstRegion);
49     nbox = RegionNumRects(pDstRegion);
50 
51     /* XXX we have to err on the side of safety when both are windows,
52      * because we don't know if IncludeInferiors is being used.
53      */
54     careful = ((pSrcDrawable == pDstDrawable) ||
55                ((pSrcDrawable->type == DRAWABLE_WINDOW) &&
56                 (pDstDrawable->type == DRAWABLE_WINDOW)));
57 
58     pboxNew1 = NULL;
59     pboxNew2 = NULL;
60     if (careful && dy < 0) {
61         upsidedown = TRUE;
62 
63         if (nbox > 1) {
64             /* keep ordering in each band, reverse order of bands */
65             pboxNew1 = xallocarray(nbox, sizeof(BoxRec));
66             if (!pboxNew1)
67                 return;
68             pboxBase = pboxNext = pbox + nbox - 1;
69             while (pboxBase >= pbox) {
70                 while ((pboxNext >= pbox) && (pboxBase->y1 == pboxNext->y1))
71                     pboxNext--;
72                 pboxTmp = pboxNext + 1;
73                 while (pboxTmp <= pboxBase) {
74                     *pboxNew1++ = *pboxTmp++;
75                 }
76                 pboxBase = pboxNext;
77             }
78             pboxNew1 -= nbox;
79             pbox = pboxNew1;
80         }
81     }
82     else {
83         /* walk source top to bottom */
84         upsidedown = FALSE;
85     }
86 
87     if (careful && dx < 0) {
88         /* walk source right to left */
89         if (dy <= 0)
90             reverse = TRUE;
91         else
92             reverse = FALSE;
93 
94         if (nbox > 1) {
95             /* reverse order of rects in each band */
96             pboxNew2 = xallocarray(nbox, sizeof(BoxRec));
97             if (!pboxNew2) {
98                 free(pboxNew1);
99                 return;
100             }
101             pboxBase = pboxNext = pbox;
102             while (pboxBase < pbox + nbox) {
103                 while ((pboxNext < pbox + nbox) &&
104                        (pboxNext->y1 == pboxBase->y1))
105                     pboxNext++;
106                 pboxTmp = pboxNext;
107                 while (pboxTmp != pboxBase) {
108                     *pboxNew2++ = *--pboxTmp;
109                 }
110                 pboxBase = pboxNext;
111             }
112             pboxNew2 -= nbox;
113             pbox = pboxNew2;
114         }
115     }
116     else {
117         /* walk source left to right */
118         reverse = FALSE;
119     }
120 
121     (*copyProc) (pSrcDrawable,
122                  pDstDrawable,
123                  pGC,
124                  pbox, nbox, dx, dy, reverse, upsidedown, bitPlane, closure);
125 
126     free(pboxNew1);
127     free(pboxNew2);
128 }
129 
130 RegionPtr
miDoCopy(DrawablePtr pSrcDrawable,DrawablePtr pDstDrawable,GCPtr pGC,int xIn,int yIn,int widthSrc,int heightSrc,int xOut,int yOut,miCopyProc copyProc,Pixel bitPlane,void * closure)131 miDoCopy(DrawablePtr pSrcDrawable,
132          DrawablePtr pDstDrawable,
133          GCPtr pGC,
134          int xIn,
135          int yIn,
136          int widthSrc,
137          int heightSrc,
138          int xOut, int yOut, miCopyProc copyProc, Pixel bitPlane, void *closure)
139 {
140     RegionPtr prgnSrcClip = NULL;       /* may be a new region, or just a copy */
141     Bool freeSrcClip = FALSE;
142     RegionPtr prgnExposed = NULL;
143     RegionRec rgnDst;
144     int dx;
145     int dy;
146     int numRects;
147     int box_x1;
148     int box_y1;
149     int box_x2;
150     int box_y2;
151     Bool fastSrc = FALSE;       /* for fast clipping with pixmap source */
152     Bool fastDst = FALSE;       /* for fast clipping with one rect dest */
153     Bool fastExpose = FALSE;    /* for fast exposures with pixmap source */
154 
155     /* Short cut for unmapped or fully clipped windows */
156     if (pDstDrawable->type == DRAWABLE_WINDOW &&
157         RegionNil(pGC->pCompositeClip)) {
158         return NULL;
159     }
160 
161     (*pSrcDrawable->pScreen->SourceValidate) (pSrcDrawable, xIn, yIn,
162                                               widthSrc, heightSrc,
163                                               pGC->subWindowMode);
164 
165     /* Compute source clip region */
166     if (pSrcDrawable->type == DRAWABLE_PIXMAP) {
167         if ((pSrcDrawable == pDstDrawable) && (!pGC->clientClip))
168             prgnSrcClip = miGetCompositeClip(pGC);
169         else
170             fastSrc = TRUE;
171     }
172     else {
173         if (pGC->subWindowMode == IncludeInferiors) {
174             /*
175              * XFree86 DDX empties the border clip when the
176              * VT is inactive, make sure the region isn't empty
177              */
178             if (!((WindowPtr) pSrcDrawable)->parent &&
179                 RegionNotEmpty(&((WindowPtr) pSrcDrawable)->borderClip)) {
180                 /*
181                  * special case bitblt from root window in
182                  * IncludeInferiors mode; just like from a pixmap
183                  */
184                 fastSrc = TRUE;
185             }
186             else if ((pSrcDrawable == pDstDrawable) && (!pGC->clientClip)) {
187                 prgnSrcClip = miGetCompositeClip(pGC);
188             }
189             else {
190                 prgnSrcClip = NotClippedByChildren((WindowPtr) pSrcDrawable);
191                 freeSrcClip = TRUE;
192             }
193         }
194         else {
195             prgnSrcClip = &((WindowPtr) pSrcDrawable)->clipList;
196         }
197     }
198 
199     xIn += pSrcDrawable->x;
200     yIn += pSrcDrawable->y;
201 
202     xOut += pDstDrawable->x;
203     yOut += pDstDrawable->y;
204 
205     box_x1 = xIn;
206     box_y1 = yIn;
207     box_x2 = xIn + widthSrc;
208     box_y2 = yIn + heightSrc;
209 
210     dx = xIn - xOut;
211     dy = yIn - yOut;
212 
213     /* Don't create a source region if we are doing a fast clip */
214     if (fastSrc) {
215         RegionPtr cclip;
216 
217         fastExpose = TRUE;
218         /*
219          * clip the source; if regions extend beyond the source size,
220          * make sure exposure events get sent
221          */
222         if (box_x1 < pSrcDrawable->x) {
223             box_x1 = pSrcDrawable->x;
224             fastExpose = FALSE;
225         }
226         if (box_y1 < pSrcDrawable->y) {
227             box_y1 = pSrcDrawable->y;
228             fastExpose = FALSE;
229         }
230         if (box_x2 > pSrcDrawable->x + (int) pSrcDrawable->width) {
231             box_x2 = pSrcDrawable->x + (int) pSrcDrawable->width;
232             fastExpose = FALSE;
233         }
234         if (box_y2 > pSrcDrawable->y + (int) pSrcDrawable->height) {
235             box_y2 = pSrcDrawable->y + (int) pSrcDrawable->height;
236             fastExpose = FALSE;
237         }
238 
239         /* Translate and clip the dst to the destination composite clip */
240         box_x1 -= dx;
241         box_x2 -= dx;
242         box_y1 -= dy;
243         box_y2 -= dy;
244 
245         /* If the destination composite clip is one rectangle we can
246            do the clip directly.  Otherwise we have to create a full
247            blown region and call intersect */
248 
249         cclip = miGetCompositeClip(pGC);
250         if (RegionNumRects(cclip) == 1) {
251             BoxPtr pBox = RegionRects(cclip);
252 
253             if (box_x1 < pBox->x1)
254                 box_x1 = pBox->x1;
255             if (box_x2 > pBox->x2)
256                 box_x2 = pBox->x2;
257             if (box_y1 < pBox->y1)
258                 box_y1 = pBox->y1;
259             if (box_y2 > pBox->y2)
260                 box_y2 = pBox->y2;
261             fastDst = TRUE;
262         }
263     }
264 
265     /* Check to see if the region is empty */
266     if (box_x1 >= box_x2 || box_y1 >= box_y2) {
267         RegionNull(&rgnDst);
268     }
269     else {
270         BoxRec box;
271 
272         box.x1 = box_x1;
273         box.y1 = box_y1;
274         box.x2 = box_x2;
275         box.y2 = box_y2;
276         RegionInit(&rgnDst, &box, 1);
277     }
278 
279     /* Clip against complex source if needed */
280     if (!fastSrc) {
281         RegionIntersect(&rgnDst, &rgnDst, prgnSrcClip);
282         RegionTranslate(&rgnDst, -dx, -dy);
283     }
284 
285     /* Clip against complex dest if needed */
286     if (!fastDst) {
287         RegionIntersect(&rgnDst, &rgnDst, miGetCompositeClip(pGC));
288     }
289 
290     /* Do bit blitting */
291     numRects = RegionNumRects(&rgnDst);
292     if (numRects && widthSrc && heightSrc)
293         miCopyRegion(pSrcDrawable, pDstDrawable, pGC,
294                      &rgnDst, dx, dy, copyProc, bitPlane, closure);
295 
296     /* Pixmap sources generate a NoExposed (we return NULL to do this) */
297     if (!fastExpose && pGC->fExpose)
298         prgnExposed = miHandleExposures(pSrcDrawable, pDstDrawable, pGC,
299                                         xIn - pSrcDrawable->x,
300                                         yIn - pSrcDrawable->y,
301                                         widthSrc, heightSrc,
302                                         xOut - pDstDrawable->x,
303                                         yOut - pDstDrawable->y);
304     RegionUninit(&rgnDst);
305     if (freeSrcClip)
306         RegionDestroy(prgnSrcClip);
307     return prgnExposed;
308 }
309