/*
 * GDI graphics operations
 *
 * Copyright 1993, 1994 Alexandre Julliard
 */

static char Copyright[] = "Copyright  Alexandre Julliard, 1993, 1994";

#include <math.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Intrinsic.h>
#ifndef PI
#define PI M_PI
#endif

#include "gdi.h"
#include "syscolor.h"
#include "stddebug.h"
/* #define DEBUG_GRAPHICS /* */
/* #undef  DEBUG_GRAPHICS /* */
#include "debug.h"


extern const int DC_XROPfunction[];

extern int COLOR_ToPhysical( DC *dc, COLORREF color );

static __inline__ void swap_int(int *a, int *b)
{
	int c;
	
	c = *a;
	*a = *b;
	*b = c;
}

/***********************************************************************
 *           LineTo    (GDI.19)
 */
BOOL LineTo( HDC hdc, short x, short y )
{
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaParam2(dc, META_LINETO, x, y);
	return TRUE;
    }

    if (DC_SetupGCForPen( dc ))
	XDrawLine(display, dc->u.x.drawable, dc->u.x.gc, 
		  dc->w.DCOrgX + XLPTODP( dc, dc->w.CursPosX ),
		  dc->w.DCOrgY + YLPTODP( dc, dc->w.CursPosY ),
		  dc->w.DCOrgX + XLPTODP( dc, x ),
		  dc->w.DCOrgY + YLPTODP( dc, y ) );
    dc->w.CursPosX = x;
    dc->w.CursPosY = y;
    return TRUE;
}


/***********************************************************************
 *           MoveTo    (GDI.20)
 */
DWORD MoveTo( HDC hdc, short x, short y )
{
    short oldx, oldy;
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaParam2(dc, META_MOVETO, x, y);
	return 0;
    }

    oldx = dc->w.CursPosX;
    oldy = dc->w.CursPosY;
    dc->w.CursPosX = x;
    dc->w.CursPosY = y;
    return oldx | (oldy << 16);
}


/***********************************************************************
 *           MoveToEx    (GDI.483)
 */
BOOL MoveToEx( HDC hdc, short x, short y, LPPOINT pt )
{
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) return FALSE;
    if (pt)
    {
	pt->x = dc->w.CursPosX;
	pt->y = dc->w.CursPosY;
    }
    dc->w.CursPosX = x;
    dc->w.CursPosY = y;
    return TRUE;
}


/***********************************************************************
 *           GRAPH_DrawArc
 *
 * Helper functions for Arc(), Chord() and Pie().
 * 'lines' is the number of lines to draw: 0 for Arc, 1 for Chord, 2 for Pie.
 */
BOOL GRAPH_DrawArc( HDC hdc, int left, int top, int right, int bottom,
		    int xstart, int ystart, int xend, int yend, int lines )
{
    int xcenter, ycenter, istart_angle, idiff_angle;
    double start_angle, end_angle;
    XPoint points[3];
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	switch (lines)
	{
	case 0:
	    MF_MetaParam8(dc, META_ARC, left, top, right, bottom,
			  xstart, ystart, xend, yend);
	    break;

	case 1:
	    MF_MetaParam8(dc, META_CHORD, left, top, right, bottom,
			  xstart, ystart, xend, yend);
	    break;

	case 2:
	    MF_MetaParam8(dc, META_PIE, left, top, right, bottom,
			  xstart, ystart, xend, yend);
	    break;
	}
	return 0;
    }

    left   = XLPTODP( dc, left );
    top    = YLPTODP( dc, top );
    right  = XLPTODP( dc, right );
    bottom = YLPTODP( dc, bottom );
    xstart = XLPTODP( dc, xstart );
    ystart = YLPTODP( dc, ystart );
    xend   = XLPTODP( dc, xend );
    yend   = YLPTODP( dc, yend );
    if ((left == right) || (top == bottom)) return FALSE;

    xcenter = (right + left) / 2;
    ycenter = (bottom + top) / 2;
    start_angle = atan2( (double)(ycenter-ystart)*(right-left),
			 (double)(xstart-xcenter)*(bottom-top) );
    end_angle   = atan2( (double)(ycenter-yend)*(right-left),
			 (double)(xend-xcenter)*(bottom-top) );
    istart_angle = (int)(start_angle * 180 * 64 / PI);
    idiff_angle  = (int)((end_angle - start_angle) * 180 * 64 / PI );
    if (idiff_angle <= 0) idiff_angle += 360 * 64;
    if (left > right) swap_int( &left, &right );
    if (top > bottom) swap_int( &top, &bottom );

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

    if ((lines > 0) && DC_SetupGCForBrush( dc ))
    {
        XSetArcMode( display, dc->u.x.gc, (lines==1) ? ArcChord : ArcPieSlice);
        XFillArc( display, dc->u.x.drawable, dc->u.x.gc,
                 dc->w.DCOrgX + left, dc->w.DCOrgY + top,
                 right-left-1, bottom-top-1, istart_angle, idiff_angle );
    }

      /* Draw arc and lines */

    if (!DC_SetupGCForPen( dc )) return TRUE;
    XDrawArc( display, dc->u.x.drawable, dc->u.x.gc,
	      dc->w.DCOrgX + left, dc->w.DCOrgY + top,
	      right-left-1, bottom-top-1, istart_angle, idiff_angle );
    if (!lines) return TRUE;

    points[0].x = dc->w.DCOrgX + xcenter + (int)(cos(start_angle) * (right-left) / 2);
    points[0].y = dc->w.DCOrgY + ycenter - (int)(sin(start_angle) * (bottom-top) / 2);
    points[1].x = dc->w.DCOrgX + xcenter + (int)(cos(end_angle) * (right-left) / 2);
    points[1].y = dc->w.DCOrgY + ycenter - (int)(sin(end_angle) * (bottom-top) / 2);
    if (lines == 2)
    {
	points[2] = points[1];
	points[1].x = dc->w.DCOrgX + xcenter;
	points[1].y = dc->w.DCOrgY + ycenter;
    }
    XDrawLines( display, dc->u.x.drawable, dc->u.x.gc,
	        points, lines+1, CoordModeOrigin );
    return TRUE;
}


/***********************************************************************
 *           Arc    (GDI.23)
 */
BOOL Arc( HDC hdc, int left, int top, int right, int bottom,
	  int xstart, int ystart, int xend, int yend )
{
    return GRAPH_DrawArc( hdc, left, top, right, bottom,
			  xstart, ystart, xend, yend, 0 );
}


/***********************************************************************
 *           Pie    (GDI.26)
 */
BOOL Pie( HDC hdc, int left, int top, int right, int bottom,
	  int xstart, int ystart, int xend, int yend )
{
    return GRAPH_DrawArc( hdc, left, top, right, bottom,
			  xstart, ystart, xend, yend, 2 );
}


/***********************************************************************
 *           Chord    (GDI.348)
 */
BOOL Chord( HDC hdc, int left, int top, int right, int bottom,
	    int xstart, int ystart, int xend, int yend )
{
    return GRAPH_DrawArc( hdc, left, top, right, bottom,
			  xstart, ystart, xend, yend, 1 );
}


/***********************************************************************
 *           Ellipse    (GDI.24)
 */
BOOL Ellipse( HDC hdc, int left, int top, int right, int bottom )
{
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaParam4(dc, META_ELLIPSE, left, top, right, bottom);
	return 0;
    }

    left   = XLPTODP( dc, left );
    top    = YLPTODP( dc, top );
    right  = XLPTODP( dc, right );
    bottom = YLPTODP( dc, bottom );
    if ((left == right) || (top == bottom)) return FALSE;

    if (right < left)
    	swap_int(&right, &left);

    if (bottom < top)
    	swap_int(&bottom, &top);
    
    if (DC_SetupGCForBrush( dc ))
	XFillArc( display, dc->u.x.drawable, dc->u.x.gc,
		  dc->w.DCOrgX + left, dc->w.DCOrgY + top,
		  right-left-1, bottom-top-1, 0, 360*64 );
    if (DC_SetupGCForPen( dc ))
	XDrawArc( display, dc->u.x.drawable, dc->u.x.gc,
		  dc->w.DCOrgX + left, dc->w.DCOrgY + top,
		  right-left-1, bottom-top-1, 0, 360*64 );
    return TRUE;
}


/***********************************************************************
 *           Rectangle    (GDI.27)
 */
BOOL Rectangle( HDC hdc, int left, int top, int right, int bottom )
{
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaParam4(dc, META_RECTANGLE, left, top, right, bottom);
	return TRUE;
    }
    left   = XLPTODP( dc, left );
    top    = YLPTODP( dc, top );
    right  = XLPTODP( dc, right );
    bottom = YLPTODP( dc, bottom );

    if (right < left)
    	swap_int(&right, &left);

    if (bottom < top)
    	swap_int(&bottom, &top);

    if ((left == right) || (top == bottom)) {
	if (DC_SetupGCForPen( dc ))
	    XDrawLine(display, dc->u.x.drawable, dc->u.x.gc, 
		  dc->w.DCOrgX + left,
		  dc->w.DCOrgY + top,
		  dc->w.DCOrgX + right,
		  dc->w.DCOrgY + bottom);
	return TRUE;
    }
    
    if (DC_SetupGCForBrush( dc ))
	XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + left, dc->w.DCOrgY + top,
		        right-left, bottom-top );
    if (DC_SetupGCForPen( dc ))
	XDrawRectangle( display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + left, dc->w.DCOrgY + top,
		        right-left-1, bottom-top-1 );
    return TRUE;
}


/***********************************************************************
 *           RoundRect    (GDI.28)
 */
BOOL RoundRect( HDC hDC, short left, short top, short right, short bottom,
					short ell_width, short ell_height)
{
    int		x1, y1, x2, y2;
    DC * dc = (DC *) GDI_GetObjPtr(hDC, DC_MAGIC);
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hDC, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaParam6(dc, META_ROUNDRECT, left, top, right, bottom,
		      ell_width, ell_height);
	return TRUE;
    }
    dprintf_graphics(stddeb, "RoundRect(%d %d %d %d  %d %d\n", 
    	left, top, right, bottom, ell_width, ell_height);
    x1 = XLPTODP(dc, left);
    y1 = YLPTODP(dc, top);
    x2 = XLPTODP(dc, right - ell_width);
    y2 = YLPTODP(dc, bottom - ell_height);
    if (DC_SetupGCForBrush(dc)) {
	XFillArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x1, dc->w.DCOrgY + y1,
		        ell_width, ell_height, 90 * 64, 90 * 64);
	XFillArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x1, dc->w.DCOrgY + y2,
		        ell_width, ell_height, 180 * 64, 90 * 64);
	XFillArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x2, dc->w.DCOrgY + y2,
		        ell_width, ell_height, 270 * 64, 90 * 64);
	XFillArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x2, dc->w.DCOrgY + y1,
		        ell_width, ell_height, 0, 90 * 64);
	ell_width /= 2;  ell_height /= 2;
	XFillRectangle(display, dc->u.x.drawable, dc->u.x.gc,
		dc->w.DCOrgX + left + ell_width, dc->w.DCOrgY + top,
		right - left - 2 * ell_width, bottom - top);
	XFillRectangle(display, dc->u.x.drawable, dc->u.x.gc,
		dc->w.DCOrgX + left, dc->w.DCOrgY + top + ell_height,
		ell_width, bottom - top - 2 * ell_height);
	XFillRectangle(display, dc->u.x.drawable, dc->u.x.gc,
		dc->w.DCOrgX + right - ell_width, dc->w.DCOrgY + top + ell_height,
		ell_width, bottom - top - 2 * ell_height);
	ell_width *= 2;  ell_height *= 2;
	}    	
    if (DC_SetupGCForPen(dc)) {
	XDrawArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x1, dc->w.DCOrgY + y1,
		        ell_width, ell_height, 90 * 64, 90 * 64);
	XDrawArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x1, dc->w.DCOrgY + y2,
		        ell_width, ell_height, 180 * 64, 90 * 64);
	XDrawArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x2, dc->w.DCOrgY + y2,
		        ell_width, ell_height, 270 * 64, 90 * 64);
	XDrawArc(display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + x2, dc->w.DCOrgY + y1,
		        ell_width, ell_height, 0, 90 * 64);
	}
    ell_width /= 2;  ell_height /= 2;
    MoveTo(hDC, left, top + ell_height);
    LineTo(hDC, left, bottom - ell_height);
    MoveTo(hDC, left + ell_width, bottom);
    LineTo(hDC, right - ell_width, bottom);
    MoveTo(hDC, right, bottom - ell_height);
    LineTo(hDC, right, top + ell_height);
    MoveTo(hDC, right - ell_width, top);
    LineTo(hDC, left + ell_width, top);
    return TRUE;
}


/***********************************************************************
 *           FillRect    (USER.81)
 */
int FillRect( HDC hdc, LPRECT rect, HBRUSH hbrush )
{
    HBRUSH prevBrush;

    if ((rect->right <= rect->left) || (rect->bottom <= rect->top)) return 0;
    if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
    PatBlt( hdc, rect->left, rect->top,
	    rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
    SelectObject( hdc, prevBrush );
    return 1;
}


/***********************************************************************
 *           InvertRect    (USER.82)
 */
void InvertRect( HDC hdc, LPRECT rect )
{
    if ((rect->right <= rect->left) || (rect->bottom <= rect->top)) return;
    PatBlt( hdc, rect->left, rect->top,
	    rect->right - rect->left, rect->bottom - rect->top, DSTINVERT );
}


/***********************************************************************
 *           FrameRect    (USER.83)
 */
int FrameRect( HDC hdc, LPRECT rect, HBRUSH hbrush )
{
    HBRUSH prevBrush;
    int left, top, right, bottom;

    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) return FALSE;

    if ((rect->right <= rect->left) || (rect->bottom <= rect->top)) return 0;
    if (!(prevBrush = SelectObject( hdc, hbrush ))) return 0;
    
    left   = XLPTODP( dc, rect->left );
    top    = YLPTODP( dc, rect->top );
    right  = XLPTODP( dc, rect->right );
    bottom = YLPTODP( dc, rect->bottom );
    
    if (DC_SetupGCForBrush( dc )) {
   	PatBlt( hdc, rect->left, rect->top, 1,
	    rect->bottom - rect->top, PATCOPY );
	PatBlt( hdc, rect->right - 1, rect->top, 1,
	    rect->bottom - rect->top, PATCOPY );
	PatBlt( hdc, rect->left, rect->top,
	    rect->right - rect->left, 1, PATCOPY );
	PatBlt( hdc, rect->left, rect->bottom - 1,
	    rect->right - rect->left, 1, PATCOPY );
	}    
    SelectObject( hdc, prevBrush );
    return 1;
}


/***********************************************************************
 *           SetPixel    (GDI.31)
 */
COLORREF SetPixel( HDC hdc, short x, short y, COLORREF color )
{
    int pixel;
    PALETTEENTRY entry;
    
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return 0;
	MF_MetaParam4(dc, META_SETPIXEL, x, y, HIWORD(color), LOWORD(color)); 
	return 1;
    }

    x = dc->w.DCOrgX + XLPTODP( dc, x );
    y = dc->w.DCOrgY + YLPTODP( dc, y );
    pixel = COLOR_ToPhysical( dc, color );
    GetPaletteEntries( dc->w.hPalette, pixel, 1, &entry );
    
    XSetForeground( display, dc->u.x.gc, pixel );
    XSetFunction( display, dc->u.x.gc, GXcopy );
    XDrawPoint( display, dc->u.x.drawable, dc->u.x.gc, x, y );

    return RGB( entry.peRed, entry.peGreen, entry.peBlue );
}


/***********************************************************************
 *           GetPixel    (GDI.83)
 */
COLORREF GetPixel( HDC hdc, short x, short y )
{
    PALETTEENTRY entry;
    XImage * image;
    
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) return 0;

#ifdef SOLITAIRE_SPEED_HACK
    return 0;
#endif

    x = dc->w.DCOrgX + XLPTODP( dc, x );
    y = dc->w.DCOrgY + YLPTODP( dc, y );
    if ((x < 0) || (y < 0)) return 0;
    
    if (!(dc->w.flags & DC_MEMORY))
    {
	XWindowAttributes win_attr;
	
	if (!XGetWindowAttributes( display, dc->u.x.drawable, &win_attr ))
	    return 0;
	if (win_attr.map_state != IsViewable) return 0;
	if ((x >= win_attr.width) || (y >= win_attr.height)) return 0;
    }
    
    image = XGetImage( display, dc->u.x.drawable, x, y,
		       1, 1, AllPlanes, ZPixmap );
    GetPaletteEntries( dc->w.hPalette, XGetPixel( image, 0, 0 ), 1, &entry );
    XDestroyImage( image );
    return RGB( entry.peRed, entry.peGreen, entry.peBlue );
}


/***********************************************************************
 *           PaintRgn    (GDI.43)
 */
BOOL PaintRgn( HDC hdc, HRGN hrgn )
{
    RECT box;
    HRGN tmpVisRgn, prevVisRgn;
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) return FALSE;

      /* Modify visible region */

    if (!(prevVisRgn = SaveVisRgn( hdc ))) return FALSE;
    if (!(tmpVisRgn = CreateRectRgn( 0, 0, 0, 0 )))
    {
        RestoreVisRgn( hdc );
        return FALSE;
    }
    CombineRgn( tmpVisRgn, prevVisRgn, hrgn, RGN_AND );
    SelectVisRgn( hdc, tmpVisRgn );
    DeleteObject( tmpVisRgn );

      /* Fill the region */

    GetClipBox( hdc, &box );
    if (DC_SetupGCForBrush( dc ))
	XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + box.left, dc->w.DCOrgY + box.top,
		        box.right-box.left, box.bottom-box.top );

      /* Restore the visible region */

    RestoreVisRgn( hdc );
    return TRUE;
}


/***********************************************************************
 *           FillRgn    (GDI.40)
 */
BOOL FillRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush )
{
    BOOL retval;
    HBRUSH prevBrush = SelectObject( hdc, hbrush );
    if (!prevBrush) return FALSE;
    retval = PaintRgn( hdc, hrgn );
    SelectObject( hdc, prevBrush );
    return retval;
}


/***********************************************************************
 *           InvertRgn    (GDI.42)
 */
BOOL InvertRgn( HDC hdc, HRGN hrgn )
{
    HBRUSH prevBrush = SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
    WORD prevROP = SetROP2( hdc, R2_NOT );
    BOOL retval = PaintRgn( hdc, hrgn );
    SelectObject( hdc, prevBrush );
    SetROP2( hdc, prevROP );
    return retval;
}


/***********************************************************************
 *           DrawFocusRect    (USER.466)
 */
void DrawFocusRect( HDC hdc, LPRECT rc )
{
    HPEN hOldPen;
    int oldDrawMode, oldBkMode;
    int left, top, right, bottom;
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) return;

    left   = XLPTODP( dc, rc->left );
    top    = YLPTODP( dc, rc->top );
    right  = XLPTODP( dc, rc->right );
    bottom = YLPTODP( dc, rc->bottom );
    
    hOldPen = (HPEN)SelectObject(hdc, sysColorObjects.hpenWindowText );
    oldDrawMode = SetROP2(hdc, R2_XORPEN);
    oldBkMode = SetBkMode(hdc, TRANSPARENT);

    if (DC_SetupGCForPen( dc ))
	XDrawRectangle( display, dc->u.x.drawable, dc->u.x.gc,
		        dc->w.DCOrgX + left, dc->w.DCOrgY + top,
		        right-left-1, bottom-top-1 );

    SetBkMode(hdc, oldBkMode);
    SetROP2(hdc, oldDrawMode);
    SelectObject(hdc, (HANDLE)hOldPen);
}


/**********************************************************************
 *          GRAPH_DrawBitmap
 *
 * Short-cut function to blit a bitmap into a device.
 * Faster than CreateCompatibleDC() + SelectBitmap() + BitBlt() + DeleteDC().
 */
BOOL GRAPH_DrawBitmap( HDC hdc, HBITMAP hbitmap, int xdest, int ydest,
		       int xsrc, int ysrc, int width, int height, int rop )
{
    XGCValues val;
    BITMAPOBJ *bmp;
    DC *dc;
    
    if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return FALSE;
    if (!(bmp = (BITMAPOBJ *) GDI_GetObjPtr( hbitmap, BITMAP_MAGIC )))
	return FALSE;
    val.function   = DC_XROPfunction[(rop >> 16) & 0x0f];
    val.foreground = dc->w.textPixel;
    val.background = dc->w.backgroundPixel;
    XChangeGC(display, dc->u.x.gc, GCFunction|GCForeground|GCBackground, &val);
    if (bmp->bitmap.bmBitsPixel == 1)
    {
	XCopyPlane( display, bmp->pixmap, dc->u.x.drawable, dc->u.x.gc,
		    xsrc, ysrc, width, height,
		    dc->w.DCOrgX + xdest, dc->w.DCOrgY + ydest, 1 );
	return TRUE;
    }
    else if (bmp->bitmap.bmBitsPixel == dc->w.bitsPerPixel)
    {
	XCopyArea( display, bmp->pixmap, dc->u.x.drawable, dc->u.x.gc,
		   xsrc, ysrc, width, height,
		   dc->w.DCOrgX + xdest, dc->w.DCOrgY + ydest );
	return TRUE;
    }
    else return FALSE;
}


/**********************************************************************
 *          GRAPH_DrawReliefRect  (Not a MSWin Call)
 */
void GRAPH_DrawReliefRect( HDC hdc, RECT *rect, int highlight_size,
                           int shadow_size, BOOL pressed )
{
    HBRUSH hbrushOld;
    int i;

    hbrushOld = SelectObject( hdc, pressed ? sysColorObjects.hbrushBtnShadow :
			                  sysColorObjects.hbrushBtnHighlight );
    for (i = 0; i < highlight_size; i++)
    {
	PatBlt( hdc, rect->left + i, rect->top,
	        1, rect->bottom - rect->top - i, PATCOPY );
	PatBlt( hdc, rect->left, rect->top + i,
	        rect->right - rect->left - i, 1, PATCOPY );
    }

    SelectObject( hdc, pressed ? sysColorObjects.hbrushBtnHighlight :
		                 sysColorObjects.hbrushBtnShadow );
    for (i = 0; i < shadow_size; i++)
    {
	PatBlt( hdc, rect->right - i - 1, rect->top + i,
	        1, rect->bottom - rect->top - i, PATCOPY );
	PatBlt( hdc, rect->left + i, rect->bottom - i - 1,
	        rect->right - rect->left - i, 1, PATCOPY );
    }

    SelectObject( hdc, hbrushOld );
}


/**********************************************************************
 *          Polyline  (GDI.37)
 */
BOOL Polyline (HDC hdc, LPPOINT pt, int count)
{
    register int i;
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaPoly(dc, META_POLYLINE, pt, count); 
	return TRUE;
    }

    if (DC_SetupGCForPen( dc ))
    {
	for (i = 0; i < count-1; i ++)
	    XDrawLine (display, dc->u.x.drawable, dc->u.x.gc,  
		       dc->w.DCOrgX + XLPTODP(dc, pt [i].x),
		       dc->w.DCOrgY + YLPTODP(dc, pt [i].y),
		       dc->w.DCOrgX + XLPTODP(dc, pt [i+1].x),
		       dc->w.DCOrgY + YLPTODP(dc, pt [i+1].y));
	XDrawLine (display, dc->u.x.drawable, dc->u.x.gc,  
		   dc->w.DCOrgX + XLPTODP(dc, pt [count-1].x),
		   dc->w.DCOrgY + YLPTODP(dc, pt [count-1].y),
		   dc->w.DCOrgX + XLPTODP(dc, pt [0].x),
		   dc->w.DCOrgY + YLPTODP(dc, pt [0].y));
    } 
	
    return (TRUE);
}


/**********************************************************************
 *          Polygon  (GDI.36)
 */
BOOL Polygon (HDC hdc, LPPOINT pt, int count)
{
    register int i;
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
    XPoint *points = (XPoint *) malloc (sizeof (XPoint) * (count+1));

    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaPoly(dc, META_POLYGON, pt, count); 
	return TRUE;
    }

    for (i = 0; i < count; i++)
    {
	points[i].x = dc->w.DCOrgX + XLPTODP( dc, pt[i].x );
	points[i].y = dc->w.DCOrgY + YLPTODP( dc, pt[i].y );
    }
    points[count] = points[0];

    if (DC_SetupGCForBrush( dc ))
	XFillPolygon( display, dc->u.x.drawable, dc->u.x.gc,
		     points, count+1, Complex, CoordModeOrigin);

    if (DC_SetupGCForPen ( dc ))
	XDrawLines( display, dc->u.x.drawable, dc->u.x.gc,
		   points, count+1, CoordModeOrigin );

    free( points );
    return TRUE;
}


/**********************************************************************
 *          PolyPolygon  (GDI.450)
 */
BOOL PolyPolygon( HDC hdc, LPPOINT pt, LPINT counts, WORD polygons )
{
    HRGN hrgn;
    DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );

    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	/* MF_MetaPoly(dc, META_POLYGON, pt, count); */
	return TRUE;
    }
      /* The points should be converted to device coords before */
      /* creating the region. But as CreatePolyPolygonRgn is not */
      /* really correct either, it doesn't matter much... */
      /* At least the outline will be correct :-) */
    hrgn = CreatePolyPolygonRgn( pt, counts, polygons, dc->w.polyFillMode );
    PaintRgn( hdc, hrgn );
    DeleteObject( hrgn );

      /* Draw the outline of the polygons */

    if (DC_SetupGCForPen ( dc ))
    {
	int i, j, max = 0;
	XPoint *points;

	for (i = 0; i < polygons; i++) if (counts[i] > max) max = counts[i];
	points = (XPoint *) malloc( sizeof(XPoint) * (max+1) );

	for (i = 0; i < polygons; i++)
	{
	    for (j = 0; j < counts[i]; j++)
	    {
		points[j].x = dc->w.DCOrgX + XLPTODP( dc, pt->x );
		points[j].y = dc->w.DCOrgY + YLPTODP( dc, pt->y );
		pt++;
	    }
	    points[j] = points[0];
	    XDrawLines( display, dc->u.x.drawable, dc->u.x.gc,
		        points, j + 1, CoordModeOrigin );
	}
	free( points );
    }
    return TRUE;
}


/**********************************************************************
 *          GRAPH_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.
 */
static void GRAPH_InternalFloodFill( XImage *image, DC *dc,
                                     int x, int y,
                                     int xOrg, int yOrg,
                                     Pixel pixel, WORD fillType )
{
    int left, right;

#define TO_FLOOD(x,y)  ((fillType == FLOODFILLBORDER) ? \
                        (XGetPixel(image,x,y) != pixel) : \
                        (XGetPixel(image,x,y) == pixel))

    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++;
    XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
                    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++)
    {
        if (fillType == FLOODFILLBORDER) XPutPixel( image, x, y, pixel );
        else XPutPixel( image, x, y, ~pixel );
    }

      /* 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++;
            GRAPH_InternalFloodFill( image, dc, x-1, y,
                                     xOrg, yOrg, pixel, fillType );
        }
    }

      /* 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++;
            GRAPH_InternalFloodFill( image, dc, x-1, y,
                                     xOrg, yOrg, pixel, fillType );
        }
    }
#undef TO_FLOOD    
}


/**********************************************************************
 *          ExtFloodFill  (GDI.372)
 */
BOOL ExtFloodFill( HDC hdc, INT x, INT y, COLORREF color, WORD fillType )
{
    RECT rect;
    Pixel pixel;
    XImage *image;
    DC *dc;

    dprintf_graphics( stddeb, "ExtFloodFill %x %d,%d %06lx %d\n",
                      hdc, x, y, color, fillType );
    dc = (DC *) GDI_GetObjPtr(hdc, DC_MAGIC);
    if (!dc) 
    {
	dc = (DC *)GDI_GetObjPtr(hdc, METAFILE_DC_MAGIC);
	if (!dc) return FALSE;
	MF_MetaParam4(dc, META_FLOODFILL, x, y, HIWORD(color), 
		      LOWORD(color)); 
	return TRUE;
    }

    if (!PtVisible( hdc, x, y )) return FALSE;
    if (GetClipBox( hdc, &rect ) == ERROR) return FALSE;
    pixel = COLOR_ToPhysical( dc, color );

    if (!(image = XGetImage( display, dc->u.x.drawable,
                             dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
                             rect.right - rect.left, rect.bottom - rect.top,
                             AllPlanes, ZPixmap ))) return FALSE;

    if (DC_SetupGCForBrush( dc ))
    {
          /* ROP mode is always GXcopy for flood-fill */
        XSetFunction( display, dc->u.x.gc, GXcopy );
          /* We can pass anything except 0 as a region */
        GRAPH_InternalFloodFill( image, dc,
                                 XLPTODP(dc,x) - rect.left,
                                 YLPTODP(dc,y) - rect.top,
                                 dc->w.DCOrgX + rect.left,
                                 dc->w.DCOrgY + rect.top,
                                 pixel, fillType );
    }

    XDestroyImage( image );
    return TRUE;
}


/**********************************************************************
 *          FloodFill  (GDI.25)
 */
BOOL FloodFill( HDC hdc, INT x, INT y, COLORREF color )
{
    return ExtFloodFill( hdc, x, y, color, FLOODFILLBORDER );
}
