2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Various Polygon Filling routines for Polygon()
5 * FILE: win32ss/gdi/ntgdi/polyfill.c
6 * PROGRAMER: Mark Tempel
14 #define FILL_EDGE_ALLOC_TAG 0x45465044
17 ** This struct is used for book keeping during polygon filling routines.
19 typedef struct _tagFILL_EDGE
21 /* Basic line information */
32 /* Active Edge List information */
36 int XDirection
, YDirection
;
38 /* The next edge in the active Edge List */
39 struct _tagFILL_EDGE
* pNext
;
42 typedef struct _FILL_EDGE_LIST
51 DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE
* list
)
53 FILL_EDGE
* pThis
= list
;
56 DPRINT1("List is NULL\n");
62 //DPRINT1("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY);
63 DPRINT1("EDGE: [%d,%d]\n", pThis
->XIntercept
[0], pThis
->XIntercept
[1] );
68 #define DEBUG_PRINT_ACTIVE_EDGELIST(x)
72 ** Hide memory clean up.
77 POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST
* list
)
85 for (i
= 0; i
< list
->Count
; i
++)
87 _PRAGMA_WARNING_SUPPRESS(__WARNING_USING_UNINIT_VAR
)
89 EngFreeMem(list
->Edges
[i
]);
92 EngFreeMem(list
->Edges
);
100 ** This makes and initializes an Edge struct for a line between two points.
105 POLYGONFILL_MakeEdge(POINT From
, POINT To
)
107 FILL_EDGE
* rc
= (FILL_EDGE
*)EngAllocMem(FL_ZERO_MEMORY
, sizeof(FILL_EDGE
), FILL_EDGE_ALLOC_TAG
);
112 //DPRINT1("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y);
113 // Now fill the struct.
122 // Lines that go up get walked backwards, so need to be offset
123 // by -1 in order to make the walk identically on a pixel-level
139 rc
->dx
= rc
->ToX
- rc
->FromX
;
140 rc
->dy
= rc
->ToY
- rc
->FromY
;
141 rc
->absdx
= abs(rc
->dx
);
142 rc
->absdy
= abs(rc
->dy
);
144 rc
->xmajor
= rc
->absdx
> rc
->absdy
;
146 rc
->ErrorMax
= max(rc
->absdx
,rc
->absdy
);
148 rc
->Error
+= rc
->ErrorMax
/ 2;
150 rc
->XDirection
= (rc
->dx
< 0)?(-1):(1);
154 //DPRINT("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n",
155 // From.x, From.y, To.x, To.y, rc->dx, rc->dy, rc->XDirection, rc->YDirection, rc->Error, rc->ErrorMax );
160 ** My Edge comparison routine.
161 ** This is for scan converting polygon fill.
162 ** First sort by MinY, then Minx, then slope.
164 ** This comparison will help us determine which
165 ** lines will become active first when scanning from
166 ** top (min y) to bottom (max y).
168 ** Return Value Meaning
169 ** Negative integer element1 < element2
170 ** Zero element1 = element2
171 ** Positive integer element1 > element2
176 FILL_EDGE_Compare(FILL_EDGE
* Edge1
, FILL_EDGE
* Edge2
)
178 int e1
= Edge1
->XIntercept
[0] + Edge1
->XIntercept
[1];
179 int e2
= Edge2
->XIntercept
[0] + Edge2
->XIntercept
[1];
186 ** Insert an edge into a list keeping the list in order.
191 POLYGONFILL_ActiveListInsert(FILL_EDGE
** activehead
, FILL_EDGE
* NewEdge
)
193 FILL_EDGE
*pPrev
, *pThis
;
194 //DPRINT1("In POLYGONFILL_ActiveListInsert()\n");
195 ASSERT(activehead
&& NewEdge
);
199 NewEdge
->pNext
= NULL
;
200 *activehead
= NewEdge
;
204 ** First lets check to see if we have a new smallest value.
206 if (FILL_EDGE_Compare(NewEdge
, *activehead
) <= 0)
208 NewEdge
->pNext
= *activehead
;
209 *activehead
= NewEdge
;
214 ** Ok, now scan to the next spot to put this item.
218 while ( pThis
&& FILL_EDGE_Compare(pThis
, NewEdge
) < 0 )
221 pThis
= pThis
->pNext
;
225 NewEdge
->pNext
= pPrev
->pNext
;
226 pPrev
->pNext
= NewEdge
;
227 //DEBUG_PRINT_ACTIVE_EDGELIST(*activehead);
231 ** Create a list of edges for a list of points.
236 POLYGONFILL_MakeEdgeList(PPOINT Points
, int Count
)
239 FILL_EDGE_LIST
* list
= 0;
242 if (0 == Points
|| 2 > Count
)
245 list
= (FILL_EDGE_LIST
*)EngAllocMem(FL_ZERO_MEMORY
, sizeof(FILL_EDGE_LIST
), FILL_EDGE_ALLOC_TAG
);
249 list
->Edges
= (FILL_EDGE
**)EngAllocMem(FL_ZERO_MEMORY
, Count
*sizeof(FILL_EDGE
*), FILL_EDGE_ALLOC_TAG
);
253 memset(list
->Edges
, 0, Count
* sizeof(FILL_EDGE
*));
255 for (CurPt
= 1; CurPt
< Count
; ++CurPt
)
257 e
= POLYGONFILL_MakeEdge ( Points
[CurPt
-1], Points
[CurPt
] );
261 // If a straight horizontal line - who cares?
265 list
->Edges
[list
->Count
++] = e
;
267 e
= POLYGONFILL_MakeEdge ( Points
[CurPt
-1], Points
[0] );
274 list
->Edges
[list
->Count
++] = e
;
279 DPRINT1("Out Of MEMORY!!\n");
280 POLYGONFILL_DestroyEdgeList ( list
);
286 ** This slow routine uses the data stored in the edge list to
287 ** calculate the x intercepts for each line in the edge list
288 ** for scanline Scanline.
289 **TODO: Get rid of this floating point arithmetic
294 POLYGONFILL_UpdateScanline(FILL_EDGE
* pEdge
, int Scanline
)
299 ASSERT(pEdge
->FromY
<= Scanline
&& pEdge
->ToY
> Scanline
);
305 ASSERT(pEdge
->y
== Scanline
);
307 // Now shoot to end of scanline collision
308 steps
= (pEdge
->ErrorMax
-pEdge
->Error
-1)/pEdge
->absdy
;
311 // Record first collision with scanline
313 pEdge
->x
+= steps
* pEdge
->XDirection
;
314 pEdge
->Error
+= steps
* pEdge
->absdy
;
315 ASSERT ( pEdge
->Error
< pEdge
->ErrorMax
);
316 pEdge
->XIntercept
[0] = min(x1
,pEdge
->x
);
317 pEdge
->XIntercept
[1] = max(x1
,pEdge
->x
);
321 pEdge
->XIntercept
[0] = pEdge
->x
;
322 pEdge
->XIntercept
[1] = pEdge
->x
;
325 // We should require exactly 1 step to step onto next scanline...
326 ASSERT((pEdge
->ErrorMax
-pEdge
->Error
-1) / pEdge
->absdy
== 0);
327 pEdge
->x
+= pEdge
->XDirection
;
328 pEdge
->Error
+= pEdge
->absdy
;
329 ASSERT(pEdge
->Error
>= pEdge
->ErrorMax
);
331 // Now step onto next scanline...
332 pEdge
->Error
-= pEdge
->absdx
;
335 else // Then this is a y-major line
337 pEdge
->XIntercept
[0] = pEdge
->x
;
338 pEdge
->XIntercept
[1] = pEdge
->x
;
340 pEdge
->Error
+= pEdge
->absdx
;
343 if (pEdge
->Error
>= pEdge
->ErrorMax
)
345 pEdge
->Error
-= pEdge
->ErrorMax
;
346 pEdge
->x
+= pEdge
->XDirection
;
347 ASSERT ( pEdge
->Error
< pEdge
->ErrorMax
);
351 //DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n",
352 // pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, pEdge->XIntercept[0], pEdge->XIntercept[1] );
356 * This method updates the Active edge collection for the scanline Scanline.
361 POLYGONFILL_BuildActiveList(
363 FILL_EDGE_LIST
* list
,
364 FILL_EDGE
** ActiveHead
)
368 ASSERT(list
&& ActiveHead
);
370 for (i
= 0; i
< list
->Count
; i
++)
372 FILL_EDGE
* pEdge
= list
->Edges
[i
];
374 if (pEdge
->FromY
<= Scanline
&& pEdge
->ToY
> Scanline
)
376 POLYGONFILL_UpdateScanline(pEdge
, Scanline
);
377 POLYGONFILL_ActiveListInsert(ActiveHead
, pEdge
);
383 ** This method fills the portion of the polygon that intersects with the scanline
389 POLYGONFILL_FillScanLineAlternate(
392 FILL_EDGE
* ActiveHead
,
397 FILL_EDGE
*pLeft
, *pRight
;
403 pRight
= pLeft
->pNext
;
406 while (NULL
!= pRight
)
408 int x1
= pLeft
->XIntercept
[0];
409 int x2
= pRight
->XIntercept
[1];
413 BoundRect
.top
= ScanLine
;
414 BoundRect
.bottom
= ScanLine
+ 1;
416 BoundRect
.right
= x2
;
418 //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
419 IntEngLineTo(&psurf
->SurfObj
,
426 &BoundRect
, // Bounding rectangle
429 pLeft
= pRight
->pNext
;
430 pRight
= pLeft
? pLeft
->pNext
: NULL
;
437 POLYGONFILL_FillScanLineWinding(
440 FILL_EDGE
* ActiveHead
,
445 FILL_EDGE
*pLeft
, *pRight
;
446 int x1
, x2
, winding
= 0;
452 BoundRect
.top
= ScanLine
;
453 BoundRect
.bottom
= ScanLine
+ 1;
456 winding
= pLeft
->YDirection
;
457 pRight
= pLeft
->pNext
;
460 // Setup first line...
461 x1
= pLeft
->XIntercept
[0];
462 x2
= pRight
->XIntercept
[1];
465 pRight
= pLeft
->pNext
;
466 winding
+= pLeft
->YDirection
;
468 while ( NULL
!= pRight
)
470 int newx1
= pLeft
->XIntercept
[0];
471 int newx2
= pRight
->XIntercept
[1];
474 // Check and see if this new line touches the previous...
475 if ((newx1
>= x1
&& newx1
<= x2
) ||
476 (newx2
>= x1
&& newx2
<= x2
) ||
477 (x1
>= newx1
&& x1
<= newx2
) ||
478 (x2
>= newx2
&& x2
<= newx2
))
480 // Yup, just tack it on to our existing line
486 // Nope - render the old line..
488 BoundRect
.right
= x2
;
490 //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
491 IntEngLineTo(&psurf
->SurfObj
,
498 &BoundRect
, // Bounding rectangle
507 pRight
= pLeft
->pNext
;
508 winding
+= pLeft
->YDirection
;
511 // There will always be a line left-over, render it now...
513 BoundRect
.right
= x2
;
515 //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
516 IntEngLineTo(&psurf
->SurfObj
,
523 &BoundRect
, // Bounding rectangle
527 // When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
528 // even-numbered polygon sides on each scan line. That is, GDI fills the area between the
529 // first and second side, between the third and fourth side, and so on.
531 // WINDING Selects winding mode (fills any region with a nonzero winding value).
532 // When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
533 // This value is defined as the number of times a pen used to draw the polygon would go around the region.
534 // The direction of each edge of the polygon is important.
547 FILL_EDGE_LIST
*list
= 0;
548 FILL_EDGE
*ActiveHead
= 0;
550 PDC_ATTR pdcattr
= dc
->pdcattr
;
552 (APIENTRY
*FillScanLine
)(
555 FILL_EDGE
* ActiveHead
,
560 //DPRINT("FillPolygon\n");
562 /* Create Edge List. */
563 list
= POLYGONFILL_MakeEdgeList(Points
, Count
);
564 /* DEBUG_PRINT_EDGELIST(list); */
568 if (WINDING
== pdcattr
->jFillMode
)
569 FillScanLine
= POLYGONFILL_FillScanLineWinding
;
571 FillScanLine
= POLYGONFILL_FillScanLineAlternate
;
573 /* For each Scanline from BoundRect.bottom to BoundRect.top,
574 * determine line segments to draw
576 for (ScanLine
= BoundRect
.top
; ScanLine
< BoundRect
.bottom
; ++ScanLine
)
578 POLYGONFILL_BuildActiveList(ScanLine
, list
, &ActiveHead
);
579 //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
580 FillScanLine(dc
, ScanLine
, ActiveHead
, psurf
, BrushObj
, RopMode
);
583 /* Free Edge List. If any are left. */
584 POLYGONFILL_DestroyEdgeList(list
);
599 FILL_EDGE_LIST
*list
= 0;
600 FILL_EDGE
*ActiveHead
= 0;
601 FILL_EDGE
*pLeft
, *pRight
;
604 //DPRINT("IntFillPolygon\n");
606 /* Create Edge List. */
607 list
= POLYGONFILL_MakeEdgeList(Points
, Count
);
608 /* DEBUG_PRINT_EDGELIST(list); */
612 /* For each Scanline from DestRect.top to DestRect.bottom, determine line segments to draw */
613 for (ScanLine
= DestRect
.top
; ScanLine
< DestRect
.bottom
; ++ScanLine
)
615 POLYGONFILL_BuildActiveList(ScanLine
, list
, &ActiveHead
);
616 //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
620 POLYGONFILL_DestroyEdgeList(list
);
625 pRight
= pLeft
->pNext
;
628 while (NULL
!= pRight
)
630 int x1
= pLeft
->XIntercept
[0];
631 int x2
= pRight
->XIntercept
[1];
636 LineRect
.top
= ScanLine
;
637 LineRect
.bottom
= ScanLine
+ 1;
641 IntEngBitBlt(&psurf
->SurfObj
,
651 ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY
));
654 pLeft
= pRight
->pNext
;
655 pRight
= pLeft
? pLeft
->pNext
: NULL
;
659 /* Free Edge List. If any are left. */
660 POLYGONFILL_DestroyEdgeList(list
);