/********************************************************************
*   DrvGraphics.c
*
*   Macintosh device specific graphics sub-system implementation.
*
*   Copyright (c) 1994-1997, Willows Software Inc.  All rights reserved.  
********************************************************************/

/* Standard Includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <QDOffscreen.h>

/* Driver includes */
#include "DrvHook.h"
#include "DrvDC.h"
#include "DrvSystem.h"
#include "DrvGraphics.h"
#include "DrvColors.h"
#include "DrvROPCodes.h"
#include "DrvUtils.h"
#include "DeviceData.h"
#include "DrvDP.h"
#include "Log.h"
#include "DrvErrors.h"

/* Prototypes */
COLORREF DrvSetPixel(DRIVERDC *dc, COLORREF dwParam, int horiz, int vert);
COLORREF DrvGetPixel(DRIVERDC *dc, int horiz, int vert);
Boolean SelectPen(DRIVERDC *lpddc, Rect *theRect);
Boolean SelectBrush(LPDRIVERDC lpddc, int *brushFlag, void **fillPat, int nROP);
void DrvPenPosition(DRIVERDC *dc, short set, short relative, int *horiz, int *vert);
void DrvLineFromTo(DRIVERDC *dc, int startX, int startY, int endX, int endY);
void DrvRectangle(DRIVERDC *dc, Rect *theRect);
void DrvRoundRect(DRIVERDC *dc, Rect *theRect, int ovalWidth, int ovalHeight);
void DrvEllipse(DRIVERDC *dc, Rect *theRect);	
void DrvArc(DRIVERDC *dc, int type, Rect *boundRect, Point startPt, Point stopPt);
void DrvPolygon(DRIVERDC *dc, short relative, void *winpts, short numPts, BOOL shouldClose);

/* Local Prototypes */
static void DrvLine(DRIVERDC *lpddc, BOOL relative, POINT pt);
static void DoRasterOps(LPDRIVERDC lpDestDC, LPDRIVERDC lpSrcDC, PixMapHandle srcPixMap, PixMapHandle destPixMap, Rect srcRect, Rect destRect, DWORD rasterOp, BOOL okToModSrc);
static DWORD DrvPatBlt(DRIVERDC *lpddc, Rect *destRect, int rasterOp);
static DWORD DrvFloodFill(LPDRIVERDC lpddc, LPLSDS_PARAMS lpStruct);
static int DrvGetDeviceCaps(LPDRIVERDC lpddc, int iCapability);
static OSErr NewBitMap(BitMap *theBits, Rect *rp);
static void DisposeBitMap(BitMap *theBits);

static BOOL 	DrvStretchBlt(LPDRIVERDC, LPLSDS_PARAMS);
static DWORD DrvSelectImage(LPDRIVERDC lpddc, LSDE_PUTIMAGEDATA *lppid);
static DWORD DrvPutImage(LPDRIVERDC lpddc, LSDE_PUTIMAGEDATA *lppid);
static DWORD DrvScrollArea(LPDRIVERDC lpddc, short xSrc, short ySrc, short width, short height, short xDest, short yDest);
void ManualRasterOps(PixMapHandle srcPixmap, PixMapHandle destPixmap,
								PixMapHandle brushPixmap, PixMapHandle resultPixmap,
								DWORD rasterOp);

extern void flip(LPINT,int);
extern int stretch(int,int,LPINT);

static short IsStretched(Rect *srcRect, Rect *destRect);
static void NormalizeRect(Rect *r, short reset);

/* Local Prototypes */
static Point PtOnArc(double angle, int radiusX, int radiusY, const Point *centerPt);
static double Pt2Angle(Point centerPt, int radiusX, int radiusY, Point thePt);
static void FillChord(DRIVERDC *lpddc, Rect *theRect, int startAngle,  int arcAngle, Point startPtOnArc, Point stopPtOnArc, Point centerPt);
static PolyHandle BuildPolygon(short relative, void *winpts, short numPts, BOOL shouldClose);
static void DrawArc(DRIVERDC *lpddc, int type, Rect *theRect, int startAngle, int arcAngle, Point startPtOnArc, Point stopPtOnArc, Point centerPt);
PixMapHandle myCopyPixMap(PixMapHandle srcPixMap);
static void DrvFillRectangle(DRIVERDC *lpddc, PixMapHandle pixmap, Rect *theRect, LPWORD lpRops);
static void DrvCopyArea(PixMapHandle srcPixmap, PixMapHandle destPixmap, Point srcOrigin, Point rectSize, Point destOrigin, LPWORD lpRops);
static void MyCopyBits(PixMapPtr srcPixmap, PixMapPtr destPixmap, RectPtr srcRect, RectPtr destRect, int rasterOp);


static DWORD OldDrawArc(LPDRIVERDC dc, LPPOINT lparc, WORD wFunc);

/* A code reduction macro */
#define FILLRECTWITHBRUSH(brush, pattern, rect)	\
	switch(brush)			\
	{ case BFP_PIXEL:	PaintRect(rect);	break;	case BFP_BITMAP: FillRect(rect, pattern);		break;		case BFP_PIXMAP: FillCRect(rect, pattern);	break;		case BFP_NULL: default:		break;	}										/* No fill */


/* Some handy globals */
RGBColor blkRgbColor, whtRgbColor;

Pattern gDashPatterns[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,				/* PS_SOLID */
0xFC, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,			/* PS_DASH */
0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,		/* PS_DOT */
//0xAA, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,			/* PS_DOT */
0xE4, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00,			/* PS_DASHDOT */
0xEA, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,			/* PS_DASHDOTDOT */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,			/* PS_NULL */
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,				/* PS_INSIDEFRAME */
0,0,0,0,0,0,0,0
};

/********************************************************************
*   PrivateGraphicsHook
*
*  Switch to the implement the desired function. 
********************************************************************/
DWORD PrivateGraphicsHook(WORD msg, LPARAM lp, LPARAM dwParam, LPVOID lpVoid)
{
    LPLSDS_PARAMS 		lpStruct = (LPLSDS_PARAMS)lpVoid;	
	LPDRIVERDC 				lpddc = (LPDRIVERDC)lp;
	POINT 						oldPt;
	int 							i, nCount, tempBrush;
	Rect							macRect;			/* Mac specific types */
	
	switch (msg) {
	/* (dwParm)?INITIALIZE:DEINITIALIZE */
	case DSUBSYSTEM_INIT:
		/* Set the global colors */
		COLORREFTOMACRGB(0, &blkRgbColor);
		COLORREFTOMACRGB(0xFFFFFF, &whtRgbColor);
	
		return (DWORD)TRUE;

	case DSUBSYSTEM_GETCAPS:
		return(0L);

	case DSUBSYSTEM_EVENTS:
		return (DWORD)0;

	/* dwParam -- get/set flag  */
	/* lpStruct -- lppt		*/
	case PGH_MOVETO:	  
		/* Get the current point to pass back when done */
		oldPt = lpddc->cpt;
	    
		/* Set the new point if needed */
		if (dwParam == LSDM_SET)
			lpddc->cpt = lpStruct->lsde.point;

		/* Return the old point in the structure */
	    lpStruct->lsde.point = oldPt;
	    
		return PGH_SUCCESS;


	/* dwParam -- RelAbs flag */
	/* lpStruct -- LSDS_POINT       */
	case PGH_LINETO:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);

		/* Call wrapper function to perform line drawing */
		DrvLine(lpddc, (dwParam == RELATIVE), lpStruct->lsde.point);

		return PGH_SUCCESS;


	/* lpStruct -- LSDS_RECT * */
	case PGH_RECTANGLE:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc,lpStruct);

		/* Convert to mac rectangle (works for LSDS rects) */
		WINRECT2MACRECT(&lpStruct->lsde.rect, &macRect);
		
		/* Perform actual drawing */
		DrvRectangle(lpddc, &macRect);
							
	    return PGH_SUCCESS;


	/* lpStruct -- points to LSDS_ARC */
	case PGH_ROUNDRECT:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);
		
		/* Get rect coords */
		macRect.left = lpStruct->lsde.arc[0].x;		macRect.right  = lpStruct->lsde.arc[1].x;
		macRect.top  = lpStruct->lsde.arc[0].y;	macRect.bottom = lpStruct->lsde.arc[1].y;	
		
		/* Perform actual drawing */
		DrvRoundRect(lpddc, &macRect, 
								lpStruct->lsde.arc[2].x, 
								lpStruct->lsde.arc[2].y);
						  
		return PGH_SUCCESS;
	
	
	/* lpStruct -- LSDS_RECT * */
	case PGH_ELLIPSE:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);

		/* Convert to mac rectangle (works for LSDS rects) */
		WINRECT2MACRECT(&lpStruct->lsde.rect, &macRect);

		/* Perform actual drawing */
		DrvEllipse(lpddc, &macRect);

		return PGH_SUCCESS;


	/* dwParam -- function (ARC/PIE/CHORD) */
	/* lpStruct -- points to LSDS_ARC */
	case PGH_ARC: {
		LPPOINT lparc = lpStruct->lsde.arc;
		Point startPt, stopPt;
		
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);

		/* Setup the boundary rectangle */
		macRect.left = lparc[0].x;		macRect.right  = lparc[1].x;
		macRect.top  = lparc[0].y;	macRect.bottom = lparc[1].y;

		startPt.h = lparc[2].x;				startPt.v = lparc[2].y;
		stopPt.h = lparc[3].x;				stopPt.v = lparc[3].y;

		DrvArc(lpddc, dwParam, &macRect, startPt, stopPt);
	
		return PGH_SUCCESS;
//		return OldDrawArc(lpddc, lpStruct->lsde.arc, (WORD)dwParam);
	}
	/* dwParam -- RelAbs flag */
	/* lpStruct -- points to LSDS_POLYPOLY */
	case PGH_POLYLINE:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);

		/* Use the poly function for now, it is only four ticks (with 1000 iterations) slower than drawing lines */
		tempBrush = lpddc->brushFlag;					/* Save current brush */
		lpddc->brushFlag = BFP_NULL;					/* Force brush to NULL, drawing lines only */
		
		DrvPolygon(lpddc, (dwParam == RELATIVE),
							lpStruct->lsde.polypoly.lpPoints,
							lpStruct->lsde.polypoly.nTotalCount, FALSE);
							
		lpddc->brushFlag = tempBrush;					/* Restore brush */
	    return PGH_SUCCESS;

	/* dwParam -- RelAbs flag */
	/* lpStruct -- points to LSDS_POLYPOLY */
	case PGH_POLYGON:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);

		DrvPolygon(lpddc, (dwParam == RELATIVE),
							lpStruct->lsde.polypoly.lpPoints,
							lpStruct->lsde.polypoly.nTotalCount, TRUE);
	
		    return PGH_SUCCESS;

    /* dwParam -- RelAbs flag */
    /* lpStruct -- points to LSDS_POLYPOLY */
	case PGH_POLYPOLYGON:
		/* Validate the DC if needed */
	    if (lpStruct->lsde_validate.dwInvalidMask)
			DrvValidate(lpddc, lpStruct);

		/* Loop through the nested polygons, drawing each one individually */
	    for (i=0, nCount=0;  i < lpStruct->lsde.polypoly.nCount; i++) {
			DrvPolygon(lpddc,  
							(dwParam == RELATIVE),
							&(lpStruct->lsde.polypoly.lpPoints[nCount]),
							lpStruct->lsde.polypoly.lpCounts[i], TRUE);

			nCount += lpStruct->lsde.polypoly.lpCounts[i];
		}


	/* lpStruct -- points to LSDS_FLOODFILL */
	case PGH_FLOODFILL:
		return DrvFloodFill(lpddc, lpStruct);

	/* lpStruct points to LPLSDS_PARAMS */
	case PGH_STRETCHBLT:
	    return DrvStretchBlt(lpddc, lpStruct);

	/* lpStruct points to LPLSDS_PARAMS */
	case PGH_SELECTIMAGE:
	    return DrvSelectImage(lpddc, &lpStruct->lsde.imagedata);

	case PGH_PUTIMAGE:
	    /* lpStruct points to PUTIMAGEDATA */
	    return DrvPutImage(lpddc,&lpStruct->lsde.imagedata);

	case PGH_SETPIXEL:
	    /* lpStruct points to POINT		*/
	    /* dwParam contains COLORREF	*/
	    return DrvSetPixel(lpddc, (COLORREF)dwParam,lpStruct->lsde.point.x, lpStruct->lsde.point.y);


	case PGH_GETPIXEL:
	    /* lpStruct points to POINT		*/
	    return DrvGetPixel(lpddc, lpStruct->lsde.point.x, lpStruct->lsde.point.y);

	case PGH_SETBRUSHORG:
	    /* lpStruct points to POINT		*/
#ifdef LATER
	    XSetTSOrigin(lpddc->display,lpddc->gc,
			(LPPOINT)lpStruct->x,(LPPOINT)lpStruct->y);
	    return PGH_SUCCESS;
#else
		return(0L);
#endif

	case PGH_GETASPECTRATIO:
	    /* lpStruct points to SIZE		*/
/*	    ((LPSIZE)lpStruct)->cx =
		(DisplayWidthMM(lpddc->dp->display,lpddc->dp->screen)*1000)/
		DisplayWidth(lpddc->dp->display,lpddc->dp->screen);
	    ((LPSIZE)lpStruct)->cy =
		(DisplayHeightMM(lpddc->dp->display,lpddc->dp->screen)*1000)/
		DisplayHeight(lpddc->dp->display,lpddc->dp->screen);

	    return 1L;
*/
		return 0L;
		
	case PGH_GETDEVICECAPS:
	    /* dwParam -- capability index */
	    return DrvGetDeviceCaps(lpddc, dwParam);

	case PGH_SCROLLDC:
		DrvScrollArea(lpddc,
								lpStruct->lsde.scrolldc.xSrc,
								lpStruct->lsde.scrolldc.ySrc,
								lpStruct->lsde.scrolldc.nWidth,
								lpStruct->lsde.scrolldc.nHeight,
								lpStruct->lsde.scrolldc.xDest,
								lpStruct->lsde.scrolldc.yDest);
	    return (DWORD)TRUE;

    }
    
	return(0L);

}


/********************************************************************
*   DrvLine
*
*  If relative=true, draw line from cur position, dist h and v.
*  If relative=false, draw line to abs pos h and v. 
*  Changes current pen position.
********************************************************************/
static void DrvLine(DRIVERDC *lpddc, BOOL relative, POINT pt)
{
int delta;
POINT modPt;

	DrvSelectDC(SAVE, lpddc);						/* Save and set the ports */

	if (!SelectPen(lpddc, NULL)) {					/* Pen is not valid, move to the new point like an invisible pen */
		lpddc->cpt.x += pt.x;
		lpddc->cpt.y += pt.y;
	}
	else {														/* Pen is valid, draw the line */
		MoveTo(lpddc->cpt.x, lpddc->cpt.y);		/* Position current point */
		if (relative) {
			Line(pt.x, pt.y);								/* Moves relative to cur position */
			lpddc->cpt.x += pt.x;						/* Update current point, relative */
			lpddc->cpt.y += pt.y;
		}
		else {
			modPt = pt;
			/* Windows does not set the final pixel, so we need to adjust the point */
			delta = pt.x - lpddc->cpt.x;
			if (delta > 0)
				modPt.x -= 1;
			else if (delta < 0)
				modPt.x += 1;
			delta = pt.y - lpddc->cpt.y;
			if (delta > 0)
				modPt.y -= 1;
			else if (delta < 0)
				modPt.y += 1;
				
			LineTo(modPt.x, modPt.y);							/* Move to absolute position */
			lpddc->cpt.x = pt.x;							/* Update absolute current point */
			lpddc->cpt.y = pt.y;
		}
	}
	
	DrvSelectDC(RESTORE, NULL);					/* Restore old port */
}



/********************************************************************
*   OldDrawArc
*
*  Call the driver arc function after paramter conversion.
********************************************************************/
static DWORD OldDrawArc(LPDRIVERDC lpddc, LPPOINT lparc, WORD wFunc)
{
Rect boundsRect;
Point startPt, stopPt;
int type = wFunc;

	/* Setup the boundary rectangle */
	boundsRect.left = lparc[0].x;		boundsRect.right  = lparc[1].x;
	boundsRect.top  = lparc[0].y;		boundsRect.bottom = lparc[1].y;

	startPt.h = lparc[2].x;				startPt.v = lparc[2].y;
	stopPt.h = lparc[3].x;				stopPt.v = lparc[3].y;

	DrvArc(lpddc, type, &boundsRect, startPt, stopPt);
	
	return PGH_SUCCESS;

}




/********************************************************************
*   DrvValidate
*
*  Validate the DC parameters as needed.
********************************************************************/
DWORD DrvValidate(LPDRIVERDC lpddc, LPLSDS_PARAMS lpStruct)
{
    LSDE_VALIDATE *lpvs = &lpStruct->lsde_validate;
    DWORD dwMask = lpvs->dwInvalidMask;
    DWORD dwBkPixel;

	/* Poly fill mode */
    if (dwMask & IM_POLYFILLMODEMASK)
		lpddc->polyFillMode = lpvs->PolyFillMode;		/* WINDING or ALTERNATE */  	

	/* Raster logical operation */
    if (dwMask & IM_ROP2MASK) {
		lpddc->ropFunction = lpvs->ROP2;
  	}

	/* Stretch mode */
    if (dwMask & IM_STRETCHMODEMASK) 
		lpddc->stretchMode = lpvs->StretchMode;

	/* Background fill mode */
    if (dwMask & IM_BKMODEMASK) 
		lpddc->bkFillMode = lpvs->BackMode;

	/* Text color */	
    if (dwMask & IM_TEXTCOLORMASK) 
		lpddc->textColor = lpvs->TextColor;			/* COLOREF */

	/* Background color */
    if (dwMask & (IM_BKCOLORMASK | IM_SRCBKCOLORMASK)) {
		if (dwMask & IM_BKCOLORMASK)
			dwBkPixel = lpvs->BackColor;
		else
			dwBkPixel = lpvs->SrcDCBkColor;
	
		if(lpddc->backColor != dwBkPixel)
			lpddc->backColor = dwBkPixel;		/* Set the background color of the lpddc */

    }

	/* Brush color, type is COLOREF */
    if (dwMask & IM_BRUSHCOLORMASK) 
		lpddc->brushColor = lpvs->BrushColor;

	/* Brush style and pattern */
    if (dwMask & IM_BRUSHFILLMASK) {
		lpddc->brushFlag = lpvs->BrushFlag;
		switch (lpddc->brushFlag) {
	    case BFP_BITMAP:							/* Pattern pointer */
	    case BFP_PIXMAP:							/* PixPatHandle */
	    	lpddc->brushFillPat = lpvs->BrushPrivate;		
			break;
	    case BFP_NULL:
	    case BFP_PIXEL:
	    default:
			break;
		}
    }

	/* Pen color, type is a pixel index */
    if (dwMask & IM_PENCOLORMASK) 
		lpddc->penColor = lpvs->PenColor;

	/* Pen style */
	if (dwMask & IM_PENSTYLEMASK) {
	    lpddc->penStyle = lpvs->PenStyle;
		//LATER: Add the rest of the pen styles when supported!
	}
	
	/* Pen width */	    
	if (dwMask & IM_PENWIDTHMASK) {
	    lpddc->penWidth = lpvs->PenWidth;
	    lpddc->penHeight = lpvs->PenHeight;
	}
	
    return 0L;			
}


/********************************************************************
*   DrvSelectImage
*
*  Selects an image into the DC.
********************************************************************/
static DWORD DrvSelectImage(LPDRIVERDC lpddc, LSDE_PUTIMAGEDATA *lppid)
{
LPDRVIMAGEDATA lpid = lppid->lpimagedata;

	if (!lpddc || !lpid || !lpid->image)
		return 0;

	if (lpid->fMono) {

	}

	lpddc->lpDrvImage = lpid;

    return PGH_SUCCESS;
}

/********************************************************************
*   DrvPutImage
*
*  Puts an image directly into the DC.
********************************************************************/
static DWORD DrvPutImage(LPDRIVERDC lpddc, LSDE_PUTIMAGEDATA *lppid)
{
LPDRVIMAGEDATA lpid;
PixMapHandle destPixMap;
Rect srcRect, destRect;

    if(!lppid || !lppid->lpimagedata || !lpddc) 
		return FALSE;

	if (!(lpid = lppid->lpimagedata))
		return FALSE;
	
	DrvSelectDC(SAVE, lpddc);

	RGBForeColor(&blkRgbColor);	RGBBackColor(&whtRgbColor);
	
	/* We are doing a straight put to the screen, adjust rect's accordingly */
	SetRect(&srcRect, lppid->xSrc, lppid->ySrc, lppid->xSrc + lppid->cx, lppid->ySrc + lppid->cy);
	SetRect(&destRect, lppid->xDest, lppid->yDest, lppid->xDest + lppid->cx, lppid->yDest + lppid->cy);
	
	destPixMap = ((CGrafPtr)lpddc->grafPort)->portPixMap;		/* Grafport's pixmap */
	DrvCopyPixMap(lpid->image, destPixMap, srcRect, destRect, srcCopy);

	DrvSelectDC(RESTORE, NULL);
	
	return TRUE;
}

/********************************************************************/
static BOOL DrvStretchBlt(LPDRIVERDC lpddc, LPLSDS_PARAMS lpStruct)
{
	LSDE_STRETCHDATA *lpsd = &lpStruct->lsde.stretchdata;
	int nWidthSrc,nHeightSrc,nWidthDest,nHeightDest;
    DWORD rop;
    BOOL fMirrorX = FALSE,fMirrorY = FALSE;
	LPDRIVERDC lpSrcDC, lpDestDC = lpddc;
	DWORD dwTemp;
	CGrafPtr tempGrafPort, srcGrafPort, destGrafPort;
	PixMapHandle tmpPix = 0, srcPix = 0;
	PixMapHandle imSrc, imDest;
	int x, y;
	RGBColor cPix;
    LPWORD lpRops;
    int	n;
    int nROP2;
    Rect srcRect, destRect, zeroOriginDestRect, zeroOriginSrcRect;
    Point srcOrigin, srcSize, destOrigin, destSize;
    static const Point pointZero = {0, 0};
	RGBColor rgbColor;

    static LPINT xbits, ybits;
    static int nxbits,nybits;

    if (!lpsd || !lpsd->nWidthDest || !lpsd->nHeightDest || 
			!lpsd->nWidthSrc || !lpsd->nHeightSrc)
	return FALSE;

    if (lpsd->hSrcDC32) {
	lpSrcDC = (LPDRIVERDC)lpsd->hSrcDC32;
	if (lpStruct->lsde_validate.dwInvalidMask & IM_SRCBKCOLORMASK) {
	    dwTemp = lpStruct->lsde_validate.dwInvalidMask;
	    lpStruct->lsde_validate.dwInvalidMask = IM_SRCBKCOLORMASK;
	    DrvValidate(lpSrcDC,lpStruct);
	    lpStruct->lsde_validate.dwInvalidMask =
				dwTemp & ~IM_SRCBKCOLORMASK;
	}
    }
    else
	lpSrcDC = 0;

    if (lpStruct->lsde_validate.dwInvalidMask)
	DrvValidate(lpddc,lpStruct);

    /* If nothing is selected into a compatible DC --  fail   */
    if (lpSrcDC && (lpSrcDC->lpDrvImage == 0) &&
		(lpSrcDC->dwDCFlags & DCX_COMPATIBLE_DC))
	    return FALSE;
    if ((lpDestDC->lpDrvImage == 0) &&
		(lpDestDC->dwDCFlags & DCX_COMPATIBLE_DC))
	return FALSE;

    nWidthSrc = lpsd->nWidthSrc;
    nWidthDest = lpsd->nWidthDest;
    nHeightSrc = lpsd->nHeightSrc;
    nHeightDest = lpsd->nHeightDest;

    /* If parameters have different signs, we have to mirror the image */

    if ((nWidthSrc < 0) || (nWidthDest < 0)) {
	if ((nWidthSrc < 0) ^ (nWidthDest < 0)) /* mirror along X-axis */
	    fMirrorX = TRUE;
	if (nWidthSrc < 0) {
	    nWidthSrc = -nWidthSrc;
	    lpsd->xSrc -= nWidthSrc - 1;
	}
	if (nWidthDest < 0) {
	    nWidthDest = -nWidthDest;
	    lpsd->xDest -= nWidthDest - 1;
	}
    }
    if ((nHeightSrc < 0) || (nHeightDest < 0)) {
	if ((nHeightSrc < 0) ^ (nHeightDest < 0)) /* mirror along Y-axis */
	    fMirrorY = TRUE;
	if (nHeightSrc < 0) {
	    nHeightSrc = -nHeightSrc;
	    lpsd->ySrc -= nHeightSrc - 1;
	}
	if (nHeightDest < 0) {
	    nHeightDest = -nHeightDest;
	    lpsd->yDest -= nHeightDest - 1;
	}
    }

    rop = lpsd->dwRop;
    if ((ROPTab[HIWORD(rop)].bRops[0] == 0) || 
		(ROPTab[HIWORD(rop)].dwROP != rop)) {
	LOGSTR((LF_OBJECTS,"StretchBlt: Unsupported ROP %x vs. %x\n",
				rop,ROPTab[HIWORD(rop)].dwROP));
    }

	SETMACRECTFROMXYWH(&srcRect, lpsd->xSrc, lpsd->ySrc, lpsd->nWidthSrc, lpsd->nHeightSrc);
	SETMACRECTFROMXYWH(&destRect, lpsd->xDest, lpsd->yDest, lpsd->nWidthDest, lpsd->nHeightDest);
	SETMACRECTFROMXYWH(&zeroOriginDestRect, 0, 0, lpsd->nWidthDest, lpsd->nHeightDest);
	SETMACRECTFROMXYWH(&zeroOriginSrcRect, 0, 0, lpsd->nWidthSrc, lpsd->nHeightSrc);
	srcOrigin.h = lpsd->xSrc;
	srcOrigin.v = lpsd->ySrc;
	srcSize.h = nWidthSrc;
	srcSize.v = nHeightSrc;
	destOrigin.h = lpsd->xDest;
	destOrigin.v = lpsd ->yDest;
	destSize.h = nWidthDest;
	destSize.v = nHeightDest;

    if (lpSrcDC && lpSrcDC->lpDrvImage && lpSrcDC->lpDrvImage->fMono ) {
    	GrafPtr savePort;
    	
    	if (!lpSrcDC->lpDrvImage->image)
    		LOGSTR((LF_WARNING, "StretchBlt: No image in the fMono case\n"));
		
		/* do I really need to create a temp grafPort? */
		/* Or is it OK to set the colors in the dest grafPort? */
		tempGrafPort = DrvMalloc(sizeof(CGrafPort));
		GetPort(&savePort);
		OpenCPort(tempGrafPort);
		srcPix = CreatePixmapFromRect(srcRect);			/* do I need to normalize srcRect before calling this? */
		SetPortPix(srcPix);

		COLORREFTOMACRGB(lpDestDC->backColor, &rgbColor);
		RGBForeColor(&rgbColor);
		COLORREFTOMACRGB(lpDestDC->penColor, &rgbColor);
		RGBBackColor(&rgbColor);
		HLock((Handle)srcPix);
		HLock((Handle)lpSrcDC->lpDrvImage->image);
		/* let CopyBits do the actual colorization work */
		CopyBits((BitMapPtr)*lpSrcDC->lpDrvImage->image,(BitMapPtr)*srcPix,&srcRect,&srcRect,srcCopy,0);
		/* srcPix now contains a colorized version of the source pixmap */
		HUnlock((Handle)srcPix);
		HUnlock((Handle)lpSrcDC->lpDrvImage->image);
		/* Zap portPixMap in the grafPort so closing the grafPort doesn't dispose of our pixmap */
		SetPortPix(0);
		SetPort(savePort);
		CloseCPort(tempGrafPort);
		DrvFree(tempGrafPort);
	}
	
    if (nWidthSrc != nWidthDest || nHeightSrc != nHeightDest ||
			fMirrorX || fMirrorY) {

	if((nxbits < nWidthDest) || fMirrorX) {
	    if(nxbits) {
		xbits = (LPINT)DrvRealloc((LPSTR)xbits,
						nWidthDest*sizeof(int));
		nxbits = nWidthDest;
	    }
	    else {
		xbits = (LPINT)DrvMalloc(nWidthDest*sizeof(int));
		nxbits = nWidthDest;
	    }
	}
				
	if((nybits < nHeightDest) || fMirrorY) {
	    if(nybits) {
		ybits = (LPINT)DrvRealloc((LPSTR)ybits,
						nHeightDest*sizeof(int));
		nybits = nHeightDest;
	    }
	    else {
		ybits = (LPINT)DrvMalloc(nHeightDest*sizeof(int));
		nybits = nHeightDest;
	    }
	}
			
	stretch(nWidthSrc,nWidthDest,xbits);
	stretch(nHeightSrc,nHeightDest,ybits);

	if (fMirrorX)
	    flip(xbits,nWidthDest);

	if (fMirrorY)
	    flip(ybits,nHeightDest);

	/* obtain the original image */	    
	imSrc = srcPix ? myCopyPixMap(srcPix) : lpSrcDC->lpDrvImage->image;
	
	if(srcPix) {
		DrvDestroyPixmap(srcPix, FALSE);
		srcPix = 0;
	}
	
	/* this will hold the stretched image */
	imDest = CreatePixmapFromRect(destRect);
	
	/* this is where we put the stretched image */
	srcPix = CreatePixmapFromRect(destRect);
	
	if(!(fMirrorX | fMirrorY)) {
		HLock((Handle)imSrc);
		HLock((Handle)imDest);
		CopyBits((BitMapPtr)*imSrc, (BitMapPtr)*imDest, &zeroOriginSrcRect, &zeroOriginDestRect, srcCopy, 0);
		HUnlock((Handle)imSrc);
		HUnlock((Handle)imDest);
	} else 

	{
    	GrafPtr savePort;
    	RGBColor *rowBuffer;
    	
    	rowBuffer = (RGBColor *)NewPtr(sizeof(RGBColor) * ((**imDest).rowBytes & 0x7FFF));
	
		GetPort(&savePort);
		srcGrafPort = DrvMalloc(sizeof(CGrafPort));
		OpenCPort(srcGrafPort);
		SetPortPix(imSrc);
		destGrafPort = DrvMalloc(sizeof(CGrafPort));
		OpenCPort(destGrafPort);
		SetPortPix(imDest);

		for(y=0;y<nHeightDest;y++) {
			SetPort((GrafPtr)srcGrafPort);
			for(x=0;x<nWidthDest;x++) {
				GetCPixel((int)xbits[x],(int)ybits[y], &rowBuffer[x]);
			}
			SetPort((GrafPtr)destGrafPort);
			for(x=0;x<nWidthDest;x++) {
				SetCPixel(x,y,&rowBuffer[x]);
			}
		}
		DisposPtr((Ptr)rowBuffer);
		SetPort((GrafPtr)srcGrafPort);
		SetPortPix(0);
		SetPort((GrafPtr)destGrafPort);
		SetPortPix(0);
		CloseCPort(srcGrafPort);
		CloseCPort(destGrafPort);
		SetPort(savePort);
	}

	/* the offset in the source has already been accounted for */
	lpsd->xSrc = lpsd->ySrc = 0;
	CopyPixMap(imDest, srcPix);
	DrvDestroyPixmap(imDest, FALSE);

	if (lpSrcDC && lpSrcDC->lpDrvImage && lpSrcDC->lpDrvImage->image == 0)
	    lpSrcDC->lpDrvImage->image = imSrc;
	else if (lpSrcDC->lpDrvImage->image != imSrc)
		/* don't destroy imSrc if it is the image selected into the DC */
	    DrvDestroyPixmap(imSrc, TRUE);
}
    else {	/* not stretching or flipping */
	if (lpSrcDC && lpSrcDC->lpDrvImage) {
		/* pin width and height if necessary */
	    if ((nWidthDest+lpsd->xSrc) > lpSrcDC->lpDrvImage->nWidth)
		nWidthDest = lpSrcDC->lpDrvImage->nWidth - lpsd->xSrc;

	    if (nHeightDest+lpsd->ySrc > lpSrcDC->lpDrvImage->nHeight)
		nHeightDest = lpSrcDC->lpDrvImage->nHeight - lpsd->ySrc;
	}
    }
    
    /* now we can actuall perform the logical blit */
    lpRops = &(ROPTab[HIWORD(rop)].bRops[0]);
    DrvSelectDC(SAVE, lpDestDC);
    for(n=0;n<4 && *lpRops;n++, lpRops++) {
    if(*lpRops & 0x8000) {	/* temporary pixmap needed */
    	if (!(tmpPix = CreatePixmapFromRect(zeroOriginDestRect)))
    		break; /* out of the loop -- failure */
    }
    nROP2 = ROPS[LOBYTE(*lpRops)];
    switch (*lpRops & 0xff00) {
    	case ROP_BD:	/* fill dest with brush */
//    		printf("ROP_BD, %d\n", *lpRops & 0x00FF);
    		DrvFillRectangle(lpDestDC, lpDestDC->lpDrvImage->image, &destRect, lpRops);
    		break;
    	case ROP_FD:	/* fill dest w/o brush */
//    		printf("ROP_FD, %d\n", *lpRops & 0x00FF);
    		DrvFillRectangle(lpDestDC, lpDestDC->lpDrvImage->image, &destRect, lpRops);
    		break;
    	case ROP_SD:	/* copy src into dest */
//    		printf("ROP_SD, %d\n", *lpRops & 0x00FF);
    		DrvCopyArea(srcPix ? srcPix : lpSrcDC->lpDrvImage->image, lpDestDC->lpDrvImage->image, srcOrigin, destSize, destOrigin, lpRops);
    		break;
    	case ROP_DB:	/* create pximap; combine dest with brush into pixmap */
//    		printf("ROP_DB, %d\n", *lpRops & 0x00FF);
    		DrvFillRectangle(lpDestDC, tmpPix, &zeroOriginDestRect, lpRops);
    		break;
    	case ROP_BP:	/* fill pixmap with brush */
//    		printf("ROP_BP, %d\n", *lpRops & 0x00FF);
    		DrvFillRectangle(lpDestDC, tmpPix, &zeroOriginDestRect, lpRops);
    		break;
    	case ROP_DS:	/* create pixmap; combine dest with src into pixmap */
//    		printf("ROP_DS, %d\n", *lpRops & 0x00FF);
    		DrvCopyArea(srcPix ? srcPix : lpSrcDC->lpDrvImage->image, tmpPix, srcOrigin, destSize, pointZero, lpRops);
    		break;
    	case ROP_SP:	/* copy src into pixmap */
//    		printf("ROP_SP, %d\n", *lpRops & 0x00FF);
    		DrvCopyArea(srcPix ? srcPix : lpSrcDC->lpDrvImage->image, tmpPix, srcOrigin, destSize, pointZero, lpRops);
    		break;
    	case ROP_SB:	/* create pixmap; combine src with brush into pixmap */
//    		printf("ROP_SB, %d\n", *lpRops & 0x00FF);
    		DrvCopyArea(srcPix ? srcPix : lpSrcDC->lpDrvImage->image, tmpPix, srcOrigin, destSize, pointZero, lpRops);
    		break;
    	case ROP_PD:	/* copy pixmap into dest */
//    		printf("ROP_PD, %d\n", *lpRops & 0x00FF);
    		DrvCopyArea(tmpPix, lpDestDC->lpDrvImage->image, pointZero, destSize, destOrigin, lpRops);
    		break;
	    default:
		printf("Invalid opcode %x (rop %x)\n",*lpRops & 0xf0, lpRops);
		break;
	}
    }
    if (tmpPix) DrvDestroyPixmap(tmpPix, TRUE);
 
	DrvSelectDC(RESTORE, NULL);

    
    /* now destroy the stretched pixmap, if it exists */
	if(srcPix)
		DrvDestroyPixmap(srcPix, TRUE);

	return PGH_SUCCESS;
}

PixMapHandle
myCopyPixMap(PixMapHandle srcPixMap) {
	PixMapHandle myPixMap;
	
	myPixMap = CreatePixmapFromRect((**srcPixMap).bounds);
	CopyPixMap(srcPixMap, myPixMap);
	return(myPixMap);
}

void DrvFillRectangle(DRIVERDC *lpddc, PixMapHandle pixmap, Rect *theRect, LPWORD lpRops)
{
RGBColor rgbColor;
BOOL bPatFill = TRUE;

	if(lpddc->brushFlag != BFP_NULL) {
		/* Set the brush color and pattern in the grafport*/
		COLORREFTOMACRGB(lpddc->brushColor, &rgbColor);
		RGBForeColor(&rgbColor);
		PenPat(&(qd.black));							/* Set to normal pen pattern */
		switch(*lpRops & 0x00FF) {
		case R2_COPYPEN:
			PenMode(patCopy);
			break;
		case R2_XORPEN:
			PenMode(patXor);
			break;
		case R2_NOT:
			PenMode(notPatXor);
			PenPat(&(qd.white));
			PaintRect(theRect);
			bPatFill = FALSE;
			break;
		case R2_BLACK:
			RGBForeColor(&blkRgbColor);
			PenMode(patCopy);
			PaintRect(theRect);
			bPatFill = FALSE;
			break;
		case R2_WHITE:
			PenMode(patCopy);
			PenPat(&(qd.white));
			PaintRect(theRect);
			bPatFill = FALSE;
			break;
		default:
			LOGSTR((LF_OBJECTS, "PGH: PatBlt unsupported rasterop = %x\n", *lpRops & 0x00FF));
			break;
		}
		if (bPatFill)
			switch(lpddc->brushFlag) {
			case BFP_PIXEL:
				PaintRect(theRect);
				break;
			case BFP_BITMAP:
				FillRect(theRect, lpddc->brushFillPat);
				break;
			case BFP_PIXMAP:
				FillCRect(theRect, lpddc->brushFillPat);
				break;
			case BFP_NULL:
			default:
				break;
			}
	} else DebugStr("\pHuh?");
}

void DrvCopyArea(PixMapHandle srcPixmap, PixMapHandle destPixmap, Point srcOrigin, Point rectSize, Point destOrigin, LPWORD lpRops)
{
	Rect srcRect;
	Rect destRect;
	
	srcRect.top = srcOrigin.v;
	srcRect.left = srcOrigin.h;
	srcRect.bottom = srcOrigin.v + rectSize.v;
	srcRect.right = srcOrigin.h + rectSize.h;
	destRect.top = destOrigin.v;
	destRect.left = destOrigin.h;
	destRect.bottom = destOrigin.v + rectSize.v;
	destRect.right = destOrigin.h + rectSize.h;
	HLock((Handle)srcPixmap);
	HLock((Handle)destPixmap);
	switch (*lpRops & 0x00FF) {
		case R2_BLACK:										/* 0 */		/* TESTED */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&blkRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_NOTMERGEPEN:							/* DPon */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_MASKNOTPEN:								/* DPna */		/* CODED */
			RGBForeColor(&whtRgbColor);
			RGBBackColor(&blkRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcOr,0);
			break;
		case R2_NOTCOPYPEN:								/* Pn */			/* CODED */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,notSrcCopy,0);
			break;
		case R2_MASKPENNOT:								/* PDna */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_NOT:											/* Dn */			/* CODED */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,notSrcCopy,0);
			break;
		case R2_XORPEN:										/* DPx */		/* needs manual ops */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcXor,0);
			break;
		case R2_NOTMASKPEN:								/* DPan */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_MASKPEN:									/* DPa */		/* TESTED */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcOr,0);
			break;
		case R2_NOTXORPEN:								/* DPxn */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_NOP:											/* D */
			break;
		case R2_MERGENOTPEN:							/* DPno */		/* TESTED */
			RGBForeColor(&whtRgbColor);
			RGBBackColor(&blkRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcOr,0);
			break;
		case R2_COPYPEN:									/* P */			/* TESTED */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_MERGEPENNOT:							/* PDno */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,srcCopy,0);
			break;
		case R2_MERGEPEN:									/* DPo */			/* needs manual ops */
			RGBForeColor(&blkRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap,(BitMapPtr)*destPixmap,&srcRect,&destRect,notSrcXor,0);
			break;
		case R2_WHITE:										/* 1 */			/* TESTED */
			RGBForeColor(&whtRgbColor);
			RGBBackColor(&whtRgbColor);
			CopyBits((BitMapPtr)*srcPixmap, (BitMapPtr)*destPixmap, &srcRect, &destRect, srcCopy, 0);
			break;
	}
	HUnlock((Handle)srcPixmap);
	HUnlock((Handle)destPixmap);
}

static void
MyCopyBits(PixMapPtr srcPixmap, PixMapPtr destPixmap, RectPtr srcRect, RectPtr destRect, int rasterOp)
{
}

long
GetPixelInPixMap(PixMapPtr pixMap, int x, int y)
{	
}

/********************************************************************
*   DrvPatBlt
*
********************************************************************/
static DWORD DrvPatBlt(DRIVERDC *lpddc, Rect *destRect, int rasterOp)
{
RGBColor rgbColor;
BOOL bPatFill = TRUE;

	DrvSelectDC(SAVE, lpddc);				/* Save and set the ports */

	/* Set the brush color and pattern in the grafport*/
	COLORREFTOMACRGB(lpddc->brushColor, &rgbColor);
	RGBForeColor(&rgbColor);
	PenPat(&(qd.black));							/* Set to normal pen pattern */
		
	switch(rasterOp) {
	default:
		LOGSTR((LF_OBJECTS,"PGH: PatBlt unsupported rasterop = %x\n", rasterOp));
	case PATCOPY:									/* Fill with COPY pattern */
		PenMode(patCopy);		break;

	case PATINVERT:								/* Fill with XOR of pattern */
		PenMode(patXor);			break;

	case PATPAINT:								/* Fill with OR of pattern */
		PenMode(patOr);			break;
	
	case DSTINVERT:								/* Invert with white brush */
		PenMode(notPatXor);
		PenPat(&(qd.white));
		PaintRect(destRect);						
		bPatFill = FALSE;					
		break;

	case BLACKNESS:								/* Copy with black brush */
		RGBForeColor(&blkRgbColor);
		PenMode(patCopy);
		PaintRect(destRect);						
		bPatFill = FALSE;					
		break;
		
	case WHITENESS:								/* Copy with white brush */
		PenMode(patCopy);
		PenPat(&(qd.white));
		PaintRect(destRect);						
		bPatFill = FALSE;					
		break;
	}	

	LOGSTR((LF_OBJECTS,"PGH: PatBlt rasterop = %x  rect = %d, %d, %d, %d  CR = %x, patFill = %x\n", rasterOp, destRect->left, destRect->top, destRect->right, destRect->bottom, lpddc->brushColor, bPatFill));

	/* Do the fill based on the brush type */
	if (bPatFill)
		switch(lpddc->brushFlag)	{
		case BFP_PIXEL:
			PaintRect(destRect);									/* Fill with solid pen */
			break;
		case BFP_BITMAP:
			FillRect(destRect, lpddc->brushFillPat);		/* Fill with *Pattern */
				break;
		case BFP_PIXMAP:								
			FillCRect(destRect, lpddc->brushFillPat); 	/* Fill with PixPatHandle */
			break;
		case BFP_NULL:
		default:
			break;														/* No fill */
		}

	DrvSelectDC(RESTORE, NULL);							/* Restore old port */
	
	return(TRUE);
}


/********************************************************************
* NewBitMap
*
********************************************************************/
OSErr NewBitMap(BitMap *theBits, Rect *rp)
{
long size;

	theBits->rowBytes = ((rp->right - rp->left + 15) / 16) * 2;
	size = (long)theBits->rowBytes * (rp->bottom - rp->top);
	theBits->baseAddr =	DrvMalloc(size);

	theBits->bounds = *rp;

	if(!theBits->baseAddr)	// out of memory!
		return(memFullErr);

	
//	memset((theBits->baseAddr), 0xFF, size);
	return(noErr);
}

/********************************************************************
* DisposeBitMap
*
********************************************************************/
void DisposeBitMap(BitMap *theBits)
{
	DrvFree(theBits->baseAddr);
}

/********************************************************************
* DrvFloodFill
*
********************************************************************/
static DWORD DrvFloodFill(LPDRIVERDC lpddc, LPLSDS_PARAMS lpStruct)
{
LSDE_FLOODFILL *floodfill = &lpStruct->lsde.floodfill;
Rect clipRect;
BitMap destBits;
RgnHandle floodRgn;
OSErr err;
PixMapHandle srcPixMap = lpddc->lpDrvImage->image;
void *fillPat;
int brushFlag;

	/* Validate the DC if needed */
    if (lpStruct->lsde_validate.dwInvalidMask)
		DrvValidate(lpddc, lpStruct);

	DrvSelectDC(SAVE, lpddc);				/* Save and set the ports */
	/* The DC will have set the clip region.  The best we can do is to use it's boundary. */
	clipRect = (**((GrafPtr)lpddc->grafPort)->clipRgn).rgnBBox;

	if (NewBitMap(&destBits, &clipRect) != noErr)		/* Bitmap for the flood mask */
		return(FALSE);
		
	HLock((Handle) srcPixMap);					/* Lock it, seed will move memory! */
	SeedCFill((BitMapPtr)*srcPixMap, &destBits, &clipRect, &clipRect, floodfill->nXStart, floodfill->nYStart, 0L, 0L);
	HUnlock((Handle) srcPixMap);
	
	/* The current method will be to create a region from the Seed mask */
	floodRgn = NewRgn();
	err = BitMapToRegion(floodRgn, &destBits);
	if ((err == noErr) && SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction))
		switch(brushFlag) {
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintRgn(floodRgn);				break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillRgn(floodRgn, fillPat);		break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCRgn(floodRgn, fillPat);	break;
		case BFP_NULL:
		default:
			break;											/* No fill */
		}

	DisposeRgn(floodRgn);
	DisposeBitMap(&destBits);

	DrvSelectDC(RESTORE, NULL);				/* Restore old port */

	return(TRUE);
}


/********************************************************************
* DrvGetDeviceCaps
*
*  Get graphics capabilities.
********************************************************************/
static int DrvGetDeviceCaps(LPDRIVERDC lpddc, int iCapability)
{
	Rect			rect;
	THPrint		hPrint;

    switch (iCapability) {
	case DRIVERVERSION:
	    return 0x30a;

	case TECHNOLOGY:
	    return DT_RASDISPLAY;

	case HORZSIZE:	{
		if ( IS_PRINTING_DC( lpddc ))
		{
			hPrint = (THPrint )lpddc->hPrint;
			rect = (*hPrint)->prInfo.rPage;
			return((( rect.right - rect.left ) / (*hPrint)->prInfo.iHRes ) * 25.4 );
		}
		else
		{						/* Width of the display in millimeters */
			/* mm = (pixels / 72dpi) * 25.4		(1inch = 25.4mm) */
			double mm = ((double) DisplaySize(DP_WIDTH) / 72.0) * 25.4;
			return(int) mm;
		}
	}
	case VERTSIZE:	{
		if ( IS_PRINTING_DC( lpddc ))
		{
			hPrint = (THPrint )lpddc->hPrint;
			rect = (*hPrint)->prInfo.rPage;
			return((( rect.bottom - rect.top ) / (*hPrint)->prInfo.iVRes ) * 25.4 );
		}
		else
		{					/* Height of the display in millimeters */
			/* millimeters = (pixels / 72dpi) * 25.4		(1inch = 25.4mm) */
			double mm = ((double) DisplaySize(DP_HEIGHT) / 72.0) * 25.4;
			return(int) mm;
		}
	}
	case HORZRES:						/* Width of the display in pixels */
		if ( IS_PRINTING_DC( lpddc ))
		{
			hPrint = (THPrint )lpddc->hPrint;
			rect = (*hPrint)->prInfo.rPage;
			return(( rect.right - rect.left ));
		}
		else
		    return DisplaySize(DP_WIDTH);

	case VERTRES:						/* Height of the display in pixels */
		if ( IS_PRINTING_DC( lpddc ))
		{
			hPrint = (THPrint )lpddc->hPrint;
			rect = (*hPrint)->prInfo.rPage;
			return(( rect.bottom - rect.top ));
		}
		else
			return DisplaySize(DP_HEIGHT);

	case BITSPIXEL:
		/* should return pixmap cmpCnt * pixmap cmpSize */
	    return (gDspl->screenDepth);

	case PLANES:
	    return 1;

	/* the following 4 caps are taken from the SVGA driver */
	case NUMBRUSHES:
	    return -1;

	case NUMPENS:
	    return 100;

	case NUMMARKERS:
	case NUMFONTS:
	    return 0;

	case NUMCOLORS:
	    /* on Windows this value is 20 (SVGA) */
	    return 1 << gDspl->screenDepth;

	case PDEVICESIZE:
	    /* on Windows - 35 bytes (SVGA) */
	    return 0;	/* LATER -- support PDEVICE */

	case CURVECAPS:
	    /* SVGA */
	    return CC_NONE;

	case LINECAPS:
	    /* SVGA */
	    return LC_POLYLINE|LC_STYLED;

	case POLYGONALCAPS:
	    /* SVGA */
	    return PC_SCANLINE;

	case TEXTCAPS:
	    /* SVGA */
	    return TC_CP_STROKE|TC_RA_ABLE;

	case CLIPCAPS:
	    /* SVGA */
	    return CP_RECTANGLE;

	case RASTERCAPS:
	    /* SVGA */
	    return RC_BITBLT |
		   RC_BITMAP64 |
		   RC_GDI20_OUTPUT |
		   RC_DI_BITMAP |
		   RC_PALETTE |
		   RC_DIBTODEV |
		   RC_BIGFONT |
		   RC_STRETCHBLT;

	case ASPECTX:
	case ASPECTY:
	    /* see windows display driver doc??? */
	    return 10;

	case ASPECTXY:
	    /* see windows display driver doc??? */
	    return 14;

	case LOGPIXELSX: {
		if ( IS_PRINTING_DC( lpddc ))
		{
			hPrint = (THPrint )lpddc->hPrint;
			return( (*hPrint)->prInfo.iHRes );
		}
		else
		{
			GDHandle gdh = GetMainDevice();
			/* Use the main devices bounds rect as opposed to the desktop area */
			/*  That way, it will be more based on actual display resolution. */
			/* X uses 96 and 120 (if dpy is >= 1024 wide) */
			/* Changed to 72 for the Mac. */
			/* return ((**gdh).gdRect.right -(**gdh).gdRect.left >= 1024) ? 120 : 96; */
			return 72;
		}
	}
	
	case LOGPIXELSY: {
		if ( IS_PRINTING_DC( lpddc ))
		{
			hPrint = (THPrint )lpddc->hPrint;
			return( (*hPrint)->prInfo.iVRes );
		}
		else
		{
			GDHandle gdh = GetMainDevice();
	    	/* seems to be working??? */
	    	/* X uses 96 and 120 (if dpy is >= 768 wide) */
			/* Changed to 72 for the Mac. */
	    	/* return ((**gdh).gdRect.bottom - (**gdh).gdRect.top >= 768) ? 120 : 96; */
			return 72;
		}
	}
	
	case SIZEPALETTE:
	    /* SVGA */
	    return 256;

	case NUMRESERVED:
	    /* SVGA */
	    return 20;

	case COLORRES:
	    /* SVGA has 18 here */
	    return 8;

	default:
	    return 0;
    }
}


/****************************************************************
*	DisplaySize
*
*	Returns display width or height in pixels (of the desktop's gray rgn).
*	The width and height do NOT include the menu bar, otherwise windows will be too large.
****************************************************************/
int DisplaySize(BOOL width)
{
#ifdef USEGRAYREGION
RgnHandle hRegion;

	/* The desktop "Gray Region" contains all visible area on all screens below the menu bar. */
	hRegion = GetGrayRgn();

	if (width)
		return (max((**hRegion).rgnBBox.right - (**hRegion).rgnBBox.left, 640));
	else
		return (max((**hRegion).rgnBBox.bottom - (**hRegion).rgnBBox.top, 480));
#else
GDHandle gdh = GetMainDevice();
short mbHeight = GetMBarHeight();

	/* Found a interesting problem with multiple monitors, in that windows programs */
	/*  will size their windows to the screen size (too big!) */
	/* So, just get the size of the main device (with the menu bar) and use that */

	if (width)
		return (max((**gdh).gdRect.right - (**gdh).gdRect.left, 640));
	else
		return (max(((**gdh).gdRect.bottom - (**gdh).gdRect.top) - mbHeight, 480));


#endif
	
}



/********************************************************************
* Scrolls an area in a DC.
********************************************************************/
static DWORD DrvScrollArea(LPDRIVERDC lpddc, short xSrc, short ySrc, short width, short height, short xDest, short yDest)
{
Rect srcRect, destRect;
short distHoriz, distVert;
RgnHandle updateRgn;

	DrvSelectDC(SAVE, lpddc);					/* Save and set the ports */

	/* Set the source rectangle to be scrolled */
	SetRect(&srcRect, xSrc, ySrc, xSrc + width, ySrc + height);
	SetRect(&destRect, xDest, yDest, xDest + width, yDest + height);

	/* Determine the scrolling distance */
	distHoriz = xDest - xSrc;
	distVert = yDest - ySrc;
	
	/* ScrollRect uses the entire are to be scrolled and also defines a temporary clipping area */
	/* Therefore, we must adjust the rectangle accordingly */
	UnionRect(&srcRect, &destRect, &srcRect);	
	
	updateRgn = NewRgn();
	
	ScrollRect(&srcRect, distHoriz, distVert, updateRgn);

	/* The library already knows what area was exposed and will handle it. */
	DisposeRgn(updateRgn);
	
	DrvSelectDC(RESTORE, NULL);				/* Restore old port */

	return(TRUE);
}



/********************************************************************
*   SelectPen
*
*  Validates pen, also resets rectangle for PS_INSIDEFRAME.
*  Returns true if everything is ok, false otherwise or if pen is NULL.
********************************************************************/
Boolean SelectPen(DRIVERDC *lpddc, Rect *theRect)
{
RGBColor rgbColor;
CGrafPtr grafPort = lpddc->grafPort;
short penMode = patCopy;
PatPtr penPat;

	/* Check for valid pen style */
	if (lpddc->penStyle == PS_NULL)
		return(false);								/* Pen is NULL, do not use */
	else if (theRect && lpddc->penStyle == PS_INSIDEFRAME)
		InsetRect(theRect, 1, 1);				/* Perform the inside frame pen function */

	/* Setup the pen color */
	/* Convert from COLOREF to RGB values */
	COLORREFTOMACRGB(lpddc->penColor, &rgbColor);

	/* Mimic a dashed lined if necessary */
#if 1
	switch (lpddc->penStyle) {
		case PS_DASH:
		case PS_DOT:
		case PS_DASHDOT:
		case PS_DASHDOTDOT:
			penPat = &qd.gray;			break;
		default:
			penPat = &qd.black;			break;
	}
#else
	penPat = &gDashPatterns[lpddc->penStyle];
#endif
	
	/* Setup for the raster operations */
	switch(lpddc->ropFunction) {
	case R2_XORPEN:							/* XOR with pen */
		if (lpddc->penColor == 0x000000)
			return(FALSE);

		penMode = patXor;
		break;

	case R2_NOTXORPEN:					/* XOR with pen */
		if (lpddc->penColor == 0xFFFFFF)
			return(FALSE);

		penMode = patXor;
		break;
		
	case R2_NOT:								/* Invert with white pen */
		penPat = &(qd.white);
		penMode = notPatXor;
		break;
		
	case R2_NOTCOPYPEN:					/* Invert pen and copy */
		InvertColor(&rgbColor);
		penMode = patCopy;		break;
		
	case R2_BLACK:							/* Copy with black pen */
		rgbColor.red = 0;	rgbColor.green  = 0;	rgbColor.blue = 0;		
		break;

	case R2_WHITE:							/* Copy with white pen */
		rgbColor = whtRgbColor;
		break;

	case R2_MERGEPENNOT:				/* (P & !D) */
		if (lpddc->penColor == 0xFFFFFF)
			penMode = patCopy;
		else
			penMode = patXor;
	
		break;

	case R2_MERGEPEN:
		if (lpddc->penColor == 0x000000)
			return(FALSE);

		break;
				
	case R2_MERGENOTPEN:				/* (!P | D) */
		if (lpddc->penColor == 0xFFFFFF)
			return(FALSE);
		InvertColor(&rgbColor);
		penMode = patOr;	
		break;

	case R2_NOTMERGEPEN:				/* LATER: Or pen and dest, inverting result */
		if (lpddc->penColor == 0xFFFFFF) {
			rgbColor = blkRgbColor;
			penMode = patCopy;
		}
		else
			penMode = patXor;
		break;	

	case R2_NOTMASKPEN:					/* Inverse of the R2_MASKPEN operation. Pixel = !(pen & screen pixel) */
		/* If the color is black, just set to white (invert) */
		if (lpddc->penColor == 0x000000) {
			rgbColor = whtRgbColor;
			penMode = patCopy;
		}
		else
			penMode = patXor;
		break;

	case R2_MASKNOTPEN:					//DPna		(!P & D)
		/* Masking means there will always be a line unless the inverse source is black! */
		if (lpddc->penColor == 0xFFFFFF){
			rgbColor = blkRgbColor;
			penMode = patCopy;
		}
		else
			return(FALSE);

		break;

	case R2_MASKPENNOT:				/* (!D & P) */
		if (lpddc->penColor != 0x000000)		/* Invert if the pen is not black */
			penMode = patXor;
			
		break;
				
	case R2_MASKPEN:
		/* Masking means there will always be a line unless the source is white! */
		if (lpddc->penColor == 0xFFFFFF)
			return(FALSE);
		break;

	case R2_COPYPEN:	
	default:												/* Overwrite background entirely */
		break;
		
	case R2_NOP:
		return(false);
	}

	grafPort->pnMode = penMode;				/* Transfer mode */
	grafPort->pnSize.h = lpddc->penWidth;	/* Set the desired pen size */
	grafPort->pnSize.v = lpddc->penHeight;
	if (!EQUALRGBCOLORS(&grafPort->rgbFgColor, &rgbColor))
		RGBForeColor(&rgbColor);					/* Set color in grafport */

	/* Set the background color */
	COLORREFTOMACRGB(lpddc->backColor, &rgbColor);
	if (!EQUALRGBCOLORS(&grafPort->rgbBkColor, &rgbColor))
		RGBBackColor(&rgbColor);					/* Set color in grafport */

	PenPat(penPat);									/* Set the pen pixel pattern */
	
	return(true);
}


/********************************************************************
*   SelectBrush
*
*  Sets up the brush, setting fore color accordingly. 
*  The RasterOps are also set.  The brushFlag and fillPat may be changed from
*    the dc's to accomodate the rasterOp.
*  Returns true if everything is ok, false otherwise.
********************************************************************/
Boolean SelectBrush(LPDRIVERDC lpddc, int *brushFlag, void **fillPat, int nROP)
{
RGBColor rgbColor;
CGrafPtr grafPort = lpddc->grafPort;
PatPtr penPat = &qd.black;
short penMode = patCopy;

	if (lpddc->brushFlag == BFP_NULL)
		return(FALSE);

	/* Set the brush color if one exists, may be changed by rasterOp */
	COLORREFTOMACRGB(lpddc->brushColor, &rgbColor);

	/* Set the brush flag and fill now, may be changed by rasterOp */
	*brushFlag = lpddc->brushFlag;
	*fillPat = lpddc->brushFillPat;
	
	/* Setup for the raster operations */
	switch(nROP) {
	case R2_XORPEN:							/* XOR with pen */
		penMode = patXor;		break;	

	case R2_NOT:								/* Invert with white pen */
		rgbColor.red = 65536;	rgbColor.green = 65536;	rgbColor.blue = 65536;		
		penMode = notPatXor;
		*brushFlag = BFP_BITMAP;
		*fillPat = &(qd.white);
		break;

	case R2_NOTCOPYPEN:					/* Invert pen and copy */
		penMode = notPatCopy;		break;
		
	case R2_BLACK:							/* Copy with black pen */
		rgbColor.red = 0;	rgbColor.green  = 0;	rgbColor.blue = 0;		
		*brushFlag = BFP_BITMAP;
		*fillPat = &(qd.black);
		break;

	case R2_WHITE:							/* Copy with white pen */
		rgbColor.red = 65536;	rgbColor.green = 65536;	rgbColor.blue = 65536;		
		*brushFlag = BFP_BITMAP;
		*fillPat = &(qd.white);
		break;

	case R2_NOTMERGEPEN:				/* LATER: Or pen and dest, inverting result */
		penMode = notPatOr;		break;	
			
	case R2_COPYPEN:	
	default:											/* Overwrite background entirely */
		break;

	case R2_NOP:
		return(false);
	}

	grafPort->pnMode = penMode;
	grafPort->pnSize.h = 1;				/* Reset the pen size in case it was changed */
	grafPort->pnSize.v = 1;
	if (!EQUALRGBCOLORS(&grafPort->rgbFgColor, &rgbColor))
		RGBForeColor(&rgbColor);		/* Set color in grafport */
	PenPat(penPat);

	return(true);
}




/********************************************************************
*   DrvSetPixel
*
********************************************************************/
COLORREF DrvSetPixel(DRIVERDC *lpddc, COLORREF cr, int horiz, int vert)
{
RGBColor rgbColor, oldRgbColor;
CGrafPort *grafPort = lpddc->grafPort;

	DrvSelectDC(SAVE, lpddc);						/* Save and set the ports */

	COLORREFTOMACRGB(cr, &rgbColor);

	/* Get the original value, then set the new value. */
	if ((**grafPort->portPixMap).pixelSize == 1) {
		oldRgbColor = GetPixel(horiz, vert) ? whtRgbColor : blkRgbColor;
		/* Setup port */
		RGBForeColor(&blkRgbColor);		RGBBackColor(&whtRgbColor);
		PenSize(1,1);
		PenPat(&qd.black);
		/* Draw the point (there is no equivalent to the SetCPixel() function */
		MoveTo(horiz, vert);
		LineTo(horiz, vert);
	}
	else {
		GetCPixel(horiz, vert, &oldRgbColor);
		SetCPixel(horiz, vert, &rgbColor);
	}
	
	DrvSelectDC(RESTORE, NULL);					/* Restore old port */

	cr = MACRGBTOCOLORREF(&oldRgbColor);
	return(cr);
}


/********************************************************************
*   DrvGetPixel
*
********************************************************************/
COLORREF DrvGetPixel(DRIVERDC *lpddc, int horiz, int vert)
{
RGBColor oldRgbColor;
CGrafPort *grafPort = lpddc->grafPort;

	DrvSelectDC(SAVE, lpddc);						/* Save and set the ports */

	if ((**grafPort->portPixMap).pixelSize == 1)
		oldRgbColor = GetPixel(horiz, vert) ? whtRgbColor : blkRgbColor;
	else
		GetCPixel(horiz, vert, &oldRgbColor);

	DrvSelectDC(RESTORE, NULL);					/* Restore old port */

	return(MACRGBTOCOLORREF(&oldRgbColor));
}



/********************************************************************
*   DrvLineFromTo
*
*  Draw a line from one point to another.
*  Does NOT change the current pen position. 
********************************************************************/
void DrvLineFromTo(DRIVERDC *lpddc, int startX, int startY, int endX, int endY)
{

	DrvSelectDC(SAVE, lpddc);					/* Save and set the ports */

	if (SelectPen(lpddc, NULL))	{				/* Verify pen is ok */
		MoveTo(startX, startY);					/* Position at start point */
		LineTo(endX, endY);							/* Draw the line */
	}
	
	DrvSelectDC(RESTORE, NULL);				/* Restore old port */
}


/********************************************************************
*   DrvRectangle
*
*  Draw and Fill rectangle according to settings in the DC.
********************************************************************/
void DrvRectangle(DRIVERDC *lpddc, Rect *theRect)
{
void *fillPat;
int brushFlag;

	DrvSelectDC(SAVE, lpddc);						/* Save and set the ports */

	/* Verify brush settings are correct */
	if (SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction))		
		FILLRECTWITHBRUSH(brushFlag, fillPat, theRect);

	if (SelectPen(lpddc, theRect))					/* Draw the rect if the pen is valid */
		FrameRect(theRect);							
		
	DrvSelectDC(RESTORE, NULL);				/* Restore old port */
}



/********************************************************************
*   DrvRoundRect
*
*  Draw and Fill rectangle with the DC's settings.
********************************************************************/
void DrvRoundRect(DRIVERDC *lpddc, Rect *theRect, int ovalWidth, int ovalHeight)
{
void *fillPat;
int brushFlag;

	DrvSelectDC(SAVE, lpddc);						/* Save and set the ports */

	/* Verify brush settings are correct */
	if (SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction))		
		switch(brushFlag)
		{
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintRoundRect(theRect, ovalWidth, ovalHeight);
			break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillRoundRect(theRect, ovalWidth, ovalHeight, fillPat);	
			break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCRoundRect(theRect, ovalWidth, ovalHeight, fillPat);	
			break;
		case BFP_NULL:
		default:
			break;												/* No fill */
		}

	if (SelectPen(lpddc, theRect))						/* Draw the rect if the pen is valid */
		FrameRoundRect(theRect, ovalWidth, ovalHeight);		

	DrvSelectDC(RESTORE, NULL);					/* Restore old port */
}




/********************************************************************
*   DrvEllipse
*
*  Fill ellipse if fill is true.
*  Draw ellipse frame if draw is true. 
********************************************************************/
void DrvEllipse(DRIVERDC *lpddc, Rect *theRect)
{
void *fillPat;
int brushFlag;

	DrvSelectDC(SAVE, lpddc);						/* Save and set the ports */

	/* Verify brush settings are correct */
	if (SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction))		
		switch(brushFlag) {
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintOval(theRect);							
			break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillOval(theRect, fillPat);	
			break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCOval(theRect, fillPat);	
			break;
		case BFP_NULL:								/* No fill */
		default:
			break;											
		}
		
	if (SelectPen(lpddc, theRect))					/* Draw the oval if the pen is valid */
		FrameOval(theRect);							

	DrvSelectDC(RESTORE, NULL);				/* Restore old port */
}


/********************************************************************
*   DrvArc
*
*	ARC:   An elliptical arc.
*	CHORD: Closed figure bounded by the intersection of an ellipse
*	  		and a line segment. 
*	PIE:   An elliptical arc whose center and two endpoints are 
*			joined by lines.
********************************************************************/
void DrvArc(DRIVERDC *lpddc, int type, Rect *boundRect, Point startPt, Point stopPt)
{
#define INTEGERVERSION 1
#if INTEGERVERSION
short startAngle, stopAngle;
#else
double startAngle, stopAngle;
#endif
int arcAngle, normStartAngle;
int radiusX, radiusY;
Point centerPt, startPtOnArc, stopPtOnArc;

	DrvSelectDC(SAVE, lpddc);			/* Save and set the ports */

	/* Calculate the radius (width) from the rect center to outside. */
	radiusX = (boundRect->right - boundRect->left) / 2;
	radiusY = (boundRect->bottom - boundRect->top) / 2;

	/* Calculate the center point of the rect */
	centerPt.h = boundRect->left + radiusX;
	centerPt.v = boundRect->top + radiusY;

	/* Calculate the "rectangular degrees" (rounding the result) */
#if INTEGERVERSION
//	startAngle = (0.5 + Pt2Angle(centerPt, radiusX, radiusY, startPt));	
	PtToAngle(boundRect, startPt, &startAngle);
//	stopAngle = (0.5 + Pt2Angle(centerPt, radiusX, radiusY, stopPt));	
	PtToAngle(boundRect, stopPt, &stopAngle);
#else
	startAngle = Pt2Angle(centerPt, radiusX, radiusY, startPt);	
	stopAngle = Pt2Angle(centerPt, radiusX, radiusY, stopPt);	
#endif

#if INTEGERVERSION
	arcAngle = stopAngle - startAngle;			/* The angle from vector one TO vector two */
#else
	arcAngle = (0.5 + stopAngle - startAngle);	/* The angle from vector one TO vector two */
#endif
//	if (arcAngle < 0) 											
//		arcAngle += 360;									/* Adjust for negative angles */
	if (arcAngle > 0) 											
		arcAngle -= 360;										/* Adjust for positive angles */

#if INTEGERVERSION
//	normStartAngle = (startAngle - 90) * -1;	/* Normalize to mac's clock coordinates (12 oclock high ) */
		normStartAngle = startAngle;
#else
	normStartAngle =((0.5 + startAngle - 90) * -1);	/* Normalize to mac's clock coordinates (12 oclock high ) */
#endif
//	arcAngle = arcAngle * -1;						/* Force direction of drawing to be counter clockwise */
	
	if (type != LSD_ARC) {							/* Pie's and Chords need some additional info */
		/* Calculate the actual point that intersects the arc and the angle vectors */
		startPtOnArc = PtOnArc(90-startAngle, radiusX, radiusY, &centerPt);
		startPtOnArc.h -= 1;
		startPtOnArc.v += 1;
		stopPtOnArc = PtOnArc(90-stopAngle, radiusX, radiusY, &centerPt);
		stopPtOnArc.h -= 1;
		stopPtOnArc.v += 1;
	}

	/* Chord's have some special requirements, fill it first */
	if(type == LSD_CHORD) 
		FillChord(lpddc, boundRect, normStartAngle, arcAngle, startPtOnArc, stopPtOnArc, centerPt);
	
	/* Fill and draw the arc */
	DrawArc(lpddc, type, boundRect, normStartAngle, arcAngle, startPtOnArc, stopPtOnArc, centerPt);
	
	DrvSelectDC(RESTORE, NULL);					/* Restore old port */
}





/********************************************************************
*   FillChord
*
*  Handle the special requirements for filling a chord.
*  The frame is NOT drawn here, the normal mechanisms can handle it. 
********************************************************************/
static void FillChord(DRIVERDC *lpddc, 
									Rect *theRect, 
									int startAngle, 		/* Rectangular Degrees */
									int arcAngle, 
									Point startPtOnArc, 		
									Point stopPtOnArc, 		
									Point centerPt)
{
RgnHandle chordRgn, saveRgn;
PenState thePnState;
void *fillPat;
int brushFlag;

	/* Verify brush settings are correct */
	if (!SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction))		
		return;
	
	/* Create a region that defines the unique "triangle" area of the chord */
	/* This region will either be removed or filled */
	chordRgn = NewRgn();						/* Create rgn to use */
	OpenRgn();										/* Begin recording */
	MoveTo(centerPt.h, centerPt.v);		/* Draw the chord "triangle" */
	LineTo(startPtOnArc.h, startPtOnArc.v);
	LineTo(stopPtOnArc.h, stopPtOnArc.v);
	LineTo(centerPt.h, centerPt.v);
	CloseRgn(chordRgn);							/* End rgn recording, save rgn */

	if (abs(arcAngle) > 180) {					/* Fill a portion of the arc */			
		GetPenState(&thePnState);			/* Save current info */	
		
		/* Draw the spokes with the back ground to patch "bleeds" */
#if 0
		MoveTo(startPtOnArc.h, startPtOnArc.v);
		LineTo(centerPt.h, centerPt.v);
		LineTo(stopPtOnArc.h, stopPtOnArc.v);
#endif

		switch(brushFlag) {
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintArc(theRect, startAngle, arcAngle);		
			PaintRgn(chordRgn);
			break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillArc(theRect, startAngle, arcAngle, fillPat);		
			FillRgn(chordRgn, fillPat);
			break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCArc(theRect, startAngle, arcAngle, fillPat);		
			FillCRgn(chordRgn, fillPat);
			break;
		case BFP_NULL:								/* No fill */
		default:
			break;											
		}
		
		SetPenState(&thePnState);			/* Restore the current point */
	}
	else {												/* Remove a portion of the arc */
		saveRgn = NewRgn();					/* Get the current clipping region */
		GetClip(saveRgn);
		
		DiffRgn(saveRgn, chordRgn, chordRgn);	/* Remove the chord triangle from the clip region */
		
		/* Now paint the arc with the "chord triangle" clipped */
		SetClip(chordRgn);		
		switch(brushFlag) {
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintArc(theRect, startAngle, arcAngle);		
			break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillArc(theRect, startAngle, arcAngle, fillPat);		
			break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCArc(theRect, startAngle, arcAngle, fillPat);		
			break;
		case BFP_NULL:								/* No fill */
		default:
			break;											
		}

		SetClip(saveRgn);							/* Restore the original clip region */
		DisposeRgn(saveRgn);					/* Free the memory */
	
	}

	DisposeRgn(chordRgn);						/* Free the memory */
	
}




/********************************************************************
*   DrvPolygon
*
********************************************************************/
void DrvPolygon(DRIVERDC *lpddc, short relative, void *winpts, short numPts, BOOL shouldClose)
{
PolyHandle	poly;
void *fillPat;
int brushFlag;

	DrvSelectDC(SAVE, lpddc);			/* Save and set the ports */

	poly = BuildPolygon(relative, winpts, numPts, shouldClose);
	
	/* Verify brush settings are correct */
	if (SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction))
		switch(brushFlag) {
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintPoly(poly);		
			break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillPoly(poly, fillPat);		
			break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCPoly(poly, fillPat);		
			break;
		case BFP_NULL:								/* No fill */
		default:
			break;											
		}

	if (SelectPen(lpddc, NULL))							/* Draw the arc if the pen is valid */
		FramePoly(poly);		

	KillPoly(poly);
	
	DrvSelectDC(RESTORE, NULL);					/* Restore old port */

}



/********************************************************************
*   BuildPolygon
*
*  Builds polygons structure from array of points.
*  It allocates memory, so call KillPolygon to dispose of the memory. 
********************************************************************/
static PolyHandle BuildPolygon(short relative, void *winpts, short numPts, BOOL shouldClose)
{
#pragma unused(relative)		/* LATER */
PolyHandle	polyH;
Polygon		*poly;
short		polySize;
Rect		polyBBox;
short		index, xVal, yVal;
POINT		*pts = winpts;
BOOL		weNeedToClose;

	/* see if we need to close the polygon ourselves */
	weNeedToClose = (pts[0].x != pts[numPts-1].x || pts[0].y != pts[numPts-1].y) && shouldClose;


	/* if we need to close the polygon, allocate an extra point in the polygon */
	/* don't forget that sizeof(Polygon) already accounts for one point */
	if(weNeedToClose)	
		polySize = sizeof(Polygon) + sizeof(Point)*(numPts);
	else
		polySize = sizeof(Polygon) + sizeof(Point)*(numPts-1);

	/* Allocate the structure to hold the polygon */
	polyH = (PolyHandle)NewHandle(polySize);

	if (!polyH)
		return(NULL);							/* Allocation failed, bail out */
		
	HLock((Handle) polyH);					/* Lock it down */
	poly = *polyH;								/* Get a pointer for easier dereferencing */

	SetRect(&polyBBox, pts[0].x, pts[0].y, pts[0].x, pts[0].y);	/* Initialize the bounds rect */
	
	for (index = 0; index < numPts; index++) {
		xVal	=	pts[index].x;					/* Use local variables to avoid dereferencing */
		yVal	=	pts[index].y;
		
		/* Accumulate bounding rect */
		if (xVal < polyBBox.left)
			polyBBox.left = xVal;
		else if (xVal > polyBBox.right)
			polyBBox.right = xVal;
	
		if (yVal < polyBBox.top)
			polyBBox.top = yVal;
		else if (yVal > polyBBox.bottom)
			polyBBox.bottom = yVal;
		
		poly->polyPoints[index].h = xVal;		/* Put points into poly struct */
		poly->polyPoints[index].v = yVal;
	
	}
	
	/* if we need to close the polygon, set the last point equal to the first point. */
	/* since this point is identical to the first point, we don't have to worry about */
	/* modifying the bounds rect */
	if(weNeedToClose) {
		poly->polyPoints[numPts].h = poly->polyPoints[0].h;
		poly->polyPoints[numPts].v = poly->polyPoints[0].v;
	}

	poly->polySize = polySize;			/* Store the size of the structure */
	poly->polyBBox = polyBBox;			/* Store the accumulated bounds rect */
	HUnlock((Handle) polyH);				/* Unlock it now were done */

	return(polyH);

}



/********************************************************************
*   PtOnArc
*
*  Returns the actual point at the intersection of the vector and arc. 
********************************************************************/
static Point PtOnArc(double angle, int radiusX, int radiusY, const Point *centerPt)
{
Point ptOnArc;
double angleMultPiDiv180 = (angle * pi) / 180.0;		/* Reduce a few mult and div's */


	ptOnArc.h = centerPt->h + (0.5 + ((double)radiusX * cos(angleMultPiDiv180)));
	ptOnArc.v = centerPt->v - (0.5 + ((double)radiusY * sin(angleMultPiDiv180)));

	return(ptOnArc);
}



/********************************************************************
*   Pt2Angle
*
*   Calculate angle from rectangular coordinates.
********************************************************************/
static double Pt2Angle(Point centerPt, int radiusX, int radiusY, Point thePt)
{
int	adjust;
double Y, X;
double theta,rtoa;
	
	Y = (double)(centerPt.v - thePt.v);
	X = (double)(thePt.h - centerPt.h);
	theta = atan2(Y, X);
	
	Y = (double)(radiusY);
	X = (double)(radiusX);
	rtoa  = atan(tan(theta) * X / Y);

	/****************************************************************************

       "skewed-angle" = atan(tan("normal-angle"))*(width/height) + adjust

       The skewed-angle and normal-angle are expressed in radians
       (rather than in degrees scaled by 64) in the range [ 0 , 2 pi  ] 
	and where atan returns a value in the range [ - pi / 2 , pi / 2] 
	and adjust is:

       0       for normal-angle in the range [ 0 , pi / 2  ]
       pi      for normal-angle in the range [ pi / 2 , 3 pi / 2  ]
       2 pi    for normal-angle in the range [ 3 pi / 2 , 2 pi  ]

	Note:  we don't use x3,y3 directly, we need to use deltax,deltay,
	to see the coordinate relative to the center of the rectangle.

	*****************************************************************************/

	if (thePt.h < centerPt.h)
		adjust = 1;
	else	
		adjust = 0;

	if (thePt.v > centerPt.v)
		adjust += 2;

	switch(adjust) {
	case 0:
		break;
	case 1:
	case 3:
		rtoa +=	pi;
		break;
	case 2:
		rtoa +=	2 * pi;
	}

	rtoa *= 180.0;
	rtoa /= pi;

	return (rtoa);
}


/********************************************************************
*   DrawArc
*
*  Fill arc (wedge) if fill is true.
*  Draw arc frame if draw is true. 
********************************************************************/
static void DrawArc(DRIVERDC *lpddc, 
								int type, 
								Rect *theRect, 
								int startAngle, 
								int arcAngle,
								Point startPtOnArc, 		
								Point stopPtOnArc, 		
								Point centerPt)
{
Point curPt;
void *fillPat;
int brushFlag;

	/* Verify brush settings are correct */
	if (SelectBrush(lpddc, &brushFlag, &fillPat, lpddc->ropFunction) && (type == LSD_PIE))	
		switch(brushFlag) {
		case BFP_PIXEL:								/* Fill with solid pen */
			PaintArc(theRect, startAngle, arcAngle);		
			break;
		case BFP_BITMAP:							/* Fill with *Pattern */
			FillArc(theRect, startAngle, arcAngle, fillPat);		
			break;
		case BFP_PIXMAP:							/* Fill with PixPatHandle */
			FillCArc(theRect, startAngle, arcAngle, fillPat);		
			break;
		case BFP_NULL:								/* No fill */
		default:
			break;											
		}

	if (SelectPen(lpddc, theRect))	{					/* Draw the arc if the pen is valid */
		FrameArc(theRect, startAngle, arcAngle);		
		
		if (type != LSD_ARC)  {
			GetPen(&curPt);							/* Save the current point */
			MoveTo(startPtOnArc.h, startPtOnArc.v);		
			
			if (type == LSD_CHORD)
				LineTo(stopPtOnArc.h, stopPtOnArc.v);
			else {
				LineTo(centerPt.h, centerPt.v);
				LineTo(stopPtOnArc.h, stopPtOnArc.v);
			}
			
			MoveTo(curPt.h, curPt.v);			/* Restore the current point */
		}
		
	}
}


