From 3203df9587fe06c9d26453b040870c5e33cd5706 Mon Sep 17 00:00:00 2001 From: Mark Tempel Date: Thu, 20 Mar 2003 03:06:52 +0000 Subject: [PATCH] Initial revision of the polygon fill routines. This implementation only supports ALTERNATE mode. This revision is slow and has some drawing inaccuracies that need to be ironed out. svn path=/trunk/; revision=4363 --- reactos/subsys/win32k/objects/polyfill.c | 548 +++++++++++++++++++++++ 1 file changed, 548 insertions(+) create mode 100644 reactos/subsys/win32k/objects/polyfill.c diff --git a/reactos/subsys/win32k/objects/polyfill.c b/reactos/subsys/win32k/objects/polyfill.c new file mode 100644 index 00000000000..f5c8ad35a61 --- /dev/null +++ b/reactos/subsys/win32k/objects/polyfill.c @@ -0,0 +1,548 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * PURPOSE: Various Polygon Filling routines for Polygon() + * FILE: subsys/win32k/objects/polyfill.c + * PROGRAMER: Mark Tempel + * REVISION HISTORY: + * 21/2/2003: Created + */ + +#undef WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +// #define NDEBUG +#include + +#define PFILL_EDGE_ALLOC_TAG 0x45465044 + +/* +** This struct is used for book keeping during polygon filling routines. +*/ +typedef struct _tagPFILL_EDGE +{ + /*Basic line information*/ + int FromX; + int FromY; + int ToX; + int ToY; + int dx; + int dy; + int MinX; + int MaxX; + int MinY; + int MaxY; + + /*Active Edge List information*/ + int XIntercept; + int ErrorTerm; + int ErrorTermAdjUp; + int ErrorTermAdjDown; + int XPerY; + int Direction; + + /* The next edge in the Edge List*/ + struct _tagPFILL_EDGE * pNext; +} PFILL_EDGE, *PPFILL_EDGE; + +typedef PPFILL_EDGE PFILL_EDGE_LIST; + +static void DEBUG_PRINT_EDGELIST(PFILL_EDGE_LIST list) +{ + PPFILL_EDGE pThis = list; + if (0 == list) + { + DPRINT("List is NULL\n"); + return; + } + + while(0 != pThis) + { + DPRINT("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY); + pThis = pThis->pNext; + } +} + +/* +** Hide memory clean up. +*/ +static void POLYGONFILL_DestroyEdge(PPFILL_EDGE pEdge) +{ + if (0 != pEdge) + EngFreeMem(pEdge); +} + +/* +** Clean up a list. +*/ +static void POLYGONFILL_DestroyEdgeList(PFILL_EDGE_LIST list) +{ + PPFILL_EDGE pThis = 0; + PPFILL_EDGE pNext = 0; + + pThis = list; + while (0 != pThis) + { + //DPRINT("Destroying Edge\n"); + pNext = pThis->pNext; + POLYGONFILL_DestroyEdge(pThis); + pThis = pNext; + } + +} + +/* +** This makes and initiaizes an Edge struct for a line between two points. +*/ +static PPFILL_EDGE POLYGONFILL_MakeEdge(POINT From, POINT To) +{ + PPFILL_EDGE rc = (PPFILL_EDGE)EngAllocMem(FL_ZERO_MEMORY, sizeof(PFILL_EDGE), PFILL_EDGE_ALLOC_TAG); + + if (0 != rc) + { + //DPRINT("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y); + //Now Fill the struct. + rc->FromX = From.x; + rc->FromY = From.y; + rc->ToX = To.x; + rc->ToY = To.y; + + rc->dx = To.x - From.x; + rc->dy = To.y - From.y; + rc->MinX = MIN(To.x, From.x); + rc->MaxX = MAX(To.x, From.x); + rc->MinY = MIN(To.y, From.y); + rc->MaxY = MAX(To.y, From.y); + + if (rc->MinY == To.y) + rc->XIntercept = To.x; + else + rc->XIntercept = From.x; + + rc->ErrorTermAdjDown = rc->dy; + rc->Direction = (rc->dx < 0)?(-1):(1); + + if (rc->dx >= 0) /*edge goes l to r*/ + { + rc->ErrorTerm = 0; + } + else/*edge goes r to l*/ + { + rc->ErrorTerm = -rc->dy +1; + } + + /*Now which part of the slope is greater?*/ + if (rc->dy == 0) + { + rc->XPerY = 0; + rc->ErrorTermAdjUp = 0; + } + else if (rc->dy >= rc->dx) + { + rc->XPerY = 0; + rc->ErrorTermAdjUp = rc->dx; + } + else + { + rc->XPerY = rc->dx / rc->dy; + rc->ErrorTermAdjUp = rc->dx % rc->dy; + } + + rc->pNext = 0; + } + return rc; +} +/* +** My Edge comparison routine. +** This is for scan converting polygon fill. +** Fist sort by MinY, then Minx, then slope. +** +** This comparison will help us determine which +** lines will become active first when scanning from +** top (min y) to bottom (max y). +** +** Return Value Meaning +** Negative integer element1 < element2 +** Zero element1 = element2 +** Positive integer element1 > element2 +*/ +static INT PFILL_EDGE_Compare(PPFILL_EDGE Edge1, PPFILL_EDGE Edge2) +{ + //DPRINT("In PFILL_EDGE_Compare()\n"); + if (Edge1->MinY == Edge2->MinY) + { + //DPRINT("In PFILL_EDGE_Compare() MinYs are equal\n"); + if (Edge1->MinX == Edge2->MinX) + { + if (0 == Edge2->dx || 0 == Edge1->dx) + { + return Edge1->dx - Edge2->dx; + } + else + { + return (Edge1->dy/Edge1->dx) - (Edge2->dy/Edge2->dx); + } + } + else + { + return Edge1->MinX - Edge2->MinX; + } + } + //DPRINT("In PFILL_EDGE_Compare() returning: %d\n",Edge1->MinY - Edge2->MinY); + return Edge1->MinY - Edge2->MinY; + +} + + +/* +** Insert an edge into a list keeping the list in order. +*/ +static void POLYGONFILL_ListInsert(PFILL_EDGE_LIST *list, PPFILL_EDGE NewEdge) +{ + PPFILL_EDGE pThis; + if (0 != list && 0 != NewEdge) + { + pThis = *list; + //DPRINT("In POLYGONFILL_ListInsert()\n"); + /* + ** First lets check to see if we have a new smallest value. + */ + if (0 < PFILL_EDGE_Compare(pThis, NewEdge)) + { + NewEdge->pNext = pThis; + *list = NewEdge; + return; + } + /* + ** Ok, now scan to the next spot to put this item. + */ + while (0 > PFILL_EDGE_Compare(pThis, NewEdge)) + { + if (0 == pThis->pNext) + break; + + pThis = pThis->pNext; + } + + NewEdge->pNext = pThis->pNext; + pThis->pNext = NewEdge; + //DEBUG_PRINT_EDGELIST(*list); + } +} + +/* +** Create a list of edges for a list of points. +*/ +static PFILL_EDGE_LIST POLYGONFILL_MakeEdgeList(PPOINT Points, int Count) +{ + int CurPt = 0; + int SeqNum = 0; + PPFILL_EDGE rc = 0; + PPFILL_EDGE NextEdge = 0; + + if (0 != Points && 2 <= Count) + { + //Establish the list with the first two points. + rc = POLYGONFILL_MakeEdge(Points[0],Points[1]); + if (0 == rc) return rc; + + for (CurPt = 1; CurPt < Count; ++CurPt,++SeqNum) + { + if (CurPt == Count - 1) + { + NextEdge = POLYGONFILL_MakeEdge(Points[CurPt],Points[0]); + } + else + { + NextEdge = POLYGONFILL_MakeEdge(Points[CurPt],Points[CurPt + 1]); + } + if (0 != NextEdge) + { + POLYGONFILL_ListInsert(&rc, NextEdge); + } + else + { + DPRINT("Out Of MEMORY!! NextEdge = 0\n"); + } + } + } + return rc; +} + + +/* +** This slow routine uses the data stored in the edge list to +** calculate the x intercepts for each line in the edge list +** for scanline Scanline. +**TODO: Get rid of this floating point arithmetic +*/ +static void POLYGONFILL_UpdateScanline(PPFILL_EDGE pEdge, int Scanline) +{ + + int Coord = 0; + float XCoord = 0; + if (0 == pEdge->dy) return; + + XCoord = (Scanline*pEdge->dx - pEdge->FromY*pEdge->dx)/pEdge->dy + pEdge->FromX; + Coord = XCoord + 0.5; + + //DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at %d\n", + // pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, Coord); + pEdge->XIntercept = Coord; + + + /*pEdge->XIntercept += pEdge->XPerY; + + if ((pEdge->ErrorTerm += pEdge->ErrorTermAdjUp) > 0) + { + pEdge->XIntercept += pEdge->Direction; + pEdge->ErrorTerm -= pEdge->ErrorTermAdjDown; + }*/ +} + +/* +** This routine removes an edge from the global edge list and inserts it into +** the active edge list (preserving the order). +** An edge is considered Active if the current scanline intersects it. +** +** Note: once an edge is no longer active, it is deleted. +*/ +static void POLYGONFILL_AECInsertInOrder(PFILL_EDGE_LIST *list, PPFILL_EDGE pEdge) +{ + BOOL Done = FALSE; + PPFILL_EDGE pThis = 0; + PPFILL_EDGE pPrev = 0; + pThis = *list; + while(0 != pThis && !Done) + { + /*pEdge goes before pThis*/ + if (pThis->XIntercept > pEdge->XIntercept) + { + if (*list == pThis) + { + *list = pEdge; + } + else + { + pPrev->pNext = pEdge; + } + pEdge->pNext = pThis; + Done = TRUE; + } + pPrev = pThis; + pThis = pThis->pNext; + } +} + +/* +** This routine reorders the Active Edge collection (list) after all +** the now inactive edges have been removed. +*/ +static void POLYGONFILL_AECReorder(PFILL_EDGE_LIST *AEC) +{ + PPFILL_EDGE pThis = 0; + PPFILL_EDGE pPrev = 0; + PPFILL_EDGE pTarg = 0; + pThis = *AEC; + + while (0 != pThis) + { + /*We are at the end of the list*/ + if (0 == pThis->pNext) + { + return; + } + + /*If the next item is out of order, pull it from the list and + re-insert it, and don't advance pThis.*/ + if (pThis->XIntercept > pThis->pNext->XIntercept) + { + pTarg = pThis->pNext; + pThis->pNext = pTarg->pNext; + pTarg->pNext = 0; + POLYGONFILL_AECInsertInOrder(AEC, pTarg); + } + else/*In order, advance pThis*/ + { + pPrev = pThis; + pThis = pThis->pNext; + } + } + +} +/* +** This method updates the Active edge collection for the scanline Scanline. +*/ +static void POLYGONFILL_UpdateActiveEdges(int Scanline, PFILL_EDGE_LIST *GEC, PFILL_EDGE_LIST *AEC) +{ + PPFILL_EDGE pThis = 0; + PPFILL_EDGE pAECLast = 0; + PPFILL_EDGE pPrev = 0; + DPRINT("In POLYGONFILL_UpdateActiveEdges() Scanline: %d\n", Scanline); + /*First scan through GEC and look for any edges that have become active*/ + pThis = *GEC; + while (0 != pThis && pThis->MinY <= Scanline) + { + //DPRINT("Moving Edge to AEC\n"); + /*Remove the edge from GEC and put it into AEC*/ + if (pThis->MinY <= Scanline) + { + /*Always peel off the front of the GEC*/ + *GEC = pThis->pNext; + + /*Now put this edge at the end of AEC*/ + if (0 == *AEC) + { + *AEC = pThis; + pThis->pNext = 0; + pAECLast = pThis; + } + else if(0 == pAECLast) + { + pAECLast = *AEC; + while(0 != pAECLast->pNext) + { + pAECLast = pAECLast->pNext; + } + + pAECLast->pNext = pThis; + pThis->pNext = 0; + pAECLast = pThis; + } + else + { + pAECLast->pNext = pThis; + pThis->pNext = 0; + pAECLast = pThis; + + } + } + + pThis = *GEC; + } + /*Now remove any edges in the AEC that are no longer active and Update the XIntercept in the AEC*/ + pThis = *AEC; + while (0 != pThis) + { + /*First check to see if this item is deleted*/ + if (pThis->MaxY <= Scanline) + { + //DPRINT("Removing Edge from AEC\n"); + if (0 == pPrev)/*First element in the list*/ + { + *AEC = pThis->pNext; + } + else + { + pPrev->pNext = pThis->pNext; + } + POLYGONFILL_DestroyEdge(pThis); + } + else/*Otherwise, update the scanline*/ + { + POLYGONFILL_UpdateScanline(pThis, Scanline); + pPrev = pThis; + } + /*List Upkeep*/ + if (0 == pPrev)/*First element in the list*/ + { + pThis = *AEC; + } + else + { + pThis = pPrev->pNext; + } + } + /*Last re Xintercept order the AEC*/ + POLYGONFILL_AECReorder(AEC); + +} + +/* +** This method fills the portion of the polygon that intersects with the scanline +** Scanline. +*/ +static void POLYGONFILL_FillScanLine(int ScanLine, PFILL_EDGE_LIST ActiveEdges, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, int OrigX, int OrigY) +{ + BOOL OnOdd = TRUE; + RECTL BoundRect; + int XInterceptOdd,XInterceptEven,ret; + PPFILL_EDGE pThis = ActiveEdges; + + while (0 != pThis) + { + if (OnOdd) + { + XInterceptOdd = pThis->XIntercept; + OnOdd = FALSE; + } + else + { + BoundRect.top = ScanLine + OrigY - 1; + BoundRect.bottom = ScanLine + OrigY + 1; + BoundRect.left = XInterceptOdd + OrigX - 2; + BoundRect.right = XInterceptEven + OrigX; + + XInterceptEven = pThis->XIntercept; + DPRINT("Fill Line (%d, %d) to (%d, %d)\n",XInterceptOdd - 1,ScanLine ,XInterceptEven - 1,ScanLine ); + ret = EngLineTo(SurfObj, + NULL, // ClipObj, + BrushObj, + XInterceptOdd + OrigX - 1, + ScanLine + OrigY, + XInterceptEven + OrigX - 1, + ScanLine + OrigY, + &BoundRect, // Bounding rectangle + RopMode); // MIX + OnOdd = TRUE; + } + pThis = pThis->pNext; + } +} + +//ALTERNATE Selects alternate mode (fills the area between odd-numbered and even-numbered +//polygon sides on each scan line). +//When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and +//even-numbered polygon sides on each scan line. That is, GDI fills the area between the +//first and second side, between the third and fourth side, and so on. +BOOL FillPolygon_ALTERNATE(SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect, int OrigX, int OrigY) +{ + PFILL_EDGE_LIST list = 0; + PFILL_EDGE_LIST ActiveEdges = 0; + int ScanLine; + DPRINT("FillPolygon_ALTERNATE\n"); + + //Create Edge List. + list = POLYGONFILL_MakeEdgeList(Points, Count); + //DEBUG_PRINT_EDGELIST(list); + if (0 == list) return FALSE; + + //For each Scanline from BoundRect.bottom to BoundRect.top, + //determine line segments to draw + for (ScanLine = BoundRect.top + 1; ScanLine < BoundRect.bottom ; ++ScanLine) + { + POLYGONFILL_UpdateActiveEdges(ScanLine, &list, &ActiveEdges); + //DEBUG_PRINT_EDGELIST(ActiveEdges); + POLYGONFILL_FillScanLine(ScanLine, ActiveEdges, SurfObj, BrushObj, RopMode, OrigX, OrigY); + } + + //Free Edge List. If any are left. + POLYGONFILL_DestroyEdgeList(list); + + return TRUE; +} + +//WINDING Selects winding mode (fills any region with a nonzero winding value). +//When the fill mode is WINDING, GDI fills any region that has a nonzero winding value. +//This value is defined as the number of times a pen used to draw the polygon would go around the region. +//The direction of each edge of the polygon is important. +BOOL FillPolygon_WINDING(SURFOBJ *SurfObj, PBRUSHOBJ BrushObj,MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect, int OrigX, int OrigY) +{ + DPRINT("FillPolygon_WINDING\n"); + return FALSE; +} \ No newline at end of file