mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 09:34:43 +00:00
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:
parent
fac59fb33a
commit
3203df9587
1 changed files with 548 additions and 0 deletions
548
reactos/subsys/win32k/objects/polyfill.c
Normal file
548
reactos/subsys/win32k/objects/polyfill.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue