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 windows */
156 
157     if (pDstDrawable->type == DRAWABLE_WINDOW &&
158         !((WindowPtr) pDstDrawable)->realized) {
159         return NULL;
160     }
161 
162     if (pSrcDrawable->pScreen->SourceValidate) {
163         (*pSrcDrawable->pScreen->SourceValidate) (pSrcDrawable, xIn, yIn,
164                                                   widthSrc, heightSrc,
165                                                   pGC->subWindowMode);
166     }
167 
168     /* Compute source clip region */
169     if (pSrcDrawable->type == DRAWABLE_PIXMAP) {
170         if ((pSrcDrawable == pDstDrawable) && (!pGC->clientClip))
171             prgnSrcClip = miGetCompositeClip(pGC);
172         else
173             fastSrc = TRUE;
174     }
175     else {
176         if (pGC->subWindowMode == IncludeInferiors) {
177             /*
178              * XFree86 DDX empties the border clip when the
179              * VT is inactive, make sure the region isn't empty
180              */
181             if (!((WindowPtr) pSrcDrawable)->parent &&
182                 RegionNotEmpty(&((WindowPtr) pSrcDrawable)->borderClip)) {
183                 /*
184                  * special case bitblt from root window in
185                  * IncludeInferiors mode; just like from a pixmap
186                  */
187                 fastSrc = TRUE;
188             }
189             else if ((pSrcDrawable == pDstDrawable) && (!pGC->clientClip)) {
190                 prgnSrcClip = miGetCompositeClip(pGC);
191             }
192             else {
193                 prgnSrcClip = NotClippedByChildren((WindowPtr) pSrcDrawable);
194                 freeSrcClip = TRUE;
195             }
196         }
197         else {
198             prgnSrcClip = &((WindowPtr) pSrcDrawable)->clipList;
199         }
200     }
201 
202     xIn += pSrcDrawable->x;
203     yIn += pSrcDrawable->y;
204 
205     xOut += pDstDrawable->x;
206     yOut += pDstDrawable->y;
207 
208     box_x1 = xIn;
209     box_y1 = yIn;
210     box_x2 = xIn + widthSrc;
211     box_y2 = yIn + heightSrc;
212 
213     dx = xIn - xOut;
214     dy = yIn - yOut;
215 
216     /* Don't create a source region if we are doing a fast clip */
217     if (fastSrc) {
218         RegionPtr cclip;
219 
220         fastExpose = TRUE;
221         /*
222          * clip the source; if regions extend beyond the source size,
223          * make sure exposure events get sent
224          */
225         if (box_x1 < pSrcDrawable->x) {
226             box_x1 = pSrcDrawable->x;
227             fastExpose = FALSE;
228         }
229         if (box_y1 < pSrcDrawable->y) {
230             box_y1 = pSrcDrawable->y;
231             fastExpose = FALSE;
232         }
233         if (box_x2 > pSrcDrawable->x + (int) pSrcDrawable->width) {
234             box_x2 = pSrcDrawable->x + (int) pSrcDrawable->width;
235             fastExpose = FALSE;
236         }
237         if (box_y2 > pSrcDrawable->y + (int) pSrcDrawable->height) {
238             box_y2 = pSrcDrawable->y + (int) pSrcDrawable->height;
239             fastExpose = FALSE;
240         }
241 
242         /* Translate and clip the dst to the destination composite clip */
243         box_x1 -= dx;
244         box_x2 -= dx;
245         box_y1 -= dy;
246         box_y2 -= dy;
247 
248         /* If the destination composite clip is one rectangle we can
249            do the clip directly.  Otherwise we have to create a full
250            blown region and call intersect */
251 
252         cclip = miGetCompositeClip(pGC);
253         if (RegionNumRects(cclip) == 1) {
254             BoxPtr pBox = RegionRects(cclip);
255 
256             if (box_x1 < pBox->x1)
257                 box_x1 = pBox->x1;
258             if (box_x2 > pBox->x2)
259                 box_x2 = pBox->x2;
260             if (box_y1 < pBox->y1)
261                 box_y1 = pBox->y1;
262             if (box_y2 > pBox->y2)
263                 box_y2 = pBox->y2;
264             fastDst = TRUE;
265         }
266     }
267 
268     /* Check to see if the region is empty */
269     if (box_x1 >= box_x2 || box_y1 >= box_y2) {
270         RegionNull(&rgnDst);
271     }
272     else {
273         BoxRec box;
274 
275         box.x1 = box_x1;
276         box.y1 = box_y1;
277         box.x2 = box_x2;
278         box.y2 = box_y2;
279         RegionInit(&rgnDst, &box, 1);
280     }
281 
282     /* Clip against complex source if needed */
283     if (!fastSrc) {
284         RegionIntersect(&rgnDst, &rgnDst, prgnSrcClip);
285         RegionTranslate(&rgnDst, -dx, -dy);
286     }
287 
288     /* Clip against complex dest if needed */
289     if (!fastDst) {
290         RegionIntersect(&rgnDst, &rgnDst, miGetCompositeClip(pGC));
291     }
292 
293     /* Do bit blitting */
294     numRects = RegionNumRects(&rgnDst);
295     if (numRects && widthSrc && heightSrc)
296         miCopyRegion(pSrcDrawable, pDstDrawable, pGC,
297                      &rgnDst, dx, dy, copyProc, bitPlane, closure);
298 
299     /* Pixmap sources generate a NoExposed (we return NULL to do this) */
300     if (!fastExpose && pGC->fExpose)
301         prgnExposed = miHandleExposures(pSrcDrawable, pDstDrawable, pGC,
302                                         xIn - pSrcDrawable->x,
303                                         yIn - pSrcDrawable->y,
304                                         widthSrc, heightSrc,
305                                         xOut - pDstDrawable->x,
306                                         yOut - pDstDrawable->y);
307     RegionUninit(&rgnDst);
308     if (freeSrcClip)
309         RegionDestroy(prgnSrcClip);
310     return prgnExposed;
311 }
312