graphics.c 57.7 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 * X11 graphics driver graphics functions
 *
 * Copyright 1993,1994 Alexandre Julliard
5
 * Copyright 1998 Huw Davies
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
22
/*
23
 * FIXME: only some of these functions obey the GM_ADVANCED
Alexandre Julliard's avatar
Alexandre Julliard committed
24 25 26
 * graphics mode
 */

Patrik Stridvall's avatar
Patrik Stridvall committed
27 28
#include "config.h"

29
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
30
#include <math.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
31 32
#ifdef HAVE_FLOAT_H
# include <float.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
33
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
34 35 36 37 38
#include <stdlib.h>
#ifndef PI
#define PI M_PI
#endif
#include <string.h>
39
#include <limits.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
40

41 42 43 44
#include "windef.h"
#include "winbase.h"
#include "winreg.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
45
#include "x11drv.h"
46
#include "wine/debug.h"
47
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
48

49
WINE_DEFAULT_DEBUG_CHANNEL(graphics);
50

Alexandre Julliard's avatar
Alexandre Julliard committed
51
#define ABS(x)    ((x)<0?(-(x)):(x))
52 53

  /* ROP code to GC function conversion */
54
static const int X11DRV_XROPfunction[16] =
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
{
    GXclear,        /* R2_BLACK */
    GXnor,          /* R2_NOTMERGEPEN */
    GXandInverted,  /* R2_MASKNOTPEN */
    GXcopyInverted, /* R2_NOTCOPYPEN */
    GXandReverse,   /* R2_MASKPENNOT */
    GXinvert,       /* R2_NOT */
    GXxor,          /* R2_XORPEN */
    GXnand,         /* R2_NOTMASKPEN */
    GXand,          /* R2_MASKPEN */
    GXequiv,        /* R2_NOTXORPEN */
    GXnoop,         /* R2_NOP */
    GXorInverted,   /* R2_MERGENOTPEN */
    GXcopy,         /* R2_COPYPEN */
    GXorReverse,    /* R2_MERGEPENNOT */
    GXor,           /* R2_MERGEPEN */
    GXset           /* R2_WHITE */
};


75 76 77 78 79 80 81 82 83 84 85
/* get the rectangle in device coordinates, with optional mirroring */
static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom )
{
    RECT rect;

    rect.left   = left;
    rect.top    = top;
    rect.right  = right;
    rect.bottom = bottom;
    if (GetLayout( hdc ) & LAYOUT_RTL)
    {
86 87 88 89
        /* shift the rectangle so that the right border is included after mirroring */
        /* it would be more correct to do this after LPtoDP but that's not what Windows does */
        rect.left--;
        rect.right--;
90
    }
91
    LPtoDP( hdc, (POINT *)&rect, 2 );
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    if (rect.left > rect.right)
    {
        int tmp = rect.left;
        rect.left = rect.right;
        rect.right = tmp;
    }
    if (rect.top > rect.bottom)
    {
        int tmp = rect.top;
        rect.top = rect.bottom;
        rect.bottom = tmp;
    }
    return rect;
}

107 108 109 110 111
static void add_pen_device_bounds( X11DRV_PDEVICE *dev, const POINT *points, int count )
{
    RECT bounds, rect;
    int width = 0;

112
    if (!dev->bounds) return;
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    reset_bounds( &bounds );

    if (dev->pen.type & PS_GEOMETRIC || dev->pen.width > 1)
    {
        /* Windows uses some heuristics to estimate the distance from the point that will be painted */
        width = dev->pen.width + 2;
        if (dev->pen.linejoin == PS_JOIN_MITER)
        {
            width *= 5;
            if (dev->pen.endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
        }
        else
        {
            if (dev->pen.endcap == PS_ENDCAP_SQUARE) width -= width / 4;
            else width = (width + 1) / 2;
        }
    }

    while (count-- > 0)
    {
        rect.left   = points->x - width;
        rect.top    = points->y - width;
        rect.right  = points->x + width + 1;
        rect.bottom = points->y + width + 1;
        add_bounds_rect( &bounds, &rect );
        points++;
    }

    add_device_bounds( dev, &bounds );
}

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
/***********************************************************************
 *           X11DRV_GetRegionData
 *
 * Calls GetRegionData on the given region and converts the rectangle
 * array to XRectangle format. The returned buffer must be freed by
 * caller using HeapFree(GetProcessHeap(),...).
 * If hdc_lptodp is not 0, the rectangles are converted through LPtoDP.
 */
RGNDATA *X11DRV_GetRegionData( HRGN hrgn, HDC hdc_lptodp )
{
    RGNDATA *data;
    DWORD size;
    unsigned int i;
    RECT *rect, tmp;
    XRectangle *xrect;

    if (!(size = GetRegionData( hrgn, 0, NULL ))) return NULL;
    if (sizeof(XRectangle) > sizeof(RECT))
    {
        /* add extra size for XRectangle array */
        int count = (size - sizeof(RGNDATAHEADER)) / sizeof(RECT);
        size += count * (sizeof(XRectangle) - sizeof(RECT));
    }
    if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
    if (!GetRegionData( hrgn, size, data ))
    {
        HeapFree( GetProcessHeap(), 0, data );
        return NULL;
    }

    rect = (RECT *)data->Buffer;
    xrect = (XRectangle *)data->Buffer;
    if (hdc_lptodp)  /* map to device coordinates */
    {
        LPtoDP( hdc_lptodp, (POINT *)rect, data->rdh.nCount * 2 );
        for (i = 0; i < data->rdh.nCount; i++)
        {
            if (rect[i].right < rect[i].left)
            {
                INT tmp = rect[i].right;
                rect[i].right = rect[i].left;
                rect[i].left = tmp;
            }
            if (rect[i].bottom < rect[i].top)
            {
                INT tmp = rect[i].bottom;
                rect[i].bottom = rect[i].top;
                rect[i].top = tmp;
            }
        }
    }

    if (sizeof(XRectangle) > sizeof(RECT))
    {
        int j;
        /* need to start from the end */
200
        xrect += data->rdh.nCount;
201 202 203
        for (j = data->rdh.nCount-1; j >= 0; j--)
        {
            tmp = rect[j];
204 205 206 207 208 209 210 211 212 213 214 215 216 217
            if (tmp.left > SHRT_MAX) continue;
            if (tmp.top > SHRT_MAX) continue;
            if (tmp.right < SHRT_MIN) continue;
            if (tmp.bottom < SHRT_MIN) continue;
            xrect--;
            xrect->x      = max( min( tmp.left, SHRT_MAX), SHRT_MIN);
            xrect->y      = max( min( tmp.top, SHRT_MAX), SHRT_MIN);
            xrect->width  = max( min( tmp.right, SHRT_MAX ) - xrect->x, 0);
            xrect->height = max( min( tmp.bottom, SHRT_MAX ) - xrect->y, 0);
        }
        if (xrect > (XRectangle *)data->Buffer)
        {
            data->rdh.nCount -= xrect - (XRectangle *)data->Buffer;
            memmove( (XRectangle *)data->Buffer, xrect, data->rdh.nCount * sizeof(*xrect) );
218 219 220 221 222 223 224
        }
    }
    else
    {
        for (i = 0; i < data->rdh.nCount; i++)
        {
            tmp = rect[i];
225 226 227 228 229 230 231 232 233
            if (tmp.left > SHRT_MAX) continue;
            if (tmp.top > SHRT_MAX) continue;
            if (tmp.right < SHRT_MIN) continue;
            if (tmp.bottom < SHRT_MIN) continue;
            xrect->x      = max( min( tmp.left, SHRT_MAX), SHRT_MIN);
            xrect->y      = max( min( tmp.top, SHRT_MAX), SHRT_MIN);
            xrect->width  = max( min( tmp.right, SHRT_MAX ) - xrect->x, 0);
            xrect->height = max( min( tmp.bottom, SHRT_MAX ) - xrect->y, 0);
            xrect++;
234
        }
235
        data->rdh.nCount = xrect - (XRectangle *)data->Buffer;
236 237 238 239 240
    }
    return data;
}


241 242 243
/***********************************************************************
 *           update_x11_clipping
 */
244
static void update_x11_clipping( X11DRV_PDEVICE *physDev, HRGN rgn )
245
{
246 247 248 249 250 251 252 253 254 255 256 257
    RGNDATA *data;

    if (!rgn)
    {
        XSetClipMask( gdi_display, physDev->gc, None );
    }
    else if ((data = X11DRV_GetRegionData( rgn, 0 )))
    {
        XSetClipRectangles( gdi_display, physDev->gc, physDev->dc_rect.left, physDev->dc_rect.top,
                            (XRectangle *)data->Buffer, data->rdh.nCount, YXBanded );
        HeapFree( GetProcessHeap(), 0, data );
    }
258 259
}

260

261 262 263 264
/***********************************************************************
 *           add_extra_clipping_region
 *
 * Temporarily add a region to the current clipping region.
265
 * The region must be restored with restore_clipping_region.
266
 */
267
BOOL add_extra_clipping_region( X11DRV_PDEVICE *dev, HRGN rgn )
268
{
269
    HRGN clip;
270

271 272
    if (!rgn) return FALSE;
    if (dev->region)
273
    {
274 275 276 277
        if (!(clip = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
        CombineRgn( clip, dev->region, rgn, RGN_AND );
        update_x11_clipping( dev, clip );
        DeleteObject( clip );
278
    }
279 280
    else update_x11_clipping( dev, rgn );
    return TRUE;
281 282 283 284 285 286
}


/***********************************************************************
 *           restore_clipping_region
 */
287
void restore_clipping_region( X11DRV_PDEVICE *dev )
288
{
289
    update_x11_clipping( dev, dev->region );
290 291 292 293 294 295
}


/***********************************************************************
 *           X11DRV_SetDeviceClipping
 */
296
void X11DRV_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
297 298 299
{
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );

300
    physDev->region = rgn;
301
    update_x11_clipping( physDev, rgn );
302 303
}

304

305 306 307 308 309 310 311
/***********************************************************************
 *           X11DRV_SetupGCForPatBlt
 *
 * Setup the GC for a PatBlt operation using current brush.
 * If fMapColors is TRUE, X pixels are mapped to Windows colors.
 * Return FALSE if brush is BS_NULL, TRUE otherwise.
 */
312
BOOL X11DRV_SetupGCForPatBlt( X11DRV_PDEVICE *physDev, GC gc, BOOL fMapColors )
313 314 315 316
{
    XGCValues val;
    unsigned long mask;
    Pixmap pixmap = 0;
317
    POINT pt;
318

319 320
    if (physDev->brush.style == BS_NULL) return FALSE;
    if (physDev->brush.pixel == -1)
321 322 323 324 325
    {
	/* Special case used for monochrome pattern brushes.
	 * We need to swap foreground and background because
	 * Windows does it the wrong way...
	 */
326 327
	val.foreground = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
	val.background = X11DRV_PALETTE_ToPhysical( physDev, GetTextColor(physDev->dev.hdc) );
328 329 330
    }
    else
    {
331
	val.foreground = physDev->brush.pixel;
332
	val.background = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
333
    }
334
    if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
335
    {
336 337
        val.foreground = X11DRV_PALETTE_XPixelToPalette[val.foreground];
        val.background = X11DRV_PALETTE_XPixelToPalette[val.background];
338 339
    }

340
    val.function = X11DRV_XROPfunction[GetROP2(physDev->dev.hdc)-1];
341 342 343 344 345 346
    /*
    ** Let's replace GXinvert by GXxor with (black xor white)
    ** This solves the selection color and leak problems in excel
    ** FIXME : Let's do that only if we work with X-pixels, not with Win-pixels
    */
    if (val.function == GXinvert)
347 348 349
    {
        val.foreground = (WhitePixel( gdi_display, DefaultScreen(gdi_display) ) ^
                          BlackPixel( gdi_display, DefaultScreen(gdi_display) ));
350
	val.function = GXxor;
351
    }
352
    val.fill_style = physDev->brush.fillStyle;
353 354 355 356
    switch(val.fill_style)
    {
    case FillStippled:
    case FillOpaqueStippled:
357
	if (GetBkMode(physDev->dev.hdc)==OPAQUE) val.fill_style = FillOpaqueStippled;
358
	val.stipple = physDev->brush.pixmap;
359 360 361 362
	mask = GCStipple;
        break;

    case FillTiled:
363
        if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
364 365 366
        {
            register int x, y;
            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 370 371 372
                               AllPlanes, ZPixmap );
            for (y = 0; y < 8; y++)
                for (x = 0; x < 8; x++)
                    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 376 377
            XDestroyImage( image );
            val.tile = pixmap;
        }
378
        else val.tile = physDev->brush.pixmap;
379 380 381 382 383 384 385
	mask = GCTile;
        break;

    default:
        mask = 0;
        break;
    }
386
    GetBrushOrgEx( physDev->dev.hdc, &pt );
387 388
    val.ts_x_origin = physDev->dc_rect.left + pt.x;
    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 392 393
	       GCFunction | GCForeground | GCBackground | GCFillStyle |
	       GCFillRule | GCTileStipXOrigin | GCTileStipYOrigin | mask,
	       &val );
394
    if (pixmap) XFreePixmap( gdi_display, pixmap );
395 396 397 398 399 400 401
    return TRUE;
}


/***********************************************************************
 *           X11DRV_SetupGCForBrush
 *
402
 * Setup physDev->gc for drawing operations using current brush.
403 404
 * Return FALSE if brush is BS_NULL, TRUE otherwise.
 */
405
BOOL X11DRV_SetupGCForBrush( X11DRV_PDEVICE *physDev )
406
{
407
    return X11DRV_SetupGCForPatBlt( physDev, physDev->gc, FALSE );
408 409 410 411 412 413
}


/***********************************************************************
 *           X11DRV_SetupGCForPen
 *
414
 * Setup physDev->gc for drawing operations using current pen.
415 416
 * Return FALSE if pen is PS_NULL, TRUE otherwise.
 */
417
static BOOL X11DRV_SetupGCForPen( X11DRV_PDEVICE *physDev )
418 419
{
    XGCValues val;
420
    UINT rop2 = GetROP2(physDev->dev.hdc);
421

422
    if (physDev->pen.style == PS_NULL) return FALSE;
423

424
    switch (rop2)
425 426
    {
    case R2_BLACK :
427
        val.foreground = BlackPixel( gdi_display, DefaultScreen(gdi_display) );
428 429 430
	val.function = GXcopy;
	break;
    case R2_WHITE :
431
        val.foreground = WhitePixel( gdi_display, DefaultScreen(gdi_display) );
432 433 434
	val.function = GXcopy;
	break;
    case R2_XORPEN :
435
	val.foreground = physDev->pen.pixel;
436 437 438
	/* It is very unlikely someone wants to XOR with 0 */
	/* This fixes the rubber-drawings in paintbrush */
	if (val.foreground == 0)
439 440
            val.foreground = (WhitePixel( gdi_display, DefaultScreen(gdi_display) ) ^
                              BlackPixel( gdi_display, DefaultScreen(gdi_display) ));
441 442 443
	val.function = GXxor;
	break;
    default :
444
	val.foreground = physDev->pen.pixel;
445
	val.function   = X11DRV_XROPfunction[rop2-1];
446
    }
447
    val.background = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
448
    val.fill_style = FillSolid;
449
    val.line_width = physDev->pen.width;
450 451 452
    if (val.line_width <= 1) {
	val.cap_style = CapNotLast;
    } else {
453
	switch (physDev->pen.endcap)
454 455 456 457 458 459 460 461 462 463 464 465
	{
	case PS_ENDCAP_SQUARE:
	    val.cap_style = CapProjecting;
	    break;
	case PS_ENDCAP_FLAT:
	    val.cap_style = CapButt;
	    break;
	case PS_ENDCAP_ROUND:
	default:
	    val.cap_style = CapRound;
	}
    }
466
    switch (physDev->pen.linejoin)
467 468 469 470 471 472 473 474 475 476 477
    {
    case PS_JOIN_BEVEL:
	val.join_style = JoinBevel;
        break;
    case PS_JOIN_MITER:
	val.join_style = JoinMiter;
        break;
    case PS_JOIN_ROUND:
    default:
	val.join_style = JoinRound;
    }
478

479
    if (physDev->pen.dash_len)
480
        val.line_style = ((GetBkMode(physDev->dev.hdc) == OPAQUE) && (!physDev->pen.ext))
481
                         ? LineDoubleDash : LineOnOffDash;
482 483
    else
        val.line_style = LineSolid;
484

485 486
    if (physDev->pen.dash_len)
        XSetDashes( gdi_display, physDev->gc, 0, physDev->pen.dashes, physDev->pen.dash_len );
487
    XChangeGC( gdi_display, physDev->gc,
488 489 490 491 492 493
	       GCFunction | GCForeground | GCBackground | GCLineWidth |
	       GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle, &val );
    return TRUE;
}


494 495 496 497 498
/***********************************************************************
 *           X11DRV_XWStoDS
 *
 * Performs a world-to-viewport transformation on the specified width.
 */
499
INT X11DRV_XWStoDS( HDC hdc, INT width )
500 501 502 503 504 505 506
{
    POINT pt[2];

    pt[0].x = 0;
    pt[0].y = 0;
    pt[1].x = width;
    pt[1].y = 0;
507
    LPtoDP( hdc, pt, 2 );
508 509 510 511 512 513 514 515
    return pt[1].x - pt[0].x;
}

/***********************************************************************
 *           X11DRV_YWStoDS
 *
 * Performs a world-to-viewport transformation on the specified height.
 */
516
INT X11DRV_YWStoDS( HDC hdc, INT height )
517 518 519 520 521 522 523
{
    POINT pt[2];

    pt[0].x = 0;
    pt[0].y = 0;
    pt[1].x = 0;
    pt[1].y = height;
524
    LPtoDP( hdc, pt, 2 );
525 526 527
    return pt[1].y - pt[0].y;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
528 529 530
/***********************************************************************
 *           X11DRV_LineTo
 */
531
BOOL X11DRV_LineTo( PHYSDEV dev, INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
532
{
533
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
534
    POINT pt[2];
535

536 537 538 539 540
    GetCurrentPositionEx( dev->hdc, &pt[0] );
    pt[1].x = x;
    pt[1].y = y;
    LPtoDP( dev->hdc, pt, 2 );
    add_pen_device_bounds( physDev, pt, 2 );
541

542
    if (X11DRV_SetupGCForPen( physDev ))
543
        XDrawLine(gdi_display, physDev->drawable, physDev->gc,
544 545
                  physDev->dc_rect.left + pt[0].x, physDev->dc_rect.top + pt[0].y,
                  physDev->dc_rect.left + pt[1].x, physDev->dc_rect.top + pt[1].y );
Alexandre Julliard's avatar
Alexandre Julliard committed
546 547 548 549 550 551
    return TRUE;
}



/***********************************************************************
552
 *           X11DRV_DrawArc
Alexandre Julliard's avatar
Alexandre Julliard committed
553 554 555
 *
 * Helper functions for Arc(), Chord() and Pie().
 * 'lines' is the number of lines to draw: 0 for Arc, 1 for Chord, 2 for Pie.
Alexandre Julliard's avatar
Alexandre Julliard committed
556
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
557
 */
558 559
static BOOL X11DRV_DrawArc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
                            INT xstart, INT ystart, INT xend, INT yend, INT lines )
Alexandre Julliard's avatar
Alexandre Julliard committed
560
{
561
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
562
    INT xcenter, ycenter, istart_angle, idiff_angle;
563
    INT width, oldwidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
564
    double start_angle, end_angle;
Alexandre Julliard's avatar
Alexandre Julliard committed
565
    XPoint points[4];
566
    POINT start, end;
567
    RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
568 569 570 571 572

    start.x = xstart;
    start.y = ystart;
    end.x = xend;
    end.y = yend;
573 574
    LPtoDP(dev->hdc, &start, 1);
    LPtoDP(dev->hdc, &end, 1);
575 576 577 578

    if ((rc.left == rc.right) || (rc.top == rc.bottom)
            ||(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; }
581

582
    oldwidth = width = physDev->pen.width;
Alexandre Julliard's avatar
Alexandre Julliard committed
583
    if (!width) width = 1;
584
    if(physDev->pen.style == PS_NULL) width = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
585

586
    if (physDev->pen.style == PS_INSIDEFRAME)
Alexandre Julliard's avatar
Alexandre Julliard committed
587
    {
588 589 590 591 592 593
        if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
        if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
        rc.left   += width / 2;
        rc.right  -= (width - 1) / 2;
        rc.top    += width / 2;
        rc.bottom -= (width - 1) / 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
594
    }
595 596
    if(width == 0) width = 1; /* more accurate */
    physDev->pen.width = width;
Alexandre Julliard's avatar
Alexandre Julliard committed
597

598 599 600 601 602 603 604
    xcenter = (rc.right + rc.left) / 2;
    ycenter = (rc.bottom + rc.top) / 2;
    start_angle = atan2( (double)(ycenter-start.y)*(rc.right-rc.left),
			 (double)(start.x-xcenter)*(rc.bottom-rc.top) );
    end_angle   = atan2( (double)(ycenter-end.y)*(rc.right-rc.left),
			 (double)(end.x-xcenter)*(rc.bottom-rc.top) );
    if ((start.x==end.x)&&(start.y==end.y))
Alexandre Julliard's avatar
Alexandre Julliard committed
605 606 607 608 609 610 611 612 613 614
      { /* A lazy program delivers xstart=xend=ystart=yend=0) */
	start_angle = 0;
	end_angle = 2* PI;
      }
    else /* notorious cases */
      if ((start_angle == PI)&&( end_angle <0))
	start_angle = - PI;
    else
      if ((end_angle == PI)&&( start_angle <0))
	end_angle = - PI;
615 616
    istart_angle = (INT)(start_angle * 180 * 64 / PI + 0.5);
    idiff_angle  = (INT)((end_angle - start_angle) * 180 * 64 / PI + 0.5);
Alexandre Julliard's avatar
Alexandre Julliard committed
617
    if (idiff_angle <= 0) idiff_angle += 360 * 64;
Alexandre Julliard's avatar
Alexandre Julliard committed
618 619 620

      /* Fill arc with brush if Chord() or Pie() */

621
    if ((lines > 0) && X11DRV_SetupGCForBrush( physDev )) {
622 623
        XSetArcMode( gdi_display, physDev->gc, (lines==1) ? ArcChord : ArcPieSlice);
        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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
626 627 628 629
    }

      /* Draw arc and lines */

630 631 632
    if (X11DRV_SetupGCForPen( physDev ))
    {
        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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
635 636 637 638 639
        if (lines) {
            /* use the truncated values */
            start_angle=(double)istart_angle*PI/64./180.;
            end_angle=(double)(istart_angle+idiff_angle)*PI/64./180.;
            /* 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);
648 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
Alexandre Julliard's avatar
Alexandre Julliard committed
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
Alexandre Julliard's avatar
Alexandre Julliard committed
655 656 657
             * from these (compile switch or whatever)
             */
            if (lines == 2) {
658
                INT dx1,dy1;
Alexandre Julliard's avatar
Alexandre Julliard committed
659
                points[3] = points[1];
660 661
                points[1].x = physDev->dc_rect.left + xcenter;
                points[1].y = physDev->dc_rect.top + ycenter;
Alexandre Julliard's avatar
Alexandre Julliard committed
662 663 664
                points[2] = points[1];
                dx1=points[1].x-points[0].x;
                dy1=points[1].y-points[0].y;
665
                if(((rc.top-rc.bottom) | -2) == -2)
Alexandre Julliard's avatar
Alexandre Julliard committed
666 667 668 669 670 671 672
                    if(dy1>0) points[1].y--;
                if(dx1<0) {
                    if (((-dx1)*64)<=ABS(dy1)*37) points[0].x--;
                    if(((-dx1*9))<(dy1*16)) points[0].y--;
                    if( dy1<0 && ((dx1*9)) < (dy1*16)) points[0].y--;
                } else {
                    if(dy1 < 0)  points[0].y--;
673
                    if(((rc.right-rc.left) | -2) == -2) points[1].x--;
Alexandre Julliard's avatar
Alexandre Julliard committed
674 675 676
                }
                dx1=points[3].x-points[2].x;
                dy1=points[3].y-points[2].y;
677
                if(((rc.top-rc.bottom) | -2 ) == -2)
Alexandre Julliard's avatar
Alexandre Julliard committed
678
                    if(dy1 < 0) points[2].y--;
679
                if( dx1<0){
Alexandre Julliard's avatar
Alexandre Julliard committed
680
                    if( dy1>0) points[3].y--;
681
                    if(((rc.right-rc.left) | -2) == -2 ) points[2].x--;
Alexandre Julliard's avatar
Alexandre Julliard committed
682 683 684 685 686
                }else {
                    points[3].y--;
                    if( dx1 * 64 < dy1 * -37 ) points[3].x--;
                }
                lines++;
687
	    }
688 689
            XDrawLines( gdi_display, physDev->drawable, physDev->gc,
                        points, lines+1, CoordModeOrigin );
Alexandre Julliard's avatar
Alexandre Julliard committed
690 691
        }
    }
692

693
    physDev->pen.width = oldwidth;
694
    add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
695 696 697 698 699 700 701
    return TRUE;
}


/***********************************************************************
 *           X11DRV_Arc
 */
702 703
BOOL X11DRV_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
                 INT xstart, INT ystart, INT xend, INT yend )
Alexandre Julliard's avatar
Alexandre Julliard committed
704
{
705
    return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
706 707 708 709 710 711
}


/***********************************************************************
 *           X11DRV_Pie
 */
712 713
BOOL X11DRV_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
                 INT xstart, INT ystart, INT xend, INT yend )
Alexandre Julliard's avatar
Alexandre Julliard committed
714
{
715
    return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
716 717 718 719 720
}

/***********************************************************************
 *           X11DRV_Chord
 */
721 722
BOOL X11DRV_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
                   INT xstart, INT ystart, INT xend, INT yend )
Alexandre Julliard's avatar
Alexandre Julliard committed
723
{
724
    return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
725 726 727 728 729 730
}


/***********************************************************************
 *           X11DRV_Ellipse
 */
731
BOOL X11DRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
Alexandre Julliard's avatar
Alexandre Julliard committed
732
{
733
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
734
    INT width, oldwidth;
735
    RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
736

737
    if ((rc.left == rc.right) || (rc.top == rc.bottom)) return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
738

739
    oldwidth = width = physDev->pen.width;
Alexandre Julliard's avatar
Alexandre Julliard committed
740
    if (!width) width = 1;
741
    if(physDev->pen.style == PS_NULL) width = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
742

743
    if (physDev->pen.style == PS_INSIDEFRAME)
Alexandre Julliard's avatar
Alexandre Julliard committed
744
    {
745 746 747 748 749 750
        if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
        if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
        rc.left   += width / 2;
        rc.right  -= (width - 1) / 2;
        rc.top    += width / 2;
        rc.bottom -= (width - 1) / 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
751
    }
752 753
    if(width == 0) width = 1; /* more accurate */
    physDev->pen.width = width;
Alexandre Julliard's avatar
Alexandre Julliard committed
754

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 );
759

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 );
764

765
    physDev->pen.width = oldwidth;
766
    add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
767 768 769 770 771 772 773
    return TRUE;
}


/***********************************************************************
 *           X11DRV_Rectangle
 */
774
BOOL X11DRV_Rectangle(PHYSDEV dev, INT left, INT top, INT right, INT bottom)
Alexandre Julliard's avatar
Alexandre Julliard committed
775
{
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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
779

780
    TRACE("(%d %d %d %d)\n", left, top, right, bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
781

782
    if ((rc.left == rc.right) || (rc.top == rc.bottom)) return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
783

784
    oldwidth = width = physDev->pen.width;
Alexandre Julliard's avatar
Alexandre Julliard committed
785
    if (!width) width = 1;
786
    if(physDev->pen.style == PS_NULL) width = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
787

788
    if (physDev->pen.style == PS_INSIDEFRAME)
Alexandre Julliard's avatar
Alexandre Julliard committed
789
    {
790 791 792 793 794 795
        if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
        if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
        rc.left   += width / 2;
        rc.right  -= (width - 1) / 2;
        rc.top    += width / 2;
        rc.bottom -= (width - 1) / 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
796
    }
797 798 799 800 801
    if(width == 1) width = 0;
    physDev->pen.width = width;
    oldjoinstyle = physDev->pen.linejoin;
    if(physDev->pen.type != PS_GEOMETRIC)
        physDev->pen.linejoin = PS_JOIN_MITER;
Alexandre Julliard's avatar
Alexandre Julliard committed
802

803 804 805
    rc.right--;
    rc.bottom--;
    if ((rc.right >= rc.left + width) && (rc.bottom >= rc.top + width))
806
    {
807
        if (X11DRV_SetupGCForBrush( physDev ))
808
            XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
809 810
                            physDev->dc_rect.left + rc.left + (width + 1) / 2,
                            physDev->dc_rect.top + rc.top + (width + 1) / 2,
811
                            rc.right-rc.left-width, rc.bottom-rc.top-width);
812
    }
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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
817

818 819
    physDev->pen.width = oldwidth;
    physDev->pen.linejoin = oldjoinstyle;
820
    add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
821 822 823 824 825 826
    return TRUE;
}

/***********************************************************************
 *           X11DRV_RoundRect
 */
827 828
BOOL X11DRV_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
                       INT ell_width, INT ell_height )
Alexandre Julliard's avatar
Alexandre Julliard committed
829
{
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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
834

835
    TRACE("(%d %d %d %d  %d %d\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
836 837
    	left, top, right, bottom, ell_width, ell_height);

838
    if ((rc.left == rc.right) || (rc.top == rc.bottom))
Alexandre Julliard's avatar
Alexandre Julliard committed
839 840
	return TRUE;

841 842
    /* Make sure ell_width and ell_height are >= 1 otherwise XDrawArc gets
       called with width/height < 0 */
843 844 845
    pts[0].x = pts[0].y = 0;
    pts[1].x = ell_width;
    pts[1].y = ell_height;
846
    LPtoDP(dev->hdc, pts, 2);
847 848
    ell_width  = max(abs( pts[1].x - pts[0].x ), 1);
    ell_height = max(abs( pts[1].y - pts[0].y ), 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
849

850 851
    oldwidth = width = physDev->pen.width;
    oldendcap = physDev->pen.endcap;
Alexandre Julliard's avatar
Alexandre Julliard committed
852
    if (!width) width = 1;
853
    if(physDev->pen.style == PS_NULL) width = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
854

855
    if (physDev->pen.style == PS_INSIDEFRAME)
Alexandre Julliard's avatar
Alexandre Julliard committed
856
    {
857 858 859 860 861 862
        if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
        if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
        rc.left   += width / 2;
        rc.right  -= (width - 1) / 2;
        rc.top    += width / 2;
        rc.bottom -= (width - 1) / 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
863
    }
864 865 866
    if(width == 0) width = 1;
    physDev->pen.width = width;
    physDev->pen.endcap = PS_ENDCAP_SQUARE;
Alexandre Julliard's avatar
Alexandre Julliard committed
867

868
    if (X11DRV_SetupGCForBrush( physDev ))
Alexandre Julliard's avatar
Alexandre Julliard committed
869
    {
870 871
        if (ell_width > (rc.right-rc.left) )
            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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
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 882
                          physDev->dc_rect.left + rc.left,
                          physDev->dc_rect.top + rc.bottom - ell_height - 1,
883
                          rc.right - rc.left - 1, ell_height, 180 * 64,
884 885
                          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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
893
        }else{
894
            XFillArc( gdi_display, physDev->drawable, physDev->gc,
895
                      physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
Alexandre Julliard's avatar
Alexandre Julliard committed
896
                      ell_width, ell_height, 90 * 64, 90 * 64 );
897
            XFillArc( gdi_display, physDev->drawable, physDev->gc,
898 899
                      physDev->dc_rect.left + rc.left,
                      physDev->dc_rect.top + rc.bottom - ell_height - 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
900
                      ell_width, ell_height, 180 * 64, 90 * 64 );
901
            XFillArc( gdi_display, physDev->drawable, physDev->gc,
902 903
                      physDev->dc_rect.left + rc.right - ell_width - 1,
                      physDev->dc_rect.top + rc.bottom - ell_height - 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
904
                      ell_width, ell_height, 270 * 64, 90 * 64 );
905
            XFillArc( gdi_display, physDev->drawable, physDev->gc,
906 907
                      physDev->dc_rect.left + rc.right - ell_width - 1,
                      physDev->dc_rect.top + rc.top,
Alexandre Julliard's avatar
Alexandre Julliard committed
908 909
                      ell_width, ell_height, 0, 90 * 64 );
        }
910
        if (ell_width < rc.right - rc.left)
Alexandre Julliard's avatar
Alexandre Julliard committed
911
        {
912
            XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
913 914
                            physDev->dc_rect.left + rc.left + (ell_width + 1) / 2,
                            physDev->dc_rect.top + rc.top + 1,
915
                            rc.right - rc.left - ell_width - 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
916
                            (ell_height + 1) / 2 - 1);
917
            XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
918 919
                            physDev->dc_rect.left + rc.left + (ell_width + 1) / 2,
                            physDev->dc_rect.top + rc.bottom - (ell_height) / 2 - 1,
920
                            rc.right - rc.left - ell_width - 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
921
                            (ell_height) / 2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
922
        }
923
        if  (ell_height < rc.bottom - rc.top)
Alexandre Julliard's avatar
Alexandre Julliard committed
924
        {
925
            XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
926 927
                            physDev->dc_rect.left + rc.left + 1,
                            physDev->dc_rect.top + rc.top + (ell_height + 1) / 2,
928 929
                            rc.right - rc.left - 2,
                            rc.bottom - rc.top - ell_height - 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
930 931
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
932 933 934 935 936 937 938 939 940
    /* FIXME: this could be done with on X call
     * more efficient and probably more correct
     * on any X server: XDrawArcs will draw
     * straight horizontal and vertical lines
     * if width or height are zero.
     *
     * BTW this stuff is optimized for an Xfree86 server
     * read the comments inside the X11DRV_DrawArc function
     */
941 942
    if (X11DRV_SetupGCForPen( physDev ))
    {
943 944
        if (ell_width > (rc.right-rc.left) )
            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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
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 954
                          physDev->dc_rect.left + rc.left,
                          physDev->dc_rect.top + rc.bottom - ell_height,
955
                          rc.right - rc.left - 1, ell_height - 1, 180 * 64 , 180 * 64 );
Alexandre Julliard's avatar
Alexandre Julliard committed
956
            }
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 963
                      physDev->dc_rect.left + rc.right - ell_width,
                      physDev->dc_rect.top + rc.top,
964
                      ell_width - 1 , rc.bottom - rc.top - 1, 270 * 64 , 180 * 64 );
Alexandre Julliard's avatar
Alexandre Julliard committed
965
	}else{
966
            XDrawArc( gdi_display, physDev->drawable, physDev->gc,
967
                      physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
Alexandre Julliard's avatar
Alexandre Julliard committed
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,
Alexandre Julliard's avatar
Alexandre Julliard committed
971
                      ell_width - 1, ell_height - 1, 180 * 64, 90 * 64 );
972
            XDrawArc( gdi_display, physDev->drawable, physDev->gc,
973 974
                      physDev->dc_rect.left + rc.right - ell_width,
                      physDev->dc_rect.top + rc.bottom - ell_height,
Alexandre Julliard's avatar
Alexandre Julliard committed
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,
Alexandre Julliard's avatar
Alexandre Julliard committed
978 979
                      ell_width - 1, ell_height - 1, 0, 90 * 64 );
	}
980
	if (ell_width < rc.right - rc.left)
Alexandre Julliard's avatar
Alexandre Julliard committed
981
	{
982
            XDrawLine( gdi_display, physDev->drawable, physDev->gc,
983 984 985 986
                       physDev->dc_rect.left + rc.left + ell_width / 2,
                       physDev->dc_rect.top + rc.top,
                       physDev->dc_rect.left + rc.right - (ell_width+1) / 2,
                       physDev->dc_rect.top + rc.top);
987
            XDrawLine( gdi_display, physDev->drawable, physDev->gc,
988 989 990 991
                       physDev->dc_rect.left + rc.left + ell_width / 2 ,
                       physDev->dc_rect.top + rc.bottom - 1,
                       physDev->dc_rect.left + rc.right - (ell_width+1)/ 2,
                       physDev->dc_rect.top + rc.bottom - 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
992
	}
993
	if (ell_height < rc.bottom - rc.top)
Alexandre Julliard's avatar
Alexandre Julliard committed
994
	{
995
            XDrawLine( gdi_display, physDev->drawable, physDev->gc,
996 997 998 999
                       physDev->dc_rect.left + rc.right - 1,
                       physDev->dc_rect.top + rc.top + ell_height / 2,
                       physDev->dc_rect.left + rc.right - 1,
                       physDev->dc_rect.top + rc.bottom - (ell_height+1) / 2);
1000
            XDrawLine( gdi_display, physDev->drawable, physDev->gc,
1001 1002 1003 1004
                       physDev->dc_rect.left + rc.left,
                       physDev->dc_rect.top + rc.top + ell_height / 2,
                       physDev->dc_rect.left + rc.left,
                       physDev->dc_rect.top + rc.bottom - (ell_height+1) / 2);
Alexandre Julliard's avatar
Alexandre Julliard committed
1005 1006
	}
    }
1007

1008 1009
    physDev->pen.width = oldwidth;
    physDev->pen.endcap = oldendcap;
1010
    add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1011 1012 1013 1014 1015 1016 1017
    return TRUE;
}


/***********************************************************************
 *           X11DRV_SetPixel
 */
1018
COLORREF X11DRV_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
Alexandre Julliard's avatar
Alexandre Julliard committed
1019
{
1020
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1021
    unsigned long pixel;
1022
    POINT pt;
1023
    RECT rect;
1024

1025 1026
    pt.x = x;
    pt.y = y;
1027
    LPtoDP( dev->hdc, &pt, 1 );
1028
    pixel = X11DRV_PALETTE_ToPhysical( physDev, color );
1029

1030 1031
    XSetForeground( gdi_display, physDev->gc, pixel );
    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 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1034

1035 1036
    SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
    add_device_bounds( physDev, &rect );
1037
    return X11DRV_PALETTE_ToLogical(physDev, pixel);
Alexandre Julliard's avatar
Alexandre Julliard committed
1038 1039 1040 1041 1042 1043
}


/***********************************************************************
 *           X11DRV_PaintRgn
 */
1044
BOOL X11DRV_PaintRgn( PHYSDEV dev, HRGN hrgn )
Alexandre Julliard's avatar
Alexandre Julliard committed
1045
{
1046
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1047
    RECT rc;
1048

1049
    if (X11DRV_SetupGCForBrush( physDev ))
1050
    {
1051
        unsigned int i;
1052
        XRectangle *rect;
1053
        RGNDATA *data = X11DRV_GetRegionData( hrgn, dev->hdc );
1054

1055 1056 1057 1058
        if (!data) return FALSE;
        rect = (XRectangle *)data->Buffer;
        for (i = 0; i < data->rdh.nCount; i++)
        {
1059 1060
            rect[i].x += physDev->dc_rect.left;
            rect[i].y += physDev->dc_rect.top;
1061
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1062

1063 1064 1065
        XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
        HeapFree( GetProcessHeap(), 0, data );
    }
1066 1067 1068 1069 1070
    if (GetRgnBox( hrgn, &rc ))
    {
        LPtoDP( dev->hdc, (POINT *)&rc, 2 );
        add_device_bounds( physDev, &rc );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1071 1072 1073 1074 1075 1076
    return TRUE;
}

/**********************************************************************
 *          X11DRV_Polygon
 */
1077
BOOL X11DRV_Polygon( PHYSDEV dev, const POINT* pt, INT count )
Alexandre Julliard's avatar
Alexandre Julliard committed
1078
{
1079 1080
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
    int i;
1081 1082 1083 1084 1085 1086 1087
    POINT *points;
    XPoint *xpoints;

    points = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pt) );
    if (!points) return FALSE;
    memcpy( points, pt, count * sizeof(*pt) );
    LPtoDP( dev->hdc, points, count );
1088
    add_pen_device_bounds( physDev, points, count );
Alexandre Julliard's avatar
Alexandre Julliard committed
1089

1090
    if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * (count+1) )))
1091
    {
1092
        HeapFree( GetProcessHeap(), 0, points );
1093 1094
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1095 1096
    for (i = 0; i < count; i++)
    {
1097 1098
        xpoints[i].x = physDev->dc_rect.left + points[i].x;
        xpoints[i].y = physDev->dc_rect.top + points[i].y;
Alexandre Julliard's avatar
Alexandre Julliard committed
1099
    }
1100
    xpoints[count] = xpoints[0];
Alexandre Julliard's avatar
Alexandre Julliard committed
1101

1102
    if (X11DRV_SetupGCForBrush( physDev ))
1103
        XFillPolygon( gdi_display, physDev->drawable, physDev->gc,
1104
                      xpoints, count+1, Complex, CoordModeOrigin);
1105

1106
    if (X11DRV_SetupGCForPen ( physDev ))
1107
        XDrawLines( gdi_display, physDev->drawable, physDev->gc,
1108
                    xpoints, count+1, CoordModeOrigin );
1109

1110
    HeapFree( GetProcessHeap(), 0, xpoints );
1111
    HeapFree( GetProcessHeap(), 0, points );
Alexandre Julliard's avatar
Alexandre Julliard committed
1112 1113 1114 1115 1116 1117 1118
    return TRUE;
}


/**********************************************************************
 *          X11DRV_PolyPolygon
 */
1119
BOOL X11DRV_PolyPolygon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polygons )
Alexandre Julliard's avatar
Alexandre Julliard committed
1120
{
1121
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
    DWORD total = 0, max = 0, pos, i;
    POINT *points;
    BOOL ret = FALSE;

    for (i = 0; i < polygons; i++)
    {
        if (counts[i] < 2) return FALSE;
        if (counts[i] > max) max = counts[i];
        total += counts[i];
    }

    points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
    if (!points) return FALSE;
    memcpy( points, pt, total * sizeof(*pt) );
    LPtoDP( dev->hdc, points, total );
1137
    add_pen_device_bounds( physDev, points, total );
1138

1139 1140 1141 1142 1143
    if (X11DRV_SetupGCForBrush( physDev ))
    {
        XRectangle *rect;
        HRGN hrgn = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ) );
        RGNDATA *data = X11DRV_GetRegionData( hrgn, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1144

1145 1146 1147 1148 1149 1150 1151 1152
        DeleteObject( hrgn );
        if (!data) goto done;
        rect = (XRectangle *)data->Buffer;
        for (i = 0; i < data->rdh.nCount; i++)
        {
            rect[i].x += physDev->dc_rect.left;
            rect[i].y += physDev->dc_rect.top;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1153

1154 1155 1156
        XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
        HeapFree( GetProcessHeap(), 0, data );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1157

1158
    if (X11DRV_SetupGCForPen ( physDev ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1159
    {
1160 1161
        XPoint *xpoints;
        int j;
Alexandre Julliard's avatar
Alexandre Julliard committed
1162

1163 1164
        if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * (max + 1) ))) goto done;
        for (i = pos = 0; i < polygons; pos += counts[i++])
1165
        {
1166 1167 1168 1169 1170 1171 1172 1173 1174
            for (j = 0; j < counts[i]; j++)
            {
                xpoints[j].x = physDev->dc_rect.left + points[pos + j].x;
                xpoints[j].y = physDev->dc_rect.top + points[pos + j].y;
            }
	    xpoints[j] = xpoints[0];
            XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j + 1, CoordModeOrigin );
        }
        HeapFree( GetProcessHeap(), 0, xpoints );
Alexandre Julliard's avatar
Alexandre Julliard committed
1175
    }
1176 1177 1178 1179 1180
    ret = TRUE;

done:
    HeapFree( GetProcessHeap(), 0, points );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1181 1182 1183
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1184 1185 1186
/**********************************************************************
 *          X11DRV_PolyPolyline
 */
1187
BOOL X11DRV_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
Alexandre Julliard's avatar
Alexandre Julliard committed
1188
{
1189
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
    DWORD total = 0, max = 0, pos, i, j;
    POINT *points;

    for (i = 0; i < polylines; i++)
    {
        if (counts[i] < 2) return FALSE;
        if (counts[i] > max) max = counts[i];
        total += counts[i];
    }

    points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
    if (!points) return FALSE;
    memcpy( points, pt, total * sizeof(*pt) );
    LPtoDP( dev->hdc, points, total );
1204
    add_pen_device_bounds( physDev, points, total );
1205

1206
    if (X11DRV_SetupGCForPen ( physDev ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1207
    {
1208
        XPoint *xpoints;
Alexandre Julliard's avatar
Alexandre Julliard committed
1209

1210
        if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * max )))
1211
        {
1212
            HeapFree( GetProcessHeap(), 0, points );
1213 1214
            return FALSE;
        }
1215
        for (i = pos = 0; i < polylines; pos += counts[i++])
Alexandre Julliard's avatar
Alexandre Julliard committed
1216 1217 1218
        {
            for (j = 0; j < counts[i]; j++)
            {
1219 1220
                xpoints[j].x = physDev->dc_rect.left + points[pos + j].x;
                xpoints[j].y = physDev->dc_rect.top + points[pos + j].y;
Alexandre Julliard's avatar
Alexandre Julliard committed
1221
            }
1222
            XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j, CoordModeOrigin );
Alexandre Julliard's avatar
Alexandre Julliard committed
1223
        }
1224
        HeapFree( GetProcessHeap(), 0, xpoints );
Alexandre Julliard's avatar
Alexandre Julliard committed
1225
    }
1226
    HeapFree( GetProcessHeap(), 0, points );
Alexandre Julliard's avatar
Alexandre Julliard committed
1227 1228 1229 1230
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1231 1232 1233 1234 1235 1236 1237
/**********************************************************************
 *          X11DRV_InternalFloodFill
 *
 * Internal helper function for flood fill.
 * (xorg,yorg) is the origin of the X image relative to the drawable.
 * (x,y) is relative to the origin of the X image.
 */
1238
static void X11DRV_InternalFloodFill(XImage *image, X11DRV_PDEVICE *physDev,
Alexandre Julliard's avatar
Alexandre Julliard committed
1239 1240
                                     int x, int y,
                                     int xOrg, int yOrg,
1241
                                     unsigned long pixel, WORD fillType, RECT *bounds )
Alexandre Julliard's avatar
Alexandre Julliard committed
1242 1243 1244 1245
{
    int left, right;

#define TO_FLOOD(x,y)  ((fillType == FLOODFILLBORDER) ? \
Alexandre Julliard's avatar
Alexandre Julliard committed
1246 1247
                        (XGetPixel(image,x,y) != pixel) : \
                        (XGetPixel(image,x,y) == pixel))
Alexandre Julliard's avatar
Alexandre Julliard committed
1248 1249 1250 1251 1252 1253 1254 1255

    if (!TO_FLOOD(x,y)) return;

      /* Find left and right boundaries */

    left = right = x;
    while ((left > 0) && TO_FLOOD( left-1, y )) left--;
    while ((right < image->width) && TO_FLOOD( right, y )) right++;
1256 1257 1258 1259
    bounds->left   = min( bounds->left, left );
    bounds->top    = min( bounds->top, y );
    bounds->right  = max( bounds->right, right );
    bounds->bottom = max( bounds->bottom, y + 1 );
1260
    XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
Alexandre Julliard's avatar
Alexandre Julliard committed
1261 1262 1263 1264 1265 1266
                    xOrg + left, yOrg + y, right-left, 1 );

      /* Set the pixels of this line so we don't fill it again */

    for (x = left; x < right; x++)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1267 1268
        if (fillType == FLOODFILLBORDER) XPutPixel( image, x, y, pixel );
        else XPutPixel( image, x, y, ~pixel );
Alexandre Julliard's avatar
Alexandre Julliard committed
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
    }

      /* Fill the line above */

    if (--y >= 0)
    {
        x = left;
        while (x < right)
        {
            while ((x < right) && !TO_FLOOD(x,y)) x++;
            if (x >= right) break;
            while ((x < right) && TO_FLOOD(x,y)) x++;
1281
            X11DRV_InternalFloodFill(image, physDev, x-1, y,
1282
                                     xOrg, yOrg, pixel, fillType, bounds );
Alexandre Julliard's avatar
Alexandre Julliard committed
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
        }
    }

      /* Fill the line below */

    if ((y += 2) < image->height)
    {
        x = left;
        while (x < right)
        {
            while ((x < right) && !TO_FLOOD(x,y)) x++;
            if (x >= right) break;
            while ((x < right) && TO_FLOOD(x,y)) x++;
1296
            X11DRV_InternalFloodFill(image, physDev, x-1, y,
1297
                                     xOrg, yOrg, pixel, fillType, bounds );
Alexandre Julliard's avatar
Alexandre Julliard committed
1298 1299
        }
    }
1300
#undef TO_FLOOD
Alexandre Julliard's avatar
Alexandre Julliard committed
1301 1302 1303
}


1304 1305 1306 1307 1308
static int ExtFloodFillXGetImageErrorHandler( Display *dpy, XErrorEvent *event, void *arg )
{
    return (event->request_code == X_GetImage && event->error_code == BadMatch);
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1309
/**********************************************************************
1310
 *          X11DRV_ExtFloodFill
Alexandre Julliard's avatar
Alexandre Julliard committed
1311
 */
1312
BOOL X11DRV_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT fillType )
Alexandre Julliard's avatar
Alexandre Julliard committed
1313
{
1314
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
Alexandre Julliard's avatar
Alexandre Julliard committed
1315
    XImage *image;
1316
    RECT rect, bounds;
1317
    POINT pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
1318

1319
    TRACE("X11DRV_ExtFloodFill %d,%d %06x %d\n", x, y, color, fillType );
1320

1321 1322
    pt.x = x;
    pt.y = y;
1323
    LPtoDP( dev->hdc, &pt, 1 );
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341

    if (!physDev->region)
    {
        rect.left   = 0;
        rect.top    = 0;
        rect.right  = physDev->dc_rect.right - physDev->dc_rect.left;
        rect.bottom = physDev->dc_rect.bottom - physDev->dc_rect.top;
    }
    else
    {
        if (!PtInRegion( physDev->region, pt.x, pt.y )) return FALSE;
        GetRgnBox( physDev->region, &rect );
        rect.left   = max( rect.left, 0 );
        rect.top    = max( rect.top, 0 );
        rect.right  = min( rect.right, physDev->dc_rect.right - physDev->dc_rect.left );
        rect.bottom = min( rect.bottom, physDev->dc_rect.bottom - physDev->dc_rect.top );
    }
    if (pt.x < rect.left || pt.x >= rect.right || pt.y < rect.top || pt.y >= rect.bottom) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1342

1343
    X11DRV_expect_error( gdi_display, ExtFloodFillXGetImageErrorHandler, NULL );
1344
    image = XGetImage( gdi_display, physDev->drawable,
1345
                       physDev->dc_rect.left + rect.left, physDev->dc_rect.top + rect.top,
1346 1347
                       rect.right - rect.left, rect.bottom - rect.top,
                       AllPlanes, ZPixmap );
1348
    if(X11DRV_check_error()) image = NULL;
1349
    if (!image) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1350

1351
    if (X11DRV_SetupGCForBrush( physDev ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1352
    {
1353 1354
        unsigned long pixel = X11DRV_PALETTE_ToPhysical( physDev, color );

1355 1356
        reset_bounds( &bounds );

1357
        X11DRV_InternalFloodFill(image, physDev,
1358 1359 1360 1361
                                 pt.x - rect.left,
                                 pt.y - rect.top,
                                 physDev->dc_rect.left + rect.left,
                                 physDev->dc_rect.top + rect.top,
1362 1363 1364 1365
                                 pixel, fillType, &bounds );

        OffsetRect( &bounds, rect.left, rect.top );
        add_device_bounds( physDev, &bounds );
Alexandre Julliard's avatar
Alexandre Julliard committed
1366 1367
    }

1368
    XDestroyImage( image );
Alexandre Julliard's avatar
Alexandre Julliard committed
1369 1370 1371
    return TRUE;
}

1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
/**********************************************************************
 *          X11DRV_GradientFill
 */
BOOL X11DRV_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
                          void *grad_array, ULONG ngrad, ULONG mode )
{
    X11DRV_PDEVICE *physdev = get_x11drv_dev( dev );
    const GRADIENT_RECT *rect = grad_array;
    TRIVERTEX v[2];
    POINT pt[2];
1382
    RECT rc, bounds;
1383 1384 1385
    unsigned int i;
    XGCValues val;

1386 1387
    /* <= 16-bpp use dithering */
    if (physdev->depth <= 16) goto fallback;
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398

    switch (mode)
    {
    case GRADIENT_FILL_RECT_H:
        val.function   = GXcopy;
        val.fill_style = FillSolid;
        val.line_width = 1;
        val.cap_style  = CapNotLast;
        val.line_style = LineSolid;
        XChangeGC( gdi_display, physdev->gc,
                   GCFunction | GCLineWidth | GCLineStyle | GCCapStyle | GCFillStyle, &val );
1399
        reset_bounds( &bounds );
1400 1401 1402

        for (i = 0; i < ngrad; i++, rect++)
        {
1403
            int x, dx;
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419

            v[0] = vert_array[rect->UpperLeft];
            v[1] = vert_array[rect->LowerRight];
            pt[0].x = v[0].x;
            pt[0].y = v[0].y;
            pt[1].x = v[1].x;
            pt[1].y = v[1].y;
            LPtoDP( dev->hdc, pt, 2 );
            dx = pt[1].x - pt[0].x;
            if (!dx) continue;
            if (dx < 0)  /* swap the colors */
            {
                v[0] = vert_array[rect->LowerRight];
                v[1] = vert_array[rect->UpperLeft];
                dx = -dx;
            }
1420 1421 1422 1423 1424 1425
            rc.left   = min( pt[0].x, pt[1].x );
            rc.top    = min( pt[0].y, pt[1].y );
            rc.right  = max( pt[0].x, pt[1].x );
            rc.bottom = max( pt[0].y, pt[1].y );
            add_bounds_rect( &bounds, &rc );
            for (x = 0; x < dx; x++)
1426
            {
1427 1428
                int color = X11DRV_PALETTE_ToPhysical( physdev,
                                 RGB( (v[0].Red   * (dx - x) + v[1].Red   * x) / dx / 256,
1429
                                      (v[0].Green * (dx - x) + v[1].Green * x) / dx / 256,
1430 1431 1432
                                      (v[0].Blue  * (dx - x) + v[1].Blue  * x) / dx / 256) );

                XSetForeground( gdi_display, physdev->gc, color );
1433
                XDrawLine( gdi_display, physdev->drawable, physdev->gc,
1434 1435
                           physdev->dc_rect.left + rc.left + x, physdev->dc_rect.top + rc.top,
                           physdev->dc_rect.left + rc.left + x, physdev->dc_rect.top + rc.bottom );
1436 1437
            }
        }
1438
        add_device_bounds( physdev, &bounds );
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
        return TRUE;

    case GRADIENT_FILL_RECT_V:
        val.function   = GXcopy;
        val.fill_style = FillSolid;
        val.line_width = 1;
        val.cap_style  = CapNotLast;
        val.line_style = LineSolid;
        XChangeGC( gdi_display, physdev->gc,
                   GCFunction | GCLineWidth | GCLineStyle | GCCapStyle | GCFillStyle, &val );
1449
        reset_bounds( &bounds );
1450 1451 1452

        for (i = 0; i < ngrad; i++, rect++)
        {
1453
            int y, dy;
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469

            v[0] = vert_array[rect->UpperLeft];
            v[1] = vert_array[rect->LowerRight];
            pt[0].x = v[0].x;
            pt[0].y = v[0].y;
            pt[1].x = v[1].x;
            pt[1].y = v[1].y;
            LPtoDP( dev->hdc, pt, 2 );
            dy = pt[1].y - pt[0].y;
            if (!dy) continue;
            if (dy < 0)  /* swap the colors */
            {
                v[0] = vert_array[rect->LowerRight];
                v[1] = vert_array[rect->UpperLeft];
                dy = -dy;
            }
1470 1471 1472 1473 1474 1475
            rc.left   = min( pt[0].x, pt[1].x );
            rc.top    = min( pt[0].y, pt[1].y );
            rc.right  = max( pt[0].x, pt[1].x );
            rc.bottom = max( pt[0].y, pt[1].y );
            add_bounds_rect( &bounds, &rc );
            for (y = 0; y < dy; y++)
1476
            {
1477 1478
                int color = X11DRV_PALETTE_ToPhysical( physdev,
                                 RGB( (v[0].Red   * (dy - y) + v[1].Red   * y) / dy / 256,
1479
                                      (v[0].Green * (dy - y) + v[1].Green * y) / dy / 256,
1480 1481 1482
                                      (v[0].Blue  * (dy - y) + v[1].Blue  * y) / dy / 256) );

                XSetForeground( gdi_display, physdev->gc, color );
1483
                XDrawLine( gdi_display, physdev->drawable, physdev->gc,
1484 1485
                           physdev->dc_rect.left + rc.left, physdev->dc_rect.top + rc.top + y,
                           physdev->dc_rect.left + rc.right, physdev->dc_rect.top + rc.top + y );
1486 1487
            }
        }
1488
        add_device_bounds( physdev, &bounds );
1489 1490 1491 1492 1493 1494 1495 1496
        return TRUE;
    }

fallback:
    dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
    return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
}

1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
static unsigned char *get_icm_profile( unsigned long *size )
{
    Atom type;
    int format;
    unsigned long count, remaining;
    unsigned char *profile, *ret = NULL;

    XGetWindowProperty( gdi_display, DefaultRootWindow(gdi_display),
                        x11drv_atom(_ICC_PROFILE), 0, ~0UL, False, AnyPropertyType,
                        &type, &format, &count, &remaining, &profile );
1507
    *size = get_property_size( format, count );
1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
    if (format && count)
    {
        if ((ret = HeapAlloc( GetProcessHeap(), 0, *size ))) memcpy( ret, profile, *size );
        XFree( profile );
    }
    return ret;
}

typedef struct
{
    unsigned int unknown[6];
    unsigned int state[5];
    unsigned int count[2];
    unsigned char buffer[64];
} sha_ctx;

extern void WINAPI A_SHAInit( sha_ctx * );
extern void WINAPI A_SHAUpdate( sha_ctx *, const unsigned char *, unsigned int );
extern void WINAPI A_SHAFinal( sha_ctx *, unsigned char * );

1528 1529 1530 1531 1532 1533 1534 1535
static const WCHAR mntr_key[] =
    {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
     'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t',
     'V','e','r','s','i','o','n','\\','I','C','M','\\','m','n','t','r',0};

static const WCHAR color_path[] =
    {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\','c','o','l','o','r','\\',0};

1536 1537 1538
/***********************************************************************
 *              GetICMProfile (X11DRV.@)
 */
1539
BOOL X11DRV_GetICMProfile( PHYSDEV dev, LPDWORD size, LPWSTR filename )
1540 1541 1542 1543 1544
{
    static const WCHAR srgb[] =
        {'s','R','G','B',' ','C','o','l','o','r',' ','S','p','a','c','e',' ',
         'P','r','o','f','i','l','e','.','i','c','m',0};
    HKEY hkey;
1545
    DWORD required, len;
1546
    WCHAR profile[MAX_PATH], fullname[2*MAX_PATH + sizeof(color_path)/sizeof(WCHAR)];
1547 1548
    unsigned char *buffer;
    unsigned long buflen;
1549 1550 1551

    if (!size) return FALSE;

1552
    GetSystemDirectoryW( fullname, MAX_PATH );
1553
    strcatW( fullname, color_path );
1554 1555

    len = sizeof(profile)/sizeof(WCHAR);
1556
    if (!RegCreateKeyExW( HKEY_LOCAL_MACHINE, mntr_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ) &&
1557
        !RegEnumValueW( hkey, 0, profile, &len, NULL, NULL, NULL, NULL )) /* FIXME handle multiple values */
1558
    {
1559
        strcatW( fullname, profile );
1560 1561
        RegCloseKey( hkey );
    }
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
    else if ((buffer = get_icm_profile( &buflen )))
    {
        static const WCHAR fmt[] = {'%','0','2','x',0};
        static const WCHAR icm[] = {'.','i','c','m',0};

        unsigned char sha1sum[20];
        unsigned int i;
        sha_ctx ctx;
        HANDLE file;

        A_SHAInit( &ctx );
        A_SHAUpdate( &ctx, buffer, buflen );
        A_SHAFinal( &ctx, sha1sum );

        for (i = 0; i < sizeof(sha1sum); i++) sprintfW( &profile[i * 2], fmt, sha1sum[i] );
        memcpy( &profile[i * 2], icm, sizeof(icm) );

        strcatW( fullname, profile );
        file = CreateFileW( fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0 );
        if (file != INVALID_HANDLE_VALUE)
        {
            DWORD written;

            if (!WriteFile( file, buffer, buflen, &written, NULL ) || written != buflen)
                ERR( "Unable to write color profile\n" );
            CloseHandle( file );
        }
        HeapFree( GetProcessHeap(), 0, buffer );
    }
    else strcatW( fullname, srgb );
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608

    required = strlenW( fullname ) + 1;
    if (*size < required)
    {
        *size = required;
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
        return FALSE;
    }
    if (filename)
    {
        strcpyW( filename, fullname );
        if (GetFileAttributesW( filename ) == INVALID_FILE_ATTRIBUTES)
            WARN( "color profile not found\n" );
    }
    *size = required;
    return TRUE;
}
1609 1610 1611 1612

/***********************************************************************
 *              EnumICMProfiles (X11DRV.@)
 */
1613
INT X11DRV_EnumICMProfiles( PHYSDEV dev, ICMENUMPROCW proc, LPARAM lparam )
1614
{
1615
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
    HKEY hkey;
    DWORD len_sysdir, len_path, len, index = 0;
    WCHAR sysdir[MAX_PATH], *profile;
    LONG res;
    INT ret;

    TRACE("%p, %p, %ld\n", physDev, proc, lparam);

    if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, mntr_key, 0, KEY_ALL_ACCESS, &hkey ))
        return -1;

    len_sysdir = GetSystemDirectoryW( sysdir, MAX_PATH );
    len_path = len_sysdir + sizeof(color_path) / sizeof(color_path[0]) - 1;
    len = 64;
    for (;;)
    {
        if (!(profile = HeapAlloc( GetProcessHeap(), 0, (len_path + len) * sizeof(WCHAR) )))
        {
            RegCloseKey( hkey );
            return -1;
        }
        res = RegEnumValueW( hkey, index, profile + len_path, &len, NULL, NULL, NULL, NULL );
        while (res == ERROR_MORE_DATA)
        {
            len *= 2;
            HeapFree( GetProcessHeap(), 0, profile );
            if (!(profile = HeapAlloc( GetProcessHeap(), 0, (len_path + len) * sizeof(WCHAR) )))
            {
                RegCloseKey( hkey );
                return -1;
            }
            res = RegEnumValueW( hkey, index, profile + len_path, &len, NULL, NULL, NULL, NULL );
        }
        if (res != ERROR_SUCCESS)
        {
            HeapFree( GetProcessHeap(), 0, profile );
            break;
        }
        memcpy( profile, sysdir, len_sysdir * sizeof(WCHAR) );
        memcpy( profile + len_sysdir, color_path, sizeof(color_path) - sizeof(WCHAR) );
        ret = proc( profile, lparam );
        HeapFree( GetProcessHeap(), 0, profile );
        if (!ret) break;
        index++;
    }
    RegCloseKey( hkey );
    return -1;
}