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