gdiplus/metafile: Added support for EmfPlusFont objects.
[wine.git] / dlls / winex11.drv / graphics.c
blobe8c59f9baf00e8de7a711f4d9a39bcfd723d6ece
1 /*
2 * X11 graphics driver graphics functions
4 * Copyright 1993,1994 Alexandre Julliard
5 * Copyright 1998 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * FIXME: only some of these functions obey the GM_ADVANCED
24 * graphics mode
27 #include "config.h"
29 #include <stdarg.h>
30 #include <math.h>
31 #ifdef HAVE_FLOAT_H
32 # include <float.h>
33 #endif
34 #include <stdlib.h>
35 #ifndef PI
36 #define PI M_PI
37 #endif
38 #include <string.h>
39 #include <limits.h>
41 #include "windef.h"
42 #include "winbase.h"
43 #include "winreg.h"
45 #include "x11drv.h"
46 #include "wine/debug.h"
47 #include "wine/unicode.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(graphics);
51 #define ABS(x) ((x)<0?(-(x)):(x))
53 /* ROP code to GC function conversion */
54 static const int X11DRV_XROPfunction[16] =
56 GXclear, /* R2_BLACK */
57 GXnor, /* R2_NOTMERGEPEN */
58 GXandInverted, /* R2_MASKNOTPEN */
59 GXcopyInverted, /* R2_NOTCOPYPEN */
60 GXandReverse, /* R2_MASKPENNOT */
61 GXinvert, /* R2_NOT */
62 GXxor, /* R2_XORPEN */
63 GXnand, /* R2_NOTMASKPEN */
64 GXand, /* R2_MASKPEN */
65 GXequiv, /* R2_NOTXORPEN */
66 GXnoop, /* R2_NOP */
67 GXorInverted, /* R2_MERGENOTPEN */
68 GXcopy, /* R2_COPYPEN */
69 GXorReverse, /* R2_MERGEPENNOT */
70 GXor, /* R2_MERGEPEN */
71 GXset /* R2_WHITE */
75 /* get the rectangle in device coordinates, with optional mirroring */
76 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom )
78 RECT rect;
80 rect.left = left;
81 rect.top = top;
82 rect.right = right;
83 rect.bottom = bottom;
84 if (GetLayout( hdc ) & LAYOUT_RTL)
86 /* shift the rectangle so that the right border is included after mirroring */
87 /* it would be more correct to do this after LPtoDP but that's not what Windows does */
88 rect.left--;
89 rect.right--;
91 LPtoDP( hdc, (POINT *)&rect, 2 );
92 if (rect.left > rect.right)
94 int tmp = rect.left;
95 rect.left = rect.right;
96 rect.right = tmp;
98 if (rect.top > rect.bottom)
100 int tmp = rect.top;
101 rect.top = rect.bottom;
102 rect.bottom = tmp;
104 return rect;
107 static void add_pen_device_bounds( X11DRV_PDEVICE *dev, const POINT *points, int count )
109 RECT bounds, rect;
110 int width = 0;
112 if (!dev->bounds) return;
113 reset_bounds( &bounds );
115 if (dev->pen.type & PS_GEOMETRIC || dev->pen.width > 1)
117 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
118 width = dev->pen.width + 2;
119 if (dev->pen.linejoin == PS_JOIN_MITER)
121 width *= 5;
122 if (dev->pen.endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
124 else
126 if (dev->pen.endcap == PS_ENDCAP_SQUARE) width -= width / 4;
127 else width = (width + 1) / 2;
131 while (count-- > 0)
133 rect.left = points->x - width;
134 rect.top = points->y - width;
135 rect.right = points->x + width + 1;
136 rect.bottom = points->y + width + 1;
137 add_bounds_rect( &bounds, &rect );
138 points++;
141 add_device_bounds( dev, &bounds );
144 /***********************************************************************
145 * X11DRV_GetRegionData
147 * Calls GetRegionData on the given region and converts the rectangle
148 * array to XRectangle format. The returned buffer must be freed by
149 * caller using HeapFree(GetProcessHeap(),...).
150 * If hdc_lptodp is not 0, the rectangles are converted through LPtoDP.
152 RGNDATA *X11DRV_GetRegionData( HRGN hrgn, HDC hdc_lptodp )
154 RGNDATA *data;
155 DWORD size;
156 unsigned int i;
157 RECT *rect, tmp;
158 XRectangle *xrect;
160 if (!(size = GetRegionData( hrgn, 0, NULL ))) return NULL;
161 if (sizeof(XRectangle) > sizeof(RECT))
163 /* add extra size for XRectangle array */
164 int count = (size - sizeof(RGNDATAHEADER)) / sizeof(RECT);
165 size += count * (sizeof(XRectangle) - sizeof(RECT));
167 if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
168 if (!GetRegionData( hrgn, size, data ))
170 HeapFree( GetProcessHeap(), 0, data );
171 return NULL;
174 rect = (RECT *)data->Buffer;
175 xrect = (XRectangle *)data->Buffer;
176 if (hdc_lptodp) /* map to device coordinates */
178 LPtoDP( hdc_lptodp, (POINT *)rect, data->rdh.nCount * 2 );
179 for (i = 0; i < data->rdh.nCount; i++)
181 if (rect[i].right < rect[i].left)
183 INT tmp = rect[i].right;
184 rect[i].right = rect[i].left;
185 rect[i].left = tmp;
187 if (rect[i].bottom < rect[i].top)
189 INT tmp = rect[i].bottom;
190 rect[i].bottom = rect[i].top;
191 rect[i].top = tmp;
196 if (sizeof(XRectangle) > sizeof(RECT))
198 int j;
199 /* need to start from the end */
200 xrect += data->rdh.nCount;
201 for (j = data->rdh.nCount-1; j >= 0; j--)
203 tmp = rect[j];
204 if (tmp.left > SHRT_MAX) continue;
205 if (tmp.top > SHRT_MAX) continue;
206 if (tmp.right < SHRT_MIN) continue;
207 if (tmp.bottom < SHRT_MIN) continue;
208 xrect--;
209 xrect->x = max( min( tmp.left, SHRT_MAX), SHRT_MIN);
210 xrect->y = max( min( tmp.top, SHRT_MAX), SHRT_MIN);
211 xrect->width = max( min( tmp.right, SHRT_MAX ) - xrect->x, 0);
212 xrect->height = max( min( tmp.bottom, SHRT_MAX ) - xrect->y, 0);
214 if (xrect > (XRectangle *)data->Buffer)
216 data->rdh.nCount -= xrect - (XRectangle *)data->Buffer;
217 memmove( (XRectangle *)data->Buffer, xrect, data->rdh.nCount * sizeof(*xrect) );
220 else
222 for (i = 0; i < data->rdh.nCount; i++)
224 tmp = rect[i];
225 if (tmp.left > SHRT_MAX) continue;
226 if (tmp.top > SHRT_MAX) continue;
227 if (tmp.right < SHRT_MIN) continue;
228 if (tmp.bottom < SHRT_MIN) continue;
229 xrect->x = max( min( tmp.left, SHRT_MAX), SHRT_MIN);
230 xrect->y = max( min( tmp.top, SHRT_MAX), SHRT_MIN);
231 xrect->width = max( min( tmp.right, SHRT_MAX ) - xrect->x, 0);
232 xrect->height = max( min( tmp.bottom, SHRT_MAX ) - xrect->y, 0);
233 xrect++;
235 data->rdh.nCount = xrect - (XRectangle *)data->Buffer;
237 return data;
241 /***********************************************************************
242 * update_x11_clipping
244 static void update_x11_clipping( X11DRV_PDEVICE *physDev, HRGN rgn )
246 RGNDATA *data;
248 if (!rgn)
250 XSetClipMask( gdi_display, physDev->gc, None );
252 else if ((data = X11DRV_GetRegionData( rgn, 0 )))
254 XSetClipRectangles( gdi_display, physDev->gc, physDev->dc_rect.left, physDev->dc_rect.top,
255 (XRectangle *)data->Buffer, data->rdh.nCount, YXBanded );
256 HeapFree( GetProcessHeap(), 0, data );
261 /***********************************************************************
262 * add_extra_clipping_region
264 * Temporarily add a region to the current clipping region.
265 * The region must be restored with restore_clipping_region.
267 BOOL add_extra_clipping_region( X11DRV_PDEVICE *dev, HRGN rgn )
269 HRGN clip;
271 if (!rgn) return FALSE;
272 if (dev->region)
274 if (!(clip = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
275 CombineRgn( clip, dev->region, rgn, RGN_AND );
276 update_x11_clipping( dev, clip );
277 DeleteObject( clip );
279 else update_x11_clipping( dev, rgn );
280 return TRUE;
284 /***********************************************************************
285 * restore_clipping_region
287 void restore_clipping_region( X11DRV_PDEVICE *dev )
289 update_x11_clipping( dev, dev->region );
293 /***********************************************************************
294 * X11DRV_SetDeviceClipping
296 void X11DRV_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
298 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
300 physDev->region = rgn;
301 update_x11_clipping( physDev, rgn );
305 /***********************************************************************
306 * X11DRV_SetupGCForPatBlt
308 * Setup the GC for a PatBlt operation using current brush.
309 * If fMapColors is TRUE, X pixels are mapped to Windows colors.
310 * Return FALSE if brush is BS_NULL, TRUE otherwise.
312 BOOL X11DRV_SetupGCForPatBlt( X11DRV_PDEVICE *physDev, GC gc, BOOL fMapColors )
314 XGCValues val;
315 unsigned long mask;
316 Pixmap pixmap = 0;
317 POINT pt;
319 if (physDev->brush.style == BS_NULL) return FALSE;
320 if (physDev->brush.pixel == -1)
322 /* Special case used for monochrome pattern brushes.
323 * We need to swap foreground and background because
324 * Windows does it the wrong way...
326 val.foreground = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
327 val.background = X11DRV_PALETTE_ToPhysical( physDev, GetTextColor(physDev->dev.hdc) );
329 else
331 val.foreground = physDev->brush.pixel;
332 val.background = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
334 if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
336 val.foreground = X11DRV_PALETTE_XPixelToPalette[val.foreground];
337 val.background = X11DRV_PALETTE_XPixelToPalette[val.background];
340 val.function = X11DRV_XROPfunction[GetROP2(physDev->dev.hdc)-1];
342 ** Let's replace GXinvert by GXxor with (black xor white)
343 ** This solves the selection color and leak problems in excel
344 ** FIXME : Let's do that only if we work with X-pixels, not with Win-pixels
346 if (val.function == GXinvert)
348 val.foreground = (WhitePixel( gdi_display, DefaultScreen(gdi_display) ) ^
349 BlackPixel( gdi_display, DefaultScreen(gdi_display) ));
350 val.function = GXxor;
352 val.fill_style = physDev->brush.fillStyle;
353 switch(val.fill_style)
355 case FillStippled:
356 case FillOpaqueStippled:
357 if (GetBkMode(physDev->dev.hdc)==OPAQUE) val.fill_style = FillOpaqueStippled;
358 val.stipple = physDev->brush.pixmap;
359 mask = GCStipple;
360 break;
362 case FillTiled:
363 if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
365 register int x, y;
366 XImage *image;
367 pixmap = XCreatePixmap( gdi_display, root_window, 8, 8, physDev->depth );
368 image = XGetImage( gdi_display, physDev->brush.pixmap, 0, 0, 8, 8,
369 AllPlanes, ZPixmap );
370 for (y = 0; y < 8; y++)
371 for (x = 0; x < 8; x++)
372 XPutPixel( image, x, y,
373 X11DRV_PALETTE_XPixelToPalette[XGetPixel( image, x, y)] );
374 XPutImage( gdi_display, pixmap, gc, image, 0, 0, 0, 0, 8, 8 );
375 XDestroyImage( image );
376 val.tile = pixmap;
378 else val.tile = physDev->brush.pixmap;
379 mask = GCTile;
380 break;
382 default:
383 mask = 0;
384 break;
386 GetBrushOrgEx( physDev->dev.hdc, &pt );
387 val.ts_x_origin = physDev->dc_rect.left + pt.x;
388 val.ts_y_origin = physDev->dc_rect.top + pt.y;
389 val.fill_rule = (GetPolyFillMode(physDev->dev.hdc) == WINDING) ? WindingRule : EvenOddRule;
390 XChangeGC( gdi_display, gc,
391 GCFunction | GCForeground | GCBackground | GCFillStyle |
392 GCFillRule | GCTileStipXOrigin | GCTileStipYOrigin | mask,
393 &val );
394 if (pixmap) XFreePixmap( gdi_display, pixmap );
395 return TRUE;
399 /***********************************************************************
400 * X11DRV_SetupGCForBrush
402 * Setup physDev->gc for drawing operations using current brush.
403 * Return FALSE if brush is BS_NULL, TRUE otherwise.
405 BOOL X11DRV_SetupGCForBrush( X11DRV_PDEVICE *physDev )
407 return X11DRV_SetupGCForPatBlt( physDev, physDev->gc, FALSE );
411 /***********************************************************************
412 * X11DRV_SetupGCForPen
414 * Setup physDev->gc for drawing operations using current pen.
415 * Return FALSE if pen is PS_NULL, TRUE otherwise.
417 static BOOL X11DRV_SetupGCForPen( X11DRV_PDEVICE *physDev )
419 XGCValues val;
420 UINT rop2 = GetROP2(physDev->dev.hdc);
422 if (physDev->pen.style == PS_NULL) return FALSE;
424 switch (rop2)
426 case R2_BLACK :
427 val.foreground = BlackPixel( gdi_display, DefaultScreen(gdi_display) );
428 val.function = GXcopy;
429 break;
430 case R2_WHITE :
431 val.foreground = WhitePixel( gdi_display, DefaultScreen(gdi_display) );
432 val.function = GXcopy;
433 break;
434 case R2_XORPEN :
435 val.foreground = physDev->pen.pixel;
436 /* It is very unlikely someone wants to XOR with 0 */
437 /* This fixes the rubber-drawings in paintbrush */
438 if (val.foreground == 0)
439 val.foreground = (WhitePixel( gdi_display, DefaultScreen(gdi_display) ) ^
440 BlackPixel( gdi_display, DefaultScreen(gdi_display) ));
441 val.function = GXxor;
442 break;
443 default :
444 val.foreground = physDev->pen.pixel;
445 val.function = X11DRV_XROPfunction[rop2-1];
447 val.background = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
448 val.fill_style = FillSolid;
449 val.line_width = physDev->pen.width;
450 if (val.line_width <= 1) {
451 val.cap_style = CapNotLast;
452 } else {
453 switch (physDev->pen.endcap)
455 case PS_ENDCAP_SQUARE:
456 val.cap_style = CapProjecting;
457 break;
458 case PS_ENDCAP_FLAT:
459 val.cap_style = CapButt;
460 break;
461 case PS_ENDCAP_ROUND:
462 default:
463 val.cap_style = CapRound;
466 switch (physDev->pen.linejoin)
468 case PS_JOIN_BEVEL:
469 val.join_style = JoinBevel;
470 break;
471 case PS_JOIN_MITER:
472 val.join_style = JoinMiter;
473 break;
474 case PS_JOIN_ROUND:
475 default:
476 val.join_style = JoinRound;
479 if (physDev->pen.dash_len)
480 val.line_style = ((GetBkMode(physDev->dev.hdc) == OPAQUE) && (!physDev->pen.ext))
481 ? LineDoubleDash : LineOnOffDash;
482 else
483 val.line_style = LineSolid;
485 if (physDev->pen.dash_len)
486 XSetDashes( gdi_display, physDev->gc, 0, physDev->pen.dashes, physDev->pen.dash_len );
487 XChangeGC( gdi_display, physDev->gc,
488 GCFunction | GCForeground | GCBackground | GCLineWidth |
489 GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle, &val );
490 return TRUE;
494 /***********************************************************************
495 * X11DRV_XWStoDS
497 * Performs a world-to-viewport transformation on the specified width.
499 INT X11DRV_XWStoDS( HDC hdc, INT width )
501 POINT pt[2];
503 pt[0].x = 0;
504 pt[0].y = 0;
505 pt[1].x = width;
506 pt[1].y = 0;
507 LPtoDP( hdc, pt, 2 );
508 return pt[1].x - pt[0].x;
511 /***********************************************************************
512 * X11DRV_YWStoDS
514 * Performs a world-to-viewport transformation on the specified height.
516 INT X11DRV_YWStoDS( HDC hdc, INT height )
518 POINT pt[2];
520 pt[0].x = 0;
521 pt[0].y = 0;
522 pt[1].x = 0;
523 pt[1].y = height;
524 LPtoDP( hdc, pt, 2 );
525 return pt[1].y - pt[0].y;
528 /***********************************************************************
529 * X11DRV_LineTo
531 BOOL X11DRV_LineTo( PHYSDEV dev, INT x, INT y )
533 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
534 POINT pt[2];
536 GetCurrentPositionEx( dev->hdc, &pt[0] );
537 pt[1].x = x;
538 pt[1].y = y;
539 LPtoDP( dev->hdc, pt, 2 );
540 add_pen_device_bounds( physDev, pt, 2 );
542 if (X11DRV_SetupGCForPen( physDev ))
543 XDrawLine(gdi_display, physDev->drawable, physDev->gc,
544 physDev->dc_rect.left + pt[0].x, physDev->dc_rect.top + pt[0].y,
545 physDev->dc_rect.left + pt[1].x, physDev->dc_rect.top + pt[1].y );
546 return TRUE;
551 /***********************************************************************
552 * X11DRV_DrawArc
554 * Helper functions for Arc(), Chord() and Pie().
555 * 'lines' is the number of lines to draw: 0 for Arc, 1 for Chord, 2 for Pie.
558 static BOOL X11DRV_DrawArc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
559 INT xstart, INT ystart, INT xend, INT yend, INT lines )
561 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
562 INT xcenter, ycenter, istart_angle, idiff_angle;
563 INT width, oldwidth;
564 double start_angle, end_angle;
565 XPoint points[4];
566 POINT start, end;
567 RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
569 start.x = xstart;
570 start.y = ystart;
571 end.x = xend;
572 end.y = yend;
573 LPtoDP(dev->hdc, &start, 1);
574 LPtoDP(dev->hdc, &end, 1);
576 if ((rc.left == rc.right) || (rc.top == rc.bottom)
577 ||(lines && ((rc.right-rc.left==1)||(rc.bottom-rc.top==1)))) return TRUE;
579 if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
580 { POINT tmp = start; start = end; end = tmp; }
582 oldwidth = width = physDev->pen.width;
583 if (!width) width = 1;
584 if(physDev->pen.style == PS_NULL) width = 0;
586 if (physDev->pen.style == PS_INSIDEFRAME)
588 if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
589 if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
590 rc.left += width / 2;
591 rc.right -= (width - 1) / 2;
592 rc.top += width / 2;
593 rc.bottom -= (width - 1) / 2;
595 if(width == 0) width = 1; /* more accurate */
596 physDev->pen.width = width;
598 xcenter = (rc.right + rc.left) / 2;
599 ycenter = (rc.bottom + rc.top) / 2;
600 start_angle = atan2( (double)(ycenter-start.y)*(rc.right-rc.left),
601 (double)(start.x-xcenter)*(rc.bottom-rc.top) );
602 end_angle = atan2( (double)(ycenter-end.y)*(rc.right-rc.left),
603 (double)(end.x-xcenter)*(rc.bottom-rc.top) );
604 if ((start.x==end.x)&&(start.y==end.y))
605 { /* A lazy program delivers xstart=xend=ystart=yend=0) */
606 start_angle = 0;
607 end_angle = 2* PI;
609 else /* notorious cases */
610 if ((start_angle == PI)&&( end_angle <0))
611 start_angle = - PI;
612 else
613 if ((end_angle == PI)&&( start_angle <0))
614 end_angle = - PI;
615 istart_angle = (INT)(start_angle * 180 * 64 / PI + 0.5);
616 idiff_angle = (INT)((end_angle - start_angle) * 180 * 64 / PI + 0.5);
617 if (idiff_angle <= 0) idiff_angle += 360 * 64;
619 /* Fill arc with brush if Chord() or Pie() */
621 if ((lines > 0) && X11DRV_SetupGCForBrush( physDev )) {
622 XSetArcMode( gdi_display, physDev->gc, (lines==1) ? ArcChord : ArcPieSlice);
623 XFillArc( gdi_display, physDev->drawable, physDev->gc,
624 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
625 rc.right-rc.left-1, rc.bottom-rc.top-1, istart_angle, idiff_angle );
628 /* Draw arc and lines */
630 if (X11DRV_SetupGCForPen( physDev ))
632 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
633 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
634 rc.right-rc.left-1, rc.bottom-rc.top-1, istart_angle, idiff_angle );
635 if (lines) {
636 /* use the truncated values */
637 start_angle=(double)istart_angle*PI/64./180.;
638 end_angle=(double)(istart_angle+idiff_angle)*PI/64./180.;
639 /* calculate the endpoints and round correctly */
640 points[0].x = (int) floor(physDev->dc_rect.left + (rc.right+rc.left)/2.0 +
641 cos(start_angle) * (rc.right-rc.left-width*2+2) / 2. + 0.5);
642 points[0].y = (int) floor(physDev->dc_rect.top + (rc.top+rc.bottom)/2.0 -
643 sin(start_angle) * (rc.bottom-rc.top-width*2+2) / 2. + 0.5);
644 points[1].x = (int) floor(physDev->dc_rect.left + (rc.right+rc.left)/2.0 +
645 cos(end_angle) * (rc.right-rc.left-width*2+2) / 2. + 0.5);
646 points[1].y = (int) floor(physDev->dc_rect.top + (rc.top+rc.bottom)/2.0 -
647 sin(end_angle) * (rc.bottom-rc.top-width*2+2) / 2. + 0.5);
649 /* OK, this stuff is optimized for Xfree86
650 * which is probably the server most used by
651 * wine users. Other X servers will not
652 * display correctly. (eXceed for instance)
653 * so if you feel you must make changes, make sure that
654 * you either use Xfree86 or separate your changes
655 * from these (compile switch or whatever)
657 if (lines == 2) {
658 INT dx1,dy1;
659 points[3] = points[1];
660 points[1].x = physDev->dc_rect.left + xcenter;
661 points[1].y = physDev->dc_rect.top + ycenter;
662 points[2] = points[1];
663 dx1=points[1].x-points[0].x;
664 dy1=points[1].y-points[0].y;
665 if(((rc.top-rc.bottom) | -2) == -2)
666 if(dy1>0) points[1].y--;
667 if(dx1<0) {
668 if (((-dx1)*64)<=ABS(dy1)*37) points[0].x--;
669 if(((-dx1*9))<(dy1*16)) points[0].y--;
670 if( dy1<0 && ((dx1*9)) < (dy1*16)) points[0].y--;
671 } else {
672 if(dy1 < 0) points[0].y--;
673 if(((rc.right-rc.left) | -2) == -2) points[1].x--;
675 dx1=points[3].x-points[2].x;
676 dy1=points[3].y-points[2].y;
677 if(((rc.top-rc.bottom) | -2 ) == -2)
678 if(dy1 < 0) points[2].y--;
679 if( dx1<0){
680 if( dy1>0) points[3].y--;
681 if(((rc.right-rc.left) | -2) == -2 ) points[2].x--;
682 }else {
683 points[3].y--;
684 if( dx1 * 64 < dy1 * -37 ) points[3].x--;
686 lines++;
688 XDrawLines( gdi_display, physDev->drawable, physDev->gc,
689 points, lines+1, CoordModeOrigin );
693 physDev->pen.width = oldwidth;
694 add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
695 return TRUE;
699 /***********************************************************************
700 * X11DRV_Arc
702 BOOL X11DRV_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
703 INT xstart, INT ystart, INT xend, INT yend )
705 return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 0 );
709 /***********************************************************************
710 * X11DRV_Pie
712 BOOL X11DRV_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
713 INT xstart, INT ystart, INT xend, INT yend )
715 return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 2 );
718 /***********************************************************************
719 * X11DRV_Chord
721 BOOL X11DRV_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
722 INT xstart, INT ystart, INT xend, INT yend )
724 return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 1 );
728 /***********************************************************************
729 * X11DRV_Ellipse
731 BOOL X11DRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
733 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
734 INT width, oldwidth;
735 RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
737 if ((rc.left == rc.right) || (rc.top == rc.bottom)) return TRUE;
739 oldwidth = width = physDev->pen.width;
740 if (!width) width = 1;
741 if(physDev->pen.style == PS_NULL) width = 0;
743 if (physDev->pen.style == PS_INSIDEFRAME)
745 if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
746 if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
747 rc.left += width / 2;
748 rc.right -= (width - 1) / 2;
749 rc.top += width / 2;
750 rc.bottom -= (width - 1) / 2;
752 if(width == 0) width = 1; /* more accurate */
753 physDev->pen.width = width;
755 if (X11DRV_SetupGCForBrush( physDev ))
756 XFillArc( gdi_display, physDev->drawable, physDev->gc,
757 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
758 rc.right-rc.left-1, rc.bottom-rc.top-1, 0, 360*64 );
760 if (X11DRV_SetupGCForPen( physDev ))
761 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
762 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
763 rc.right-rc.left-1, rc.bottom-rc.top-1, 0, 360*64 );
765 physDev->pen.width = oldwidth;
766 add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
767 return TRUE;
771 /***********************************************************************
772 * X11DRV_Rectangle
774 BOOL X11DRV_Rectangle(PHYSDEV dev, INT left, INT top, INT right, INT bottom)
776 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
777 INT width, oldwidth, oldjoinstyle;
778 RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
780 TRACE("(%d %d %d %d)\n", left, top, right, bottom);
782 if ((rc.left == rc.right) || (rc.top == rc.bottom)) return TRUE;
784 oldwidth = width = physDev->pen.width;
785 if (!width) width = 1;
786 if(physDev->pen.style == PS_NULL) width = 0;
788 if (physDev->pen.style == PS_INSIDEFRAME)
790 if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
791 if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
792 rc.left += width / 2;
793 rc.right -= (width - 1) / 2;
794 rc.top += width / 2;
795 rc.bottom -= (width - 1) / 2;
797 if(width == 1) width = 0;
798 physDev->pen.width = width;
799 oldjoinstyle = physDev->pen.linejoin;
800 if(physDev->pen.type != PS_GEOMETRIC)
801 physDev->pen.linejoin = PS_JOIN_MITER;
803 rc.right--;
804 rc.bottom--;
805 if ((rc.right >= rc.left + width) && (rc.bottom >= rc.top + width))
807 if (X11DRV_SetupGCForBrush( physDev ))
808 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
809 physDev->dc_rect.left + rc.left + (width + 1) / 2,
810 physDev->dc_rect.top + rc.top + (width + 1) / 2,
811 rc.right-rc.left-width, rc.bottom-rc.top-width);
813 if (X11DRV_SetupGCForPen( physDev ))
814 XDrawRectangle( gdi_display, physDev->drawable, physDev->gc,
815 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
816 rc.right-rc.left, rc.bottom-rc.top );
818 physDev->pen.width = oldwidth;
819 physDev->pen.linejoin = oldjoinstyle;
820 add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
821 return TRUE;
824 /***********************************************************************
825 * X11DRV_RoundRect
827 BOOL X11DRV_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
828 INT ell_width, INT ell_height )
830 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
831 INT width, oldwidth, oldendcap;
832 POINT pts[2];
833 RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
835 TRACE("(%d %d %d %d %d %d\n",
836 left, top, right, bottom, ell_width, ell_height);
838 if ((rc.left == rc.right) || (rc.top == rc.bottom))
839 return TRUE;
841 /* Make sure ell_width and ell_height are >= 1 otherwise XDrawArc gets
842 called with width/height < 0 */
843 pts[0].x = pts[0].y = 0;
844 pts[1].x = ell_width;
845 pts[1].y = ell_height;
846 LPtoDP(dev->hdc, pts, 2);
847 ell_width = max(abs( pts[1].x - pts[0].x ), 1);
848 ell_height = max(abs( pts[1].y - pts[0].y ), 1);
850 oldwidth = width = physDev->pen.width;
851 oldendcap = physDev->pen.endcap;
852 if (!width) width = 1;
853 if(physDev->pen.style == PS_NULL) width = 0;
855 if (physDev->pen.style == PS_INSIDEFRAME)
857 if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
858 if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
859 rc.left += width / 2;
860 rc.right -= (width - 1) / 2;
861 rc.top += width / 2;
862 rc.bottom -= (width - 1) / 2;
864 if(width == 0) width = 1;
865 physDev->pen.width = width;
866 physDev->pen.endcap = PS_ENDCAP_SQUARE;
868 if (X11DRV_SetupGCForBrush( physDev ))
870 if (ell_width > (rc.right-rc.left) )
871 if (ell_height > (rc.bottom-rc.top) )
872 XFillArc( gdi_display, physDev->drawable, physDev->gc,
873 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
874 rc.right - rc.left - 1, rc.bottom - rc.top - 1,
875 0, 360 * 64 );
876 else{
877 XFillArc( gdi_display, physDev->drawable, physDev->gc,
878 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
879 rc.right - rc.left - 1, ell_height, 0, 180 * 64 );
880 XFillArc( gdi_display, physDev->drawable, physDev->gc,
881 physDev->dc_rect.left + rc.left,
882 physDev->dc_rect.top + rc.bottom - ell_height - 1,
883 rc.right - rc.left - 1, ell_height, 180 * 64,
884 180 * 64 );
886 else if (ell_height > (rc.bottom-rc.top) ){
887 XFillArc( gdi_display, physDev->drawable, physDev->gc,
888 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
889 ell_width, rc.bottom - rc.top - 1, 90 * 64, 180 * 64 );
890 XFillArc( gdi_display, physDev->drawable, physDev->gc,
891 physDev->dc_rect.left + rc.right - ell_width - 1, physDev->dc_rect.top + rc.top,
892 ell_width, rc.bottom - rc.top - 1, 270 * 64, 180 * 64 );
893 }else{
894 XFillArc( gdi_display, physDev->drawable, physDev->gc,
895 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
896 ell_width, ell_height, 90 * 64, 90 * 64 );
897 XFillArc( gdi_display, physDev->drawable, physDev->gc,
898 physDev->dc_rect.left + rc.left,
899 physDev->dc_rect.top + rc.bottom - ell_height - 1,
900 ell_width, ell_height, 180 * 64, 90 * 64 );
901 XFillArc( gdi_display, physDev->drawable, physDev->gc,
902 physDev->dc_rect.left + rc.right - ell_width - 1,
903 physDev->dc_rect.top + rc.bottom - ell_height - 1,
904 ell_width, ell_height, 270 * 64, 90 * 64 );
905 XFillArc( gdi_display, physDev->drawable, physDev->gc,
906 physDev->dc_rect.left + rc.right - ell_width - 1,
907 physDev->dc_rect.top + rc.top,
908 ell_width, ell_height, 0, 90 * 64 );
910 if (ell_width < rc.right - rc.left)
912 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
913 physDev->dc_rect.left + rc.left + (ell_width + 1) / 2,
914 physDev->dc_rect.top + rc.top + 1,
915 rc.right - rc.left - ell_width - 1,
916 (ell_height + 1) / 2 - 1);
917 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
918 physDev->dc_rect.left + rc.left + (ell_width + 1) / 2,
919 physDev->dc_rect.top + rc.bottom - (ell_height) / 2 - 1,
920 rc.right - rc.left - ell_width - 1,
921 (ell_height) / 2 );
923 if (ell_height < rc.bottom - rc.top)
925 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
926 physDev->dc_rect.left + rc.left + 1,
927 physDev->dc_rect.top + rc.top + (ell_height + 1) / 2,
928 rc.right - rc.left - 2,
929 rc.bottom - rc.top - ell_height - 1);
932 /* FIXME: this could be done with on X call
933 * more efficient and probably more correct
934 * on any X server: XDrawArcs will draw
935 * straight horizontal and vertical lines
936 * if width or height are zero.
938 * BTW this stuff is optimized for an Xfree86 server
939 * read the comments inside the X11DRV_DrawArc function
941 if (X11DRV_SetupGCForPen( physDev ))
943 if (ell_width > (rc.right-rc.left) )
944 if (ell_height > (rc.bottom-rc.top) )
945 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
946 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
947 rc.right - rc.left - 1, rc.bottom - rc.top - 1, 0 , 360 * 64 );
948 else{
949 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
950 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
951 rc.right - rc.left - 1, ell_height - 1, 0 , 180 * 64 );
952 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
953 physDev->dc_rect.left + rc.left,
954 physDev->dc_rect.top + rc.bottom - ell_height,
955 rc.right - rc.left - 1, ell_height - 1, 180 * 64 , 180 * 64 );
957 else if (ell_height > (rc.bottom-rc.top) ){
958 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
959 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
960 ell_width - 1 , rc.bottom - rc.top - 1, 90 * 64 , 180 * 64 );
961 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
962 physDev->dc_rect.left + rc.right - ell_width,
963 physDev->dc_rect.top + rc.top,
964 ell_width - 1 , rc.bottom - rc.top - 1, 270 * 64 , 180 * 64 );
965 }else{
966 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
967 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
968 ell_width - 1, ell_height - 1, 90 * 64, 90 * 64 );
969 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
970 physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.bottom - ell_height,
971 ell_width - 1, ell_height - 1, 180 * 64, 90 * 64 );
972 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
973 physDev->dc_rect.left + rc.right - ell_width,
974 physDev->dc_rect.top + rc.bottom - ell_height,
975 ell_width - 1, ell_height - 1, 270 * 64, 90 * 64 );
976 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
977 physDev->dc_rect.left + rc.right - ell_width, physDev->dc_rect.top + rc.top,
978 ell_width - 1, ell_height - 1, 0, 90 * 64 );
980 if (ell_width < rc.right - rc.left)
982 XDrawLine( gdi_display, physDev->drawable, physDev->gc,
983 physDev->dc_rect.left + rc.left + ell_width / 2,
984 physDev->dc_rect.top + rc.top,
985 physDev->dc_rect.left + rc.right - (ell_width+1) / 2,
986 physDev->dc_rect.top + rc.top);
987 XDrawLine( gdi_display, physDev->drawable, physDev->gc,
988 physDev->dc_rect.left + rc.left + ell_width / 2 ,
989 physDev->dc_rect.top + rc.bottom - 1,
990 physDev->dc_rect.left + rc.right - (ell_width+1)/ 2,
991 physDev->dc_rect.top + rc.bottom - 1);
993 if (ell_height < rc.bottom - rc.top)
995 XDrawLine( gdi_display, physDev->drawable, physDev->gc,
996 physDev->dc_rect.left + rc.right - 1,
997 physDev->dc_rect.top + rc.top + ell_height / 2,
998 physDev->dc_rect.left + rc.right - 1,
999 physDev->dc_rect.top + rc.bottom - (ell_height+1) / 2);
1000 XDrawLine( gdi_display, physDev->drawable, physDev->gc,
1001 physDev->dc_rect.left + rc.left,
1002 physDev->dc_rect.top + rc.top + ell_height / 2,
1003 physDev->dc_rect.left + rc.left,
1004 physDev->dc_rect.top + rc.bottom - (ell_height+1) / 2);
1008 physDev->pen.width = oldwidth;
1009 physDev->pen.endcap = oldendcap;
1010 add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
1011 return TRUE;
1015 /***********************************************************************
1016 * X11DRV_SetPixel
1018 COLORREF X11DRV_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1020 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1021 unsigned long pixel;
1022 POINT pt;
1023 RECT rect;
1025 pt.x = x;
1026 pt.y = y;
1027 LPtoDP( dev->hdc, &pt, 1 );
1028 pixel = X11DRV_PALETTE_ToPhysical( physDev, color );
1030 XSetForeground( gdi_display, physDev->gc, pixel );
1031 XSetFunction( gdi_display, physDev->gc, GXcopy );
1032 XDrawPoint( gdi_display, physDev->drawable, physDev->gc,
1033 physDev->dc_rect.left + pt.x, physDev->dc_rect.top + pt.y );
1035 SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
1036 add_device_bounds( physDev, &rect );
1037 return X11DRV_PALETTE_ToLogical(physDev, pixel);
1041 /***********************************************************************
1042 * X11DRV_PaintRgn
1044 BOOL X11DRV_PaintRgn( PHYSDEV dev, HRGN hrgn )
1046 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1047 RECT rc;
1049 if (X11DRV_SetupGCForBrush( physDev ))
1051 unsigned int i;
1052 XRectangle *rect;
1053 RGNDATA *data = X11DRV_GetRegionData( hrgn, dev->hdc );
1055 if (!data) return FALSE;
1056 rect = (XRectangle *)data->Buffer;
1057 for (i = 0; i < data->rdh.nCount; i++)
1059 rect[i].x += physDev->dc_rect.left;
1060 rect[i].y += physDev->dc_rect.top;
1063 XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
1064 HeapFree( GetProcessHeap(), 0, data );
1066 if (GetRgnBox( hrgn, &rc ))
1068 LPtoDP( dev->hdc, (POINT *)&rc, 2 );
1069 add_device_bounds( physDev, &rc );
1071 return TRUE;
1074 /**********************************************************************
1075 * X11DRV_Polygon
1077 BOOL X11DRV_Polygon( PHYSDEV dev, const POINT* pt, INT count )
1079 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1080 int i;
1081 POINT *points;
1082 XPoint *xpoints;
1084 points = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pt) );
1085 if (!points) return FALSE;
1086 memcpy( points, pt, count * sizeof(*pt) );
1087 LPtoDP( dev->hdc, points, count );
1088 add_pen_device_bounds( physDev, points, count );
1090 if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * (count+1) )))
1092 HeapFree( GetProcessHeap(), 0, points );
1093 return FALSE;
1095 for (i = 0; i < count; i++)
1097 xpoints[i].x = physDev->dc_rect.left + points[i].x;
1098 xpoints[i].y = physDev->dc_rect.top + points[i].y;
1100 xpoints[count] = xpoints[0];
1102 if (X11DRV_SetupGCForBrush( physDev ))
1103 XFillPolygon( gdi_display, physDev->drawable, physDev->gc,
1104 xpoints, count+1, Complex, CoordModeOrigin);
1106 if (X11DRV_SetupGCForPen ( physDev ))
1107 XDrawLines( gdi_display, physDev->drawable, physDev->gc,
1108 xpoints, count+1, CoordModeOrigin );
1110 HeapFree( GetProcessHeap(), 0, xpoints );
1111 HeapFree( GetProcessHeap(), 0, points );
1112 return TRUE;
1116 /**********************************************************************
1117 * X11DRV_PolyPolygon
1119 BOOL X11DRV_PolyPolygon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polygons )
1121 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1122 DWORD total = 0, max = 0, pos, i;
1123 POINT *points;
1124 BOOL ret = FALSE;
1126 for (i = 0; i < polygons; i++)
1128 if (counts[i] < 2) return FALSE;
1129 if (counts[i] > max) max = counts[i];
1130 total += counts[i];
1133 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1134 if (!points) return FALSE;
1135 memcpy( points, pt, total * sizeof(*pt) );
1136 LPtoDP( dev->hdc, points, total );
1137 add_pen_device_bounds( physDev, points, total );
1139 if (X11DRV_SetupGCForBrush( physDev ))
1141 XRectangle *rect;
1142 HRGN hrgn = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ) );
1143 RGNDATA *data = X11DRV_GetRegionData( hrgn, 0 );
1145 DeleteObject( hrgn );
1146 if (!data) goto done;
1147 rect = (XRectangle *)data->Buffer;
1148 for (i = 0; i < data->rdh.nCount; i++)
1150 rect[i].x += physDev->dc_rect.left;
1151 rect[i].y += physDev->dc_rect.top;
1154 XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
1155 HeapFree( GetProcessHeap(), 0, data );
1158 if (X11DRV_SetupGCForPen ( physDev ))
1160 XPoint *xpoints;
1161 int j;
1163 if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * (max + 1) ))) goto done;
1164 for (i = pos = 0; i < polygons; pos += counts[i++])
1166 for (j = 0; j < counts[i]; j++)
1168 xpoints[j].x = physDev->dc_rect.left + points[pos + j].x;
1169 xpoints[j].y = physDev->dc_rect.top + points[pos + j].y;
1171 xpoints[j] = xpoints[0];
1172 XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j + 1, CoordModeOrigin );
1174 HeapFree( GetProcessHeap(), 0, xpoints );
1176 ret = TRUE;
1178 done:
1179 HeapFree( GetProcessHeap(), 0, points );
1180 return ret;
1184 /**********************************************************************
1185 * X11DRV_PolyPolyline
1187 BOOL X11DRV_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1189 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1190 DWORD total = 0, max = 0, pos, i, j;
1191 POINT *points;
1193 for (i = 0; i < polylines; i++)
1195 if (counts[i] < 2) return FALSE;
1196 if (counts[i] > max) max = counts[i];
1197 total += counts[i];
1200 points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1201 if (!points) return FALSE;
1202 memcpy( points, pt, total * sizeof(*pt) );
1203 LPtoDP( dev->hdc, points, total );
1204 add_pen_device_bounds( physDev, points, total );
1206 if (X11DRV_SetupGCForPen ( physDev ))
1208 XPoint *xpoints;
1210 if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * max )))
1212 HeapFree( GetProcessHeap(), 0, points );
1213 return FALSE;
1215 for (i = pos = 0; i < polylines; pos += counts[i++])
1217 for (j = 0; j < counts[i]; j++)
1219 xpoints[j].x = physDev->dc_rect.left + points[pos + j].x;
1220 xpoints[j].y = physDev->dc_rect.top + points[pos + j].y;
1222 XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j, CoordModeOrigin );
1224 HeapFree( GetProcessHeap(), 0, xpoints );
1226 HeapFree( GetProcessHeap(), 0, points );
1227 return TRUE;
1230 /* helper for path stroking and filling functions */
1231 static BOOL x11drv_stroke_and_fill_path( PHYSDEV dev, BOOL stroke, BOOL fill )
1233 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1234 POINT *points;
1235 BYTE *flags;
1236 BOOL ret = FALSE;
1237 XPoint *xpoints;
1238 int i, j, size;
1240 FlattenPath( dev->hdc );
1241 if ((size = GetPath( dev->hdc, NULL, NULL, 0 )) == -1) return FALSE;
1242 if (!size)
1244 AbortPath( dev->hdc );
1245 return TRUE;
1247 xpoints = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(*xpoints) );
1248 points = HeapAlloc( GetProcessHeap(), 0, size * sizeof(*points) );
1249 flags = HeapAlloc( GetProcessHeap(), 0, size * sizeof(*flags) );
1250 if (!points || !flags || !xpoints) goto done;
1251 if (GetPath( dev->hdc, points, flags, size ) == -1) goto done;
1252 LPtoDP( dev->hdc, points, size );
1254 if (fill && X11DRV_SetupGCForBrush( physDev ))
1256 XRectangle *rect;
1257 HRGN hrgn = PathToRegion( dev->hdc );
1258 RGNDATA *data = X11DRV_GetRegionData( hrgn, 0 );
1260 DeleteObject( hrgn );
1261 if (!data) goto done;
1262 rect = (XRectangle *)data->Buffer;
1263 for (i = 0; i < data->rdh.nCount; i++)
1265 rect[i].x += physDev->dc_rect.left;
1266 rect[i].y += physDev->dc_rect.top;
1269 XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
1270 HeapFree( GetProcessHeap(), 0, data );
1273 if (stroke && X11DRV_SetupGCForPen ( physDev ))
1275 for (i = j = 0; i < size; i++, j++)
1277 if (flags[i] == PT_MOVETO)
1279 if (j > 1)
1281 if (fill || (flags[i - 1] & PT_CLOSEFIGURE)) xpoints[j++] = xpoints[0];
1282 XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j, CoordModeOrigin );
1284 j = 0;
1286 xpoints[j].x = physDev->dc_rect.left + points[i].x;
1287 xpoints[j].y = physDev->dc_rect.top + points[i].y;
1289 if (j > 1)
1291 if (fill || (flags[i - 1] & PT_CLOSEFIGURE)) xpoints[j++] = xpoints[0];
1292 XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j, CoordModeOrigin );
1296 add_pen_device_bounds( physDev, points, size );
1297 AbortPath( dev->hdc );
1298 ret = TRUE;
1300 done:
1301 HeapFree( GetProcessHeap(), 0, xpoints );
1302 HeapFree( GetProcessHeap(), 0, points );
1303 HeapFree( GetProcessHeap(), 0, flags );
1304 return ret;
1307 /**********************************************************************
1308 * X11DRV_FillPath
1310 BOOL X11DRV_FillPath( PHYSDEV dev )
1312 return x11drv_stroke_and_fill_path( dev, FALSE, TRUE );
1315 /**********************************************************************
1316 * X11DRV_StrokeAndFillPath
1318 BOOL X11DRV_StrokeAndFillPath( PHYSDEV dev )
1320 return x11drv_stroke_and_fill_path( dev, TRUE, TRUE );
1323 /**********************************************************************
1324 * X11DRV_StrokePath
1326 BOOL X11DRV_StrokePath( PHYSDEV dev )
1328 return x11drv_stroke_and_fill_path( dev, TRUE, FALSE );
1332 /**********************************************************************
1333 * X11DRV_InternalFloodFill
1335 * Internal helper function for flood fill.
1336 * (xorg,yorg) is the origin of the X image relative to the drawable.
1337 * (x,y) is relative to the origin of the X image.
1339 static void X11DRV_InternalFloodFill(XImage *image, X11DRV_PDEVICE *physDev,
1340 int x, int y,
1341 int xOrg, int yOrg,
1342 unsigned long pixel, WORD fillType, RECT *bounds )
1344 int left, right;
1346 #define TO_FLOOD(x,y) ((fillType == FLOODFILLBORDER) ? \
1347 (XGetPixel(image,x,y) != pixel) : \
1348 (XGetPixel(image,x,y) == pixel))
1350 if (!TO_FLOOD(x,y)) return;
1352 /* Find left and right boundaries */
1354 left = right = x;
1355 while ((left > 0) && TO_FLOOD( left-1, y )) left--;
1356 while ((right < image->width) && TO_FLOOD( right, y )) right++;
1357 bounds->left = min( bounds->left, left );
1358 bounds->top = min( bounds->top, y );
1359 bounds->right = max( bounds->right, right );
1360 bounds->bottom = max( bounds->bottom, y + 1 );
1361 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1362 xOrg + left, yOrg + y, right-left, 1 );
1364 /* Set the pixels of this line so we don't fill it again */
1366 for (x = left; x < right; x++)
1368 if (fillType == FLOODFILLBORDER) XPutPixel( image, x, y, pixel );
1369 else XPutPixel( image, x, y, ~pixel );
1372 /* Fill the line above */
1374 if (--y >= 0)
1376 x = left;
1377 while (x < right)
1379 while ((x < right) && !TO_FLOOD(x,y)) x++;
1380 if (x >= right) break;
1381 while ((x < right) && TO_FLOOD(x,y)) x++;
1382 X11DRV_InternalFloodFill(image, physDev, x-1, y,
1383 xOrg, yOrg, pixel, fillType, bounds );
1387 /* Fill the line below */
1389 if ((y += 2) < image->height)
1391 x = left;
1392 while (x < right)
1394 while ((x < right) && !TO_FLOOD(x,y)) x++;
1395 if (x >= right) break;
1396 while ((x < right) && TO_FLOOD(x,y)) x++;
1397 X11DRV_InternalFloodFill(image, physDev, x-1, y,
1398 xOrg, yOrg, pixel, fillType, bounds );
1401 #undef TO_FLOOD
1405 static int ExtFloodFillXGetImageErrorHandler( Display *dpy, XErrorEvent *event, void *arg )
1407 return (event->request_code == X_GetImage && event->error_code == BadMatch);
1410 /**********************************************************************
1411 * X11DRV_ExtFloodFill
1413 BOOL X11DRV_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT fillType )
1415 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1416 XImage *image;
1417 RECT rect, bounds;
1418 POINT pt;
1420 TRACE("X11DRV_ExtFloodFill %d,%d %06x %d\n", x, y, color, fillType );
1422 pt.x = x;
1423 pt.y = y;
1424 LPtoDP( dev->hdc, &pt, 1 );
1426 if (!physDev->region)
1428 rect.left = 0;
1429 rect.top = 0;
1430 rect.right = physDev->dc_rect.right - physDev->dc_rect.left;
1431 rect.bottom = physDev->dc_rect.bottom - physDev->dc_rect.top;
1433 else
1435 if (!PtInRegion( physDev->region, pt.x, pt.y )) return FALSE;
1436 GetRgnBox( physDev->region, &rect );
1437 rect.left = max( rect.left, 0 );
1438 rect.top = max( rect.top, 0 );
1439 rect.right = min( rect.right, physDev->dc_rect.right - physDev->dc_rect.left );
1440 rect.bottom = min( rect.bottom, physDev->dc_rect.bottom - physDev->dc_rect.top );
1442 if (pt.x < rect.left || pt.x >= rect.right || pt.y < rect.top || pt.y >= rect.bottom) return FALSE;
1444 X11DRV_expect_error( gdi_display, ExtFloodFillXGetImageErrorHandler, NULL );
1445 image = XGetImage( gdi_display, physDev->drawable,
1446 physDev->dc_rect.left + rect.left, physDev->dc_rect.top + rect.top,
1447 rect.right - rect.left, rect.bottom - rect.top,
1448 AllPlanes, ZPixmap );
1449 if(X11DRV_check_error()) image = NULL;
1450 if (!image) return FALSE;
1452 if (X11DRV_SetupGCForBrush( physDev ))
1454 unsigned long pixel = X11DRV_PALETTE_ToPhysical( physDev, color );
1456 reset_bounds( &bounds );
1458 X11DRV_InternalFloodFill(image, physDev,
1459 pt.x - rect.left,
1460 pt.y - rect.top,
1461 physDev->dc_rect.left + rect.left,
1462 physDev->dc_rect.top + rect.top,
1463 pixel, fillType, &bounds );
1465 OffsetRect( &bounds, rect.left, rect.top );
1466 add_device_bounds( physDev, &bounds );
1469 XDestroyImage( image );
1470 return TRUE;
1473 /**********************************************************************
1474 * X11DRV_GradientFill
1476 BOOL X11DRV_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
1477 void *grad_array, ULONG ngrad, ULONG mode )
1479 X11DRV_PDEVICE *physdev = get_x11drv_dev( dev );
1480 const GRADIENT_RECT *rect = grad_array;
1481 TRIVERTEX v[2];
1482 POINT pt[2];
1483 RECT rc, bounds;
1484 unsigned int i;
1485 XGCValues val;
1487 /* <= 16-bpp use dithering */
1488 if (physdev->depth <= 16) goto fallback;
1490 switch (mode)
1492 case GRADIENT_FILL_RECT_H:
1493 val.function = GXcopy;
1494 val.fill_style = FillSolid;
1495 val.line_width = 1;
1496 val.cap_style = CapNotLast;
1497 val.line_style = LineSolid;
1498 XChangeGC( gdi_display, physdev->gc,
1499 GCFunction | GCLineWidth | GCLineStyle | GCCapStyle | GCFillStyle, &val );
1500 reset_bounds( &bounds );
1502 for (i = 0; i < ngrad; i++, rect++)
1504 int x, dx;
1506 v[0] = vert_array[rect->UpperLeft];
1507 v[1] = vert_array[rect->LowerRight];
1508 pt[0].x = v[0].x;
1509 pt[0].y = v[0].y;
1510 pt[1].x = v[1].x;
1511 pt[1].y = v[1].y;
1512 LPtoDP( dev->hdc, pt, 2 );
1513 dx = pt[1].x - pt[0].x;
1514 if (!dx) continue;
1515 if (dx < 0) /* swap the colors */
1517 v[0] = vert_array[rect->LowerRight];
1518 v[1] = vert_array[rect->UpperLeft];
1519 dx = -dx;
1521 rc.left = min( pt[0].x, pt[1].x );
1522 rc.top = min( pt[0].y, pt[1].y );
1523 rc.right = max( pt[0].x, pt[1].x );
1524 rc.bottom = max( pt[0].y, pt[1].y );
1525 add_bounds_rect( &bounds, &rc );
1526 for (x = 0; x < dx; x++)
1528 int color = X11DRV_PALETTE_ToPhysical( physdev,
1529 RGB( (v[0].Red * (dx - x) + v[1].Red * x) / dx / 256,
1530 (v[0].Green * (dx - x) + v[1].Green * x) / dx / 256,
1531 (v[0].Blue * (dx - x) + v[1].Blue * x) / dx / 256) );
1533 XSetForeground( gdi_display, physdev->gc, color );
1534 XDrawLine( gdi_display, physdev->drawable, physdev->gc,
1535 physdev->dc_rect.left + rc.left + x, physdev->dc_rect.top + rc.top,
1536 physdev->dc_rect.left + rc.left + x, physdev->dc_rect.top + rc.bottom );
1539 add_device_bounds( physdev, &bounds );
1540 return TRUE;
1542 case GRADIENT_FILL_RECT_V:
1543 val.function = GXcopy;
1544 val.fill_style = FillSolid;
1545 val.line_width = 1;
1546 val.cap_style = CapNotLast;
1547 val.line_style = LineSolid;
1548 XChangeGC( gdi_display, physdev->gc,
1549 GCFunction | GCLineWidth | GCLineStyle | GCCapStyle | GCFillStyle, &val );
1550 reset_bounds( &bounds );
1552 for (i = 0; i < ngrad; i++, rect++)
1554 int y, dy;
1556 v[0] = vert_array[rect->UpperLeft];
1557 v[1] = vert_array[rect->LowerRight];
1558 pt[0].x = v[0].x;
1559 pt[0].y = v[0].y;
1560 pt[1].x = v[1].x;
1561 pt[1].y = v[1].y;
1562 LPtoDP( dev->hdc, pt, 2 );
1563 dy = pt[1].y - pt[0].y;
1564 if (!dy) continue;
1565 if (dy < 0) /* swap the colors */
1567 v[0] = vert_array[rect->LowerRight];
1568 v[1] = vert_array[rect->UpperLeft];
1569 dy = -dy;
1571 rc.left = min( pt[0].x, pt[1].x );
1572 rc.top = min( pt[0].y, pt[1].y );
1573 rc.right = max( pt[0].x, pt[1].x );
1574 rc.bottom = max( pt[0].y, pt[1].y );
1575 add_bounds_rect( &bounds, &rc );
1576 for (y = 0; y < dy; y++)
1578 int color = X11DRV_PALETTE_ToPhysical( physdev,
1579 RGB( (v[0].Red * (dy - y) + v[1].Red * y) / dy / 256,
1580 (v[0].Green * (dy - y) + v[1].Green * y) / dy / 256,
1581 (v[0].Blue * (dy - y) + v[1].Blue * y) / dy / 256) );
1583 XSetForeground( gdi_display, physdev->gc, color );
1584 XDrawLine( gdi_display, physdev->drawable, physdev->gc,
1585 physdev->dc_rect.left + rc.left, physdev->dc_rect.top + rc.top + y,
1586 physdev->dc_rect.left + rc.right, physdev->dc_rect.top + rc.top + y );
1589 add_device_bounds( physdev, &bounds );
1590 return TRUE;
1593 fallback:
1594 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
1595 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
1598 static unsigned char *get_icm_profile( unsigned long *size )
1600 Atom type;
1601 int format;
1602 unsigned long count, remaining;
1603 unsigned char *profile, *ret = NULL;
1605 XGetWindowProperty( gdi_display, DefaultRootWindow(gdi_display),
1606 x11drv_atom(_ICC_PROFILE), 0, ~0UL, False, AnyPropertyType,
1607 &type, &format, &count, &remaining, &profile );
1608 *size = get_property_size( format, count );
1609 if (format && count)
1611 if ((ret = HeapAlloc( GetProcessHeap(), 0, *size ))) memcpy( ret, profile, *size );
1612 XFree( profile );
1614 return ret;
1617 typedef struct
1619 unsigned int unknown[6];
1620 unsigned int state[5];
1621 unsigned int count[2];
1622 unsigned char buffer[64];
1623 } sha_ctx;
1625 extern void WINAPI A_SHAInit( sha_ctx * );
1626 extern void WINAPI A_SHAUpdate( sha_ctx *, const unsigned char *, unsigned int );
1627 extern void WINAPI A_SHAFinal( sha_ctx *, unsigned char * );
1629 static const WCHAR mntr_key[] =
1630 {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
1631 'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t',
1632 'V','e','r','s','i','o','n','\\','I','C','M','\\','m','n','t','r',0};
1634 static const WCHAR color_path[] =
1635 {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\','c','o','l','o','r','\\',0};
1637 /***********************************************************************
1638 * GetICMProfile (X11DRV.@)
1640 BOOL X11DRV_GetICMProfile( PHYSDEV dev, LPDWORD size, LPWSTR filename )
1642 static const WCHAR srgb[] =
1643 {'s','R','G','B',' ','C','o','l','o','r',' ','S','p','a','c','e',' ',
1644 'P','r','o','f','i','l','e','.','i','c','m',0};
1645 HKEY hkey;
1646 DWORD required, len;
1647 WCHAR profile[MAX_PATH], fullname[2*MAX_PATH + sizeof(color_path)/sizeof(WCHAR)];
1648 unsigned char *buffer;
1649 unsigned long buflen;
1651 if (!size) return FALSE;
1653 GetSystemDirectoryW( fullname, MAX_PATH );
1654 strcatW( fullname, color_path );
1656 len = sizeof(profile)/sizeof(WCHAR);
1657 if (!RegCreateKeyExW( HKEY_LOCAL_MACHINE, mntr_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ) &&
1658 !RegEnumValueW( hkey, 0, profile, &len, NULL, NULL, NULL, NULL )) /* FIXME handle multiple values */
1660 strcatW( fullname, profile );
1661 RegCloseKey( hkey );
1663 else if ((buffer = get_icm_profile( &buflen )))
1665 static const WCHAR fmt[] = {'%','0','2','x',0};
1666 static const WCHAR icm[] = {'.','i','c','m',0};
1668 unsigned char sha1sum[20];
1669 unsigned int i;
1670 sha_ctx ctx;
1671 HANDLE file;
1673 A_SHAInit( &ctx );
1674 A_SHAUpdate( &ctx, buffer, buflen );
1675 A_SHAFinal( &ctx, sha1sum );
1677 for (i = 0; i < sizeof(sha1sum); i++) sprintfW( &profile[i * 2], fmt, sha1sum[i] );
1678 memcpy( &profile[i * 2], icm, sizeof(icm) );
1680 strcatW( fullname, profile );
1681 file = CreateFileW( fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0 );
1682 if (file != INVALID_HANDLE_VALUE)
1684 DWORD written;
1686 if (!WriteFile( file, buffer, buflen, &written, NULL ) || written != buflen)
1687 ERR( "Unable to write color profile\n" );
1688 CloseHandle( file );
1690 HeapFree( GetProcessHeap(), 0, buffer );
1692 else strcatW( fullname, srgb );
1694 required = strlenW( fullname ) + 1;
1695 if (*size < required)
1697 *size = required;
1698 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1699 return FALSE;
1701 if (filename)
1703 strcpyW( filename, fullname );
1704 if (GetFileAttributesW( filename ) == INVALID_FILE_ATTRIBUTES)
1705 WARN( "color profile not found\n" );
1707 *size = required;
1708 return TRUE;
1711 /***********************************************************************
1712 * EnumICMProfiles (X11DRV.@)
1714 INT X11DRV_EnumICMProfiles( PHYSDEV dev, ICMENUMPROCW proc, LPARAM lparam )
1716 X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1717 HKEY hkey;
1718 DWORD len_sysdir, len_path, len, index = 0;
1719 WCHAR sysdir[MAX_PATH], *profile;
1720 LONG res;
1721 INT ret;
1723 TRACE("%p, %p, %ld\n", physDev, proc, lparam);
1725 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, mntr_key, 0, KEY_ALL_ACCESS, &hkey ))
1726 return -1;
1728 len_sysdir = GetSystemDirectoryW( sysdir, MAX_PATH );
1729 len_path = len_sysdir + sizeof(color_path) / sizeof(color_path[0]) - 1;
1730 len = 64;
1731 for (;;)
1733 if (!(profile = HeapAlloc( GetProcessHeap(), 0, (len_path + len) * sizeof(WCHAR) )))
1735 RegCloseKey( hkey );
1736 return -1;
1738 res = RegEnumValueW( hkey, index, profile + len_path, &len, NULL, NULL, NULL, NULL );
1739 while (res == ERROR_MORE_DATA)
1741 len *= 2;
1742 HeapFree( GetProcessHeap(), 0, profile );
1743 if (!(profile = HeapAlloc( GetProcessHeap(), 0, (len_path + len) * sizeof(WCHAR) )))
1745 RegCloseKey( hkey );
1746 return -1;
1748 res = RegEnumValueW( hkey, index, profile + len_path, &len, NULL, NULL, NULL, NULL );
1750 if (res != ERROR_SUCCESS)
1752 HeapFree( GetProcessHeap(), 0, profile );
1753 break;
1755 memcpy( profile, sysdir, len_sysdir * sizeof(WCHAR) );
1756 memcpy( profile + len_sysdir, color_path, sizeof(color_path) - sizeof(WCHAR) );
1757 ret = proc( profile, lparam );
1758 HeapFree( GetProcessHeap(), 0, profile );
1759 if (!ret) break;
1760 index++;
1762 RegCloseKey( hkey );
1763 return -1;