Fixed a bug which causes the PolyBezier to not be updated on redraw
[wine] / graphics / x11drv / graphics.c
1 /*
2  * X11 graphics driver graphics functions
3  *
4  * Copyright 1993,1994 Alexandre Julliard
5  */
6
7 /*
8  * FIXME: none of these functions obey the GM_ADVANCED
9  * graphics mode
10  */
11
12 #include "config.h"
13
14 #ifndef X_DISPLAY_MISSING
15
16 #include <X11/Intrinsic.h>
17 #include "ts_xlib.h"
18 #include "ts_xutil.h"
19
20 #include <math.h>
21 #ifdef HAVE_FLOAT_H
22 # include <float.h>
23 #endif
24 #include <stdlib.h>
25 #ifndef PI
26 #define PI M_PI
27 #endif
28 #include <string.h>
29
30 #include "x11drv.h"
31 #include "x11font.h"
32 #include "bitmap.h"
33 #include "gdi.h"
34 #include "dc.h"
35 #include "monitor.h"
36 #include "bitmap.h"
37 #include "callback.h"
38 #include "metafile.h"
39 #include "palette.h"
40 #include "color.h"
41 #include "region.h"
42 #include "struct32.h"
43 #include "debug.h"
44 #include "xmalloc.h"
45
46 DEFAULT_DEBUG_CHANNEL(graphics)
47
48 #define ABS(x)    ((x)<0?(-(x)):(x))
49
50   /* ROP code to GC function conversion */
51 const int X11DRV_XROPfunction[16] =
52 {
53     GXclear,        /* R2_BLACK */
54     GXnor,          /* R2_NOTMERGEPEN */
55     GXandInverted,  /* R2_MASKNOTPEN */
56     GXcopyInverted, /* R2_NOTCOPYPEN */
57     GXandReverse,   /* R2_MASKPENNOT */
58     GXinvert,       /* R2_NOT */
59     GXxor,          /* R2_XORPEN */
60     GXnand,         /* R2_NOTMASKPEN */
61     GXand,          /* R2_MASKPEN */
62     GXequiv,        /* R2_NOTXORPEN */
63     GXnoop,         /* R2_NOP */
64     GXorInverted,   /* R2_MERGENOTPEN */
65     GXcopy,         /* R2_COPYPEN */
66     GXorReverse,    /* R2_MERGEPENNOT */
67     GXor,           /* R2_MERGEPEN */
68     GXset           /* R2_WHITE */
69 };
70
71
72 /***********************************************************************
73  *           X11DRV_SetupGCForPatBlt
74  *
75  * Setup the GC for a PatBlt operation using current brush.
76  * If fMapColors is TRUE, X pixels are mapped to Windows colors.
77  * Return FALSE if brush is BS_NULL, TRUE otherwise.
78  */
79 BOOL X11DRV_SetupGCForPatBlt( DC * dc, GC gc, BOOL fMapColors )
80 {
81     XGCValues val;
82     unsigned long mask;
83     Pixmap pixmap = 0;
84     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
85
86     if (physDev->brush.style == BS_NULL) return FALSE;
87     if (physDev->brush.pixel == -1)
88     {
89         /* Special case used for monochrome pattern brushes.
90          * We need to swap foreground and background because
91          * Windows does it the wrong way...
92          */
93         val.foreground = physDev->backgroundPixel;
94         val.background = physDev->textPixel;
95     }
96     else
97     {
98         val.foreground = physDev->brush.pixel;
99         val.background = physDev->backgroundPixel;
100     }
101     if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
102     {
103         val.foreground = X11DRV_PALETTE_XPixelToPalette[val.foreground];
104         val.background = X11DRV_PALETTE_XPixelToPalette[val.background];
105     }
106
107     if (dc->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion(dc);
108
109     val.function = X11DRV_XROPfunction[dc->w.ROPmode-1];
110     /*
111     ** Let's replace GXinvert by GXxor with (black xor white)
112     ** This solves the selection color and leak problems in excel
113     ** FIXME : Let's do that only if we work with X-pixels, not with Win-pixels
114     */
115     if (val.function == GXinvert)
116         {
117         val.foreground = BlackPixelOfScreen(X11DRV_GetXScreen()) ^ WhitePixelOfScreen(X11DRV_GetXScreen());
118         val.function = GXxor;
119         }
120     val.fill_style = physDev->brush.fillStyle;
121     switch(val.fill_style)
122     {
123     case FillStippled:
124     case FillOpaqueStippled:
125         if (dc->w.backgroundMode==OPAQUE) val.fill_style = FillOpaqueStippled;
126         val.stipple = physDev->brush.pixmap;
127         mask = GCStipple;
128         break;
129
130     case FillTiled:
131         if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
132         {
133             register int x, y;
134             XImage *image;
135             EnterCriticalSection( &X11DRV_CritSection );
136             pixmap = XCreatePixmap( display, 
137                                     X11DRV_GetXRootWindow(), 
138                                     8, 8, 
139                                     MONITOR_GetDepth(&MONITOR_PrimaryMonitor) );
140             image = XGetImage( display, physDev->brush.pixmap, 0, 0, 8, 8,
141                                AllPlanes, ZPixmap );
142             for (y = 0; y < 8; y++)
143                 for (x = 0; x < 8; x++)
144                     XPutPixel( image, x, y,
145                                X11DRV_PALETTE_XPixelToPalette[XGetPixel( image, x, y)] );
146             XPutImage( display, pixmap, gc, image, 0, 0, 0, 0, 8, 8 );
147             XDestroyImage( image );
148             LeaveCriticalSection( &X11DRV_CritSection );
149             val.tile = pixmap;
150         }
151         else val.tile = physDev->brush.pixmap;
152         mask = GCTile;
153         break;
154
155     default:
156         mask = 0;
157         break;
158     }
159     val.ts_x_origin = dc->w.DCOrgX + dc->w.brushOrgX;
160     val.ts_y_origin = dc->w.DCOrgY + dc->w.brushOrgY;
161     val.fill_rule = (dc->w.polyFillMode==WINDING) ? WindingRule : EvenOddRule;
162     TSXChangeGC( display, gc, 
163                GCFunction | GCForeground | GCBackground | GCFillStyle |
164                GCFillRule | GCTileStipXOrigin | GCTileStipYOrigin | mask,
165                &val );
166     if (pixmap) TSXFreePixmap( display, pixmap );
167     return TRUE;
168 }
169
170
171 /***********************************************************************
172  *           X11DRV_SetupGCForBrush
173  *
174  * Setup physDev->gc for drawing operations using current brush.
175  * Return FALSE if brush is BS_NULL, TRUE otherwise.
176  */
177 BOOL X11DRV_SetupGCForBrush( DC * dc )
178 {
179     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
180     return X11DRV_SetupGCForPatBlt( dc, physDev->gc, FALSE );
181 }
182
183
184 /***********************************************************************
185  *           X11DRV_SetupGCForPen
186  *
187  * Setup physDev->gc for drawing operations using current pen.
188  * Return FALSE if pen is PS_NULL, TRUE otherwise.
189  */
190 BOOL X11DRV_SetupGCForPen( DC * dc )
191 {
192     XGCValues val;
193     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
194
195     if (physDev->pen.style == PS_NULL) return FALSE;
196
197     if (dc->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion(dc); 
198
199     switch (dc->w.ROPmode)
200     {
201     case R2_BLACK :
202         val.foreground = BlackPixelOfScreen( X11DRV_GetXScreen() );
203         val.function = GXcopy;
204         break;
205     case R2_WHITE :
206         val.foreground = WhitePixelOfScreen( X11DRV_GetXScreen() );
207         val.function = GXcopy;
208         break;
209     case R2_XORPEN :
210         val.foreground = physDev->pen.pixel;
211         /* It is very unlikely someone wants to XOR with 0 */
212         /* This fixes the rubber-drawings in paintbrush */
213         if (val.foreground == 0)
214             val.foreground = BlackPixelOfScreen( X11DRV_GetXScreen() )
215                             ^ WhitePixelOfScreen( X11DRV_GetXScreen() );
216         val.function = GXxor;
217         break;
218     default :
219         val.foreground = physDev->pen.pixel;
220         val.function   = X11DRV_XROPfunction[dc->w.ROPmode-1];
221     }
222     val.background = physDev->backgroundPixel;
223     val.fill_style = FillSolid;
224     if ((physDev->pen.width <= 1) &&
225         (physDev->pen.style != PS_SOLID) &&
226         (physDev->pen.style != PS_INSIDEFRAME))
227     {
228         TSXSetDashes( display, physDev->gc, 0, physDev->pen.dashes,
229                       physDev->pen.dash_len );
230         val.line_style = (dc->w.backgroundMode == OPAQUE) ?
231                               LineDoubleDash : LineOnOffDash;
232     }
233     else val.line_style = LineSolid;
234     val.line_width = physDev->pen.width;
235     if (val.line_width <= 1) {
236         val.cap_style = CapNotLast;
237     } else {
238         switch (physDev->pen.endcap)
239         {
240         case PS_ENDCAP_SQUARE:
241             val.cap_style = CapProjecting;
242             break;
243         case PS_ENDCAP_FLAT:
244             val.cap_style = CapButt;
245             break;
246         case PS_ENDCAP_ROUND:
247         default:
248             val.cap_style = CapRound;
249         }
250     }
251     switch (physDev->pen.linejoin)
252     {
253     case PS_JOIN_BEVEL:
254         val.join_style = JoinBevel;
255         break;
256     case PS_JOIN_MITER:
257         val.join_style = JoinMiter;
258         break;
259     case PS_JOIN_ROUND:
260     default:
261         val.join_style = JoinRound;
262     }
263     TSXChangeGC( display, physDev->gc, 
264                GCFunction | GCForeground | GCBackground | GCLineWidth |
265                GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle, &val );
266     return TRUE;
267 }
268
269
270 /***********************************************************************
271  *           X11DRV_SetupGCForText
272  *
273  * Setup physDev->gc for text drawing operations.
274  * Return FALSE if the font is null, TRUE otherwise.
275  */
276 BOOL X11DRV_SetupGCForText( DC * dc )
277 {
278     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
279     XFontStruct* xfs = XFONT_GetFontStruct( physDev->font );
280
281     if( xfs )
282     {
283         XGCValues val;
284
285         if (dc->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion(dc);
286
287         val.function   = GXcopy;  /* Text is always GXcopy */
288         val.foreground = physDev->textPixel;
289         val.background = physDev->backgroundPixel;
290         val.fill_style = FillSolid;
291         val.font       = xfs->fid;
292
293         TSXChangeGC( display, physDev->gc,
294                    GCFunction | GCForeground | GCBackground | GCFillStyle |
295                    GCFont, &val );
296         return TRUE;
297     } 
298     WARN(graphics, "Physical font failure\n" );
299     return FALSE;
300 }
301
302
303 /**********************************************************************
304  *           X11DRV_MoveToEx
305  */
306 BOOL
307 X11DRV_MoveToEx(DC *dc,INT x,INT y,LPPOINT pt) {
308     if (pt)
309     {
310         pt->x = dc->w.CursPosX;
311         pt->y = dc->w.CursPosY;
312     }
313     dc->w.CursPosX = x;
314     dc->w.CursPosY = y;
315     return TRUE;
316 }
317
318 /***********************************************************************
319  *           X11DRV_LineTo
320  */
321 BOOL
322 X11DRV_LineTo( DC *dc, INT x, INT y )
323 {
324     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
325
326     if (X11DRV_SetupGCForPen( dc ))
327         TSXDrawLine(display, physDev->drawable, physDev->gc, 
328                   dc->w.DCOrgX + XLPTODP( dc, dc->w.CursPosX ),
329                   dc->w.DCOrgY + YLPTODP( dc, dc->w.CursPosY ),
330                   dc->w.DCOrgX + XLPTODP( dc, x ),
331                   dc->w.DCOrgY + YLPTODP( dc, y ) );
332     dc->w.CursPosX = x;
333     dc->w.CursPosY = y;
334     return TRUE;
335 }
336
337
338
339 /***********************************************************************
340  *           X11DRV_DrawArc
341  *
342  * Helper functions for Arc(), Chord() and Pie().
343  * 'lines' is the number of lines to draw: 0 for Arc, 1 for Chord, 2 for Pie.
344  *
345  */
346 static BOOL
347 X11DRV_DrawArc( DC *dc, INT left, INT top, INT right,
348                 INT bottom, INT xstart, INT ystart,
349                 INT xend, INT yend, INT lines )
350 {
351     INT xcenter, ycenter, istart_angle, idiff_angle;
352     INT width, oldwidth, oldendcap;
353     double start_angle, end_angle;
354     XPoint points[4];
355     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
356
357     left   = XLPTODP( dc, left );
358     top    = YLPTODP( dc, top );
359     right  = XLPTODP( dc, right );
360     bottom = YLPTODP( dc, bottom );
361     xstart = XLPTODP( dc, xstart );
362     ystart = YLPTODP( dc, ystart );
363     xend   = XLPTODP( dc, xend );
364     yend   = YLPTODP( dc, yend );
365
366     if (right < left) { INT tmp = right; right = left; left = tmp; }
367     if (bottom < top) { INT tmp = bottom; bottom = top; top = tmp; }
368     if ((left == right) || (top == bottom)
369             ||(lines && ((right-left==1)||(bottom-top==1)))) return TRUE;
370
371     oldwidth = width = physDev->pen.width;
372     oldendcap = physDev->pen.endcap;
373     if (!width) width = 1;
374     if(physDev->pen.style == PS_NULL) width = 0;
375
376     if ((physDev->pen.style == PS_INSIDEFRAME))
377     {
378         if (2*width > (right-left)) width=(right-left + 1)/2;
379         if (2*width > (bottom-top)) width=(bottom-top + 1)/2;
380         left   += width / 2;
381         right  -= (width - 1) / 2;
382         top    += width / 2;
383         bottom -= (width - 1) / 2;
384     }
385     if(width == 0) width = 1; /* more accurate */
386     physDev->pen.width = width;
387     physDev->pen.endcap = PS_ENDCAP_SQUARE;
388
389     xcenter = (right + left) / 2;
390     ycenter = (bottom + top) / 2;
391     start_angle = atan2( (double)(ycenter-ystart)*(right-left),
392                          (double)(xstart-xcenter)*(bottom-top) );
393     end_angle   = atan2( (double)(ycenter-yend)*(right-left),
394                          (double)(xend-xcenter)*(bottom-top) );
395     if ((xstart==xend)&&(ystart==yend))
396       { /* A lazy program delivers xstart=xend=ystart=yend=0) */
397         start_angle = 0;
398         end_angle = 2* PI;
399       }
400     else /* notorious cases */
401       if ((start_angle == PI)&&( end_angle <0))
402         start_angle = - PI;
403     else
404       if ((end_angle == PI)&&( start_angle <0))
405         end_angle = - PI;
406     istart_angle = (INT)(start_angle * 180 * 64 / PI + 0.5);
407     idiff_angle  = (INT)((end_angle - start_angle) * 180 * 64 / PI + 0.5);
408     if (idiff_angle <= 0) idiff_angle += 360 * 64;
409
410       /* Fill arc with brush if Chord() or Pie() */
411
412     if ((lines > 0) && X11DRV_SetupGCForBrush( dc )) {
413         TSXSetArcMode( display, physDev->gc,
414                        (lines==1) ? ArcChord : ArcPieSlice);
415         TSXFillArc( display, physDev->drawable, physDev->gc,
416                  dc->w.DCOrgX + left, dc->w.DCOrgY + top,
417                  right-left-1, bottom-top-1, istart_angle, idiff_angle );
418     }
419
420       /* Draw arc and lines */
421
422     if (X11DRV_SetupGCForPen( dc )){
423     TSXDrawArc( display, physDev->drawable, physDev->gc,
424               dc->w.DCOrgX + left, dc->w.DCOrgY + top,
425               right-left-1, bottom-top-1, istart_angle, idiff_angle );
426         if (lines) {
427             /* use the truncated values */
428             start_angle=(double)istart_angle*PI/64./180.;
429             end_angle=(double)(istart_angle+idiff_angle)*PI/64./180.;
430             /* calculate the endpoints and round correctly */
431             points[0].x = (int) floor(dc->w.DCOrgX + (right+left)/2.0 +
432                     cos(start_angle) * (right-left-width*2+2) / 2. + 0.5);
433             points[0].y = (int) floor(dc->w.DCOrgY + (top+bottom)/2.0 -
434                     sin(start_angle) * (bottom-top-width*2+2) / 2. + 0.5);
435             points[1].x = (int) floor(dc->w.DCOrgX + (right+left)/2.0 +
436                     cos(end_angle) * (right-left-width*2+2) / 2. + 0.5);
437             points[1].y = (int) floor(dc->w.DCOrgY + (top+bottom)/2.0 -
438                     sin(end_angle) * (bottom-top-width*2+2) / 2. + 0.5);
439                     
440             /* OK this stuff is optimized for Xfree86 
441              * which is probably the most used server by
442              * wine users. Other X servers will not 
443              * display correctly. (eXceed for instance)
444              * so if you feel you must change make sure that
445              * you either use Xfree86 or seperate your changes 
446              * from these (compile switch or whatever)
447              */
448             if (lines == 2) {
449                 INT dx1,dy1;
450                 points[3] = points[1];
451         points[1].x = dc->w.DCOrgX + xcenter;
452         points[1].y = dc->w.DCOrgY + ycenter;
453                 points[2] = points[1];
454                 dx1=points[1].x-points[0].x;
455                 dy1=points[1].y-points[0].y;
456                 if(((top-bottom) | -2) == -2)
457                     if(dy1>0) points[1].y--;
458                 if(dx1<0) {
459                     if (((-dx1)*64)<=ABS(dy1)*37) points[0].x--;
460                     if(((-dx1*9))<(dy1*16)) points[0].y--;
461                     if( dy1<0 && ((dx1*9)) < (dy1*16)) points[0].y--;
462                 } else {
463                     if(dy1 < 0)  points[0].y--;
464                     if(((right-left) | -2) == -2) points[1].x--;
465                 }
466                 dx1=points[3].x-points[2].x;
467                 dy1=points[3].y-points[2].y;
468                 if(((top-bottom) | -2 ) == -2)
469                     if(dy1 < 0) points[2].y--;
470                 if( dx1<0){ 
471                     if( dy1>0) points[3].y--;
472                     if(((right-left) | -2) == -2 ) points[2].x--;
473                 }else {
474                     points[3].y--;
475                     if( dx1 * 64 < dy1 * -37 ) points[3].x--;
476                 }
477                 lines++;
478     }
479     TSXDrawLines( display, physDev->drawable, physDev->gc,
480                 points, lines+1, CoordModeOrigin );
481         }
482     }
483     physDev->pen.width = oldwidth;
484     physDev->pen.endcap = oldendcap;
485     return TRUE;
486 }
487
488
489 /***********************************************************************
490  *           X11DRV_Arc
491  */
492 BOOL
493 X11DRV_Arc( DC *dc, INT left, INT top, INT right, INT bottom,
494             INT xstart, INT ystart, INT xend, INT yend )
495 {
496     return X11DRV_DrawArc( dc, left, top, right, bottom,
497                            xstart, ystart, xend, yend, 0 );
498 }
499
500
501 /***********************************************************************
502  *           X11DRV_Pie
503  */
504 BOOL
505 X11DRV_Pie( DC *dc, INT left, INT top, INT right, INT bottom,
506             INT xstart, INT ystart, INT xend, INT yend )
507 {
508     return X11DRV_DrawArc( dc, left, top, right, bottom,
509                            xstart, ystart, xend, yend, 2 );
510 }
511
512 /***********************************************************************
513  *           X11DRV_Chord
514  */
515 BOOL
516 X11DRV_Chord( DC *dc, INT left, INT top, INT right, INT bottom,
517               INT xstart, INT ystart, INT xend, INT yend )
518 {
519     return X11DRV_DrawArc( dc, left, top, right, bottom,
520                            xstart, ystart, xend, yend, 1 );
521 }
522
523
524 /***********************************************************************
525  *           X11DRV_Ellipse
526  */
527 BOOL
528 X11DRV_Ellipse( DC *dc, INT left, INT top, INT right, INT bottom )
529 {
530     INT width, oldwidth;
531     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
532
533     left   = XLPTODP( dc, left );
534     top    = YLPTODP( dc, top );
535     right  = XLPTODP( dc, right );
536     bottom = YLPTODP( dc, bottom );
537     if ((left == right) || (top == bottom)) return TRUE;
538
539     if (right < left) { INT tmp = right; right = left; left = tmp; }
540     if (bottom < top) { INT tmp = bottom; bottom = top; top = tmp; }
541     
542     oldwidth = width = physDev->pen.width;
543     if (!width) width = 1;
544     if(physDev->pen.style == PS_NULL) width = 0;
545
546     if ((physDev->pen.style == PS_INSIDEFRAME))
547     {
548         if (2*width > (right-left)) width=(right-left + 1)/2;
549         if (2*width > (bottom-top)) width=(bottom-top + 1)/2;
550         left   += width / 2;
551         right  -= (width - 1) / 2;
552         top    += width / 2;
553         bottom -= (width - 1) / 2;
554     }
555     if(width == 0) width = 1; /* more accurate */
556     physDev->pen.width = width;
557
558     if (X11DRV_SetupGCForBrush( dc ))
559         TSXFillArc( display, physDev->drawable, physDev->gc,
560                   dc->w.DCOrgX + left, dc->w.DCOrgY + top,
561                   right-left-1, bottom-top-1, 0, 360*64 );
562     if (X11DRV_SetupGCForPen( dc ))
563         TSXDrawArc( display, physDev->drawable, physDev->gc,
564                   dc->w.DCOrgX + left, dc->w.DCOrgY + top,
565                   right-left-1, bottom-top-1, 0, 360*64 );
566     physDev->pen.width = oldwidth;
567     return TRUE;
568 }
569
570
571 /***********************************************************************
572  *           X11DRV_Rectangle
573  */
574 BOOL
575 X11DRV_Rectangle(DC *dc, INT left, INT top, INT right, INT bottom)
576 {
577     INT width, oldwidth, oldjoinstyle;
578     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
579
580     TRACE(graphics, "(%d %d %d %d)\n", 
581         left, top, right, bottom);
582
583     left   = XLPTODP( dc, left );
584     top    = YLPTODP( dc, top );
585     right  = XLPTODP( dc, right );
586     bottom = YLPTODP( dc, bottom );
587
588     if ((left == right) || (top == bottom)) return TRUE;
589
590     if (right < left) { INT tmp = right; right = left; left = tmp; }
591     if (bottom < top) { INT tmp = bottom; bottom = top; top = tmp; }
592
593     oldwidth = width = physDev->pen.width;
594     if (!width) width = 1;
595     if(physDev->pen.style == PS_NULL) width = 0;
596
597     if ((physDev->pen.style == PS_INSIDEFRAME))
598     {
599         if (2*width > (right-left)) width=(right-left + 1)/2;
600         if (2*width > (bottom-top)) width=(bottom-top + 1)/2;
601         left   += width / 2;
602         right  -= (width - 1) / 2;
603         top    += width / 2;
604         bottom -= (width - 1) / 2;
605     }
606     if(width == 1) width = 0;
607     physDev->pen.width = width;
608     oldjoinstyle = physDev->pen.linejoin;
609     if(physDev->pen.type != PS_GEOMETRIC)
610         physDev->pen.linejoin = PS_JOIN_MITER;
611
612     if ((right > left + width) && (bottom > top + width))
613     {
614         if (X11DRV_SetupGCForBrush( dc ))
615             TSXFillRectangle( display, physDev->drawable, physDev->gc,
616                             dc->w.DCOrgX + left + (width + 1) / 2,
617                             dc->w.DCOrgY + top + (width + 1) / 2,
618                             right-left-width-1, bottom-top-width-1);
619     }
620     if (X11DRV_SetupGCForPen( dc ))
621         TSXDrawRectangle( display, physDev->drawable, physDev->gc,
622                         dc->w.DCOrgX + left, dc->w.DCOrgY + top,
623                         right-left-1, bottom-top-1 );
624
625     physDev->pen.width = oldwidth;
626     physDev->pen.linejoin = oldjoinstyle;
627     return TRUE;
628 }
629
630 /***********************************************************************
631  *           X11DRV_RoundRect
632  */
633 BOOL
634 X11DRV_RoundRect( DC *dc, INT left, INT top, INT right,
635                   INT bottom, INT ell_width, INT ell_height )
636 {
637     INT width, oldwidth, oldendcap;
638     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
639
640     TRACE(graphics, "(%d %d %d %d  %d %d\n", 
641         left, top, right, bottom, ell_width, ell_height);
642
643     left   = XLPTODP( dc, left );
644     top    = YLPTODP( dc, top );
645     right  = XLPTODP( dc, right );
646     bottom = YLPTODP( dc, bottom );
647
648     if ((left == right) || (top == bottom))
649         return TRUE;
650
651     /* Make sure ell_width and ell_height are >= 1 otherwise XDrawArc gets
652        called with width/height < 0 */
653     ell_width  = MAX(abs( ell_width * dc->vportExtX / dc->wndExtX ), 1);
654     ell_height = MAX(abs( ell_height * dc->vportExtY / dc->wndExtY ), 1);
655
656     /* Fix the coordinates */
657
658     if (right < left) { INT tmp = right; right = left; left = tmp; }
659     if (bottom < top) { INT tmp = bottom; bottom = top; top = tmp; }
660
661     oldwidth = width = physDev->pen.width;
662     oldendcap = physDev->pen.endcap;
663     if (!width) width = 1;
664     if(physDev->pen.style == PS_NULL) width = 0;
665
666     if ((physDev->pen.style == PS_INSIDEFRAME))
667     {
668         if (2*width > (right-left)) width=(right-left + 1)/2;
669         if (2*width > (bottom-top)) width=(bottom-top + 1)/2;
670         left   += width / 2;
671         right  -= (width - 1) / 2;
672         top    += width / 2;
673         bottom -= (width - 1) / 2;
674     }
675     if(width == 0) width = 1;
676     physDev->pen.width = width;
677     physDev->pen.endcap = PS_ENDCAP_SQUARE;
678
679     if (X11DRV_SetupGCForBrush( dc ))
680     {
681         if (ell_width > (right-left) )
682             if (ell_height > (bottom-top) )
683                     TSXFillArc( display, physDev->drawable, physDev->gc,
684                                 dc->w.DCOrgX + left, dc->w.DCOrgY + top,
685                                 right - left - 1, bottom - top - 1,
686                                 0, 360 * 64 );
687             else{
688                     TSXFillArc( display, physDev->drawable, physDev->gc,
689                                 dc->w.DCOrgX + left, dc->w.DCOrgY + top,
690                                 right - left - 1, ell_height, 0, 180 * 64 );
691                     TSXFillArc( display, physDev->drawable, physDev->gc,
692                                 dc->w.DCOrgX + left,
693                                 dc->w.DCOrgY + bottom - ell_height - 1,
694                                 right - left - 1, ell_height, 180 * 64,
695                                 180 * 64 );
696            }
697         else if (ell_height > (bottom-top) ){
698                 TSXFillArc( display, physDev->drawable, physDev->gc,
699                       dc->w.DCOrgX + left, dc->w.DCOrgY + top,
700                       ell_width, bottom - top - 1, 90 * 64, 180 * 64 );
701                 TSXFillArc( display, physDev->drawable, physDev->gc,
702                       dc->w.DCOrgX + right - ell_width -1, dc->w.DCOrgY + top,
703                       ell_width, bottom - top - 1, 270 * 64, 180 * 64 );
704         }else{
705                 TSXFillArc( display, physDev->drawable, physDev->gc,
706                       dc->w.DCOrgX + left, dc->w.DCOrgY + top,
707                       ell_width, ell_height, 90 * 64, 90 * 64 );
708                 TSXFillArc( display, physDev->drawable, physDev->gc,
709                       dc->w.DCOrgX + left,
710                       dc->w.DCOrgY + bottom - ell_height - 1,
711                       ell_width, ell_height, 180 * 64, 90 * 64 );
712                 TSXFillArc( display, physDev->drawable, physDev->gc,
713                       dc->w.DCOrgX + right - ell_width - 1,
714                       dc->w.DCOrgY + bottom - ell_height - 1,
715                       ell_width, ell_height, 270 * 64, 90 * 64 );
716                 TSXFillArc( display, physDev->drawable, physDev->gc,
717                       dc->w.DCOrgX + right - ell_width - 1,
718                       dc->w.DCOrgY + top,
719                       ell_width, ell_height, 0, 90 * 64 );
720         }
721         if (ell_width < right - left)
722         {
723             TSXFillRectangle( display, physDev->drawable, physDev->gc,
724                             dc->w.DCOrgX + left + (ell_width + 1) / 2,
725                             dc->w.DCOrgY + top + 1,
726                             right - left - ell_width - 1,
727                             (ell_height + 1) / 2 - 1);
728             TSXFillRectangle( display, physDev->drawable, physDev->gc,
729                             dc->w.DCOrgX + left + (ell_width + 1) / 2,
730                             dc->w.DCOrgY + bottom - (ell_height) / 2 - 1,
731                             right - left - ell_width - 1,
732                             (ell_height) / 2 );
733         }
734         if  (ell_height < bottom - top)
735         {
736             TSXFillRectangle( display, physDev->drawable, physDev->gc,
737                             dc->w.DCOrgX + left + 1,
738                             dc->w.DCOrgY + top + (ell_height + 1) / 2,
739                             right - left - 2,
740                             bottom - top - ell_height - 1);
741         }
742     }
743     /* FIXME: this could be done with on X call
744      * more efficient and probably more correct
745      * on any X server: XDrawArcs will draw
746      * straight horizontal and vertical lines
747      * if width or height are zero.
748      *
749      * BTW this stuff is optimized for an Xfree86 server
750      * read the comments inside the X11DRV_DrawArc function
751      */
752     if (X11DRV_SetupGCForPen(dc)) {
753         if (ell_width > (right-left) )
754             if (ell_height > (bottom-top) )
755                 TSXDrawArc( display, physDev->drawable, physDev->gc,
756                       dc->w.DCOrgX + left, dc->w.DCOrgY + top,
757                       right - left - 1, bottom -top - 1, 0 , 360 * 64 );
758             else{
759                 TSXDrawArc( display, physDev->drawable, physDev->gc,
760                       dc->w.DCOrgX + left, dc->w.DCOrgY + top,
761                       right - left - 1, ell_height - 1, 0 , 180 * 64 );
762                 TSXDrawArc( display, physDev->drawable, physDev->gc,
763                       dc->w.DCOrgX + left, 
764                       dc->w.DCOrgY + bottom - ell_height,
765                       right - left - 1, ell_height - 1, 180 * 64 , 180 * 64 );
766             }
767         else if (ell_height > (bottom-top) ){
768                 TSXDrawArc( display, physDev->drawable, physDev->gc,
769                       dc->w.DCOrgX + left, dc->w.DCOrgY + top,
770                       ell_width - 1 , bottom - top - 1, 90 * 64 , 180 * 64 );
771                 TSXDrawArc( display, physDev->drawable, physDev->gc,
772                       dc->w.DCOrgX + right - ell_width, 
773                       dc->w.DCOrgY + top,
774                       ell_width - 1 , bottom - top - 1, 270 * 64 , 180 * 64 );
775         }else{
776             TSXDrawArc( display, physDev->drawable, physDev->gc,
777                       dc->w.DCOrgX + left, dc->w.DCOrgY + top,
778                       ell_width - 1, ell_height - 1, 90 * 64, 90 * 64 );
779             TSXDrawArc( display, physDev->drawable, physDev->gc,
780                       dc->w.DCOrgX + left, dc->w.DCOrgY + bottom - ell_height,
781                       ell_width - 1, ell_height - 1, 180 * 64, 90 * 64 );
782             TSXDrawArc( display, physDev->drawable, physDev->gc,
783                       dc->w.DCOrgX + right - ell_width,
784                       dc->w.DCOrgY + bottom - ell_height,
785                       ell_width - 1, ell_height - 1, 270 * 64, 90 * 64 );
786             TSXDrawArc( display, physDev->drawable, physDev->gc,
787                       dc->w.DCOrgX + right - ell_width, dc->w.DCOrgY + top,
788                       ell_width - 1, ell_height - 1, 0, 90 * 64 );
789         }
790         if (ell_width < right - left)
791         {
792             TSXDrawLine( display, physDev->drawable, physDev->gc, 
793                dc->w.DCOrgX + left + ell_width / 2,
794                        dc->w.DCOrgY + top,
795                dc->w.DCOrgX + right - (ell_width+1) / 2,
796                        dc->w.DCOrgY + top);
797             TSXDrawLine( display, physDev->drawable, physDev->gc, 
798                dc->w.DCOrgX + left + ell_width / 2 ,
799                        dc->w.DCOrgY + bottom - 1,
800                dc->w.DCOrgX + right - (ell_width+1)/ 2,
801                        dc->w.DCOrgY + bottom - 1);
802         }
803         if (ell_height < bottom - top)
804         {
805             TSXDrawLine( display, physDev->drawable, physDev->gc, 
806                        dc->w.DCOrgX + right - 1,
807                dc->w.DCOrgY + top + ell_height / 2,
808                        dc->w.DCOrgX + right - 1,
809                dc->w.DCOrgY + bottom - (ell_height+1) / 2);
810             TSXDrawLine( display, physDev->drawable, physDev->gc, 
811                        dc->w.DCOrgX + left,
812                dc->w.DCOrgY + top + ell_height / 2,
813                        dc->w.DCOrgX + left,
814                dc->w.DCOrgY + bottom - (ell_height+1) / 2);
815         }
816     }
817     physDev->pen.width = oldwidth;
818     physDev->pen.endcap = oldendcap;
819     return TRUE;
820 }
821
822
823 /***********************************************************************
824  *           X11DRV_SetPixel
825  */
826 COLORREF
827 X11DRV_SetPixel( DC *dc, INT x, INT y, COLORREF color )
828 {
829     Pixel pixel;
830     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
831     
832     x = dc->w.DCOrgX + XLPTODP( dc, x );
833     y = dc->w.DCOrgY + YLPTODP( dc, y );
834     pixel = X11DRV_PALETTE_ToPhysical( dc, color );
835     
836     TSXSetForeground( display, physDev->gc, pixel );
837     TSXSetFunction( display, physDev->gc, GXcopy );
838     TSXDrawPoint( display, physDev->drawable, physDev->gc, x, y );
839
840     /* inefficient but simple... */
841
842     return X11DRV_PALETTE_ToLogical(pixel);
843 }
844
845
846 /***********************************************************************
847  *           X11DRV_GetPixel
848  */
849 COLORREF
850 X11DRV_GetPixel( DC *dc, INT x, INT y )
851 {
852     static Pixmap pixmap = 0;
853     XImage * image;
854     int pixel;
855     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
856
857     x = dc->w.DCOrgX + XLPTODP( dc, x );
858     y = dc->w.DCOrgY + YLPTODP( dc, y );
859     EnterCriticalSection( &X11DRV_CritSection );
860     if (dc->w.flags & DC_MEMORY)
861     {
862         image = XGetImage( display, physDev->drawable, x, y, 1, 1,
863                            AllPlanes, ZPixmap );
864     }
865     else
866     {
867         /* If we are reading from the screen, use a temporary copy */
868         /* to avoid a BadMatch error */
869         if (!pixmap) pixmap = XCreatePixmap( display, X11DRV_GetXRootWindow(),
870                                              1, 1, dc->w.bitsPerPixel );
871         XCopyArea( display, physDev->drawable, pixmap, BITMAP_colorGC,
872                    x, y, 1, 1, 0, 0 );
873         image = XGetImage( display, pixmap, 0, 0, 1, 1, AllPlanes, ZPixmap );
874     }
875     pixel = XGetPixel( image, 0, 0 );
876     XDestroyImage( image );
877     LeaveCriticalSection( &X11DRV_CritSection );
878     
879     return X11DRV_PALETTE_ToLogical(pixel);
880 }
881
882
883 /***********************************************************************
884  *           X11DRV_PaintRgn
885  */
886 BOOL
887 X11DRV_PaintRgn( DC *dc, HRGN hrgn )
888 {
889     RECT box;
890     HRGN tmpVisRgn, prevVisRgn;
891     HDC  hdc = dc->hSelf; /* FIXME: should not mix dc/hdc this way */
892     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
893
894     if (!(tmpVisRgn = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
895
896       /* Transform region into device co-ords */
897     if (  !REGION_LPTODP( hdc, tmpVisRgn, hrgn )
898         || OffsetRgn( tmpVisRgn, dc->w.DCOrgX, dc->w.DCOrgY ) == ERROR) {
899         DeleteObject( tmpVisRgn );
900         return FALSE;
901     }
902
903       /* Modify visible region */
904     if (!(prevVisRgn = SaveVisRgn16( hdc ))) {
905         DeleteObject( tmpVisRgn );
906         return FALSE;
907     }
908     CombineRgn( tmpVisRgn, prevVisRgn, tmpVisRgn, RGN_AND );
909     SelectVisRgn16( hdc, tmpVisRgn );
910     DeleteObject( tmpVisRgn );
911
912       /* Fill the region */
913
914     GetRgnBox( dc->w.hGCClipRgn, &box );
915     if (X11DRV_SetupGCForBrush( dc ))
916         TSXFillRectangle( display, physDev->drawable, physDev->gc,
917                           box.left, box.top,
918                           box.right-box.left, box.bottom-box.top );
919
920       /* Restore the visible region */
921
922     RestoreVisRgn16( hdc );
923     return TRUE;
924 }
925
926 /**********************************************************************
927  *          X11DRV_Polyline
928  */
929 BOOL
930 X11DRV_Polyline( DC *dc, const POINT* pt, INT count )
931 {
932     INT oldwidth;
933     register int i;
934     XPoint *points;
935     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
936
937     if((oldwidth = physDev->pen.width) == 0) physDev->pen.width = 1;
938
939     points = (XPoint *) xmalloc (sizeof (XPoint) * (count));
940     for (i = 0; i < count; i++)
941     {
942     points[i].x = dc->w.DCOrgX + XLPTODP( dc, pt[i].x );
943     points[i].y = dc->w.DCOrgY + YLPTODP( dc, pt[i].y );
944     }
945
946     if (X11DRV_SetupGCForPen ( dc ))
947     TSXDrawLines( display, physDev->drawable, physDev->gc,
948            points, count, CoordModeOrigin );
949
950     free( points );
951     physDev->pen.width = oldwidth;
952     return TRUE;
953 }
954
955
956 /**********************************************************************
957  *          X11DRV_Polygon
958  */
959 BOOL
960 X11DRV_Polygon( DC *dc, const POINT* pt, INT count )
961 {
962     register int i;
963     XPoint *points;
964     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
965
966     points = (XPoint *) xmalloc (sizeof (XPoint) * (count+1));
967     for (i = 0; i < count; i++)
968     {
969         points[i].x = dc->w.DCOrgX + XLPTODP( dc, pt[i].x );
970         points[i].y = dc->w.DCOrgY + YLPTODP( dc, pt[i].y );
971     }
972     points[count] = points[0];
973
974     if (X11DRV_SetupGCForBrush( dc ))
975         TSXFillPolygon( display, physDev->drawable, physDev->gc,
976                      points, count+1, Complex, CoordModeOrigin);
977
978     if (X11DRV_SetupGCForPen ( dc ))
979         TSXDrawLines( display, physDev->drawable, physDev->gc,
980                    points, count+1, CoordModeOrigin );
981
982     free( points );
983     return TRUE;
984 }
985
986
987 /**********************************************************************
988  *          X11DRV_PolyPolygon
989  */
990 BOOL 
991 X11DRV_PolyPolygon( DC *dc, const POINT* pt, const INT* counts, UINT polygons)
992 {
993     HRGN hrgn;
994     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
995
996     /* FIXME: The points should be converted to device coords before */
997     /* creating the region. */
998
999     hrgn = CreatePolyPolygonRgn( pt, counts, polygons, dc->w.polyFillMode );
1000     X11DRV_PaintRgn( dc, hrgn );
1001     DeleteObject( hrgn );
1002
1003       /* Draw the outline of the polygons */
1004
1005     if (X11DRV_SetupGCForPen ( dc ))
1006     {
1007         int i, j, max = 0;
1008         XPoint *points;
1009
1010         for (i = 0; i < polygons; i++) if (counts[i] > max) max = counts[i];
1011         points = (XPoint *) xmalloc( sizeof(XPoint) * (max+1) );
1012
1013         for (i = 0; i < polygons; i++)
1014         {
1015             for (j = 0; j < counts[i]; j++)
1016             {
1017                 points[j].x = dc->w.DCOrgX + XLPTODP( dc, pt->x );
1018                 points[j].y = dc->w.DCOrgY + YLPTODP( dc, pt->y );
1019                 pt++;
1020             }
1021             points[j] = points[0];
1022             TSXDrawLines( display, physDev->drawable, physDev->gc,
1023                         points, j + 1, CoordModeOrigin );
1024         }
1025         free( points );
1026     }
1027     return TRUE;
1028 }
1029
1030
1031 /**********************************************************************
1032  *          X11DRV_PolyPolyline
1033  */
1034 BOOL 
1035 X11DRV_PolyPolyline( DC *dc, const POINT* pt, const DWORD* counts, DWORD polylines )
1036 {
1037     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1038     if (X11DRV_SetupGCForPen ( dc ))
1039     {
1040         int i, j, max = 0;
1041         XPoint *points;
1042
1043         for (i = 0; i < polylines; i++) if (counts[i] > max) max = counts[i];
1044         points = (XPoint *) xmalloc( sizeof(XPoint) * (max+1) );
1045
1046         for (i = 0; i < polylines; i++)
1047         {
1048             for (j = 0; j < counts[i]; j++)
1049             {
1050                 points[j].x = dc->w.DCOrgX + XLPTODP( dc, pt->x );
1051                 points[j].y = dc->w.DCOrgY + YLPTODP( dc, pt->y );
1052                 pt++;
1053             }
1054             points[j] = points[0];
1055             TSXDrawLines( display, physDev->drawable, physDev->gc,
1056                         points, j + 1, CoordModeOrigin );
1057         }
1058         free( points );
1059     }
1060     return TRUE;
1061 }
1062
1063
1064 /**********************************************************************
1065  *          X11DRV_InternalFloodFill
1066  *
1067  * Internal helper function for flood fill.
1068  * (xorg,yorg) is the origin of the X image relative to the drawable.
1069  * (x,y) is relative to the origin of the X image.
1070  */
1071 static void X11DRV_InternalFloodFill(XImage *image, DC *dc,
1072                                      int x, int y,
1073                                      int xOrg, int yOrg,
1074                                      Pixel pixel, WORD fillType )
1075 {
1076     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1077     int left, right;
1078
1079 #define TO_FLOOD(x,y)  ((fillType == FLOODFILLBORDER) ? \
1080                         (XGetPixel(image,x,y) != pixel) : \
1081                         (XGetPixel(image,x,y) == pixel))
1082
1083     if (!TO_FLOOD(x,y)) return;
1084
1085       /* Find left and right boundaries */
1086
1087     left = right = x;
1088     while ((left > 0) && TO_FLOOD( left-1, y )) left--;
1089     while ((right < image->width) && TO_FLOOD( right, y )) right++;
1090     XFillRectangle( display, physDev->drawable, physDev->gc,
1091                     xOrg + left, yOrg + y, right-left, 1 );
1092
1093       /* Set the pixels of this line so we don't fill it again */
1094
1095     for (x = left; x < right; x++)
1096     {
1097         if (fillType == FLOODFILLBORDER) XPutPixel( image, x, y, pixel );
1098         else XPutPixel( image, x, y, ~pixel );
1099     }
1100
1101       /* Fill the line above */
1102
1103     if (--y >= 0)
1104     {
1105         x = left;
1106         while (x < right)
1107         {
1108             while ((x < right) && !TO_FLOOD(x,y)) x++;
1109             if (x >= right) break;
1110             while ((x < right) && TO_FLOOD(x,y)) x++;
1111             X11DRV_InternalFloodFill(image, dc, x-1, y,
1112                                      xOrg, yOrg, pixel, fillType );
1113         }
1114     }
1115
1116       /* Fill the line below */
1117
1118     if ((y += 2) < image->height)
1119     {
1120         x = left;
1121         while (x < right)
1122         {
1123             while ((x < right) && !TO_FLOOD(x,y)) x++;
1124             if (x >= right) break;
1125             while ((x < right) && TO_FLOOD(x,y)) x++;
1126             X11DRV_InternalFloodFill(image, dc, x-1, y,
1127                                      xOrg, yOrg, pixel, fillType );
1128         }
1129     }
1130 #undef TO_FLOOD    
1131 }
1132
1133
1134 /**********************************************************************
1135  *          X11DRV_DoFloodFill
1136  *
1137  * Main flood-fill routine.
1138  *
1139  * The Xlib critical section must be entered before calling this function.
1140  */
1141
1142 struct FloodFill_params
1143 {
1144     DC      *dc;
1145     INT    x;
1146     INT    y;
1147     COLORREF color;
1148     UINT   fillType;
1149 };
1150
1151 static BOOL X11DRV_DoFloodFill( const struct FloodFill_params *params )
1152 {
1153     XImage *image;
1154     RECT rect;
1155     DC *dc = params->dc;
1156     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1157
1158     if (GetRgnBox( dc->w.hGCClipRgn, &rect ) == ERROR) return FALSE;
1159
1160     if (!(image = XGetImage( display, physDev->drawable,
1161                              rect.left,
1162                              rect.top,
1163                              rect.right - rect.left,
1164                              rect.bottom - rect.top,
1165                              AllPlanes, ZPixmap ))) return FALSE;
1166
1167     if (X11DRV_SetupGCForBrush( dc ))
1168     {
1169           /* ROP mode is always GXcopy for flood-fill */
1170         XSetFunction( display, physDev->gc, GXcopy );
1171         X11DRV_InternalFloodFill(image, dc,
1172                                  XLPTODP(dc,params->x) + dc->w.DCOrgX - rect.left,
1173                                  YLPTODP(dc,params->y) + dc->w.DCOrgY - rect.top,
1174                                  rect.left,
1175                                  rect.top,
1176                                  X11DRV_PALETTE_ToPhysical( dc, params->color ),
1177                                  params->fillType );
1178     }
1179
1180     XDestroyImage( image );
1181     return TRUE;
1182 }
1183
1184
1185 /**********************************************************************
1186  *          X11DRV_ExtFloodFill
1187  */
1188 BOOL
1189 X11DRV_ExtFloodFill( DC *dc, INT x, INT y, COLORREF color,
1190                      UINT fillType )
1191 {
1192     BOOL result;
1193     struct FloodFill_params params;
1194
1195     TRACE(graphics, "X11DRV_ExtFloodFill %d,%d %06lx %d\n",
1196                       x, y, color, fillType );
1197
1198     params.dc = dc;
1199     params.x = x;
1200     params.y = y;
1201     params.color = color;
1202     params.fillType = fillType;
1203
1204     if (!PtVisible( dc->hSelf, x, y )) return FALSE;
1205     EnterCriticalSection( &X11DRV_CritSection );
1206     result = CALL_LARGE_STACK( X11DRV_DoFloodFill, &params );
1207     LeaveCriticalSection( &X11DRV_CritSection );
1208     return result;
1209 }
1210
1211 /******************************************************************
1212  * 
1213  *   *Very* simple bezier drawing code, 
1214  *
1215  *   It uses a recursive algorithm to divide the curve in a series
1216  *   of straight line segements. Not ideal but for me sufficient.
1217  *   If you are in need for something better look for some incremental
1218  *   algorithm.
1219  *
1220  *   7 July 1998 Rein Klazes
1221  */
1222
1223  /* 
1224   * some macro definitions for bezier drawing
1225   *
1226   * to avoid trucation errors the coordinates are
1227   * shifted upwards. When used in drawing they are
1228   * shifted down again, including correct rounding
1229   * and avoiding floating point arithmatic
1230   * 4 bits should allow 27 bits coordinates which I saw
1231   * somewere in the win32 doc's
1232   * 
1233   */
1234
1235 #define BEZIERSHIFTBITS 4
1236 #define BEZIERSHIFTUP(x)    ((x)<<BEZIERSHIFTBITS)
1237 #define BEZIERPIXEL        BEZIERSHIFTUP(1)    
1238 #define BEZIERSHIFTDOWN(x)  (((x)+(1<<(BEZIERSHIFTBITS-1)))>>BEZIERSHIFTBITS)
1239 /* maximum depth of recursion */
1240 #define BEZIERMAXDEPTH  8
1241
1242 /* size of array to store points on */
1243 /* enough for one curve */
1244 #define BEZMAXPOINTS    (150)
1245
1246 /* calculate Bezier average, in this case the middle 
1247  * correctly rounded...
1248  * */
1249
1250 #define BEZIERMIDDLE(Mid, P1, P2) \
1251     (Mid).x=((P1).x+(P2).x + 1)/2;\
1252     (Mid).y=((P1).y+(P2).y + 1)/2;
1253     
1254 /**********************************************************
1255 * BezierCheck helper function to check
1256 * that recursion can be terminated
1257 *       Points[0] and Points[3] are begin and endpoint
1258 *       Points[1] and Points[2] are control points
1259 *       level is the recursion depth
1260 *       returns true if the recusion can be terminated
1261 */
1262 static BOOL BezierCheck( int level, POINT *Points)
1263
1264     INT dx, dy;
1265     dx=Points[3].x-Points[0].x;
1266     dy=Points[3].y-Points[0].y;
1267     if(ABS(dy)<=ABS(dx)){/* shallow line */
1268         /* check that control points are between begin and end */
1269         if(Points[1].x < Points[0].x){
1270             if(Points[1].x < Points[3].x)
1271                 return FALSE;
1272         }else
1273             if(Points[1].x > Points[3].x)
1274                 return FALSE;
1275         if(Points[2].x < Points[0].x){
1276             if(Points[2].x < Points[3].x)
1277                 return FALSE;
1278         }else
1279             if(Points[2].x > Points[3].x)
1280                 return FALSE;
1281         dx=BEZIERSHIFTDOWN(dx);
1282         if(!dx) return TRUE;
1283         if(abs(Points[1].y-Points[0].y-(dy/dx)*
1284                 BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL ||
1285            abs(Points[2].y-Points[0].y-(dy/dx)*
1286                    BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL )
1287             return FALSE;
1288         else
1289             return TRUE;
1290     }else{ /* steep line */
1291         /* check that control points are between begin and end */
1292         if(Points[1].y < Points[0].y){
1293             if(Points[1].y < Points[3].y)
1294                 return FALSE;
1295         }else
1296             if(Points[1].y > Points[3].y)
1297                 return FALSE;
1298         if(Points[2].y < Points[0].y){
1299             if(Points[2].y < Points[3].y)
1300                 return FALSE;
1301         }else
1302             if(Points[2].y > Points[3].y)
1303                 return FALSE;
1304         dy=BEZIERSHIFTDOWN(dy);
1305         if(!dy) return TRUE;
1306         if(abs(Points[1].x-Points[0].x-(dx/dy)*
1307                 BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL ||
1308            abs(Points[2].x-Points[0].x-(dx/dy)*
1309                    BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL )
1310             return FALSE;
1311         else
1312             return TRUE;
1313     }
1314 }
1315     
1316 /***********************************************************************
1317  *           X11DRV_Bezier
1318  *   Draw a -what microsoft calls- bezier curve
1319  *   The routine recursively devides the curve
1320  *   in two parts until a straight line can be drawn
1321  *
1322  *   level      recusion depth counted backwards
1323  *   dc         device context
1324  *   Points     array of begin(0), end(3) and control points(1 and 2)
1325  *   XPoints    array with points calculated sofar
1326  *   *pIx       nr points calculated sofar
1327  *   
1328  */
1329 static void X11DRV_Bezier(int level, DC * dc, POINT *Points, 
1330                           XPoint* xpoints, unsigned int* pIx)
1331 {
1332     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1333
1334     if(*pIx == BEZMAXPOINTS){
1335         if (X11DRV_SetupGCForPen( dc )) 
1336             TSXDrawLines( display, physDev->drawable, physDev->gc,
1337                           xpoints, *pIx, CoordModeOrigin );
1338         *pIx=0;
1339     }
1340     if(!level || BezierCheck(level, Points)) {
1341         if(*pIx == 0){
1342             xpoints[*pIx].x= dc->w.DCOrgX + BEZIERSHIFTDOWN(Points[0].x);
1343             xpoints[*pIx].y= dc->w.DCOrgY + BEZIERSHIFTDOWN(Points[0].y);
1344             *pIx=1;
1345         }
1346         xpoints[*pIx].x= dc->w.DCOrgX + BEZIERSHIFTDOWN(Points[3].x);
1347         xpoints[*pIx].y= dc->w.DCOrgY + BEZIERSHIFTDOWN(Points[3].y);
1348         (*pIx) ++;
1349     } else {
1350         POINT Points2[4]; /* for the second recursive call */
1351         Points2[3]=Points[3];
1352         BEZIERMIDDLE(Points2[2], Points[2], Points[3]);
1353         BEZIERMIDDLE(Points2[0], Points[1], Points[2]);
1354         BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]);
1355
1356         BEZIERMIDDLE(Points[1], Points[0],  Points[1]);
1357         BEZIERMIDDLE(Points[2], Points[1], Points2[0]);
1358         BEZIERMIDDLE(Points[3], Points[2], Points2[1]);
1359
1360         Points2[0]=Points[3];
1361
1362         /* do the two halves */
1363         X11DRV_Bezier(level-1, dc, Points, xpoints, pIx);
1364         X11DRV_Bezier(level-1, dc, Points2, xpoints, pIx);
1365     }
1366 }
1367
1368 /***********************************************************************
1369  *           X11DRV_PolyBezier
1370  *      Implement functionality for PolyBezier and PolyBezierTo
1371  *      calls. 
1372  *      [i] dc pointer to device context
1373  *      [i] start, first point in curve
1374  *      [i] BezierPoints , array of point filled with rest of the points
1375  *      [i] count, number of points in BezierPoints, must be a 
1376  *          multiple of 3.
1377  */
1378 BOOL
1379 X11DRV_PolyBezier(DC *dc, POINT start, const POINT* BezierPoints, DWORD count)
1380 {
1381     POINT Points[4]; 
1382     int i;
1383     unsigned int ix=0;
1384     XPoint* xpoints;
1385     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1386
1387     TRACE(graphics, "dc=%p count=%ld %ld,%ld - %ld,%ld - %ld,%ld - %ld,%ld\n", 
1388             dc, count,
1389             start.x, start.y,
1390             (Points+0)->x, (Points+0)->y, 
1391             (Points+1)->x, (Points+1)->y, 
1392             (Points+2)->x, (Points+2)->y); 
1393     if(!count || count % 3){/* paranoid */
1394         WARN(graphics," bad value for count : %ld\n", count);
1395         return FALSE; 
1396     }
1397     xpoints=(XPoint*) xmalloc( sizeof(XPoint)*BEZMAXPOINTS);
1398     Points[3].x=BEZIERSHIFTUP(XLPTODP(dc,start.x));
1399     Points[3].y=BEZIERSHIFTUP(YLPTODP(dc,start.y));
1400     while(count){
1401         Points[0]=Points[3];
1402         for(i=1;i<4;i++) {
1403             Points[i].x= BEZIERSHIFTUP(XLPTODP(dc,BezierPoints->x));
1404             Points[i].y= BEZIERSHIFTUP(YLPTODP(dc,BezierPoints->y));
1405             BezierPoints++;
1406         }
1407         X11DRV_Bezier(BEZIERMAXDEPTH , dc, Points, xpoints, &ix );
1408         count -=3;
1409     }
1410     if (ix && X11DRV_SetupGCForPen( dc ))
1411          TSXDrawLines( display, physDev->drawable, physDev->gc,
1412                           xpoints, ix, CoordModeOrigin );
1413     free(xpoints);
1414     return TRUE;
1415 }
1416
1417 /**********************************************************************
1418  *          X11DRV_SetBkColor
1419  */
1420 COLORREF
1421 X11DRV_SetBkColor( DC *dc, COLORREF color )
1422 {
1423     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1424     COLORREF oldColor;
1425
1426     oldColor = dc->w.backgroundColor;
1427     dc->w.backgroundColor = color;
1428
1429     physDev->backgroundPixel = X11DRV_PALETTE_ToPhysical( dc, color );
1430
1431     return oldColor;
1432 }
1433
1434 /**********************************************************************
1435  *          X11DRV_SetTextColor
1436  */
1437 COLORREF
1438 X11DRV_SetTextColor( DC *dc, COLORREF color )
1439 {
1440     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
1441     COLORREF oldColor;
1442
1443     oldColor = dc->w.textColor;
1444     dc->w.textColor = color;
1445
1446     physDev->textPixel = X11DRV_PALETTE_ToPhysical( dc, color );
1447
1448     return oldColor;
1449 }
1450
1451 #endif /* !defined(X_DISPLAY_MISSING) */