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