rootlessGC.c revision 05b261ec
105b261ecSmrg/*
205b261ecSmrg * Graphics Context support for generic rootless X server
305b261ecSmrg */
405b261ecSmrg/*
505b261ecSmrg * Copyright (c) 2001 Greg Parker. All Rights Reserved.
605b261ecSmrg * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
705b261ecSmrg * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
805b261ecSmrg *
905b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a
1005b261ecSmrg * copy of this software and associated documentation files (the "Software"),
1105b261ecSmrg * to deal in the Software without restriction, including without limitation
1205b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1305b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the
1405b261ecSmrg * Software is furnished to do so, subject to the following conditions:
1505b261ecSmrg *
1605b261ecSmrg * The above copyright notice and this permission notice shall be included in
1705b261ecSmrg * all copies or substantial portions of the Software.
1805b261ecSmrg *
1905b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2005b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2105b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2205b261ecSmrg * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
2305b261ecSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2405b261ecSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2505b261ecSmrg * DEALINGS IN THE SOFTWARE.
2605b261ecSmrg *
2705b261ecSmrg * Except as contained in this notice, the name(s) of the above copyright
2805b261ecSmrg * holders shall not be used in advertising or otherwise to promote the sale,
2905b261ecSmrg * use or other dealings in this Software without prior written authorization.
3005b261ecSmrg */
3105b261ecSmrg
3205b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
3305b261ecSmrg#include <dix-config.h>
3405b261ecSmrg#endif
3505b261ecSmrg
3605b261ecSmrg#include <stddef.h> /* For NULL */
3705b261ecSmrg#include "mi.h"
3805b261ecSmrg#include "scrnintstr.h"
3905b261ecSmrg#include "gcstruct.h"
4005b261ecSmrg#include "pixmapstr.h"
4105b261ecSmrg#include "windowstr.h"
4205b261ecSmrg#include "dixfontstr.h"
4305b261ecSmrg#include "mivalidate.h"
4405b261ecSmrg#include "fb.h"
4505b261ecSmrg
4605b261ecSmrg#include <sys/types.h>
4705b261ecSmrg#include <sys/stat.h>
4805b261ecSmrg#include <fcntl.h>
4905b261ecSmrg
5005b261ecSmrg#include "rootlessCommon.h"
5105b261ecSmrg
5205b261ecSmrg
5305b261ecSmrg// GC functions
5405b261ecSmrgstatic void RootlessValidateGC(GCPtr pGC, unsigned long changes,
5505b261ecSmrg                               DrawablePtr pDrawable);
5605b261ecSmrgstatic void RootlessChangeGC(GCPtr pGC, unsigned long mask);
5705b261ecSmrgstatic void RootlessCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst);
5805b261ecSmrgstatic void RootlessDestroyGC(GCPtr pGC);
5905b261ecSmrgstatic void RootlessChangeClip(GCPtr pGC, int type, pointer pvalue,
6005b261ecSmrg                               int nrects);
6105b261ecSmrgstatic void RootlessDestroyClip(GCPtr pGC);
6205b261ecSmrgstatic void RootlessCopyClip(GCPtr pgcDst, GCPtr pgcSrc);
6305b261ecSmrg
6405b261ecSmrgGCFuncs rootlessGCFuncs = {
6505b261ecSmrg    RootlessValidateGC,
6605b261ecSmrg    RootlessChangeGC,
6705b261ecSmrg    RootlessCopyGC,
6805b261ecSmrg    RootlessDestroyGC,
6905b261ecSmrg    RootlessChangeClip,
7005b261ecSmrg    RootlessDestroyClip,
7105b261ecSmrg    RootlessCopyClip,
7205b261ecSmrg};
7305b261ecSmrg
7405b261ecSmrg// GC operations
7505b261ecSmrgstatic void RootlessFillSpans();
7605b261ecSmrgstatic void RootlessSetSpans();
7705b261ecSmrgstatic void RootlessPutImage();
7805b261ecSmrgstatic RegionPtr RootlessCopyArea();
7905b261ecSmrgstatic RegionPtr RootlessCopyPlane();
8005b261ecSmrgstatic void RootlessPolyPoint();
8105b261ecSmrgstatic void RootlessPolylines();
8205b261ecSmrgstatic void RootlessPolySegment();
8305b261ecSmrgstatic void RootlessPolyRectangle();
8405b261ecSmrgstatic void RootlessPolyArc();
8505b261ecSmrgstatic void RootlessFillPolygon();
8605b261ecSmrgstatic void RootlessPolyFillRect();
8705b261ecSmrgstatic void RootlessPolyFillArc();
8805b261ecSmrgstatic int RootlessPolyText8();
8905b261ecSmrgstatic int RootlessPolyText16();
9005b261ecSmrgstatic void RootlessImageText8();
9105b261ecSmrgstatic void RootlessImageText16();
9205b261ecSmrgstatic void RootlessImageGlyphBlt();
9305b261ecSmrgstatic void RootlessPolyGlyphBlt();
9405b261ecSmrgstatic void RootlessPushPixels();
9505b261ecSmrg
9605b261ecSmrgstatic GCOps rootlessGCOps = {
9705b261ecSmrg    RootlessFillSpans,
9805b261ecSmrg    RootlessSetSpans,
9905b261ecSmrg    RootlessPutImage,
10005b261ecSmrg    RootlessCopyArea,
10105b261ecSmrg    RootlessCopyPlane,
10205b261ecSmrg    RootlessPolyPoint,
10305b261ecSmrg    RootlessPolylines,
10405b261ecSmrg    RootlessPolySegment,
10505b261ecSmrg    RootlessPolyRectangle,
10605b261ecSmrg    RootlessPolyArc,
10705b261ecSmrg    RootlessFillPolygon,
10805b261ecSmrg    RootlessPolyFillRect,
10905b261ecSmrg    RootlessPolyFillArc,
11005b261ecSmrg    RootlessPolyText8,
11105b261ecSmrg    RootlessPolyText16,
11205b261ecSmrg    RootlessImageText8,
11305b261ecSmrg    RootlessImageText16,
11405b261ecSmrg    RootlessImageGlyphBlt,
11505b261ecSmrg    RootlessPolyGlyphBlt,
11605b261ecSmrg    RootlessPushPixels
11705b261ecSmrg};
11805b261ecSmrg
11905b261ecSmrg/*
12005b261ecSmrg   There are two issues we must contend with when drawing. These are
12105b261ecSmrg   controlled with ROOTLESS_PROTECT_ALPHA and ROOTLESS_ACCEL.
12205b261ecSmrg
12305b261ecSmrg   If ROOTLESS_PROTECT_ALPHA is set, we have to make sure that the alpha
12405b261ecSmrg   channel of the on screen windows is always opaque. fb makes this harder
12505b261ecSmrg   than it would otherwise be by noticing that a planemask of 0x00ffffff
12605b261ecSmrg   includes all bits when depth==24, and so it "optimizes" the planemask to
12705b261ecSmrg   0xffffffff. We work around this by temporarily setting depth=bpp while
12805b261ecSmrg   changing the GC.
12905b261ecSmrg
13005b261ecSmrg   So the normal situation (in 32 bit mode) is that the planemask is
13105b261ecSmrg   0x00ffffff and thus fb leaves the alpha channel alone. The rootless
13205b261ecSmrg   implementation is responsible for setting the alpha channel opaque
13305b261ecSmrg   initially.
13405b261ecSmrg
13505b261ecSmrg   Unfortunately drawing with a planemask that doesn't have all bits set
13605b261ecSmrg   normally causes fb to fall off its fastest paths when blitting and
13705b261ecSmrg   filling.  So we try to recognize when we can relax the planemask back to
13805b261ecSmrg   0xffffffff, and do that for the duration of the drawing operation,
13905b261ecSmrg   setting the alpha channel in fg/bg pixels to opaque at the same time. We
14005b261ecSmrg   can do this when drawing op is GXcopy. We can also do this when copying
14105b261ecSmrg   from another window since its alpha channel must also be opaque.
14205b261ecSmrg
14305b261ecSmrg   The other issue to consider is that the rootless implementation may
14405b261ecSmrg   provide accelerated drawing functions if ROOTLESS_ACCEL is set. For some
14505b261ecSmrg   drawing primitives we swap in rootless acceleration functions, which use
14605b261ecSmrg   the accelerated drawing functions where possible.
14705b261ecSmrg
14805b261ecSmrg   Where both alpha protection and acceleration is used, it is even a bigger
14905b261ecSmrg   win to relax the planemask to all ones because most accelerated drawing
15005b261ecSmrg   functions can only be used in this case. However, even if we can't set
15105b261ecSmrg   the planemask to all ones, we can still use the accelerated
15205b261ecSmrg   CompositePixels function for GXcopy if it is a forward copy. This is
15305b261ecSmrg   mainly intended for copying from pixmaps to windows. The CompositePixels
15405b261ecSmrg   operation used sets alpha to 0xFF during the copy.
15505b261ecSmrg
15605b261ecSmrg   The three macros below are used to implement this, potentially accelerated
15705b261ecSmrg   drawing ops look something like this:
15805b261ecSmrg
15905b261ecSmrg   OP {
16005b261ecSmrg       GC_SAVE(gc);
16105b261ecSmrg       GCOP_UNWRAP(gc);
16205b261ecSmrg
16305b261ecSmrg       ...
16405b261ecSmrg
16505b261ecSmrg       if (canAccelxxx(..) && otherwise-suitable)
16605b261ecSmrg            GC_UNSET_PM(gc, dst);
16705b261ecSmrg
16805b261ecSmrg       gc->funcs->OP(gc, ...);
16905b261ecSmrg
17005b261ecSmrg       GC_RESTORE(gc, dst);
17105b261ecSmrg       GCOP_WRAP(gc);
17205b261ecSmrg   }
17305b261ecSmrg
17405b261ecSmrg */
17505b261ecSmrg
17605b261ecSmrg#define GC_SAVE(pGC) 				\
17705b261ecSmrg    unsigned long _save_fg = (pGC)->fgPixel;	\
17805b261ecSmrg    unsigned long _save_bg = (pGC)->bgPixel;	\
17905b261ecSmrg    unsigned long _save_pm = (pGC)->planemask;	\
18005b261ecSmrg    Bool _changed = FALSE
18105b261ecSmrg
18205b261ecSmrg#define GC_RESTORE(pGC, pDraw)					\
18305b261ecSmrg    do {							\
18405b261ecSmrg        if (_changed) {						\
18505b261ecSmrg            unsigned int depth = (pDraw)->depth;		\
18605b261ecSmrg            (pGC)->fgPixel = _save_fg;				\
18705b261ecSmrg            (pGC)->bgPixel = _save_bg;				\
18805b261ecSmrg            (pGC)->planemask = _save_pm;			\
18905b261ecSmrg            (pDraw)->depth = (pDraw)->bitsPerPixel;		\
19005b261ecSmrg            VALIDATE_GC(pGC, GCForeground | GCBackground |	\
19105b261ecSmrg                        GCPlaneMask, pDraw);			\
19205b261ecSmrg            (pDraw)->depth = depth;				\
19305b261ecSmrg        }							\
19405b261ecSmrg    } while (0)
19505b261ecSmrg
19605b261ecSmrg#define GC_UNSET_PM(pGC, pDraw)						\
19705b261ecSmrg    do {								\
19805b261ecSmrg        unsigned int mask = RootlessAlphaMask ((pDraw)->bitsPerPixel);	\
19905b261ecSmrg        if (((pGC)->planemask & mask) != mask) {			\
20005b261ecSmrg            unsigned int depth = (pDraw)->depth;			\
20105b261ecSmrg            (pGC)->fgPixel |= mask;					\
20205b261ecSmrg            (pGC)->bgPixel |= mask;					\
20305b261ecSmrg            (pGC)->planemask |= mask;					\
20405b261ecSmrg            (pDraw)->depth = (pDraw)->bitsPerPixel;			\
20505b261ecSmrg            VALIDATE_GC(pGC, GCForeground |				\
20605b261ecSmrg                        GCBackground | GCPlaneMask, pDraw);		\
20705b261ecSmrg            (pDraw)->depth = depth;					\
20805b261ecSmrg            _changed = TRUE;						\
20905b261ecSmrg        }								\
21005b261ecSmrg    } while (0)
21105b261ecSmrg
21205b261ecSmrg#define VALIDATE_GC(pGC, changes, pDrawable)				\
21305b261ecSmrg    do {								\
21405b261ecSmrg        pGC->funcs->ValidateGC(pGC, changes, pDrawable);		\
21505b261ecSmrg        if (((WindowPtr) pDrawable)->viewable) {			\
21605b261ecSmrg            gcrec->originalOps = pGC->ops;				\
21705b261ecSmrg        }								\
21805b261ecSmrg    } while(0)
21905b261ecSmrg
22005b261ecSmrgstatic RootlessWindowRec *
22105b261ecSmrgcanAccelBlit (DrawablePtr pDraw, GCPtr pGC)
22205b261ecSmrg{
22305b261ecSmrg    WindowPtr pTop;
22405b261ecSmrg    RootlessWindowRec *winRec;
22505b261ecSmrg    unsigned int pm;
22605b261ecSmrg
22705b261ecSmrg    if (pGC->alu != GXcopy)
22805b261ecSmrg        return NULL;
22905b261ecSmrg
23005b261ecSmrg    if (pDraw->type != DRAWABLE_WINDOW)
23105b261ecSmrg        return NULL;
23205b261ecSmrg
23305b261ecSmrg    pm = ~RootlessAlphaMask(pDraw->bitsPerPixel);
23405b261ecSmrg    if ((pGC->planemask & pm) != pm)
23505b261ecSmrg        return NULL;
23605b261ecSmrg
23705b261ecSmrg    pTop = TopLevelParent((WindowPtr) pDraw);
23805b261ecSmrg    if (pTop == NULL)
23905b261ecSmrg        return NULL;
24005b261ecSmrg
24105b261ecSmrg    winRec = WINREC(pTop);
24205b261ecSmrg    if (winRec == NULL)
24305b261ecSmrg        return NULL;
24405b261ecSmrg
24505b261ecSmrg    return winRec;
24605b261ecSmrg}
24705b261ecSmrg
24805b261ecSmrgstatic inline RootlessWindowRec *
24905b261ecSmrgcanAccelFill(DrawablePtr pDraw, GCPtr pGC)
25005b261ecSmrg{
25105b261ecSmrg    if (pGC->fillStyle != FillSolid)
25205b261ecSmrg        return NULL;
25305b261ecSmrg
25405b261ecSmrg    return canAccelBlit(pDraw, pGC);
25505b261ecSmrg}
25605b261ecSmrg
25705b261ecSmrgstatic unsigned int
25805b261ecSmrgboxBytes(DrawablePtr pDraw, BoxRec *box)
25905b261ecSmrg{
26005b261ecSmrg    unsigned int pixels;
26105b261ecSmrg
26205b261ecSmrg    pixels = (box->x2 - box->x1) * (box->y2 - box->y1);
26305b261ecSmrg
26405b261ecSmrg    return pixels * (pDraw->bitsPerPixel >> 3);
26505b261ecSmrg}
26605b261ecSmrg
26705b261ecSmrg
26805b261ecSmrg/*
26905b261ecSmrg * Screen function to create a graphics context
27005b261ecSmrg */
27105b261ecSmrgBool
27205b261ecSmrgRootlessCreateGC(GCPtr pGC)
27305b261ecSmrg{
27405b261ecSmrg    RootlessGCRec *gcrec;
27505b261ecSmrg    RootlessScreenRec *s;
27605b261ecSmrg    Bool result;
27705b261ecSmrg
27805b261ecSmrg    SCREEN_UNWRAP(pGC->pScreen, CreateGC);
27905b261ecSmrg    s = (RootlessScreenRec *) pGC->pScreen->
28005b261ecSmrg            devPrivates[rootlessScreenPrivateIndex].ptr;
28105b261ecSmrg    result = s->CreateGC(pGC);
28205b261ecSmrg
28305b261ecSmrg    gcrec = (RootlessGCRec *) pGC->devPrivates[rootlessGCPrivateIndex].ptr;
28405b261ecSmrg    gcrec->originalOps = NULL; // don't wrap ops yet
28505b261ecSmrg    gcrec->originalFuncs = pGC->funcs;
28605b261ecSmrg    pGC->funcs = &rootlessGCFuncs;
28705b261ecSmrg
28805b261ecSmrg    SCREEN_WRAP(pGC->pScreen, CreateGC);
28905b261ecSmrg    return result;
29005b261ecSmrg}
29105b261ecSmrg
29205b261ecSmrg
29305b261ecSmrg/*
29405b261ecSmrg * GC funcs
29505b261ecSmrg *
29605b261ecSmrg * These wrap lower level GC funcs.
29705b261ecSmrg * ValidateGC wraps the GC ops iff dest is viewable.
29805b261ecSmrg * All the others just unwrap and call.
29905b261ecSmrg */
30005b261ecSmrg
30105b261ecSmrg// GCFUNC_UNRAP assumes funcs have been wrapped and
30205b261ecSmrg// does not assume ops have been wrapped
30305b261ecSmrg#define GCFUNC_UNWRAP(pGC) \
30405b261ecSmrg    RootlessGCRec *gcrec = (RootlessGCRec *) \
30505b261ecSmrg        (pGC)->devPrivates[rootlessGCPrivateIndex].ptr; \
30605b261ecSmrg    (pGC)->funcs = gcrec->originalFuncs; \
30705b261ecSmrg    if (gcrec->originalOps) { \
30805b261ecSmrg        (pGC)->ops = gcrec->originalOps; \
30905b261ecSmrg}
31005b261ecSmrg
31105b261ecSmrg#define GCFUNC_WRAP(pGC) \
31205b261ecSmrg    gcrec->originalFuncs = (pGC)->funcs; \
31305b261ecSmrg    (pGC)->funcs = &rootlessGCFuncs; \
31405b261ecSmrg    if (gcrec->originalOps) { \
31505b261ecSmrg        gcrec->originalOps = (pGC)->ops; \
31605b261ecSmrg        (pGC)->ops = &rootlessGCOps; \
31705b261ecSmrg}
31805b261ecSmrg
31905b261ecSmrg
32005b261ecSmrgstatic void
32105b261ecSmrgRootlessValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
32205b261ecSmrg{
32305b261ecSmrg    GCFUNC_UNWRAP(pGC);
32405b261ecSmrg
32505b261ecSmrg    gcrec->originalOps = NULL;
32605b261ecSmrg
32705b261ecSmrg    if (pDrawable->type == DRAWABLE_WINDOW)
32805b261ecSmrg    {
32905b261ecSmrg#ifdef ROOTLESS_PROTECT_ALPHA
33005b261ecSmrg        unsigned int depth = pDrawable->depth;
33105b261ecSmrg
33205b261ecSmrg        // We force a planemask so fb doesn't overwrite the alpha channel.
33305b261ecSmrg        // Left to its own devices, fb will optimize away the planemask.
33405b261ecSmrg        pDrawable->depth = pDrawable->bitsPerPixel;
33505b261ecSmrg        pGC->planemask &= ~RootlessAlphaMask(pDrawable->bitsPerPixel);
33605b261ecSmrg        VALIDATE_GC(pGC, changes | GCPlaneMask, pDrawable);
33705b261ecSmrg        pDrawable->depth = depth;
33805b261ecSmrg#else
33905b261ecSmrg        VALIDATE_GC(pGC, changes, pDrawable);
34005b261ecSmrg#endif
34105b261ecSmrg    } else {
34205b261ecSmrg        pGC->funcs->ValidateGC(pGC, changes, pDrawable);
34305b261ecSmrg    }
34405b261ecSmrg
34505b261ecSmrg    GCFUNC_WRAP(pGC);
34605b261ecSmrg}
34705b261ecSmrg
34805b261ecSmrgstatic void RootlessChangeGC(GCPtr pGC, unsigned long mask)
34905b261ecSmrg{
35005b261ecSmrg    GCFUNC_UNWRAP(pGC);
35105b261ecSmrg    pGC->funcs->ChangeGC(pGC, mask);
35205b261ecSmrg    GCFUNC_WRAP(pGC);
35305b261ecSmrg}
35405b261ecSmrg
35505b261ecSmrgstatic void RootlessCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
35605b261ecSmrg{
35705b261ecSmrg    GCFUNC_UNWRAP(pGCDst);
35805b261ecSmrg    pGCDst->funcs->CopyGC(pGCSrc, mask, pGCDst);
35905b261ecSmrg    GCFUNC_WRAP(pGCDst);
36005b261ecSmrg}
36105b261ecSmrg
36205b261ecSmrgstatic void RootlessDestroyGC(GCPtr pGC)
36305b261ecSmrg{
36405b261ecSmrg    GCFUNC_UNWRAP(pGC);
36505b261ecSmrg    pGC->funcs->DestroyGC(pGC);
36605b261ecSmrg    GCFUNC_WRAP(pGC);
36705b261ecSmrg}
36805b261ecSmrg
36905b261ecSmrgstatic void RootlessChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
37005b261ecSmrg{
37105b261ecSmrg    GCFUNC_UNWRAP(pGC);
37205b261ecSmrg    pGC->funcs->ChangeClip(pGC, type, pvalue, nrects);
37305b261ecSmrg    GCFUNC_WRAP(pGC);
37405b261ecSmrg}
37505b261ecSmrg
37605b261ecSmrgstatic void RootlessDestroyClip(GCPtr pGC)
37705b261ecSmrg{
37805b261ecSmrg    GCFUNC_UNWRAP(pGC);
37905b261ecSmrg    pGC->funcs->DestroyClip(pGC);
38005b261ecSmrg    GCFUNC_WRAP(pGC);
38105b261ecSmrg}
38205b261ecSmrg
38305b261ecSmrgstatic void RootlessCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
38405b261ecSmrg{
38505b261ecSmrg    GCFUNC_UNWRAP(pgcDst);
38605b261ecSmrg    pgcDst->funcs->CopyClip(pgcDst, pgcSrc);
38705b261ecSmrg    GCFUNC_WRAP(pgcDst);
38805b261ecSmrg}
38905b261ecSmrg
39005b261ecSmrg
39105b261ecSmrg/*
39205b261ecSmrg * GC ops
39305b261ecSmrg *
39405b261ecSmrg * We can't use shadowfb because shadowfb assumes one pixmap
39505b261ecSmrg * and our root window is a special case.
39605b261ecSmrg * However, much of this code is copied from shadowfb.
39705b261ecSmrg */
39805b261ecSmrg
39905b261ecSmrg// assumes both funcs and ops are wrapped
40005b261ecSmrg#define GCOP_UNWRAP(pGC) \
40105b261ecSmrg    RootlessGCRec *gcrec = (RootlessGCRec *) \
40205b261ecSmrg        (pGC)->devPrivates[rootlessGCPrivateIndex].ptr; \
40305b261ecSmrg    GCFuncs *saveFuncs = pGC->funcs; \
40405b261ecSmrg    (pGC)->funcs = gcrec->originalFuncs; \
40505b261ecSmrg    (pGC)->ops = gcrec->originalOps;
40605b261ecSmrg
40705b261ecSmrg#define GCOP_WRAP(pGC) \
40805b261ecSmrg    gcrec->originalOps = (pGC)->ops; \
40905b261ecSmrg    (pGC)->funcs = saveFuncs; \
41005b261ecSmrg    (pGC)->ops = &rootlessGCOps;
41105b261ecSmrg
41205b261ecSmrg/* Turn drawing on the root into a no-op */
41305b261ecSmrg#define GC_IS_ROOT(pDst) ((pDst)->type == DRAWABLE_WINDOW \
41405b261ecSmrg                            && IsRoot ((WindowPtr) (pDst)))
41505b261ecSmrg
41605b261ecSmrg#define GC_SKIP_ROOT(pDst)			\
41705b261ecSmrg    do {					\
41805b261ecSmrg        if (GC_IS_ROOT (pDst))			\
41905b261ecSmrg            return;				\
42005b261ecSmrg    } while (0)
42105b261ecSmrg
42205b261ecSmrg
42305b261ecSmrgstatic void
42405b261ecSmrgRootlessFillSpans(DrawablePtr dst, GCPtr pGC, int nInit,
42505b261ecSmrg                  DDXPointPtr pptInit, int *pwidthInit, int sorted)
42605b261ecSmrg{
42705b261ecSmrg    GC_SAVE(pGC);
42805b261ecSmrg    GCOP_UNWRAP(pGC);
42905b261ecSmrg    GC_SKIP_ROOT(dst);
43005b261ecSmrg    RL_DEBUG_MSG("fill spans start ");
43105b261ecSmrg
43205b261ecSmrg    if (nInit <= 0) {
43305b261ecSmrg        pGC->ops->FillSpans(dst, pGC, nInit, pptInit, pwidthInit, sorted);
43405b261ecSmrg    } else {
43505b261ecSmrg        DDXPointPtr ppt = pptInit;
43605b261ecSmrg        int *pwidth = pwidthInit;
43705b261ecSmrg        int i = nInit;
43805b261ecSmrg        BoxRec box;
43905b261ecSmrg
44005b261ecSmrg        box.x1 = ppt->x;
44105b261ecSmrg        box.x2 = box.x1 + *pwidth;
44205b261ecSmrg        box.y2 = box.y1 = ppt->y;
44305b261ecSmrg
44405b261ecSmrg        while (--i) {
44505b261ecSmrg            ppt++;
44605b261ecSmrg            pwidth++;
44705b261ecSmrg            if (box.x1 > ppt->x)
44805b261ecSmrg                box.x1 = ppt->x;
44905b261ecSmrg            if (box.x2 < (ppt->x + *pwidth))
45005b261ecSmrg                box.x2 = ppt->x + *pwidth;
45105b261ecSmrg            if (box.y1 > ppt->y)
45205b261ecSmrg                box.y1 = ppt->y;
45305b261ecSmrg            else if (box.y2 < ppt->y)
45405b261ecSmrg                box.y2 = ppt->y;
45505b261ecSmrg        }
45605b261ecSmrg
45705b261ecSmrg        box.y2++;
45805b261ecSmrg
45905b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
46005b261ecSmrg
46105b261ecSmrg        if (canAccelFill(dst, pGC) &&
46205b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
46305b261ecSmrg        {
46405b261ecSmrg            GC_UNSET_PM(pGC, dst);
46505b261ecSmrg        }
46605b261ecSmrg
46705b261ecSmrg        pGC->ops->FillSpans(dst, pGC, nInit, pptInit, pwidthInit, sorted);
46805b261ecSmrg
46905b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
47005b261ecSmrg        if (BOX_NOT_EMPTY(box))
47105b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
47205b261ecSmrg    }
47305b261ecSmrg
47405b261ecSmrg    GC_RESTORE(pGC, dst);
47505b261ecSmrg    GCOP_WRAP(pGC);
47605b261ecSmrg    RL_DEBUG_MSG("fill spans end\n");
47705b261ecSmrg}
47805b261ecSmrg
47905b261ecSmrgstatic void
48005b261ecSmrgRootlessSetSpans(DrawablePtr dst, GCPtr pGC, char *pSrc,
48105b261ecSmrg                 DDXPointPtr pptInit, int *pwidthInit,
48205b261ecSmrg                 int nspans, int sorted)
48305b261ecSmrg{
48405b261ecSmrg    GCOP_UNWRAP(pGC);
48505b261ecSmrg    GC_SKIP_ROOT(dst);
48605b261ecSmrg    RL_DEBUG_MSG("set spans start ");
48705b261ecSmrg
48805b261ecSmrg    if (nspans <= 0) {
48905b261ecSmrg        pGC->ops->SetSpans(dst, pGC, pSrc, pptInit, pwidthInit,
49005b261ecSmrg                           nspans, sorted);
49105b261ecSmrg    } else {
49205b261ecSmrg        DDXPointPtr ppt = pptInit;
49305b261ecSmrg        int *pwidth = pwidthInit;
49405b261ecSmrg        int i = nspans;
49505b261ecSmrg        BoxRec box;
49605b261ecSmrg
49705b261ecSmrg        box.x1 = ppt->x;
49805b261ecSmrg        box.x2 = box.x1 + *pwidth;
49905b261ecSmrg        box.y2 = box.y1 = ppt->y;
50005b261ecSmrg
50105b261ecSmrg        while (--i) {
50205b261ecSmrg            ppt++;
50305b261ecSmrg            pwidth++;
50405b261ecSmrg            if (box.x1 > ppt->x)
50505b261ecSmrg                box.x1 = ppt->x;
50605b261ecSmrg            if (box.x2 < (ppt->x + *pwidth))
50705b261ecSmrg                box.x2 = ppt->x + *pwidth;
50805b261ecSmrg            if (box.y1 > ppt->y)
50905b261ecSmrg                box.y1 = ppt->y;
51005b261ecSmrg            else if (box.y2 < ppt->y)
51105b261ecSmrg                box.y2 = ppt->y;
51205b261ecSmrg        }
51305b261ecSmrg
51405b261ecSmrg        box.y2++;
51505b261ecSmrg
51605b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
51705b261ecSmrg        pGC->ops->SetSpans(dst, pGC, pSrc, pptInit, pwidthInit,
51805b261ecSmrg                           nspans, sorted);
51905b261ecSmrg
52005b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
52105b261ecSmrg        if (BOX_NOT_EMPTY(box))
52205b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
52305b261ecSmrg    }
52405b261ecSmrg    GCOP_WRAP(pGC);
52505b261ecSmrg    RL_DEBUG_MSG("set spans end\n");
52605b261ecSmrg}
52705b261ecSmrg
52805b261ecSmrgstatic void
52905b261ecSmrgRootlessPutImage(DrawablePtr dst, GCPtr pGC,
53005b261ecSmrg                 int depth, int x, int y, int w, int h,
53105b261ecSmrg                 int leftPad, int format, char *pBits)
53205b261ecSmrg{
53305b261ecSmrg    BoxRec box;
53405b261ecSmrg
53505b261ecSmrg    GCOP_UNWRAP(pGC);
53605b261ecSmrg    GC_SKIP_ROOT(dst);
53705b261ecSmrg    RL_DEBUG_MSG("put image start ");
53805b261ecSmrg
53905b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
54005b261ecSmrg    pGC->ops->PutImage(dst, pGC, depth, x,y,w,h, leftPad, format, pBits);
54105b261ecSmrg
54205b261ecSmrg    box.x1 = x + dst->x;
54305b261ecSmrg    box.x2 = box.x1 + w;
54405b261ecSmrg    box.y1 = y + dst->y;
54505b261ecSmrg    box.y2 = box.y1 + h;
54605b261ecSmrg
54705b261ecSmrg    TRIM_BOX(box, pGC);
54805b261ecSmrg    if (BOX_NOT_EMPTY(box))
54905b261ecSmrg        RootlessDamageBox ((WindowPtr) dst, &box);
55005b261ecSmrg
55105b261ecSmrg    GCOP_WRAP(pGC);
55205b261ecSmrg    RL_DEBUG_MSG("put image end\n");
55305b261ecSmrg}
55405b261ecSmrg
55505b261ecSmrg/* changed area is *dest* rect */
55605b261ecSmrgstatic RegionPtr
55705b261ecSmrgRootlessCopyArea(DrawablePtr pSrc, DrawablePtr dst, GCPtr pGC,
55805b261ecSmrg                 int srcx, int srcy, int w, int h,
55905b261ecSmrg                 int dstx, int dsty)
56005b261ecSmrg{
56105b261ecSmrg    RegionPtr result;
56205b261ecSmrg    BoxRec box;
56305b261ecSmrg
56405b261ecSmrg    GC_SAVE(pGC);
56505b261ecSmrg    GCOP_UNWRAP(pGC);
56605b261ecSmrg
56705b261ecSmrg    if (GC_IS_ROOT(dst) || GC_IS_ROOT(pSrc))
56805b261ecSmrg        return NULL;			/* nothing exposed */
56905b261ecSmrg
57005b261ecSmrg    RL_DEBUG_MSG("copy area start (src 0x%x, dst 0x%x)", pSrc, dst);
57105b261ecSmrg
57205b261ecSmrg    if (pSrc->type == DRAWABLE_WINDOW && IsFramedWindow((WindowPtr)pSrc)) {
57305b261ecSmrg        unsigned int bytes;
57405b261ecSmrg
57505b261ecSmrg        /* If both source and dest are windows, and we're doing
57605b261ecSmrg           a simple copy operation, we can remove the alpha-protecting
57705b261ecSmrg           planemask (since source has opaque alpha as well) */
57805b261ecSmrg
57905b261ecSmrg        bytes = w * h * (pSrc->depth >> 3);
58005b261ecSmrg
58105b261ecSmrg        if (bytes >= rootless_CopyBytes_threshold && canAccelBlit(pSrc, pGC))
58205b261ecSmrg        {
58305b261ecSmrg            GC_UNSET_PM(pGC, dst);
58405b261ecSmrg        }
58505b261ecSmrg
58605b261ecSmrg        RootlessStartDrawing((WindowPtr) pSrc);
58705b261ecSmrg    }
58805b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
58905b261ecSmrg    result = pGC->ops->CopyArea(pSrc, dst, pGC, srcx, srcy, w, h, dstx, dsty);
59005b261ecSmrg
59105b261ecSmrg    box.x1 = dstx + dst->x;
59205b261ecSmrg    box.x2 = box.x1 + w;
59305b261ecSmrg    box.y1 = dsty + dst->y;
59405b261ecSmrg    box.y2 = box.y1 + h;
59505b261ecSmrg
59605b261ecSmrg    TRIM_BOX(box, pGC);
59705b261ecSmrg    if (BOX_NOT_EMPTY(box))
59805b261ecSmrg        RootlessDamageBox ((WindowPtr) dst, &box);
59905b261ecSmrg
60005b261ecSmrg    GC_RESTORE(pGC, dst);
60105b261ecSmrg    GCOP_WRAP(pGC);
60205b261ecSmrg    RL_DEBUG_MSG("copy area end\n");
60305b261ecSmrg    return result;
60405b261ecSmrg}
60505b261ecSmrg
60605b261ecSmrg/* changed area is *dest* rect */
60705b261ecSmrgstatic RegionPtr RootlessCopyPlane(DrawablePtr pSrc, DrawablePtr dst,
60805b261ecSmrg                                   GCPtr pGC, int srcx, int srcy,
60905b261ecSmrg                                   int w, int h, int dstx, int dsty,
61005b261ecSmrg                                   unsigned long plane)
61105b261ecSmrg{
61205b261ecSmrg    RegionPtr result;
61305b261ecSmrg    BoxRec box;
61405b261ecSmrg
61505b261ecSmrg    GCOP_UNWRAP(pGC);
61605b261ecSmrg
61705b261ecSmrg    if (GC_IS_ROOT(dst) || GC_IS_ROOT(pSrc))
61805b261ecSmrg        return NULL;			/* nothing exposed */
61905b261ecSmrg
62005b261ecSmrg    RL_DEBUG_MSG("copy plane start ");
62105b261ecSmrg
62205b261ecSmrg    if (pSrc->type == DRAWABLE_WINDOW && IsFramedWindow((WindowPtr)pSrc)) {
62305b261ecSmrg        RootlessStartDrawing((WindowPtr) pSrc);
62405b261ecSmrg    }
62505b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
62605b261ecSmrg    result = pGC->ops->CopyPlane(pSrc, dst, pGC, srcx, srcy, w, h,
62705b261ecSmrg                                 dstx, dsty, plane);
62805b261ecSmrg
62905b261ecSmrg    box.x1 = dstx + dst->x;
63005b261ecSmrg    box.x2 = box.x1 + w;
63105b261ecSmrg    box.y1 = dsty + dst->y;
63205b261ecSmrg    box.y2 = box.y1 + h;
63305b261ecSmrg
63405b261ecSmrg    TRIM_BOX(box, pGC);
63505b261ecSmrg    if (BOX_NOT_EMPTY(box))
63605b261ecSmrg        RootlessDamageBox ((WindowPtr) dst, &box);
63705b261ecSmrg
63805b261ecSmrg    GCOP_WRAP(pGC);
63905b261ecSmrg    RL_DEBUG_MSG("copy plane end\n");
64005b261ecSmrg    return result;
64105b261ecSmrg}
64205b261ecSmrg
64305b261ecSmrg// Options for size of changed area:
64405b261ecSmrg//  0 = box per point
64505b261ecSmrg//  1 = big box around all points
64605b261ecSmrg//  2 = accumulate point in 20 pixel radius
64705b261ecSmrg#define ROOTLESS_CHANGED_AREA 1
64805b261ecSmrg#define abs(a) ((a) > 0 ? (a) : -(a))
64905b261ecSmrg
65005b261ecSmrg/* changed area is box around all points */
65105b261ecSmrgstatic void RootlessPolyPoint(DrawablePtr dst, GCPtr pGC,
65205b261ecSmrg                              int mode, int npt, DDXPointPtr pptInit)
65305b261ecSmrg{
65405b261ecSmrg    GCOP_UNWRAP(pGC);
65505b261ecSmrg    GC_SKIP_ROOT(dst);
65605b261ecSmrg    RL_DEBUG_MSG("polypoint start ");
65705b261ecSmrg
65805b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
65905b261ecSmrg    pGC->ops->PolyPoint(dst, pGC, mode, npt, pptInit);
66005b261ecSmrg
66105b261ecSmrg    if (npt > 0) {
66205b261ecSmrg#if ROOTLESS_CHANGED_AREA==0
66305b261ecSmrg        // box per point
66405b261ecSmrg        BoxRec box;
66505b261ecSmrg
66605b261ecSmrg        while (npt) {
66705b261ecSmrg            box.x1 = pptInit->x;
66805b261ecSmrg            box.y1 = pptInit->y;
66905b261ecSmrg            box.x2 = box.x1 + 1;
67005b261ecSmrg            box.y2 = box.y1 + 1;
67105b261ecSmrg
67205b261ecSmrg            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
67305b261ecSmrg            if (BOX_NOT_EMPTY(box))
67405b261ecSmrg                RootlessDamageBox ((WindowPtr) dst, &box);
67505b261ecSmrg
67605b261ecSmrg            npt--;
67705b261ecSmrg            pptInit++;
67805b261ecSmrg        }
67905b261ecSmrg
68005b261ecSmrg#elif ROOTLESS_CHANGED_AREA==1
68105b261ecSmrg        // one big box
68205b261ecSmrg        BoxRec box;
68305b261ecSmrg
68405b261ecSmrg        box.x2 = box.x1 = pptInit->x;
68505b261ecSmrg        box.y2 = box.y1 = pptInit->y;
68605b261ecSmrg        while (--npt) {
68705b261ecSmrg            pptInit++;
68805b261ecSmrg            if (box.x1 > pptInit->x)
68905b261ecSmrg                box.x1 = pptInit->x;
69005b261ecSmrg            else if (box.x2 < pptInit->x)
69105b261ecSmrg                box.x2 = pptInit->x;
69205b261ecSmrg            if (box.y1 > pptInit->y)
69305b261ecSmrg                box.y1 = pptInit->y;
69405b261ecSmrg            else if (box.y2 < pptInit->y)
69505b261ecSmrg                box.y2 = pptInit->y;
69605b261ecSmrg        }
69705b261ecSmrg
69805b261ecSmrg        box.x2++;
69905b261ecSmrg        box.y2++;
70005b261ecSmrg
70105b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
70205b261ecSmrg        if (BOX_NOT_EMPTY(box))
70305b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
70405b261ecSmrg
70505b261ecSmrg#elif ROOTLESS_CHANGED_AREA==2
70605b261ecSmrg        // clever(?) method: accumulate point in 20-pixel radius
70705b261ecSmrg        BoxRec box;
70805b261ecSmrg        int firstx, firsty;
70905b261ecSmrg
71005b261ecSmrg        box.x2 = box.x1 = firstx = pptInit->x;
71105b261ecSmrg        box.y2 = box.y1 = firsty = pptInit->y;
71205b261ecSmrg        while (--npt) {
71305b261ecSmrg            pptInit++;
71405b261ecSmrg            if (abs(pptInit->x - firstx) > 20 ||
71505b261ecSmrg                abs(pptInit->y - firsty) > 20) {
71605b261ecSmrg                box.x2++;
71705b261ecSmrg                box.y2++;
71805b261ecSmrg                TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
71905b261ecSmrg                if (BOX_NOT_EMPTY(box))
72005b261ecSmrg                    RootlessDamageBox ((WindowPtr) dst, &box);
72105b261ecSmrg                box.x2 = box.x1 = firstx = pptInit->x;
72205b261ecSmrg                box.y2 = box.y1 = firsty = pptInit->y;
72305b261ecSmrg            } else {
72405b261ecSmrg                if (box.x1 > pptInit->x) box.x1 = pptInit->x;
72505b261ecSmrg                else if (box.x2 < pptInit->x) box.x2 = pptInit->x;
72605b261ecSmrg                if (box.y1 > pptInit->y) box.y1 = pptInit->y;
72705b261ecSmrg                else if (box.y2 < pptInit->y) box.y2 = pptInit->y;
72805b261ecSmrg            }
72905b261ecSmrg        }
73005b261ecSmrg        box.x2++;
73105b261ecSmrg        box.y2++;
73205b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
73305b261ecSmrg        if (BOX_NOT_EMPTY(box))
73405b261ecSmrg            RootlessDamageBox((WindowPtr) dst, &box);
73505b261ecSmrg#endif  /* ROOTLESS_CHANGED_AREA */
73605b261ecSmrg    }
73705b261ecSmrg
73805b261ecSmrg    GCOP_WRAP(pGC);
73905b261ecSmrg    RL_DEBUG_MSG("polypoint end\n");
74005b261ecSmrg}
74105b261ecSmrg
74205b261ecSmrg#undef ROOTLESS_CHANGED_AREA
74305b261ecSmrg
74405b261ecSmrg/* changed area is box around each line */
74505b261ecSmrgstatic void RootlessPolylines(DrawablePtr dst, GCPtr pGC,
74605b261ecSmrg                              int mode, int npt, DDXPointPtr pptInit)
74705b261ecSmrg{
74805b261ecSmrg    GCOP_UNWRAP(pGC);
74905b261ecSmrg    GC_SKIP_ROOT(dst);
75005b261ecSmrg    RL_DEBUG_MSG("poly lines start ");
75105b261ecSmrg
75205b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
75305b261ecSmrg    pGC->ops->Polylines(dst, pGC, mode, npt, pptInit);
75405b261ecSmrg
75505b261ecSmrg    if (npt > 0) {
75605b261ecSmrg        BoxRec box;
75705b261ecSmrg        int extra = pGC->lineWidth >> 1;
75805b261ecSmrg
75905b261ecSmrg        box.x2 = box.x1 = pptInit->x;
76005b261ecSmrg        box.y2 = box.y1 = pptInit->y;
76105b261ecSmrg
76205b261ecSmrg        if (npt > 1) {
76305b261ecSmrg            if (pGC->joinStyle == JoinMiter)
76405b261ecSmrg                extra = 6 * pGC->lineWidth;
76505b261ecSmrg            else if (pGC->capStyle == CapProjecting)
76605b261ecSmrg                extra = pGC->lineWidth;
76705b261ecSmrg        }
76805b261ecSmrg
76905b261ecSmrg        if (mode == CoordModePrevious) {
77005b261ecSmrg            int x = box.x1;
77105b261ecSmrg            int y = box.y1;
77205b261ecSmrg
77305b261ecSmrg            while (--npt) {
77405b261ecSmrg                pptInit++;
77505b261ecSmrg                x += pptInit->x;
77605b261ecSmrg                y += pptInit->y;
77705b261ecSmrg                if (box.x1 > x)
77805b261ecSmrg                    box.x1 = x;
77905b261ecSmrg                else if (box.x2 < x)
78005b261ecSmrg                    box.x2 = x;
78105b261ecSmrg                if (box.y1 > y)
78205b261ecSmrg                    box.y1 = y;
78305b261ecSmrg                else if (box.y2 < y)
78405b261ecSmrg                    box.y2 = y;
78505b261ecSmrg            }
78605b261ecSmrg        } else {
78705b261ecSmrg            while (--npt) {
78805b261ecSmrg                pptInit++;
78905b261ecSmrg                if (box.x1 > pptInit->x)
79005b261ecSmrg                    box.x1 = pptInit->x;
79105b261ecSmrg                else if (box.x2 < pptInit->x)
79205b261ecSmrg                    box.x2 = pptInit->x;
79305b261ecSmrg                if (box.y1 > pptInit->y)
79405b261ecSmrg                    box.y1 = pptInit->y;
79505b261ecSmrg                else if (box.y2 < pptInit->y)
79605b261ecSmrg                    box.y2 = pptInit->y;
79705b261ecSmrg            }
79805b261ecSmrg        }
79905b261ecSmrg
80005b261ecSmrg        box.x2++;
80105b261ecSmrg        box.y2++;
80205b261ecSmrg
80305b261ecSmrg        if (extra) {
80405b261ecSmrg            box.x1 -= extra;
80505b261ecSmrg            box.x2 += extra;
80605b261ecSmrg            box.y1 -= extra;
80705b261ecSmrg            box.y2 += extra;
80805b261ecSmrg        }
80905b261ecSmrg
81005b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
81105b261ecSmrg        if (BOX_NOT_EMPTY(box))
81205b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
81305b261ecSmrg    }
81405b261ecSmrg
81505b261ecSmrg    GCOP_WRAP(pGC);
81605b261ecSmrg    RL_DEBUG_MSG("poly lines end\n");
81705b261ecSmrg}
81805b261ecSmrg
81905b261ecSmrg/* changed area is box around each line segment */
82005b261ecSmrgstatic void RootlessPolySegment(DrawablePtr dst, GCPtr pGC,
82105b261ecSmrg                                int nseg, xSegment *pSeg)
82205b261ecSmrg{
82305b261ecSmrg    GCOP_UNWRAP(pGC);
82405b261ecSmrg    GC_SKIP_ROOT(dst);
82505b261ecSmrg    RL_DEBUG_MSG("poly segment start (win 0x%x)", dst);
82605b261ecSmrg
82705b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
82805b261ecSmrg    pGC->ops->PolySegment(dst, pGC, nseg, pSeg);
82905b261ecSmrg
83005b261ecSmrg    if (nseg > 0) {
83105b261ecSmrg        BoxRec box;
83205b261ecSmrg        int extra = pGC->lineWidth;
83305b261ecSmrg
83405b261ecSmrg        if (pGC->capStyle != CapProjecting)
83505b261ecSmrg        extra >>= 1;
83605b261ecSmrg
83705b261ecSmrg        if (pSeg->x2 > pSeg->x1) {
83805b261ecSmrg            box.x1 = pSeg->x1;
83905b261ecSmrg            box.x2 = pSeg->x2;
84005b261ecSmrg        } else {
84105b261ecSmrg            box.x2 = pSeg->x1;
84205b261ecSmrg            box.x1 = pSeg->x2;
84305b261ecSmrg        }
84405b261ecSmrg
84505b261ecSmrg        if (pSeg->y2 > pSeg->y1) {
84605b261ecSmrg            box.y1 = pSeg->y1;
84705b261ecSmrg            box.y2 = pSeg->y2;
84805b261ecSmrg        } else {
84905b261ecSmrg            box.y2 = pSeg->y1;
85005b261ecSmrg            box.y1 = pSeg->y2;
85105b261ecSmrg        }
85205b261ecSmrg
85305b261ecSmrg        while (--nseg) {
85405b261ecSmrg            pSeg++;
85505b261ecSmrg            if (pSeg->x2 > pSeg->x1) {
85605b261ecSmrg                if (pSeg->x1 < box.x1) box.x1 = pSeg->x1;
85705b261ecSmrg                if (pSeg->x2 > box.x2) box.x2 = pSeg->x2;
85805b261ecSmrg            } else {
85905b261ecSmrg                if (pSeg->x2 < box.x1) box.x1 = pSeg->x2;
86005b261ecSmrg                if (pSeg->x1 > box.x2) box.x2 = pSeg->x1;
86105b261ecSmrg            }
86205b261ecSmrg            if (pSeg->y2 > pSeg->y1) {
86305b261ecSmrg                if (pSeg->y1 < box.y1) box.y1 = pSeg->y1;
86405b261ecSmrg                if (pSeg->y2 > box.y2) box.y2 = pSeg->y2;
86505b261ecSmrg            } else {
86605b261ecSmrg                if (pSeg->y2 < box.y1) box.y1 = pSeg->y2;
86705b261ecSmrg                if (pSeg->y1 > box.y2) box.y2 = pSeg->y1;
86805b261ecSmrg            }
86905b261ecSmrg        }
87005b261ecSmrg
87105b261ecSmrg        box.x2++;
87205b261ecSmrg        box.y2++;
87305b261ecSmrg
87405b261ecSmrg        if (extra) {
87505b261ecSmrg            box.x1 -= extra;
87605b261ecSmrg            box.x2 += extra;
87705b261ecSmrg            box.y1 -= extra;
87805b261ecSmrg            box.y2 += extra;
87905b261ecSmrg        }
88005b261ecSmrg
88105b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
88205b261ecSmrg        if (BOX_NOT_EMPTY(box))
88305b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
88405b261ecSmrg    }
88505b261ecSmrg
88605b261ecSmrg    GCOP_WRAP(pGC);
88705b261ecSmrg    RL_DEBUG_MSG("poly segment end\n");
88805b261ecSmrg}
88905b261ecSmrg
89005b261ecSmrg/* changed area is box around each line (not entire rects) */
89105b261ecSmrgstatic void RootlessPolyRectangle(DrawablePtr dst, GCPtr pGC,
89205b261ecSmrg                                  int nRects, xRectangle *pRects)
89305b261ecSmrg{
89405b261ecSmrg    GCOP_UNWRAP(pGC);
89505b261ecSmrg    GC_SKIP_ROOT(dst);
89605b261ecSmrg    RL_DEBUG_MSG("poly rectangle start ");
89705b261ecSmrg
89805b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
89905b261ecSmrg    pGC->ops->PolyRectangle(dst, pGC, nRects, pRects);
90005b261ecSmrg
90105b261ecSmrg    if (nRects > 0) {
90205b261ecSmrg        BoxRec box;
90305b261ecSmrg        int offset1, offset2, offset3;
90405b261ecSmrg
90505b261ecSmrg        offset2 = pGC->lineWidth;
90605b261ecSmrg        if (!offset2) offset2 = 1;
90705b261ecSmrg        offset1 = offset2 >> 1;
90805b261ecSmrg        offset3 = offset2 - offset1;
90905b261ecSmrg
91005b261ecSmrg        while (nRects--) {
91105b261ecSmrg            box.x1 = pRects->x - offset1;
91205b261ecSmrg            box.y1 = pRects->y - offset1;
91305b261ecSmrg            box.x2 = box.x1 + pRects->width + offset2;
91405b261ecSmrg            box.y2 = box.y1 + offset2;
91505b261ecSmrg            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
91605b261ecSmrg            if (BOX_NOT_EMPTY(box))
91705b261ecSmrg                RootlessDamageBox ((WindowPtr) dst, &box);
91805b261ecSmrg
91905b261ecSmrg            box.x1 = pRects->x - offset1;
92005b261ecSmrg            box.y1 = pRects->y + offset3;
92105b261ecSmrg            box.x2 = box.x1 + offset2;
92205b261ecSmrg            box.y2 = box.y1 + pRects->height - offset2;
92305b261ecSmrg            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
92405b261ecSmrg            if (BOX_NOT_EMPTY(box))
92505b261ecSmrg                RootlessDamageBox ((WindowPtr) dst, &box);
92605b261ecSmrg
92705b261ecSmrg            box.x1 = pRects->x + pRects->width - offset1;
92805b261ecSmrg            box.y1 = pRects->y + offset3;
92905b261ecSmrg            box.x2 = box.x1 + offset2;
93005b261ecSmrg            box.y2 = box.y1 + pRects->height - offset2;
93105b261ecSmrg            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
93205b261ecSmrg            if (BOX_NOT_EMPTY(box))
93305b261ecSmrg                RootlessDamageBox ((WindowPtr) dst, &box);
93405b261ecSmrg
93505b261ecSmrg            box.x1 = pRects->x - offset1;
93605b261ecSmrg            box.y1 = pRects->y + pRects->height - offset1;
93705b261ecSmrg            box.x2 = box.x1 + pRects->width + offset2;
93805b261ecSmrg            box.y2 = box.y1 + offset2;
93905b261ecSmrg            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
94005b261ecSmrg            if (BOX_NOT_EMPTY(box))
94105b261ecSmrg                RootlessDamageBox ((WindowPtr) dst, &box);
94205b261ecSmrg
94305b261ecSmrg            pRects++;
94405b261ecSmrg        }
94505b261ecSmrg    }
94605b261ecSmrg
94705b261ecSmrg    GCOP_WRAP(pGC);
94805b261ecSmrg    RL_DEBUG_MSG("poly rectangle end\n");
94905b261ecSmrg}
95005b261ecSmrg
95105b261ecSmrg
95205b261ecSmrg/* changed area is box around each arc (assumes all arcs are 360 degrees) */
95305b261ecSmrgstatic void RootlessPolyArc(DrawablePtr dst, GCPtr pGC, int narcs, xArc *parcs)
95405b261ecSmrg{
95505b261ecSmrg    GCOP_UNWRAP(pGC);
95605b261ecSmrg    GC_SKIP_ROOT(dst);
95705b261ecSmrg    RL_DEBUG_MSG("poly arc start ");
95805b261ecSmrg
95905b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
96005b261ecSmrg    pGC->ops->PolyArc(dst, pGC, narcs, parcs);
96105b261ecSmrg
96205b261ecSmrg    if (narcs > 0) {
96305b261ecSmrg        int extra = pGC->lineWidth >> 1;
96405b261ecSmrg        BoxRec box;
96505b261ecSmrg
96605b261ecSmrg        box.x1 = parcs->x;
96705b261ecSmrg        box.x2 = box.x1 + parcs->width;
96805b261ecSmrg        box.y1 = parcs->y;
96905b261ecSmrg        box.y2 = box.y1 + parcs->height;
97005b261ecSmrg
97105b261ecSmrg        /* should I break these up instead ? */
97205b261ecSmrg
97305b261ecSmrg        while (--narcs) {
97405b261ecSmrg            parcs++;
97505b261ecSmrg            if (box.x1 > parcs->x)
97605b261ecSmrg                box.x1 = parcs->x;
97705b261ecSmrg            if (box.x2 < (parcs->x + parcs->width))
97805b261ecSmrg                box.x2 = parcs->x + parcs->width;
97905b261ecSmrg            if (box.y1 > parcs->y)
98005b261ecSmrg                box.y1 = parcs->y;
98105b261ecSmrg            if (box.y2 < (parcs->y + parcs->height))
98205b261ecSmrg                box.y2 = parcs->y + parcs->height;
98305b261ecSmrg        }
98405b261ecSmrg
98505b261ecSmrg        if (extra) {
98605b261ecSmrg            box.x1 -= extra;
98705b261ecSmrg            box.x2 += extra;
98805b261ecSmrg            box.y1 -= extra;
98905b261ecSmrg            box.y2 += extra;
99005b261ecSmrg        }
99105b261ecSmrg
99205b261ecSmrg        box.x2++;
99305b261ecSmrg        box.y2++;
99405b261ecSmrg
99505b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
99605b261ecSmrg        if (BOX_NOT_EMPTY(box))
99705b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
99805b261ecSmrg    }
99905b261ecSmrg
100005b261ecSmrg    GCOP_WRAP(pGC);
100105b261ecSmrg    RL_DEBUG_MSG("poly arc end\n");
100205b261ecSmrg}
100305b261ecSmrg
100405b261ecSmrg
100505b261ecSmrg/* changed area is box around each poly */
100605b261ecSmrgstatic void RootlessFillPolygon(DrawablePtr dst, GCPtr pGC,
100705b261ecSmrg                                int shape, int mode, int count,
100805b261ecSmrg                                DDXPointPtr pptInit)
100905b261ecSmrg{
101005b261ecSmrg    GC_SAVE(pGC);
101105b261ecSmrg    GCOP_UNWRAP(pGC);
101205b261ecSmrg    GC_SKIP_ROOT(dst);
101305b261ecSmrg    RL_DEBUG_MSG("fill poly start (win 0x%x, fillStyle 0x%x)", dst,
101405b261ecSmrg                 pGC->fillStyle);
101505b261ecSmrg
101605b261ecSmrg    if (count <= 2) {
101705b261ecSmrg        pGC->ops->FillPolygon(dst, pGC, shape, mode, count, pptInit);
101805b261ecSmrg    } else {
101905b261ecSmrg        DDXPointPtr ppt = pptInit;
102005b261ecSmrg        int i = count;
102105b261ecSmrg        BoxRec box;
102205b261ecSmrg
102305b261ecSmrg        box.x2 = box.x1 = ppt->x;
102405b261ecSmrg        box.y2 = box.y1 = ppt->y;
102505b261ecSmrg
102605b261ecSmrg        if (mode != CoordModeOrigin) {
102705b261ecSmrg            int x = box.x1;
102805b261ecSmrg            int y = box.y1;
102905b261ecSmrg
103005b261ecSmrg            while (--i) {
103105b261ecSmrg                ppt++;
103205b261ecSmrg                x += ppt->x;
103305b261ecSmrg                y += ppt->y;
103405b261ecSmrg                if (box.x1 > x)
103505b261ecSmrg                    box.x1 = x;
103605b261ecSmrg                else if (box.x2 < x)
103705b261ecSmrg                    box.x2 = x;
103805b261ecSmrg                if (box.y1 > y)
103905b261ecSmrg                    box.y1 = y;
104005b261ecSmrg                else if (box.y2 < y)
104105b261ecSmrg                    box.y2 = y;
104205b261ecSmrg            }
104305b261ecSmrg        } else {
104405b261ecSmrg            while (--i) {
104505b261ecSmrg                ppt++;
104605b261ecSmrg                if (box.x1 > ppt->x)
104705b261ecSmrg                    box.x1 = ppt->x;
104805b261ecSmrg                else if (box.x2 < ppt->x)
104905b261ecSmrg                    box.x2 = ppt->x;
105005b261ecSmrg                if (box.y1 > ppt->y)
105105b261ecSmrg                    box.y1 = ppt->y;
105205b261ecSmrg                else if (box.y2 < ppt->y)
105305b261ecSmrg                    box.y2 = ppt->y;
105405b261ecSmrg            }
105505b261ecSmrg        }
105605b261ecSmrg
105705b261ecSmrg        box.x2++;
105805b261ecSmrg        box.y2++;
105905b261ecSmrg
106005b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
106105b261ecSmrg
106205b261ecSmrg        if (canAccelFill(dst, pGC) &&
106305b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
106405b261ecSmrg        {
106505b261ecSmrg            GC_UNSET_PM(pGC, dst);
106605b261ecSmrg        }
106705b261ecSmrg
106805b261ecSmrg        pGC->ops->FillPolygon(dst, pGC, shape, mode, count, pptInit);
106905b261ecSmrg
107005b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
107105b261ecSmrg        if (BOX_NOT_EMPTY(box))
107205b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
107305b261ecSmrg    }
107405b261ecSmrg
107505b261ecSmrg    GC_RESTORE(pGC, dst);
107605b261ecSmrg    GCOP_WRAP(pGC);
107705b261ecSmrg    RL_DEBUG_MSG("fill poly end\n");
107805b261ecSmrg}
107905b261ecSmrg
108005b261ecSmrg/* changed area is the rects */
108105b261ecSmrgstatic void RootlessPolyFillRect(DrawablePtr dst, GCPtr pGC,
108205b261ecSmrg                                 int nRectsInit, xRectangle *pRectsInit)
108305b261ecSmrg{
108405b261ecSmrg    GC_SAVE(pGC);
108505b261ecSmrg    GCOP_UNWRAP(pGC);
108605b261ecSmrg    GC_SKIP_ROOT(dst);
108705b261ecSmrg    RL_DEBUG_MSG("fill rect start (win 0x%x, fillStyle 0x%x)", dst,
108805b261ecSmrg                 pGC->fillStyle);
108905b261ecSmrg
109005b261ecSmrg    if (nRectsInit <= 0) {
109105b261ecSmrg        pGC->ops->PolyFillRect(dst, pGC, nRectsInit, pRectsInit);
109205b261ecSmrg    } else {
109305b261ecSmrg        BoxRec box;
109405b261ecSmrg        xRectangle *pRects = pRectsInit;
109505b261ecSmrg        int nRects = nRectsInit;
109605b261ecSmrg
109705b261ecSmrg        box.x1 = pRects->x;
109805b261ecSmrg        box.x2 = box.x1 + pRects->width;
109905b261ecSmrg        box.y1 = pRects->y;
110005b261ecSmrg        box.y2 = box.y1 + pRects->height;
110105b261ecSmrg
110205b261ecSmrg        while (--nRects) {
110305b261ecSmrg            pRects++;
110405b261ecSmrg            if (box.x1 > pRects->x)
110505b261ecSmrg                box.x1 = pRects->x;
110605b261ecSmrg            if (box.x2 < (pRects->x + pRects->width))
110705b261ecSmrg                box.x2 = pRects->x + pRects->width;
110805b261ecSmrg            if (box.y1 > pRects->y)
110905b261ecSmrg                box.y1 = pRects->y;
111005b261ecSmrg            if (box.y2 < (pRects->y + pRects->height))
111105b261ecSmrg                box.y2 = pRects->y + pRects->height;
111205b261ecSmrg        }
111305b261ecSmrg
111405b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
111505b261ecSmrg
111605b261ecSmrg        if (canAccelFill(dst, pGC) &&
111705b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
111805b261ecSmrg        {
111905b261ecSmrg            GC_UNSET_PM(pGC, dst);
112005b261ecSmrg        }
112105b261ecSmrg
112205b261ecSmrg       pGC->ops->PolyFillRect(dst, pGC, nRectsInit, pRectsInit);
112305b261ecSmrg
112405b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
112505b261ecSmrg        if (BOX_NOT_EMPTY(box))
112605b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
112705b261ecSmrg    }
112805b261ecSmrg
112905b261ecSmrg    GC_RESTORE(pGC, dst);
113005b261ecSmrg    GCOP_WRAP(pGC);
113105b261ecSmrg    RL_DEBUG_MSG("fill rect end\n");
113205b261ecSmrg}
113305b261ecSmrg
113405b261ecSmrg
113505b261ecSmrg/* changed area is box around each arc (assuming arcs are all 360 degrees) */
113605b261ecSmrgstatic void RootlessPolyFillArc(DrawablePtr dst, GCPtr pGC,
113705b261ecSmrg                                int narcsInit, xArc *parcsInit)
113805b261ecSmrg{
113905b261ecSmrg    GC_SAVE(pGC);
114005b261ecSmrg    GCOP_UNWRAP(pGC);
114105b261ecSmrg    GC_SKIP_ROOT(dst);
114205b261ecSmrg    RL_DEBUG_MSG("fill arc start ");
114305b261ecSmrg
114405b261ecSmrg    if (narcsInit > 0) {
114505b261ecSmrg        BoxRec box;
114605b261ecSmrg        int narcs = narcsInit;
114705b261ecSmrg        xArc *parcs = parcsInit;
114805b261ecSmrg
114905b261ecSmrg        box.x1 = parcs->x;
115005b261ecSmrg        box.x2 = box.x1 + parcs->width;
115105b261ecSmrg        box.y1 = parcs->y;
115205b261ecSmrg        box.y2 = box.y1 + parcs->height;
115305b261ecSmrg
115405b261ecSmrg        /* should I break these up instead ? */
115505b261ecSmrg
115605b261ecSmrg        while (--narcs) {
115705b261ecSmrg            parcs++;
115805b261ecSmrg            if (box.x1 > parcs->x)
115905b261ecSmrg                box.x1 = parcs->x;
116005b261ecSmrg            if (box.x2 < (parcs->x + parcs->width))
116105b261ecSmrg                box.x2 = parcs->x + parcs->width;
116205b261ecSmrg            if (box.y1 > parcs->y)
116305b261ecSmrg                box.y1 = parcs->y;
116405b261ecSmrg            if (box.y2 < (parcs->y + parcs->height))
116505b261ecSmrg                box.y2 = parcs->y + parcs->height;
116605b261ecSmrg        }
116705b261ecSmrg
116805b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
116905b261ecSmrg
117005b261ecSmrg        if (canAccelFill(dst, pGC) &&
117105b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
117205b261ecSmrg        {
117305b261ecSmrg            GC_UNSET_PM(pGC, dst);
117405b261ecSmrg        }
117505b261ecSmrg
117605b261ecSmrg        pGC->ops->PolyFillArc(dst, pGC, narcsInit, parcsInit);
117705b261ecSmrg
117805b261ecSmrg        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
117905b261ecSmrg        if (BOX_NOT_EMPTY(box))
118005b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
118105b261ecSmrg    } else {
118205b261ecSmrg        pGC->ops->PolyFillArc(dst, pGC, narcsInit, parcsInit);
118305b261ecSmrg    }
118405b261ecSmrg
118505b261ecSmrg    GC_RESTORE(pGC, dst);
118605b261ecSmrg    GCOP_WRAP(pGC);
118705b261ecSmrg    RL_DEBUG_MSG("fill arc end\n");
118805b261ecSmrg}
118905b261ecSmrg
119005b261ecSmrg
119105b261ecSmrgstatic void RootlessImageText8(DrawablePtr dst, GCPtr pGC,
119205b261ecSmrg                               int x, int y, int count, char *chars)
119305b261ecSmrg{
119405b261ecSmrg    GC_SAVE(pGC);
119505b261ecSmrg    GCOP_UNWRAP(pGC);
119605b261ecSmrg    GC_SKIP_ROOT(dst);
119705b261ecSmrg    RL_DEBUG_MSG("imagetext8 start ");
119805b261ecSmrg
119905b261ecSmrg    if (count > 0) {
120005b261ecSmrg        int top, bot, Min, Max;
120105b261ecSmrg        BoxRec box;
120205b261ecSmrg
120305b261ecSmrg        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
120405b261ecSmrg        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
120505b261ecSmrg
120605b261ecSmrg        Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
120705b261ecSmrg        if (Min > 0) Min = 0;
120805b261ecSmrg        Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
120905b261ecSmrg        if (Max < 0) Max = 0;
121005b261ecSmrg
121105b261ecSmrg        /* ugh */
121205b261ecSmrg        box.x1 = dst->x + x + Min +
121305b261ecSmrg        FONTMINBOUNDS(pGC->font, leftSideBearing);
121405b261ecSmrg        box.x2 = dst->x + x + Max +
121505b261ecSmrg        FONTMAXBOUNDS(pGC->font, rightSideBearing);
121605b261ecSmrg
121705b261ecSmrg        box.y1 = dst->y + y - top;
121805b261ecSmrg        box.y2 = dst->y + y + bot;
121905b261ecSmrg
122005b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
122105b261ecSmrg
122205b261ecSmrg        if (canAccelFill(dst, pGC) &&
122305b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
122405b261ecSmrg        {
122505b261ecSmrg            GC_UNSET_PM(pGC, dst);
122605b261ecSmrg        }
122705b261ecSmrg
122805b261ecSmrg        pGC->ops->ImageText8(dst, pGC, x, y, count, chars);
122905b261ecSmrg
123005b261ecSmrg        TRIM_BOX(box, pGC);
123105b261ecSmrg        if (BOX_NOT_EMPTY(box))
123205b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
123305b261ecSmrg    } else {
123405b261ecSmrg        pGC->ops->ImageText8(dst, pGC, x, y, count, chars);
123505b261ecSmrg    }
123605b261ecSmrg
123705b261ecSmrg    GC_RESTORE(pGC, dst);
123805b261ecSmrg    GCOP_WRAP(pGC);
123905b261ecSmrg    RL_DEBUG_MSG("imagetext8 end\n");
124005b261ecSmrg}
124105b261ecSmrg
124205b261ecSmrgstatic int RootlessPolyText8(DrawablePtr dst, GCPtr pGC,
124305b261ecSmrg                             int x, int y, int count, char *chars)
124405b261ecSmrg{
124505b261ecSmrg    int width; // the result, sorta
124605b261ecSmrg
124705b261ecSmrg    GCOP_UNWRAP(pGC);
124805b261ecSmrg
124905b261ecSmrg    if (GC_IS_ROOT(dst))
125005b261ecSmrg        return 0;
125105b261ecSmrg
125205b261ecSmrg    RL_DEBUG_MSG("polytext8 start ");
125305b261ecSmrg
125405b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
125505b261ecSmrg    width = pGC->ops->PolyText8(dst, pGC, x, y, count, chars);
125605b261ecSmrg    width -= x;
125705b261ecSmrg
125805b261ecSmrg    if (width > 0) {
125905b261ecSmrg        BoxRec box;
126005b261ecSmrg
126105b261ecSmrg        /* ugh */
126205b261ecSmrg        box.x1 = dst->x + x + FONTMINBOUNDS(pGC->font, leftSideBearing);
126305b261ecSmrg        box.x2 = dst->x + x + FONTMAXBOUNDS(pGC->font, rightSideBearing);
126405b261ecSmrg
126505b261ecSmrg        if (count > 1) {
126605b261ecSmrg            if (width > 0) box.x2 += width;
126705b261ecSmrg            else box.x1 += width;
126805b261ecSmrg        }
126905b261ecSmrg
127005b261ecSmrg        box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
127105b261ecSmrg        box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
127205b261ecSmrg
127305b261ecSmrg        TRIM_BOX(box, pGC);
127405b261ecSmrg        if (BOX_NOT_EMPTY(box))
127505b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
127605b261ecSmrg    }
127705b261ecSmrg
127805b261ecSmrg    GCOP_WRAP(pGC);
127905b261ecSmrg    RL_DEBUG_MSG("polytext8 end\n");
128005b261ecSmrg    return (width + x);
128105b261ecSmrg}
128205b261ecSmrg
128305b261ecSmrgstatic void RootlessImageText16(DrawablePtr dst, GCPtr pGC,
128405b261ecSmrg                                int x, int y, int count, unsigned short *chars)
128505b261ecSmrg{
128605b261ecSmrg    GC_SAVE(pGC);
128705b261ecSmrg    GCOP_UNWRAP(pGC);
128805b261ecSmrg    GC_SKIP_ROOT(dst);
128905b261ecSmrg    RL_DEBUG_MSG("imagetext16 start ");
129005b261ecSmrg
129105b261ecSmrg    if (count > 0) {
129205b261ecSmrg        int top, bot, Min, Max;
129305b261ecSmrg        BoxRec box;
129405b261ecSmrg
129505b261ecSmrg        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
129605b261ecSmrg        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
129705b261ecSmrg
129805b261ecSmrg        Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
129905b261ecSmrg        if (Min > 0) Min = 0;
130005b261ecSmrg        Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
130105b261ecSmrg        if (Max < 0) Max = 0;
130205b261ecSmrg
130305b261ecSmrg        /* ugh */
130405b261ecSmrg        box.x1 = dst->x + x + Min +
130505b261ecSmrg            FONTMINBOUNDS(pGC->font, leftSideBearing);
130605b261ecSmrg        box.x2 = dst->x + x + Max +
130705b261ecSmrg            FONTMAXBOUNDS(pGC->font, rightSideBearing);
130805b261ecSmrg
130905b261ecSmrg        box.y1 = dst->y + y - top;
131005b261ecSmrg        box.y2 = dst->y + y + bot;
131105b261ecSmrg
131205b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
131305b261ecSmrg
131405b261ecSmrg        if (canAccelFill(dst, pGC) &&
131505b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
131605b261ecSmrg        {
131705b261ecSmrg            GC_UNSET_PM(pGC, dst);
131805b261ecSmrg        }
131905b261ecSmrg
132005b261ecSmrg        pGC->ops->ImageText16(dst, pGC, x, y, count, chars);
132105b261ecSmrg
132205b261ecSmrg        TRIM_BOX(box, pGC);
132305b261ecSmrg        if (BOX_NOT_EMPTY(box))
132405b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
132505b261ecSmrg    } else {
132605b261ecSmrg        pGC->ops->ImageText16(dst, pGC, x, y, count, chars);
132705b261ecSmrg    }
132805b261ecSmrg
132905b261ecSmrg    GC_RESTORE(pGC, dst);
133005b261ecSmrg    GCOP_WRAP(pGC);
133105b261ecSmrg    RL_DEBUG_MSG("imagetext16 end\n");
133205b261ecSmrg}
133305b261ecSmrg
133405b261ecSmrgstatic int RootlessPolyText16(DrawablePtr dst, GCPtr pGC,
133505b261ecSmrg                            int x, int y, int count, unsigned short *chars)
133605b261ecSmrg{
133705b261ecSmrg    int width; // the result, sorta
133805b261ecSmrg
133905b261ecSmrg    GCOP_UNWRAP(pGC);
134005b261ecSmrg
134105b261ecSmrg    if (GC_IS_ROOT(dst))
134205b261ecSmrg        return 0;
134305b261ecSmrg
134405b261ecSmrg    RL_DEBUG_MSG("polytext16 start ");
134505b261ecSmrg
134605b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
134705b261ecSmrg    width = pGC->ops->PolyText16(dst, pGC, x, y, count, chars);
134805b261ecSmrg    width -= x;
134905b261ecSmrg
135005b261ecSmrg    if (width > 0) {
135105b261ecSmrg        BoxRec box;
135205b261ecSmrg
135305b261ecSmrg        /* ugh */
135405b261ecSmrg        box.x1 = dst->x + x + FONTMINBOUNDS(pGC->font, leftSideBearing);
135505b261ecSmrg        box.x2 = dst->x + x + FONTMAXBOUNDS(pGC->font, rightSideBearing);
135605b261ecSmrg
135705b261ecSmrg        if (count > 1) {
135805b261ecSmrg            if (width > 0) box.x2 += width;
135905b261ecSmrg            else box.x1 += width;
136005b261ecSmrg        }
136105b261ecSmrg
136205b261ecSmrg        box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
136305b261ecSmrg        box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
136405b261ecSmrg
136505b261ecSmrg        TRIM_BOX(box, pGC);
136605b261ecSmrg        if (BOX_NOT_EMPTY(box))
136705b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
136805b261ecSmrg    }
136905b261ecSmrg
137005b261ecSmrg    GCOP_WRAP(pGC);
137105b261ecSmrg    RL_DEBUG_MSG("polytext16 end\n");
137205b261ecSmrg    return width + x;
137305b261ecSmrg}
137405b261ecSmrg
137505b261ecSmrgstatic void RootlessImageGlyphBlt(DrawablePtr dst, GCPtr pGC,
137605b261ecSmrg                                  int x, int y, unsigned int nglyphInit,
137705b261ecSmrg                                  CharInfoPtr *ppciInit, pointer unused)
137805b261ecSmrg{
137905b261ecSmrg    GC_SAVE(pGC);
138005b261ecSmrg    GCOP_UNWRAP(pGC);
138105b261ecSmrg    GC_SKIP_ROOT(dst);
138205b261ecSmrg    RL_DEBUG_MSG("imageglyph start ");
138305b261ecSmrg
138405b261ecSmrg    if (nglyphInit > 0) {
138505b261ecSmrg        int top, bot, width = 0;
138605b261ecSmrg        BoxRec box;
138705b261ecSmrg        unsigned int nglyph = nglyphInit;
138805b261ecSmrg        CharInfoPtr *ppci = ppciInit;
138905b261ecSmrg
139005b261ecSmrg        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
139105b261ecSmrg        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
139205b261ecSmrg
139305b261ecSmrg        box.x1 = ppci[0]->metrics.leftSideBearing;
139405b261ecSmrg        if (box.x1 > 0) box.x1 = 0;
139505b261ecSmrg        box.x2 = ppci[nglyph - 1]->metrics.rightSideBearing -
139605b261ecSmrg            ppci[nglyph - 1]->metrics.characterWidth;
139705b261ecSmrg        if (box.x2 < 0) box.x2 = 0;
139805b261ecSmrg
139905b261ecSmrg        box.x2 += dst->x + x;
140005b261ecSmrg        box.x1 += dst->x + x;
140105b261ecSmrg
140205b261ecSmrg        while (nglyph--) {
140305b261ecSmrg            width += (*ppci)->metrics.characterWidth;
140405b261ecSmrg            ppci++;
140505b261ecSmrg        }
140605b261ecSmrg
140705b261ecSmrg        if (width > 0)
140805b261ecSmrg            box.x2 += width;
140905b261ecSmrg        else
141005b261ecSmrg            box.x1 += width;
141105b261ecSmrg
141205b261ecSmrg        box.y1 = dst->y + y - top;
141305b261ecSmrg        box.y2 = dst->y + y + bot;
141405b261ecSmrg
141505b261ecSmrg        RootlessStartDrawing((WindowPtr) dst);
141605b261ecSmrg
141705b261ecSmrg        if (canAccelFill(dst, pGC) &&
141805b261ecSmrg            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
141905b261ecSmrg        {
142005b261ecSmrg            GC_UNSET_PM(pGC, dst);
142105b261ecSmrg        }
142205b261ecSmrg
142305b261ecSmrg        pGC->ops->ImageGlyphBlt(dst, pGC, x, y, nglyphInit, ppciInit, unused);
142405b261ecSmrg
142505b261ecSmrg        TRIM_BOX(box, pGC);
142605b261ecSmrg        if (BOX_NOT_EMPTY(box))
142705b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
142805b261ecSmrg    } else {
142905b261ecSmrg        pGC->ops->ImageGlyphBlt(dst, pGC, x, y, nglyphInit, ppciInit, unused);
143005b261ecSmrg    }
143105b261ecSmrg
143205b261ecSmrg    GC_RESTORE(pGC, dst);
143305b261ecSmrg    GCOP_WRAP(pGC);
143405b261ecSmrg    RL_DEBUG_MSG("imageglyph end\n");
143505b261ecSmrg}
143605b261ecSmrg
143705b261ecSmrgstatic void RootlessPolyGlyphBlt(DrawablePtr dst, GCPtr pGC,
143805b261ecSmrg                                 int x, int y, unsigned int nglyph,
143905b261ecSmrg                                 CharInfoPtr *ppci, pointer pglyphBase)
144005b261ecSmrg{
144105b261ecSmrg    GCOP_UNWRAP(pGC);
144205b261ecSmrg    GC_SKIP_ROOT(dst);
144305b261ecSmrg    RL_DEBUG_MSG("polyglyph start ");
144405b261ecSmrg
144505b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
144605b261ecSmrg    pGC->ops->PolyGlyphBlt(dst, pGC, x, y, nglyph, ppci, pglyphBase);
144705b261ecSmrg
144805b261ecSmrg    if (nglyph > 0) {
144905b261ecSmrg        BoxRec box;
145005b261ecSmrg
145105b261ecSmrg        /* ugh */
145205b261ecSmrg        box.x1 = dst->x + x + ppci[0]->metrics.leftSideBearing;
145305b261ecSmrg        box.x2 = dst->x + x + ppci[nglyph - 1]->metrics.rightSideBearing;
145405b261ecSmrg
145505b261ecSmrg        if (nglyph > 1) {
145605b261ecSmrg            int width = 0;
145705b261ecSmrg
145805b261ecSmrg            while (--nglyph) {
145905b261ecSmrg                width += (*ppci)->metrics.characterWidth;
146005b261ecSmrg                ppci++;
146105b261ecSmrg            }
146205b261ecSmrg
146305b261ecSmrg            if (width > 0) box.x2 += width;
146405b261ecSmrg            else box.x1 += width;
146505b261ecSmrg        }
146605b261ecSmrg
146705b261ecSmrg        box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
146805b261ecSmrg        box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
146905b261ecSmrg
147005b261ecSmrg        TRIM_BOX(box, pGC);
147105b261ecSmrg        if (BOX_NOT_EMPTY(box))
147205b261ecSmrg            RootlessDamageBox ((WindowPtr) dst, &box);
147305b261ecSmrg    }
147405b261ecSmrg
147505b261ecSmrg    GCOP_WRAP(pGC);
147605b261ecSmrg    RL_DEBUG_MSG("polyglyph end\n");
147705b261ecSmrg}
147805b261ecSmrg
147905b261ecSmrg
148005b261ecSmrg/* changed area is in dest */
148105b261ecSmrgstatic void
148205b261ecSmrgRootlessPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr dst,
148305b261ecSmrg                   int dx, int dy, int xOrg, int yOrg)
148405b261ecSmrg{
148505b261ecSmrg    BoxRec box;
148605b261ecSmrg
148705b261ecSmrg    GCOP_UNWRAP(pGC);
148805b261ecSmrg    GC_SKIP_ROOT(dst);
148905b261ecSmrg    RL_DEBUG_MSG("push pixels start ");
149005b261ecSmrg
149105b261ecSmrg    RootlessStartDrawing((WindowPtr) dst);
149205b261ecSmrg    pGC->ops->PushPixels(pGC, pBitMap, dst, dx, dy, xOrg, yOrg);
149305b261ecSmrg
149405b261ecSmrg    box.x1 = xOrg + dst->x;
149505b261ecSmrg    box.x2 = box.x1 + dx;
149605b261ecSmrg    box.y1 = yOrg + dst->y;
149705b261ecSmrg    box.y2 = box.y1 + dy;
149805b261ecSmrg
149905b261ecSmrg    TRIM_BOX(box, pGC);
150005b261ecSmrg    if (BOX_NOT_EMPTY(box))
150105b261ecSmrg        RootlessDamageBox ((WindowPtr) dst, &box);
150205b261ecSmrg
150305b261ecSmrg    GCOP_WRAP(pGC);
150405b261ecSmrg    RL_DEBUG_MSG("push pixels end\n");
150505b261ecSmrg}
1506