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