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
This commit is contained in:
Mark Tempel 2003-03-20 03:06:52 +00:00
parent fac59fb33a
commit 3203df9587

View file

@ -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 <windows.h>
#include <ddk/ntddk.h>
#include <win32k/fillshap.h>
#include <win32k/dc.h>
#include <win32k/pen.h>
#include <include/object.h>
// #define NDEBUG
#include <win32k/debug1.h>
#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;
}