2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Various Polygon Filling routines for Polygon()
5 * FILE: subsys/win32k/objects/polyfill.c
6 * PROGRAMER: Mark Tempel
11 #undef WIN32_LEAN_AND_MEAN
13 #include <ddk/ntddk.h>
14 #include <win32k/fillshap.h>
15 #include <win32k/dc.h>
16 #include <win32k/pen.h>
17 #include <include/object.h>
20 #include <win32k/debug1.h>
22 #define PFILL_EDGE_ALLOC_TAG 0x45465044
25 ** This struct is used for book keeping during polygon filling routines.
27 typedef struct _tagPFILL_EDGE
29 /*Basic line information*/
41 /*Active Edge List information*/
49 /* The next edge in the Edge List*/
50 struct _tagPFILL_EDGE * pNext;
51 } PFILL_EDGE, *PPFILL_EDGE;
53 typedef PPFILL_EDGE PFILL_EDGE_LIST;
55 static void DEBUG_PRINT_EDGELIST(PFILL_EDGE_LIST list)
57 PPFILL_EDGE pThis = list;
60 DPRINT("List is NULL\n");
66 DPRINT("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY);
72 ** Hide memory clean up.
74 static void POLYGONFILL_DestroyEdge(PPFILL_EDGE pEdge)
83 static void POLYGONFILL_DestroyEdgeList(PFILL_EDGE_LIST list)
85 PPFILL_EDGE pThis = 0;
86 PPFILL_EDGE pNext = 0;
91 //DPRINT("Destroying Edge\n");
93 POLYGONFILL_DestroyEdge(pThis);
100 ** This makes and initiaizes an Edge struct for a line between two points.
102 static PPFILL_EDGE POLYGONFILL_MakeEdge(POINT From, POINT To)
104 PPFILL_EDGE rc = (PPFILL_EDGE)EngAllocMem(FL_ZERO_MEMORY, sizeof(PFILL_EDGE), PFILL_EDGE_ALLOC_TAG);
108 //DPRINT("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y);
109 //Now Fill the struct.
115 rc->dx = To.x - From.x;
116 rc->dy = To.y - From.y;
117 rc->MinX = MIN(To.x, From.x);
118 rc->MaxX = MAX(To.x, From.x);
119 rc->MinY = MIN(To.y, From.y);
120 rc->MaxY = MAX(To.y, From.y);
122 if (rc->MinY == To.y)
123 rc->XIntercept = To.x;
125 rc->XIntercept = From.x;
127 rc->ErrorTermAdjDown = rc->dy;
128 rc->Direction = (rc->dx < 0)?(-1):(1);
130 if (rc->dx >= 0) /*edge goes l to r*/
134 else/*edge goes r to l*/
136 rc->ErrorTerm = -rc->dy +1;
139 /*Now which part of the slope is greater?*/
143 rc->ErrorTermAdjUp = 0;
145 else if (rc->dy >= rc->dx)
148 rc->ErrorTermAdjUp = rc->dx;
152 rc->XPerY = rc->dx / rc->dy;
153 rc->ErrorTermAdjUp = rc->dx % rc->dy;
161 ** My Edge comparison routine.
162 ** This is for scan converting polygon fill.
163 ** Fist sort by MinY, then Minx, then slope.
165 ** This comparison will help us determine which
166 ** lines will become active first when scanning from
167 ** top (min y) to bottom (max y).
169 ** Return Value Meaning
170 ** Negative integer element1 < element2
171 ** Zero element1 = element2
172 ** Positive integer element1 > element2
174 static INT PFILL_EDGE_Compare(PPFILL_EDGE Edge1, PPFILL_EDGE Edge2)
176 //DPRINT("In PFILL_EDGE_Compare()\n");
177 if (Edge1->MinY == Edge2->MinY)
179 //DPRINT("In PFILL_EDGE_Compare() MinYs are equal\n");
180 if (Edge1->MinX == Edge2->MinX)
182 if (0 == Edge2->dx || 0 == Edge1->dx)
184 return Edge1->dx - Edge2->dx;
188 return (Edge1->dy/Edge1->dx) - (Edge2->dy/Edge2->dx);
193 return Edge1->MinX - Edge2->MinX;
196 //DPRINT("In PFILL_EDGE_Compare() returning: %d\n",Edge1->MinY - Edge2->MinY);
197 return Edge1->MinY - Edge2->MinY;
203 ** Insert an edge into a list keeping the list in order.
205 static void POLYGONFILL_ListInsert(PFILL_EDGE_LIST *list, PPFILL_EDGE NewEdge)
208 if (0 != list && 0 != NewEdge)
211 //DPRINT("In POLYGONFILL_ListInsert()\n");
213 ** First lets check to see if we have a new smallest value.
215 if (0 < PFILL_EDGE_Compare(pThis, NewEdge))
217 NewEdge->pNext = pThis;
222 ** Ok, now scan to the next spot to put this item.
224 while (0 > PFILL_EDGE_Compare(pThis, NewEdge))
226 if (0 == pThis->pNext)
229 pThis = pThis->pNext;
232 NewEdge->pNext = pThis->pNext;
233 pThis->pNext = NewEdge;
234 //DEBUG_PRINT_EDGELIST(*list);
239 ** Create a list of edges for a list of points.
241 static PFILL_EDGE_LIST POLYGONFILL_MakeEdgeList(PPOINT Points, int Count)
246 PPFILL_EDGE NextEdge = 0;
248 if (0 != Points && 2 <= Count)
250 //Establish the list with the first two points.
251 rc = POLYGONFILL_MakeEdge(Points[0],Points[1]);
252 if (0 == rc) return rc;
254 for (CurPt = 1; CurPt < Count; ++CurPt,++SeqNum)
256 if (CurPt == Count - 1)
258 NextEdge = POLYGONFILL_MakeEdge(Points[CurPt],Points[0]);
262 NextEdge = POLYGONFILL_MakeEdge(Points[CurPt],Points[CurPt + 1]);
266 POLYGONFILL_ListInsert(&rc, NextEdge);
270 DPRINT("Out Of MEMORY!! NextEdge = 0\n");
279 ** This slow routine uses the data stored in the edge list to
280 ** calculate the x intercepts for each line in the edge list
281 ** for scanline Scanline.
282 **TODO: Get rid of this floating point arithmetic
284 static void POLYGONFILL_UpdateScanline(PPFILL_EDGE pEdge, int Scanline)
289 if (0 == pEdge->dy) return;
291 XCoord = (Scanline*pEdge->dx - pEdge->FromY*pEdge->dx)/pEdge->dy + pEdge->FromX;
292 Coord = XCoord + 0.5;
294 //DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at %d\n",
295 // pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, Coord);
296 pEdge->XIntercept = Coord;
299 /*pEdge->XIntercept += pEdge->XPerY;
301 if ((pEdge->ErrorTerm += pEdge->ErrorTermAdjUp) > 0)
303 pEdge->XIntercept += pEdge->Direction;
304 pEdge->ErrorTerm -= pEdge->ErrorTermAdjDown;
309 ** This routine removes an edge from the global edge list and inserts it into
310 ** the active edge list (preserving the order).
311 ** An edge is considered Active if the current scanline intersects it.
313 ** Note: once an edge is no longer active, it is deleted.
315 static void POLYGONFILL_AECInsertInOrder(PFILL_EDGE_LIST *list, PPFILL_EDGE pEdge)
318 PPFILL_EDGE pThis = 0;
319 PPFILL_EDGE pPrev = 0;
321 while(0 != pThis && !Done)
323 /*pEdge goes before pThis*/
324 if (pThis->XIntercept > pEdge->XIntercept)
332 pPrev->pNext = pEdge;
334 pEdge->pNext = pThis;
338 pThis = pThis->pNext;
343 ** This routine reorders the Active Edge collection (list) after all
344 ** the now inactive edges have been removed.
346 static void POLYGONFILL_AECReorder(PFILL_EDGE_LIST *AEC)
348 PPFILL_EDGE pThis = 0;
349 PPFILL_EDGE pPrev = 0;
350 PPFILL_EDGE pTarg = 0;
355 /*We are at the end of the list*/
356 if (0 == pThis->pNext)
361 /*If the next item is out of order, pull it from the list and
362 re-insert it, and don't advance pThis.*/
363 if (pThis->XIntercept > pThis->pNext->XIntercept)
365 pTarg = pThis->pNext;
366 pThis->pNext = pTarg->pNext;
368 POLYGONFILL_AECInsertInOrder(AEC, pTarg);
370 else/*In order, advance pThis*/
373 pThis = pThis->pNext;
379 ** This method updates the Active edge collection for the scanline Scanline.
381 static void POLYGONFILL_UpdateActiveEdges(int Scanline, PFILL_EDGE_LIST *GEC, PFILL_EDGE_LIST *AEC)
383 PPFILL_EDGE pThis = 0;
384 PPFILL_EDGE pAECLast = 0;
385 PPFILL_EDGE pPrev = 0;
386 DPRINT("In POLYGONFILL_UpdateActiveEdges() Scanline: %d\n", Scanline);
387 /*First scan through GEC and look for any edges that have become active*/
389 while (0 != pThis && pThis->MinY <= Scanline)
391 //DPRINT("Moving Edge to AEC\n");
392 /*Remove the edge from GEC and put it into AEC*/
393 if (pThis->MinY <= Scanline)
395 /*Always peel off the front of the GEC*/
398 /*Now put this edge at the end of AEC*/
405 else if(0 == pAECLast)
408 while(0 != pAECLast->pNext)
410 pAECLast = pAECLast->pNext;
413 pAECLast->pNext = pThis;
419 pAECLast->pNext = pThis;
428 /*Now remove any edges in the AEC that are no longer active and Update the XIntercept in the AEC*/
432 /*First check to see if this item is deleted*/
433 if (pThis->MaxY <= Scanline)
435 //DPRINT("Removing Edge from AEC\n");
436 if (0 == pPrev)/*First element in the list*/
442 pPrev->pNext = pThis->pNext;
444 POLYGONFILL_DestroyEdge(pThis);
446 else/*Otherwise, update the scanline*/
448 POLYGONFILL_UpdateScanline(pThis, Scanline);
452 if (0 == pPrev)/*First element in the list*/
458 pThis = pPrev->pNext;
461 /*Last re Xintercept order the AEC*/
462 POLYGONFILL_AECReorder(AEC);
467 ** This method fills the portion of the polygon that intersects with the scanline
470 static void POLYGONFILL_FillScanLine(int ScanLine, PFILL_EDGE_LIST ActiveEdges, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, int OrigX, int OrigY)
474 int XInterceptOdd,XInterceptEven,ret;
475 PPFILL_EDGE pThis = ActiveEdges;
481 XInterceptOdd = pThis->XIntercept;
486 BoundRect.top = ScanLine + OrigY - 1;
487 BoundRect.bottom = ScanLine + OrigY + 1;
488 BoundRect.left = XInterceptOdd + OrigX - 2;
489 BoundRect.right = XInterceptEven + OrigX;
491 XInterceptEven = pThis->XIntercept;
492 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",XInterceptOdd - 1,ScanLine ,XInterceptEven - 1,ScanLine );
493 ret = EngLineTo(SurfObj,
496 XInterceptOdd + OrigX - 1,
498 XInterceptEven + OrigX - 1,
500 &BoundRect, // Bounding rectangle
504 pThis = pThis->pNext;
508 //ALTERNATE Selects alternate mode (fills the area between odd-numbered and even-numbered
509 //polygon sides on each scan line).
510 //When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
511 //even-numbered polygon sides on each scan line. That is, GDI fills the area between the
512 //first and second side, between the third and fourth side, and so on.
513 BOOL FillPolygon_ALTERNATE(SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect, int OrigX, int OrigY)
515 PFILL_EDGE_LIST list = 0;
516 PFILL_EDGE_LIST ActiveEdges = 0;
518 DPRINT("FillPolygon_ALTERNATE\n");
521 list = POLYGONFILL_MakeEdgeList(Points, Count);
522 //DEBUG_PRINT_EDGELIST(list);
523 if (0 == list) return FALSE;
525 //For each Scanline from BoundRect.bottom to BoundRect.top,
526 //determine line segments to draw
527 for (ScanLine = BoundRect.top + 1; ScanLine < BoundRect.bottom ; ++ScanLine)
529 POLYGONFILL_UpdateActiveEdges(ScanLine, &list, &ActiveEdges);
530 //DEBUG_PRINT_EDGELIST(ActiveEdges);
531 POLYGONFILL_FillScanLine(ScanLine, ActiveEdges, SurfObj, BrushObj, RopMode, OrigX, OrigY);
534 //Free Edge List. If any are left.
535 POLYGONFILL_DestroyEdgeList(list);
540 //WINDING Selects winding mode (fills any region with a nonzero winding value).
541 //When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
542 //This value is defined as the number of times a pen used to draw the polygon would go around the region.
543 //The direction of each edge of the polygon is important.
544 BOOL FillPolygon_WINDING(SURFOBJ *SurfObj, PBRUSHOBJ BrushObj,MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect, int OrigX, int OrigY)
546 DPRINT("FillPolygon_WINDING\n");