rootlessGC.c revision 05b261ec
1/*
2 * Graphics Context support for generic rootless X server
3 */
4/*
5 * Copyright (c) 2001 Greg Parker. All Rights Reserved.
6 * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
7 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the sale,
29 * use or other dealings in this Software without prior written authorization.
30 */
31
32#ifdef HAVE_DIX_CONFIG_H
33#include <dix-config.h>
34#endif
35
36#include <stddef.h> /* For NULL */
37#include "mi.h"
38#include "scrnintstr.h"
39#include "gcstruct.h"
40#include "pixmapstr.h"
41#include "windowstr.h"
42#include "dixfontstr.h"
43#include "mivalidate.h"
44#include "fb.h"
45
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <fcntl.h>
49
50#include "rootlessCommon.h"
51
52
53// GC functions
54static void RootlessValidateGC(GCPtr pGC, unsigned long changes,
55                               DrawablePtr pDrawable);
56static void RootlessChangeGC(GCPtr pGC, unsigned long mask);
57static void RootlessCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst);
58static void RootlessDestroyGC(GCPtr pGC);
59static void RootlessChangeClip(GCPtr pGC, int type, pointer pvalue,
60                               int nrects);
61static void RootlessDestroyClip(GCPtr pGC);
62static void RootlessCopyClip(GCPtr pgcDst, GCPtr pgcSrc);
63
64GCFuncs rootlessGCFuncs = {
65    RootlessValidateGC,
66    RootlessChangeGC,
67    RootlessCopyGC,
68    RootlessDestroyGC,
69    RootlessChangeClip,
70    RootlessDestroyClip,
71    RootlessCopyClip,
72};
73
74// GC operations
75static void RootlessFillSpans();
76static void RootlessSetSpans();
77static void RootlessPutImage();
78static RegionPtr RootlessCopyArea();
79static RegionPtr RootlessCopyPlane();
80static void RootlessPolyPoint();
81static void RootlessPolylines();
82static void RootlessPolySegment();
83static void RootlessPolyRectangle();
84static void RootlessPolyArc();
85static void RootlessFillPolygon();
86static void RootlessPolyFillRect();
87static void RootlessPolyFillArc();
88static int RootlessPolyText8();
89static int RootlessPolyText16();
90static void RootlessImageText8();
91static void RootlessImageText16();
92static void RootlessImageGlyphBlt();
93static void RootlessPolyGlyphBlt();
94static void RootlessPushPixels();
95
96static GCOps rootlessGCOps = {
97    RootlessFillSpans,
98    RootlessSetSpans,
99    RootlessPutImage,
100    RootlessCopyArea,
101    RootlessCopyPlane,
102    RootlessPolyPoint,
103    RootlessPolylines,
104    RootlessPolySegment,
105    RootlessPolyRectangle,
106    RootlessPolyArc,
107    RootlessFillPolygon,
108    RootlessPolyFillRect,
109    RootlessPolyFillArc,
110    RootlessPolyText8,
111    RootlessPolyText16,
112    RootlessImageText8,
113    RootlessImageText16,
114    RootlessImageGlyphBlt,
115    RootlessPolyGlyphBlt,
116    RootlessPushPixels
117};
118
119/*
120   There are two issues we must contend with when drawing. These are
121   controlled with ROOTLESS_PROTECT_ALPHA and ROOTLESS_ACCEL.
122
123   If ROOTLESS_PROTECT_ALPHA is set, we have to make sure that the alpha
124   channel of the on screen windows is always opaque. fb makes this harder
125   than it would otherwise be by noticing that a planemask of 0x00ffffff
126   includes all bits when depth==24, and so it "optimizes" the planemask to
127   0xffffffff. We work around this by temporarily setting depth=bpp while
128   changing the GC.
129
130   So the normal situation (in 32 bit mode) is that the planemask is
131   0x00ffffff and thus fb leaves the alpha channel alone. The rootless
132   implementation is responsible for setting the alpha channel opaque
133   initially.
134
135   Unfortunately drawing with a planemask that doesn't have all bits set
136   normally causes fb to fall off its fastest paths when blitting and
137   filling.  So we try to recognize when we can relax the planemask back to
138   0xffffffff, and do that for the duration of the drawing operation,
139   setting the alpha channel in fg/bg pixels to opaque at the same time. We
140   can do this when drawing op is GXcopy. We can also do this when copying
141   from another window since its alpha channel must also be opaque.
142
143   The other issue to consider is that the rootless implementation may
144   provide accelerated drawing functions if ROOTLESS_ACCEL is set. For some
145   drawing primitives we swap in rootless acceleration functions, which use
146   the accelerated drawing functions where possible.
147
148   Where both alpha protection and acceleration is used, it is even a bigger
149   win to relax the planemask to all ones because most accelerated drawing
150   functions can only be used in this case. However, even if we can't set
151   the planemask to all ones, we can still use the accelerated
152   CompositePixels function for GXcopy if it is a forward copy. This is
153   mainly intended for copying from pixmaps to windows. The CompositePixels
154   operation used sets alpha to 0xFF during the copy.
155
156   The three macros below are used to implement this, potentially accelerated
157   drawing ops look something like this:
158
159   OP {
160       GC_SAVE(gc);
161       GCOP_UNWRAP(gc);
162
163       ...
164
165       if (canAccelxxx(..) && otherwise-suitable)
166            GC_UNSET_PM(gc, dst);
167
168       gc->funcs->OP(gc, ...);
169
170       GC_RESTORE(gc, dst);
171       GCOP_WRAP(gc);
172   }
173
174 */
175
176#define GC_SAVE(pGC) 				\
177    unsigned long _save_fg = (pGC)->fgPixel;	\
178    unsigned long _save_bg = (pGC)->bgPixel;	\
179    unsigned long _save_pm = (pGC)->planemask;	\
180    Bool _changed = FALSE
181
182#define GC_RESTORE(pGC, pDraw)					\
183    do {							\
184        if (_changed) {						\
185            unsigned int depth = (pDraw)->depth;		\
186            (pGC)->fgPixel = _save_fg;				\
187            (pGC)->bgPixel = _save_bg;				\
188            (pGC)->planemask = _save_pm;			\
189            (pDraw)->depth = (pDraw)->bitsPerPixel;		\
190            VALIDATE_GC(pGC, GCForeground | GCBackground |	\
191                        GCPlaneMask, pDraw);			\
192            (pDraw)->depth = depth;				\
193        }							\
194    } while (0)
195
196#define GC_UNSET_PM(pGC, pDraw)						\
197    do {								\
198        unsigned int mask = RootlessAlphaMask ((pDraw)->bitsPerPixel);	\
199        if (((pGC)->planemask & mask) != mask) {			\
200            unsigned int depth = (pDraw)->depth;			\
201            (pGC)->fgPixel |= mask;					\
202            (pGC)->bgPixel |= mask;					\
203            (pGC)->planemask |= mask;					\
204            (pDraw)->depth = (pDraw)->bitsPerPixel;			\
205            VALIDATE_GC(pGC, GCForeground |				\
206                        GCBackground | GCPlaneMask, pDraw);		\
207            (pDraw)->depth = depth;					\
208            _changed = TRUE;						\
209        }								\
210    } while (0)
211
212#define VALIDATE_GC(pGC, changes, pDrawable)				\
213    do {								\
214        pGC->funcs->ValidateGC(pGC, changes, pDrawable);		\
215        if (((WindowPtr) pDrawable)->viewable) {			\
216            gcrec->originalOps = pGC->ops;				\
217        }								\
218    } while(0)
219
220static RootlessWindowRec *
221canAccelBlit (DrawablePtr pDraw, GCPtr pGC)
222{
223    WindowPtr pTop;
224    RootlessWindowRec *winRec;
225    unsigned int pm;
226
227    if (pGC->alu != GXcopy)
228        return NULL;
229
230    if (pDraw->type != DRAWABLE_WINDOW)
231        return NULL;
232
233    pm = ~RootlessAlphaMask(pDraw->bitsPerPixel);
234    if ((pGC->planemask & pm) != pm)
235        return NULL;
236
237    pTop = TopLevelParent((WindowPtr) pDraw);
238    if (pTop == NULL)
239        return NULL;
240
241    winRec = WINREC(pTop);
242    if (winRec == NULL)
243        return NULL;
244
245    return winRec;
246}
247
248static inline RootlessWindowRec *
249canAccelFill(DrawablePtr pDraw, GCPtr pGC)
250{
251    if (pGC->fillStyle != FillSolid)
252        return NULL;
253
254    return canAccelBlit(pDraw, pGC);
255}
256
257static unsigned int
258boxBytes(DrawablePtr pDraw, BoxRec *box)
259{
260    unsigned int pixels;
261
262    pixels = (box->x2 - box->x1) * (box->y2 - box->y1);
263
264    return pixels * (pDraw->bitsPerPixel >> 3);
265}
266
267
268/*
269 * Screen function to create a graphics context
270 */
271Bool
272RootlessCreateGC(GCPtr pGC)
273{
274    RootlessGCRec *gcrec;
275    RootlessScreenRec *s;
276    Bool result;
277
278    SCREEN_UNWRAP(pGC->pScreen, CreateGC);
279    s = (RootlessScreenRec *) pGC->pScreen->
280            devPrivates[rootlessScreenPrivateIndex].ptr;
281    result = s->CreateGC(pGC);
282
283    gcrec = (RootlessGCRec *) pGC->devPrivates[rootlessGCPrivateIndex].ptr;
284    gcrec->originalOps = NULL; // don't wrap ops yet
285    gcrec->originalFuncs = pGC->funcs;
286    pGC->funcs = &rootlessGCFuncs;
287
288    SCREEN_WRAP(pGC->pScreen, CreateGC);
289    return result;
290}
291
292
293/*
294 * GC funcs
295 *
296 * These wrap lower level GC funcs.
297 * ValidateGC wraps the GC ops iff dest is viewable.
298 * All the others just unwrap and call.
299 */
300
301// GCFUNC_UNRAP assumes funcs have been wrapped and
302// does not assume ops have been wrapped
303#define GCFUNC_UNWRAP(pGC) \
304    RootlessGCRec *gcrec = (RootlessGCRec *) \
305        (pGC)->devPrivates[rootlessGCPrivateIndex].ptr; \
306    (pGC)->funcs = gcrec->originalFuncs; \
307    if (gcrec->originalOps) { \
308        (pGC)->ops = gcrec->originalOps; \
309}
310
311#define GCFUNC_WRAP(pGC) \
312    gcrec->originalFuncs = (pGC)->funcs; \
313    (pGC)->funcs = &rootlessGCFuncs; \
314    if (gcrec->originalOps) { \
315        gcrec->originalOps = (pGC)->ops; \
316        (pGC)->ops = &rootlessGCOps; \
317}
318
319
320static void
321RootlessValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
322{
323    GCFUNC_UNWRAP(pGC);
324
325    gcrec->originalOps = NULL;
326
327    if (pDrawable->type == DRAWABLE_WINDOW)
328    {
329#ifdef ROOTLESS_PROTECT_ALPHA
330        unsigned int depth = pDrawable->depth;
331
332        // We force a planemask so fb doesn't overwrite the alpha channel.
333        // Left to its own devices, fb will optimize away the planemask.
334        pDrawable->depth = pDrawable->bitsPerPixel;
335        pGC->planemask &= ~RootlessAlphaMask(pDrawable->bitsPerPixel);
336        VALIDATE_GC(pGC, changes | GCPlaneMask, pDrawable);
337        pDrawable->depth = depth;
338#else
339        VALIDATE_GC(pGC, changes, pDrawable);
340#endif
341    } else {
342        pGC->funcs->ValidateGC(pGC, changes, pDrawable);
343    }
344
345    GCFUNC_WRAP(pGC);
346}
347
348static void RootlessChangeGC(GCPtr pGC, unsigned long mask)
349{
350    GCFUNC_UNWRAP(pGC);
351    pGC->funcs->ChangeGC(pGC, mask);
352    GCFUNC_WRAP(pGC);
353}
354
355static void RootlessCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
356{
357    GCFUNC_UNWRAP(pGCDst);
358    pGCDst->funcs->CopyGC(pGCSrc, mask, pGCDst);
359    GCFUNC_WRAP(pGCDst);
360}
361
362static void RootlessDestroyGC(GCPtr pGC)
363{
364    GCFUNC_UNWRAP(pGC);
365    pGC->funcs->DestroyGC(pGC);
366    GCFUNC_WRAP(pGC);
367}
368
369static void RootlessChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
370{
371    GCFUNC_UNWRAP(pGC);
372    pGC->funcs->ChangeClip(pGC, type, pvalue, nrects);
373    GCFUNC_WRAP(pGC);
374}
375
376static void RootlessDestroyClip(GCPtr pGC)
377{
378    GCFUNC_UNWRAP(pGC);
379    pGC->funcs->DestroyClip(pGC);
380    GCFUNC_WRAP(pGC);
381}
382
383static void RootlessCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
384{
385    GCFUNC_UNWRAP(pgcDst);
386    pgcDst->funcs->CopyClip(pgcDst, pgcSrc);
387    GCFUNC_WRAP(pgcDst);
388}
389
390
391/*
392 * GC ops
393 *
394 * We can't use shadowfb because shadowfb assumes one pixmap
395 * and our root window is a special case.
396 * However, much of this code is copied from shadowfb.
397 */
398
399// assumes both funcs and ops are wrapped
400#define GCOP_UNWRAP(pGC) \
401    RootlessGCRec *gcrec = (RootlessGCRec *) \
402        (pGC)->devPrivates[rootlessGCPrivateIndex].ptr; \
403    GCFuncs *saveFuncs = pGC->funcs; \
404    (pGC)->funcs = gcrec->originalFuncs; \
405    (pGC)->ops = gcrec->originalOps;
406
407#define GCOP_WRAP(pGC) \
408    gcrec->originalOps = (pGC)->ops; \
409    (pGC)->funcs = saveFuncs; \
410    (pGC)->ops = &rootlessGCOps;
411
412/* Turn drawing on the root into a no-op */
413#define GC_IS_ROOT(pDst) ((pDst)->type == DRAWABLE_WINDOW \
414                            && IsRoot ((WindowPtr) (pDst)))
415
416#define GC_SKIP_ROOT(pDst)			\
417    do {					\
418        if (GC_IS_ROOT (pDst))			\
419            return;				\
420    } while (0)
421
422
423static void
424RootlessFillSpans(DrawablePtr dst, GCPtr pGC, int nInit,
425                  DDXPointPtr pptInit, int *pwidthInit, int sorted)
426{
427    GC_SAVE(pGC);
428    GCOP_UNWRAP(pGC);
429    GC_SKIP_ROOT(dst);
430    RL_DEBUG_MSG("fill spans start ");
431
432    if (nInit <= 0) {
433        pGC->ops->FillSpans(dst, pGC, nInit, pptInit, pwidthInit, sorted);
434    } else {
435        DDXPointPtr ppt = pptInit;
436        int *pwidth = pwidthInit;
437        int i = nInit;
438        BoxRec box;
439
440        box.x1 = ppt->x;
441        box.x2 = box.x1 + *pwidth;
442        box.y2 = box.y1 = ppt->y;
443
444        while (--i) {
445            ppt++;
446            pwidth++;
447            if (box.x1 > ppt->x)
448                box.x1 = ppt->x;
449            if (box.x2 < (ppt->x + *pwidth))
450                box.x2 = ppt->x + *pwidth;
451            if (box.y1 > ppt->y)
452                box.y1 = ppt->y;
453            else if (box.y2 < ppt->y)
454                box.y2 = ppt->y;
455        }
456
457        box.y2++;
458
459        RootlessStartDrawing((WindowPtr) dst);
460
461        if (canAccelFill(dst, pGC) &&
462            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
463        {
464            GC_UNSET_PM(pGC, dst);
465        }
466
467        pGC->ops->FillSpans(dst, pGC, nInit, pptInit, pwidthInit, sorted);
468
469        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
470        if (BOX_NOT_EMPTY(box))
471            RootlessDamageBox ((WindowPtr) dst, &box);
472    }
473
474    GC_RESTORE(pGC, dst);
475    GCOP_WRAP(pGC);
476    RL_DEBUG_MSG("fill spans end\n");
477}
478
479static void
480RootlessSetSpans(DrawablePtr dst, GCPtr pGC, char *pSrc,
481                 DDXPointPtr pptInit, int *pwidthInit,
482                 int nspans, int sorted)
483{
484    GCOP_UNWRAP(pGC);
485    GC_SKIP_ROOT(dst);
486    RL_DEBUG_MSG("set spans start ");
487
488    if (nspans <= 0) {
489        pGC->ops->SetSpans(dst, pGC, pSrc, pptInit, pwidthInit,
490                           nspans, sorted);
491    } else {
492        DDXPointPtr ppt = pptInit;
493        int *pwidth = pwidthInit;
494        int i = nspans;
495        BoxRec box;
496
497        box.x1 = ppt->x;
498        box.x2 = box.x1 + *pwidth;
499        box.y2 = box.y1 = ppt->y;
500
501        while (--i) {
502            ppt++;
503            pwidth++;
504            if (box.x1 > ppt->x)
505                box.x1 = ppt->x;
506            if (box.x2 < (ppt->x + *pwidth))
507                box.x2 = ppt->x + *pwidth;
508            if (box.y1 > ppt->y)
509                box.y1 = ppt->y;
510            else if (box.y2 < ppt->y)
511                box.y2 = ppt->y;
512        }
513
514        box.y2++;
515
516        RootlessStartDrawing((WindowPtr) dst);
517        pGC->ops->SetSpans(dst, pGC, pSrc, pptInit, pwidthInit,
518                           nspans, sorted);
519
520        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
521        if (BOX_NOT_EMPTY(box))
522            RootlessDamageBox ((WindowPtr) dst, &box);
523    }
524    GCOP_WRAP(pGC);
525    RL_DEBUG_MSG("set spans end\n");
526}
527
528static void
529RootlessPutImage(DrawablePtr dst, GCPtr pGC,
530                 int depth, int x, int y, int w, int h,
531                 int leftPad, int format, char *pBits)
532{
533    BoxRec box;
534
535    GCOP_UNWRAP(pGC);
536    GC_SKIP_ROOT(dst);
537    RL_DEBUG_MSG("put image start ");
538
539    RootlessStartDrawing((WindowPtr) dst);
540    pGC->ops->PutImage(dst, pGC, depth, x,y,w,h, leftPad, format, pBits);
541
542    box.x1 = x + dst->x;
543    box.x2 = box.x1 + w;
544    box.y1 = y + dst->y;
545    box.y2 = box.y1 + h;
546
547    TRIM_BOX(box, pGC);
548    if (BOX_NOT_EMPTY(box))
549        RootlessDamageBox ((WindowPtr) dst, &box);
550
551    GCOP_WRAP(pGC);
552    RL_DEBUG_MSG("put image end\n");
553}
554
555/* changed area is *dest* rect */
556static RegionPtr
557RootlessCopyArea(DrawablePtr pSrc, DrawablePtr dst, GCPtr pGC,
558                 int srcx, int srcy, int w, int h,
559                 int dstx, int dsty)
560{
561    RegionPtr result;
562    BoxRec box;
563
564    GC_SAVE(pGC);
565    GCOP_UNWRAP(pGC);
566
567    if (GC_IS_ROOT(dst) || GC_IS_ROOT(pSrc))
568        return NULL;			/* nothing exposed */
569
570    RL_DEBUG_MSG("copy area start (src 0x%x, dst 0x%x)", pSrc, dst);
571
572    if (pSrc->type == DRAWABLE_WINDOW && IsFramedWindow((WindowPtr)pSrc)) {
573        unsigned int bytes;
574
575        /* If both source and dest are windows, and we're doing
576           a simple copy operation, we can remove the alpha-protecting
577           planemask (since source has opaque alpha as well) */
578
579        bytes = w * h * (pSrc->depth >> 3);
580
581        if (bytes >= rootless_CopyBytes_threshold && canAccelBlit(pSrc, pGC))
582        {
583            GC_UNSET_PM(pGC, dst);
584        }
585
586        RootlessStartDrawing((WindowPtr) pSrc);
587    }
588    RootlessStartDrawing((WindowPtr) dst);
589    result = pGC->ops->CopyArea(pSrc, dst, pGC, srcx, srcy, w, h, dstx, dsty);
590
591    box.x1 = dstx + dst->x;
592    box.x2 = box.x1 + w;
593    box.y1 = dsty + dst->y;
594    box.y2 = box.y1 + h;
595
596    TRIM_BOX(box, pGC);
597    if (BOX_NOT_EMPTY(box))
598        RootlessDamageBox ((WindowPtr) dst, &box);
599
600    GC_RESTORE(pGC, dst);
601    GCOP_WRAP(pGC);
602    RL_DEBUG_MSG("copy area end\n");
603    return result;
604}
605
606/* changed area is *dest* rect */
607static RegionPtr RootlessCopyPlane(DrawablePtr pSrc, DrawablePtr dst,
608                                   GCPtr pGC, int srcx, int srcy,
609                                   int w, int h, int dstx, int dsty,
610                                   unsigned long plane)
611{
612    RegionPtr result;
613    BoxRec box;
614
615    GCOP_UNWRAP(pGC);
616
617    if (GC_IS_ROOT(dst) || GC_IS_ROOT(pSrc))
618        return NULL;			/* nothing exposed */
619
620    RL_DEBUG_MSG("copy plane start ");
621
622    if (pSrc->type == DRAWABLE_WINDOW && IsFramedWindow((WindowPtr)pSrc)) {
623        RootlessStartDrawing((WindowPtr) pSrc);
624    }
625    RootlessStartDrawing((WindowPtr) dst);
626    result = pGC->ops->CopyPlane(pSrc, dst, pGC, srcx, srcy, w, h,
627                                 dstx, dsty, plane);
628
629    box.x1 = dstx + dst->x;
630    box.x2 = box.x1 + w;
631    box.y1 = dsty + dst->y;
632    box.y2 = box.y1 + h;
633
634    TRIM_BOX(box, pGC);
635    if (BOX_NOT_EMPTY(box))
636        RootlessDamageBox ((WindowPtr) dst, &box);
637
638    GCOP_WRAP(pGC);
639    RL_DEBUG_MSG("copy plane end\n");
640    return result;
641}
642
643// Options for size of changed area:
644//  0 = box per point
645//  1 = big box around all points
646//  2 = accumulate point in 20 pixel radius
647#define ROOTLESS_CHANGED_AREA 1
648#define abs(a) ((a) > 0 ? (a) : -(a))
649
650/* changed area is box around all points */
651static void RootlessPolyPoint(DrawablePtr dst, GCPtr pGC,
652                              int mode, int npt, DDXPointPtr pptInit)
653{
654    GCOP_UNWRAP(pGC);
655    GC_SKIP_ROOT(dst);
656    RL_DEBUG_MSG("polypoint start ");
657
658    RootlessStartDrawing((WindowPtr) dst);
659    pGC->ops->PolyPoint(dst, pGC, mode, npt, pptInit);
660
661    if (npt > 0) {
662#if ROOTLESS_CHANGED_AREA==0
663        // box per point
664        BoxRec box;
665
666        while (npt) {
667            box.x1 = pptInit->x;
668            box.y1 = pptInit->y;
669            box.x2 = box.x1 + 1;
670            box.y2 = box.y1 + 1;
671
672            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
673            if (BOX_NOT_EMPTY(box))
674                RootlessDamageBox ((WindowPtr) dst, &box);
675
676            npt--;
677            pptInit++;
678        }
679
680#elif ROOTLESS_CHANGED_AREA==1
681        // one big box
682        BoxRec box;
683
684        box.x2 = box.x1 = pptInit->x;
685        box.y2 = box.y1 = pptInit->y;
686        while (--npt) {
687            pptInit++;
688            if (box.x1 > pptInit->x)
689                box.x1 = pptInit->x;
690            else if (box.x2 < pptInit->x)
691                box.x2 = pptInit->x;
692            if (box.y1 > pptInit->y)
693                box.y1 = pptInit->y;
694            else if (box.y2 < pptInit->y)
695                box.y2 = pptInit->y;
696        }
697
698        box.x2++;
699        box.y2++;
700
701        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
702        if (BOX_NOT_EMPTY(box))
703            RootlessDamageBox ((WindowPtr) dst, &box);
704
705#elif ROOTLESS_CHANGED_AREA==2
706        // clever(?) method: accumulate point in 20-pixel radius
707        BoxRec box;
708        int firstx, firsty;
709
710        box.x2 = box.x1 = firstx = pptInit->x;
711        box.y2 = box.y1 = firsty = pptInit->y;
712        while (--npt) {
713            pptInit++;
714            if (abs(pptInit->x - firstx) > 20 ||
715                abs(pptInit->y - firsty) > 20) {
716                box.x2++;
717                box.y2++;
718                TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
719                if (BOX_NOT_EMPTY(box))
720                    RootlessDamageBox ((WindowPtr) dst, &box);
721                box.x2 = box.x1 = firstx = pptInit->x;
722                box.y2 = box.y1 = firsty = pptInit->y;
723            } else {
724                if (box.x1 > pptInit->x) box.x1 = pptInit->x;
725                else if (box.x2 < pptInit->x) box.x2 = pptInit->x;
726                if (box.y1 > pptInit->y) box.y1 = pptInit->y;
727                else if (box.y2 < pptInit->y) box.y2 = pptInit->y;
728            }
729        }
730        box.x2++;
731        box.y2++;
732        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
733        if (BOX_NOT_EMPTY(box))
734            RootlessDamageBox((WindowPtr) dst, &box);
735#endif  /* ROOTLESS_CHANGED_AREA */
736    }
737
738    GCOP_WRAP(pGC);
739    RL_DEBUG_MSG("polypoint end\n");
740}
741
742#undef ROOTLESS_CHANGED_AREA
743
744/* changed area is box around each line */
745static void RootlessPolylines(DrawablePtr dst, GCPtr pGC,
746                              int mode, int npt, DDXPointPtr pptInit)
747{
748    GCOP_UNWRAP(pGC);
749    GC_SKIP_ROOT(dst);
750    RL_DEBUG_MSG("poly lines start ");
751
752    RootlessStartDrawing((WindowPtr) dst);
753    pGC->ops->Polylines(dst, pGC, mode, npt, pptInit);
754
755    if (npt > 0) {
756        BoxRec box;
757        int extra = pGC->lineWidth >> 1;
758
759        box.x2 = box.x1 = pptInit->x;
760        box.y2 = box.y1 = pptInit->y;
761
762        if (npt > 1) {
763            if (pGC->joinStyle == JoinMiter)
764                extra = 6 * pGC->lineWidth;
765            else if (pGC->capStyle == CapProjecting)
766                extra = pGC->lineWidth;
767        }
768
769        if (mode == CoordModePrevious) {
770            int x = box.x1;
771            int y = box.y1;
772
773            while (--npt) {
774                pptInit++;
775                x += pptInit->x;
776                y += pptInit->y;
777                if (box.x1 > x)
778                    box.x1 = x;
779                else if (box.x2 < x)
780                    box.x2 = x;
781                if (box.y1 > y)
782                    box.y1 = y;
783                else if (box.y2 < y)
784                    box.y2 = y;
785            }
786        } else {
787            while (--npt) {
788                pptInit++;
789                if (box.x1 > pptInit->x)
790                    box.x1 = pptInit->x;
791                else if (box.x2 < pptInit->x)
792                    box.x2 = pptInit->x;
793                if (box.y1 > pptInit->y)
794                    box.y1 = pptInit->y;
795                else if (box.y2 < pptInit->y)
796                    box.y2 = pptInit->y;
797            }
798        }
799
800        box.x2++;
801        box.y2++;
802
803        if (extra) {
804            box.x1 -= extra;
805            box.x2 += extra;
806            box.y1 -= extra;
807            box.y2 += extra;
808        }
809
810        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
811        if (BOX_NOT_EMPTY(box))
812            RootlessDamageBox ((WindowPtr) dst, &box);
813    }
814
815    GCOP_WRAP(pGC);
816    RL_DEBUG_MSG("poly lines end\n");
817}
818
819/* changed area is box around each line segment */
820static void RootlessPolySegment(DrawablePtr dst, GCPtr pGC,
821                                int nseg, xSegment *pSeg)
822{
823    GCOP_UNWRAP(pGC);
824    GC_SKIP_ROOT(dst);
825    RL_DEBUG_MSG("poly segment start (win 0x%x)", dst);
826
827    RootlessStartDrawing((WindowPtr) dst);
828    pGC->ops->PolySegment(dst, pGC, nseg, pSeg);
829
830    if (nseg > 0) {
831        BoxRec box;
832        int extra = pGC->lineWidth;
833
834        if (pGC->capStyle != CapProjecting)
835        extra >>= 1;
836
837        if (pSeg->x2 > pSeg->x1) {
838            box.x1 = pSeg->x1;
839            box.x2 = pSeg->x2;
840        } else {
841            box.x2 = pSeg->x1;
842            box.x1 = pSeg->x2;
843        }
844
845        if (pSeg->y2 > pSeg->y1) {
846            box.y1 = pSeg->y1;
847            box.y2 = pSeg->y2;
848        } else {
849            box.y2 = pSeg->y1;
850            box.y1 = pSeg->y2;
851        }
852
853        while (--nseg) {
854            pSeg++;
855            if (pSeg->x2 > pSeg->x1) {
856                if (pSeg->x1 < box.x1) box.x1 = pSeg->x1;
857                if (pSeg->x2 > box.x2) box.x2 = pSeg->x2;
858            } else {
859                if (pSeg->x2 < box.x1) box.x1 = pSeg->x2;
860                if (pSeg->x1 > box.x2) box.x2 = pSeg->x1;
861            }
862            if (pSeg->y2 > pSeg->y1) {
863                if (pSeg->y1 < box.y1) box.y1 = pSeg->y1;
864                if (pSeg->y2 > box.y2) box.y2 = pSeg->y2;
865            } else {
866                if (pSeg->y2 < box.y1) box.y1 = pSeg->y2;
867                if (pSeg->y1 > box.y2) box.y2 = pSeg->y1;
868            }
869        }
870
871        box.x2++;
872        box.y2++;
873
874        if (extra) {
875            box.x1 -= extra;
876            box.x2 += extra;
877            box.y1 -= extra;
878            box.y2 += extra;
879        }
880
881        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
882        if (BOX_NOT_EMPTY(box))
883            RootlessDamageBox ((WindowPtr) dst, &box);
884    }
885
886    GCOP_WRAP(pGC);
887    RL_DEBUG_MSG("poly segment end\n");
888}
889
890/* changed area is box around each line (not entire rects) */
891static void RootlessPolyRectangle(DrawablePtr dst, GCPtr pGC,
892                                  int nRects, xRectangle *pRects)
893{
894    GCOP_UNWRAP(pGC);
895    GC_SKIP_ROOT(dst);
896    RL_DEBUG_MSG("poly rectangle start ");
897
898    RootlessStartDrawing((WindowPtr) dst);
899    pGC->ops->PolyRectangle(dst, pGC, nRects, pRects);
900
901    if (nRects > 0) {
902        BoxRec box;
903        int offset1, offset2, offset3;
904
905        offset2 = pGC->lineWidth;
906        if (!offset2) offset2 = 1;
907        offset1 = offset2 >> 1;
908        offset3 = offset2 - offset1;
909
910        while (nRects--) {
911            box.x1 = pRects->x - offset1;
912            box.y1 = pRects->y - offset1;
913            box.x2 = box.x1 + pRects->width + offset2;
914            box.y2 = box.y1 + offset2;
915            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
916            if (BOX_NOT_EMPTY(box))
917                RootlessDamageBox ((WindowPtr) dst, &box);
918
919            box.x1 = pRects->x - offset1;
920            box.y1 = pRects->y + offset3;
921            box.x2 = box.x1 + offset2;
922            box.y2 = box.y1 + pRects->height - offset2;
923            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
924            if (BOX_NOT_EMPTY(box))
925                RootlessDamageBox ((WindowPtr) dst, &box);
926
927            box.x1 = pRects->x + pRects->width - offset1;
928            box.y1 = pRects->y + offset3;
929            box.x2 = box.x1 + offset2;
930            box.y2 = box.y1 + pRects->height - offset2;
931            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
932            if (BOX_NOT_EMPTY(box))
933                RootlessDamageBox ((WindowPtr) dst, &box);
934
935            box.x1 = pRects->x - offset1;
936            box.y1 = pRects->y + pRects->height - offset1;
937            box.x2 = box.x1 + pRects->width + offset2;
938            box.y2 = box.y1 + offset2;
939            TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
940            if (BOX_NOT_EMPTY(box))
941                RootlessDamageBox ((WindowPtr) dst, &box);
942
943            pRects++;
944        }
945    }
946
947    GCOP_WRAP(pGC);
948    RL_DEBUG_MSG("poly rectangle end\n");
949}
950
951
952/* changed area is box around each arc (assumes all arcs are 360 degrees) */
953static void RootlessPolyArc(DrawablePtr dst, GCPtr pGC, int narcs, xArc *parcs)
954{
955    GCOP_UNWRAP(pGC);
956    GC_SKIP_ROOT(dst);
957    RL_DEBUG_MSG("poly arc start ");
958
959    RootlessStartDrawing((WindowPtr) dst);
960    pGC->ops->PolyArc(dst, pGC, narcs, parcs);
961
962    if (narcs > 0) {
963        int extra = pGC->lineWidth >> 1;
964        BoxRec box;
965
966        box.x1 = parcs->x;
967        box.x2 = box.x1 + parcs->width;
968        box.y1 = parcs->y;
969        box.y2 = box.y1 + parcs->height;
970
971        /* should I break these up instead ? */
972
973        while (--narcs) {
974            parcs++;
975            if (box.x1 > parcs->x)
976                box.x1 = parcs->x;
977            if (box.x2 < (parcs->x + parcs->width))
978                box.x2 = parcs->x + parcs->width;
979            if (box.y1 > parcs->y)
980                box.y1 = parcs->y;
981            if (box.y2 < (parcs->y + parcs->height))
982                box.y2 = parcs->y + parcs->height;
983        }
984
985        if (extra) {
986            box.x1 -= extra;
987            box.x2 += extra;
988            box.y1 -= extra;
989            box.y2 += extra;
990        }
991
992        box.x2++;
993        box.y2++;
994
995        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
996        if (BOX_NOT_EMPTY(box))
997            RootlessDamageBox ((WindowPtr) dst, &box);
998    }
999
1000    GCOP_WRAP(pGC);
1001    RL_DEBUG_MSG("poly arc end\n");
1002}
1003
1004
1005/* changed area is box around each poly */
1006static void RootlessFillPolygon(DrawablePtr dst, GCPtr pGC,
1007                                int shape, int mode, int count,
1008                                DDXPointPtr pptInit)
1009{
1010    GC_SAVE(pGC);
1011    GCOP_UNWRAP(pGC);
1012    GC_SKIP_ROOT(dst);
1013    RL_DEBUG_MSG("fill poly start (win 0x%x, fillStyle 0x%x)", dst,
1014                 pGC->fillStyle);
1015
1016    if (count <= 2) {
1017        pGC->ops->FillPolygon(dst, pGC, shape, mode, count, pptInit);
1018    } else {
1019        DDXPointPtr ppt = pptInit;
1020        int i = count;
1021        BoxRec box;
1022
1023        box.x2 = box.x1 = ppt->x;
1024        box.y2 = box.y1 = ppt->y;
1025
1026        if (mode != CoordModeOrigin) {
1027            int x = box.x1;
1028            int y = box.y1;
1029
1030            while (--i) {
1031                ppt++;
1032                x += ppt->x;
1033                y += ppt->y;
1034                if (box.x1 > x)
1035                    box.x1 = x;
1036                else if (box.x2 < x)
1037                    box.x2 = x;
1038                if (box.y1 > y)
1039                    box.y1 = y;
1040                else if (box.y2 < y)
1041                    box.y2 = y;
1042            }
1043        } else {
1044            while (--i) {
1045                ppt++;
1046                if (box.x1 > ppt->x)
1047                    box.x1 = ppt->x;
1048                else if (box.x2 < ppt->x)
1049                    box.x2 = ppt->x;
1050                if (box.y1 > ppt->y)
1051                    box.y1 = ppt->y;
1052                else if (box.y2 < ppt->y)
1053                    box.y2 = ppt->y;
1054            }
1055        }
1056
1057        box.x2++;
1058        box.y2++;
1059
1060        RootlessStartDrawing((WindowPtr) dst);
1061
1062        if (canAccelFill(dst, pGC) &&
1063            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
1064        {
1065            GC_UNSET_PM(pGC, dst);
1066        }
1067
1068        pGC->ops->FillPolygon(dst, pGC, shape, mode, count, pptInit);
1069
1070        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
1071        if (BOX_NOT_EMPTY(box))
1072            RootlessDamageBox ((WindowPtr) dst, &box);
1073    }
1074
1075    GC_RESTORE(pGC, dst);
1076    GCOP_WRAP(pGC);
1077    RL_DEBUG_MSG("fill poly end\n");
1078}
1079
1080/* changed area is the rects */
1081static void RootlessPolyFillRect(DrawablePtr dst, GCPtr pGC,
1082                                 int nRectsInit, xRectangle *pRectsInit)
1083{
1084    GC_SAVE(pGC);
1085    GCOP_UNWRAP(pGC);
1086    GC_SKIP_ROOT(dst);
1087    RL_DEBUG_MSG("fill rect start (win 0x%x, fillStyle 0x%x)", dst,
1088                 pGC->fillStyle);
1089
1090    if (nRectsInit <= 0) {
1091        pGC->ops->PolyFillRect(dst, pGC, nRectsInit, pRectsInit);
1092    } else {
1093        BoxRec box;
1094        xRectangle *pRects = pRectsInit;
1095        int nRects = nRectsInit;
1096
1097        box.x1 = pRects->x;
1098        box.x2 = box.x1 + pRects->width;
1099        box.y1 = pRects->y;
1100        box.y2 = box.y1 + pRects->height;
1101
1102        while (--nRects) {
1103            pRects++;
1104            if (box.x1 > pRects->x)
1105                box.x1 = pRects->x;
1106            if (box.x2 < (pRects->x + pRects->width))
1107                box.x2 = pRects->x + pRects->width;
1108            if (box.y1 > pRects->y)
1109                box.y1 = pRects->y;
1110            if (box.y2 < (pRects->y + pRects->height))
1111                box.y2 = pRects->y + pRects->height;
1112        }
1113
1114        RootlessStartDrawing((WindowPtr) dst);
1115
1116        if (canAccelFill(dst, pGC) &&
1117            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
1118        {
1119            GC_UNSET_PM(pGC, dst);
1120        }
1121
1122       pGC->ops->PolyFillRect(dst, pGC, nRectsInit, pRectsInit);
1123
1124        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
1125        if (BOX_NOT_EMPTY(box))
1126            RootlessDamageBox ((WindowPtr) dst, &box);
1127    }
1128
1129    GC_RESTORE(pGC, dst);
1130    GCOP_WRAP(pGC);
1131    RL_DEBUG_MSG("fill rect end\n");
1132}
1133
1134
1135/* changed area is box around each arc (assuming arcs are all 360 degrees) */
1136static void RootlessPolyFillArc(DrawablePtr dst, GCPtr pGC,
1137                                int narcsInit, xArc *parcsInit)
1138{
1139    GC_SAVE(pGC);
1140    GCOP_UNWRAP(pGC);
1141    GC_SKIP_ROOT(dst);
1142    RL_DEBUG_MSG("fill arc start ");
1143
1144    if (narcsInit > 0) {
1145        BoxRec box;
1146        int narcs = narcsInit;
1147        xArc *parcs = parcsInit;
1148
1149        box.x1 = parcs->x;
1150        box.x2 = box.x1 + parcs->width;
1151        box.y1 = parcs->y;
1152        box.y2 = box.y1 + parcs->height;
1153
1154        /* should I break these up instead ? */
1155
1156        while (--narcs) {
1157            parcs++;
1158            if (box.x1 > parcs->x)
1159                box.x1 = parcs->x;
1160            if (box.x2 < (parcs->x + parcs->width))
1161                box.x2 = parcs->x + parcs->width;
1162            if (box.y1 > parcs->y)
1163                box.y1 = parcs->y;
1164            if (box.y2 < (parcs->y + parcs->height))
1165                box.y2 = parcs->y + parcs->height;
1166        }
1167
1168        RootlessStartDrawing((WindowPtr) dst);
1169
1170        if (canAccelFill(dst, pGC) &&
1171            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
1172        {
1173            GC_UNSET_PM(pGC, dst);
1174        }
1175
1176        pGC->ops->PolyFillArc(dst, pGC, narcsInit, parcsInit);
1177
1178        TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
1179        if (BOX_NOT_EMPTY(box))
1180            RootlessDamageBox ((WindowPtr) dst, &box);
1181    } else {
1182        pGC->ops->PolyFillArc(dst, pGC, narcsInit, parcsInit);
1183    }
1184
1185    GC_RESTORE(pGC, dst);
1186    GCOP_WRAP(pGC);
1187    RL_DEBUG_MSG("fill arc end\n");
1188}
1189
1190
1191static void RootlessImageText8(DrawablePtr dst, GCPtr pGC,
1192                               int x, int y, int count, char *chars)
1193{
1194    GC_SAVE(pGC);
1195    GCOP_UNWRAP(pGC);
1196    GC_SKIP_ROOT(dst);
1197    RL_DEBUG_MSG("imagetext8 start ");
1198
1199    if (count > 0) {
1200        int top, bot, Min, Max;
1201        BoxRec box;
1202
1203        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
1204        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
1205
1206        Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
1207        if (Min > 0) Min = 0;
1208        Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
1209        if (Max < 0) Max = 0;
1210
1211        /* ugh */
1212        box.x1 = dst->x + x + Min +
1213        FONTMINBOUNDS(pGC->font, leftSideBearing);
1214        box.x2 = dst->x + x + Max +
1215        FONTMAXBOUNDS(pGC->font, rightSideBearing);
1216
1217        box.y1 = dst->y + y - top;
1218        box.y2 = dst->y + y + bot;
1219
1220        RootlessStartDrawing((WindowPtr) dst);
1221
1222        if (canAccelFill(dst, pGC) &&
1223            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
1224        {
1225            GC_UNSET_PM(pGC, dst);
1226        }
1227
1228        pGC->ops->ImageText8(dst, pGC, x, y, count, chars);
1229
1230        TRIM_BOX(box, pGC);
1231        if (BOX_NOT_EMPTY(box))
1232            RootlessDamageBox ((WindowPtr) dst, &box);
1233    } else {
1234        pGC->ops->ImageText8(dst, pGC, x, y, count, chars);
1235    }
1236
1237    GC_RESTORE(pGC, dst);
1238    GCOP_WRAP(pGC);
1239    RL_DEBUG_MSG("imagetext8 end\n");
1240}
1241
1242static int RootlessPolyText8(DrawablePtr dst, GCPtr pGC,
1243                             int x, int y, int count, char *chars)
1244{
1245    int width; // the result, sorta
1246
1247    GCOP_UNWRAP(pGC);
1248
1249    if (GC_IS_ROOT(dst))
1250        return 0;
1251
1252    RL_DEBUG_MSG("polytext8 start ");
1253
1254    RootlessStartDrawing((WindowPtr) dst);
1255    width = pGC->ops->PolyText8(dst, pGC, x, y, count, chars);
1256    width -= x;
1257
1258    if (width > 0) {
1259        BoxRec box;
1260
1261        /* ugh */
1262        box.x1 = dst->x + x + FONTMINBOUNDS(pGC->font, leftSideBearing);
1263        box.x2 = dst->x + x + FONTMAXBOUNDS(pGC->font, rightSideBearing);
1264
1265        if (count > 1) {
1266            if (width > 0) box.x2 += width;
1267            else box.x1 += width;
1268        }
1269
1270        box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
1271        box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
1272
1273        TRIM_BOX(box, pGC);
1274        if (BOX_NOT_EMPTY(box))
1275            RootlessDamageBox ((WindowPtr) dst, &box);
1276    }
1277
1278    GCOP_WRAP(pGC);
1279    RL_DEBUG_MSG("polytext8 end\n");
1280    return (width + x);
1281}
1282
1283static void RootlessImageText16(DrawablePtr dst, GCPtr pGC,
1284                                int x, int y, int count, unsigned short *chars)
1285{
1286    GC_SAVE(pGC);
1287    GCOP_UNWRAP(pGC);
1288    GC_SKIP_ROOT(dst);
1289    RL_DEBUG_MSG("imagetext16 start ");
1290
1291    if (count > 0) {
1292        int top, bot, Min, Max;
1293        BoxRec box;
1294
1295        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
1296        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
1297
1298        Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
1299        if (Min > 0) Min = 0;
1300        Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
1301        if (Max < 0) Max = 0;
1302
1303        /* ugh */
1304        box.x1 = dst->x + x + Min +
1305            FONTMINBOUNDS(pGC->font, leftSideBearing);
1306        box.x2 = dst->x + x + Max +
1307            FONTMAXBOUNDS(pGC->font, rightSideBearing);
1308
1309        box.y1 = dst->y + y - top;
1310        box.y2 = dst->y + y + bot;
1311
1312        RootlessStartDrawing((WindowPtr) dst);
1313
1314        if (canAccelFill(dst, pGC) &&
1315            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
1316        {
1317            GC_UNSET_PM(pGC, dst);
1318        }
1319
1320        pGC->ops->ImageText16(dst, pGC, x, y, count, chars);
1321
1322        TRIM_BOX(box, pGC);
1323        if (BOX_NOT_EMPTY(box))
1324            RootlessDamageBox ((WindowPtr) dst, &box);
1325    } else {
1326        pGC->ops->ImageText16(dst, pGC, x, y, count, chars);
1327    }
1328
1329    GC_RESTORE(pGC, dst);
1330    GCOP_WRAP(pGC);
1331    RL_DEBUG_MSG("imagetext16 end\n");
1332}
1333
1334static int RootlessPolyText16(DrawablePtr dst, GCPtr pGC,
1335                            int x, int y, int count, unsigned short *chars)
1336{
1337    int width; // the result, sorta
1338
1339    GCOP_UNWRAP(pGC);
1340
1341    if (GC_IS_ROOT(dst))
1342        return 0;
1343
1344    RL_DEBUG_MSG("polytext16 start ");
1345
1346    RootlessStartDrawing((WindowPtr) dst);
1347    width = pGC->ops->PolyText16(dst, pGC, x, y, count, chars);
1348    width -= x;
1349
1350    if (width > 0) {
1351        BoxRec box;
1352
1353        /* ugh */
1354        box.x1 = dst->x + x + FONTMINBOUNDS(pGC->font, leftSideBearing);
1355        box.x2 = dst->x + x + FONTMAXBOUNDS(pGC->font, rightSideBearing);
1356
1357        if (count > 1) {
1358            if (width > 0) box.x2 += width;
1359            else box.x1 += width;
1360        }
1361
1362        box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
1363        box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
1364
1365        TRIM_BOX(box, pGC);
1366        if (BOX_NOT_EMPTY(box))
1367            RootlessDamageBox ((WindowPtr) dst, &box);
1368    }
1369
1370    GCOP_WRAP(pGC);
1371    RL_DEBUG_MSG("polytext16 end\n");
1372    return width + x;
1373}
1374
1375static void RootlessImageGlyphBlt(DrawablePtr dst, GCPtr pGC,
1376                                  int x, int y, unsigned int nglyphInit,
1377                                  CharInfoPtr *ppciInit, pointer unused)
1378{
1379    GC_SAVE(pGC);
1380    GCOP_UNWRAP(pGC);
1381    GC_SKIP_ROOT(dst);
1382    RL_DEBUG_MSG("imageglyph start ");
1383
1384    if (nglyphInit > 0) {
1385        int top, bot, width = 0;
1386        BoxRec box;
1387        unsigned int nglyph = nglyphInit;
1388        CharInfoPtr *ppci = ppciInit;
1389
1390        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
1391        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
1392
1393        box.x1 = ppci[0]->metrics.leftSideBearing;
1394        if (box.x1 > 0) box.x1 = 0;
1395        box.x2 = ppci[nglyph - 1]->metrics.rightSideBearing -
1396            ppci[nglyph - 1]->metrics.characterWidth;
1397        if (box.x2 < 0) box.x2 = 0;
1398
1399        box.x2 += dst->x + x;
1400        box.x1 += dst->x + x;
1401
1402        while (nglyph--) {
1403            width += (*ppci)->metrics.characterWidth;
1404            ppci++;
1405        }
1406
1407        if (width > 0)
1408            box.x2 += width;
1409        else
1410            box.x1 += width;
1411
1412        box.y1 = dst->y + y - top;
1413        box.y2 = dst->y + y + bot;
1414
1415        RootlessStartDrawing((WindowPtr) dst);
1416
1417        if (canAccelFill(dst, pGC) &&
1418            boxBytes(dst, &box) >= rootless_FillBytes_threshold)
1419        {
1420            GC_UNSET_PM(pGC, dst);
1421        }
1422
1423        pGC->ops->ImageGlyphBlt(dst, pGC, x, y, nglyphInit, ppciInit, unused);
1424
1425        TRIM_BOX(box, pGC);
1426        if (BOX_NOT_EMPTY(box))
1427            RootlessDamageBox ((WindowPtr) dst, &box);
1428    } else {
1429        pGC->ops->ImageGlyphBlt(dst, pGC, x, y, nglyphInit, ppciInit, unused);
1430    }
1431
1432    GC_RESTORE(pGC, dst);
1433    GCOP_WRAP(pGC);
1434    RL_DEBUG_MSG("imageglyph end\n");
1435}
1436
1437static void RootlessPolyGlyphBlt(DrawablePtr dst, GCPtr pGC,
1438                                 int x, int y, unsigned int nglyph,
1439                                 CharInfoPtr *ppci, pointer pglyphBase)
1440{
1441    GCOP_UNWRAP(pGC);
1442    GC_SKIP_ROOT(dst);
1443    RL_DEBUG_MSG("polyglyph start ");
1444
1445    RootlessStartDrawing((WindowPtr) dst);
1446    pGC->ops->PolyGlyphBlt(dst, pGC, x, y, nglyph, ppci, pglyphBase);
1447
1448    if (nglyph > 0) {
1449        BoxRec box;
1450
1451        /* ugh */
1452        box.x1 = dst->x + x + ppci[0]->metrics.leftSideBearing;
1453        box.x2 = dst->x + x + ppci[nglyph - 1]->metrics.rightSideBearing;
1454
1455        if (nglyph > 1) {
1456            int width = 0;
1457
1458            while (--nglyph) {
1459                width += (*ppci)->metrics.characterWidth;
1460                ppci++;
1461            }
1462
1463            if (width > 0) box.x2 += width;
1464            else box.x1 += width;
1465        }
1466
1467        box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
1468        box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
1469
1470        TRIM_BOX(box, pGC);
1471        if (BOX_NOT_EMPTY(box))
1472            RootlessDamageBox ((WindowPtr) dst, &box);
1473    }
1474
1475    GCOP_WRAP(pGC);
1476    RL_DEBUG_MSG("polyglyph end\n");
1477}
1478
1479
1480/* changed area is in dest */
1481static void
1482RootlessPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr dst,
1483                   int dx, int dy, int xOrg, int yOrg)
1484{
1485    BoxRec box;
1486
1487    GCOP_UNWRAP(pGC);
1488    GC_SKIP_ROOT(dst);
1489    RL_DEBUG_MSG("push pixels start ");
1490
1491    RootlessStartDrawing((WindowPtr) dst);
1492    pGC->ops->PushPixels(pGC, pBitMap, dst, dx, dy, xOrg, yOrg);
1493
1494    box.x1 = xOrg + dst->x;
1495    box.x2 = box.x1 + dx;
1496    box.y1 = yOrg + dst->y;
1497    box.y2 = box.y1 + dy;
1498
1499    TRIM_BOX(box, pGC);
1500    if (BOX_NOT_EMPTY(box))
1501        RootlessDamageBox ((WindowPtr) dst, &box);
1502
1503    GCOP_WRAP(pGC);
1504    RL_DEBUG_MSG("push pixels end\n");
1505}
1506