1 // this is a little 'sandbox' application I put together that duplicates
2 // the 'guts' of the Polygon algorithm. It allows for quick turn-around
3 // in testing the algorithm to see what effect your changes have.
7 // the stuff immediately following is support so that the sandbox code
8 // is nearly identical to the real thing.
9 // search for the _tagFILL_EDGE struct to find the beginning of the
30 #define MmCopyFromCaller memmove
47 typedef struct tagPOINT
50 } POINT
, *PPOINT
, *LPPOINT
;
54 long left
, top
, right
, bottom
;
57 #define EngFreeMem free
59 #define FL_ZERO_MEMORY 1
61 #define DPRINT1 printf("%i:",__LINE__);printf
62 inline void DPRINT(...){}
66 char screen
[SCREENY
][SCREENX
];
71 void* EngAllocMem ( int zero
, unsigned long size
, int tag
=0 )
73 void* p
= malloc ( size
);
75 memset ( p
, 0, size
);
80 inline T
MIN ( T a
, T b
)
86 inline T
MAX ( T a
, T b
)
94 return t
< 0 ? -t
: t
;
97 void putpixel ( int x
, int y
, char c
)
99 ASSERT( x
>= 0 && x
< SCREENX
&& y
>= 0 && y
< SCREENY
);
100 if ( screen
[y
][x
] == c
)
102 if ( screen
[y
][x
] == ' ' )
112 int x1
, int y1
, int x2
, int y2
,
120 int EMax
= MAX(absdx
,absdy
);
122 int xinc
= dx
< 0 ? -1 : 1,
123 yinc
= dy
< 0 ? -1 : 1;
128 putpixel ( x1
, y1
, mix
);
137 putpixel ( x1
, y1
, mix
);
142 for ( int i
= 0; i
< EMax
; i
++ )
144 putpixel ( x1
, y1
, mix
);
168 #define FILL_EDGE_ALLOC_TAG 0x45465044
171 ** This struct is used for book keeping during polygon filling routines.
173 typedef struct _tagFILL_EDGE
175 /*Basic line information*/
186 /*Active Edge List information*/
190 int XDirection
, YDirection
;
192 /* The next edge in the active Edge List*/
193 struct _tagFILL_EDGE
* pNext
;
196 typedef struct _FILL_EDGE_LIST
205 DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE
* list
)
207 FILL_EDGE
* pThis
= list
;
210 DPRINT1("List is NULL\n");
216 //DPRINT1("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY);
217 DPRINT1("EDGE: [%d,%d]\n", pThis
->XIntercept
[0], pThis
->XIntercept
[1] );
218 pThis
= pThis
->pNext
;
222 #define DEBUG_PRINT_ACTIVE_EDGELIST(x)
226 ** Hide memory clean up.
231 POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST
* list
)
238 for ( i
= 0; i
< list
->Count
; i
++ )
240 if ( list
->Edges
[i
] )
241 EngFreeMem ( list
->Edges
[i
] );
243 EngFreeMem ( list
->Edges
);
250 ** This makes and initiaizes an Edge struct for a line between two points.
255 POLYGONFILL_MakeEdge(POINT From
, POINT To
)
257 FILL_EDGE
* rc
= (FILL_EDGE
*)EngAllocMem(FL_ZERO_MEMORY
, sizeof(FILL_EDGE
), FILL_EDGE_ALLOC_TAG
);
262 //DPRINT1("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y);
263 //Now Fill the struct.
272 // lines that go up get walked backwards, so need to be offset
273 // by -1 in order to make the walk identically on a pixel-level
289 rc
->dx
= rc
->ToX
- rc
->FromX
;
290 rc
->dy
= rc
->ToY
- rc
->FromY
;
291 rc
->absdx
= abs(rc
->dx
);
292 rc
->absdy
= abs(rc
->dy
);
294 rc
->xmajor
= rc
->absdx
> rc
->absdy
;
296 rc
->ErrorMax
= MAX(rc
->absdx
,rc
->absdy
);
298 rc
->Error
+= rc
->ErrorMax
/ 2;
300 rc
->XDirection
= (rc
->dx
< 0)?(-1):(1);
304 DPRINT("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n",
305 From
.x
, From
.y
, To
.x
, To
.y
, rc
->dx
, rc
->dy
, rc
->XDirection
, rc
->YDirection
, rc
->Error
, rc
->ErrorMax
);
310 ** My Edge comparison routine.
311 ** This is for scan converting polygon fill.
312 ** First sort by MinY, then Minx, then slope.
314 ** This comparison will help us determine which
315 ** lines will become active first when scanning from
316 ** top (min y) to bottom (max y).
318 ** Return Value Meaning
319 ** Negative integer element1 < element2
320 ** Zero element1 = element2
321 ** Positive integer element1 > element2
326 FILL_EDGE_Compare(FILL_EDGE
* Edge1
, FILL_EDGE
* Edge2
)
328 int e1
= Edge1
->XIntercept
[0] + Edge1
->XIntercept
[1];
329 int e2
= Edge2
->XIntercept
[0] + Edge2
->XIntercept
[1];
336 ** Insert an edge into a list keeping the list in order.
341 POLYGONFILL_ActiveListInsert(FILL_EDGE
** activehead
, FILL_EDGE
* NewEdge
)
343 FILL_EDGE
*pPrev
, *pThis
;
344 //DPRINT1("In POLYGONFILL_ActiveListInsert()\n");
345 ASSERT ( activehead
&& NewEdge
);
348 NewEdge
->pNext
= NULL
;
349 *activehead
= NewEdge
;
353 ** First lets check to see if we have a new smallest value.
355 if (FILL_EDGE_Compare(NewEdge
, *activehead
) <= 0)
357 NewEdge
->pNext
= *activehead
;
358 *activehead
= NewEdge
;
362 ** Ok, now scan to the next spot to put this item.
366 while ( pThis
&& FILL_EDGE_Compare(pThis
, NewEdge
) < 0 )
369 pThis
= pThis
->pNext
;
373 NewEdge
->pNext
= pPrev
->pNext
;
374 pPrev
->pNext
= NewEdge
;
375 //DEBUG_PRINT_ACTIVE_EDGELIST(*activehead);
379 ** Create a list of edges for a list of points.
384 POLYGONFILL_MakeEdgeList(PPOINT Points
, int Count
)
387 FILL_EDGE_LIST
* list
= 0;
390 if ( 0 == Points
|| 2 > Count
)
393 list
= (FILL_EDGE_LIST
*)EngAllocMem(FL_ZERO_MEMORY
, sizeof(FILL_EDGE_LIST
), FILL_EDGE_ALLOC_TAG
);
397 list
->Edges
= (FILL_EDGE
**)EngAllocMem(FL_ZERO_MEMORY
, Count
*sizeof(FILL_EDGE
*), FILL_EDGE_ALLOC_TAG
);
400 memset ( list
->Edges
, 0, Count
* sizeof(FILL_EDGE
*) );
402 for ( CurPt
= 1; CurPt
< Count
; ++CurPt
)
404 e
= POLYGONFILL_MakeEdge ( Points
[CurPt
-1], Points
[CurPt
] );
407 // if a straight horizontal line - who cares?
411 list
->Edges
[list
->Count
++] = e
;
413 e
= POLYGONFILL_MakeEdge ( Points
[CurPt
-1], Points
[0] );
419 list
->Edges
[list
->Count
++] = e
;
423 DPRINT1("Out Of MEMORY!!\n");
424 POLYGONFILL_DestroyEdgeList ( list
);
430 ** This slow routine uses the data stored in the edge list to
431 ** calculate the x intercepts for each line in the edge list
432 ** for scanline Scanline.
433 **TODO: Get rid of this floating point arithmetic
438 POLYGONFILL_UpdateScanline(FILL_EDGE
* pEdge
, int Scanline
)
440 if ( 0 == pEdge
->dy
)
443 ASSERT ( pEdge
->FromY
<= Scanline
&& pEdge
->ToY
> Scanline
);
449 ASSERT ( pEdge
->y
== Scanline
);
451 // now shoot to end of scanline collision
452 steps
= (pEdge
->ErrorMax
-pEdge
->Error
-1)/pEdge
->absdy
;
455 // record first collision with scanline
457 pEdge
->x
+= steps
* pEdge
->XDirection
;
458 pEdge
->Error
+= steps
* pEdge
->absdy
;
459 ASSERT ( pEdge
->Error
< pEdge
->ErrorMax
);
460 pEdge
->XIntercept
[0] = MIN(x1
,pEdge
->x
);
461 pEdge
->XIntercept
[1] = MAX(x1
,pEdge
->x
);
465 pEdge
->XIntercept
[0] = pEdge
->x
;
466 pEdge
->XIntercept
[1] = pEdge
->x
;
469 // we should require exactly 1 step to step onto next scanline...
470 ASSERT ( (pEdge
->ErrorMax
-pEdge
->Error
-1) / pEdge
->absdy
== 0 );
471 pEdge
->x
+= pEdge
->XDirection
;
472 pEdge
->Error
+= pEdge
->absdy
;
473 ASSERT ( pEdge
->Error
>= pEdge
->ErrorMax
);
475 // now step onto next scanline...
476 pEdge
->Error
-= pEdge
->absdx
;
479 else // then this is a y-major line
481 pEdge
->XIntercept
[0] = pEdge
->x
;
482 pEdge
->XIntercept
[1] = pEdge
->x
;
484 pEdge
->Error
+= pEdge
->absdx
;
487 if ( pEdge
->Error
>= pEdge
->ErrorMax
)
489 pEdge
->Error
-= pEdge
->ErrorMax
;
490 pEdge
->x
+= pEdge
->XDirection
;
491 ASSERT ( pEdge
->Error
< pEdge
->ErrorMax
);
495 DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n",
496 pEdge
->FromX
, pEdge
->FromY
, pEdge
->ToX
, pEdge
->ToY
, Scanline
, pEdge
->XIntercept
[0], pEdge
->XIntercept
[1] );
500 ** This method updates the Active edge collection for the scanline Scanline.
505 POLYGONFILL_BuildActiveList ( int Scanline
, FILL_EDGE_LIST
* list
, FILL_EDGE
** ActiveHead
)
509 ASSERT ( list
&& ActiveHead
);
511 for ( i
= 0; i
< list
->Count
; i
++ )
513 FILL_EDGE
* pEdge
= list
->Edges
[i
];
515 if ( pEdge
->FromY
<= Scanline
&& pEdge
->ToY
> Scanline
)
517 POLYGONFILL_UpdateScanline ( pEdge
, Scanline
);
518 POLYGONFILL_ActiveListInsert ( ActiveHead
, pEdge
);
524 ** This method fills the portion of the polygon that intersects with the scanline
530 POLYGONFILL_FillScanLineAlternate(
533 FILL_EDGE
* ActiveHead
,
538 FILL_EDGE
*pLeft
, *pRight
;
544 pRight
= pLeft
->pNext
;
547 while ( NULL
!= pRight
)
549 int x1
= pLeft
->XIntercept
[0];
550 int x2
= pRight
->XIntercept
[1];
554 BoundRect
.top
= ScanLine
;
555 BoundRect
.bottom
= ScanLine
+ 1;
557 BoundRect
.right
= x2
;
559 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1
, ScanLine
, x2
, ScanLine
);
560 IntEngLineTo( SurfObj
,
567 &BoundRect
, // Bounding rectangle
570 pLeft
= pRight
->pNext
;
571 pRight
= pLeft
? pLeft
->pNext
: NULL
;
578 POLYGONFILL_FillScanLineWinding(
581 FILL_EDGE
* ActiveHead
,
586 FILL_EDGE
*pLeft
, *pRight
;
587 int x1
, x2
, winding
= 0;
593 BoundRect
.top
= ScanLine
;
594 BoundRect
.bottom
= ScanLine
+ 1;
597 winding
= pLeft
->YDirection
;
598 pRight
= pLeft
->pNext
;
601 // setup first line...
602 x1
= pLeft
->XIntercept
[0];
603 x2
= pRight
->XIntercept
[1];
606 pRight
= pLeft
->pNext
;
607 winding
+= pLeft
->YDirection
;
609 while ( NULL
!= pRight
)
611 int newx1
= pLeft
->XIntercept
[0];
612 int newx2
= pRight
->XIntercept
[1];
615 // check and see if this new line touches the previous...
616 if ( (newx1
>= x1
&& newx1
<= x2
)
617 || (newx2
>= x1
&& newx2
<= x2
)
618 || (x1
>= newx1
&& x1
<= newx2
)
619 || (x2
>= newx2
&& x2
<= newx2
)
622 // yup, just tack it on to our existing line
628 // nope - render the old line..
630 BoundRect
.right
= x2
;
632 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1
, ScanLine
, x2
, ScanLine
);
633 IntEngLineTo( SurfObj
,
640 &BoundRect
, // Bounding rectangle
648 pRight
= pLeft
->pNext
;
649 winding
+= pLeft
->YDirection
;
651 // there will always be a line left-over, render it now...
653 BoundRect
.right
= x2
;
655 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1
, ScanLine
, x2
, ScanLine
);
656 IntEngLineTo( SurfObj
,
663 &BoundRect
, // Bounding rectangle
667 //When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
668 //even-numbered polygon sides on each scan line. That is, GDI fills the area between the
669 //first and second side, between the third and fourth side, and so on.
671 //WINDING Selects winding mode (fills any region with a nonzero winding value).
672 //When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
673 //This value is defined as the number of times a pen used to draw the polygon would go around the region.
674 //The direction of each edge of the polygon is important.
687 FILL_EDGE_LIST
*list
= 0;
688 FILL_EDGE
*ActiveHead
= 0;
696 FILL_EDGE
* ActiveHead
,
701 DPRINT("FillPolygon\n");
703 /* Create Edge List. */
704 list
= POLYGONFILL_MakeEdgeList(Points
, Count
);
705 /* DEBUG_PRINT_EDGELIST(list); */
709 if ( WINDING
== dc
->w
.polyFillMode
)
710 FillScanLine
= POLYGONFILL_FillScanLineWinding
;
712 FillScanLine
= POLYGONFILL_FillScanLineAlternate
;
714 /* For each Scanline from BoundRect.bottom to BoundRect.top,
715 * determine line segments to draw
717 for ( ScanLine
= BoundRect
.top
; ScanLine
< BoundRect
.bottom
; ++ScanLine
)
719 POLYGONFILL_BuildActiveList(ScanLine
, list
, &ActiveHead
);
720 //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
721 FillScanLine ( dc
, ScanLine
, ActiveHead
, SurfObj
, BrushObj
, RopMode
);
724 /* Free Edge List. If any are left. */
725 POLYGONFILL_DestroyEdgeList(list
);
734 // this is highly hacked from W32kPolygon...
736 Polygon ( CONST PPOINT UnsafePoints
, int Count
, int polyFillMode
)
742 SURFOBJ
* SurfObj
= 0;
744 PBRUSHOBJ OutBrushObj
= 0;
747 dc
.w
.polyFillMode
= polyFillMode
;
749 DPRINT1("In W32kPolygon()\n");
751 if ( NULL
== UnsafePoints
|| Count
< 2)
753 DPRINT1("ERROR_INVALID_PARAMETER\n");
757 /* Copy points from userspace to kernelspace */
758 Points
= (PPOINT
)EngAllocMem(0, Count
* sizeof(POINT
));
761 DPRINT1("ERROR_NOT_ENOUGH_MEMORY\n");
764 MmCopyFromCaller(Points
, UnsafePoints
, Count
* sizeof(POINT
));
765 if ( memcmp ( Points
, UnsafePoints
, Count
* sizeof(POINT
) ) )
771 DestRect
.left
= Points
[0].x
;
772 DestRect
.right
= Points
[0].x
;
773 DestRect
.top
= Points
[0].y
;
774 DestRect
.bottom
= Points
[0].y
;
776 for (CurrentPoint
= 1; CurrentPoint
< Count
; ++CurrentPoint
)
778 DestRect
.left
= MIN(DestRect
.left
, Points
[CurrentPoint
].x
);
779 DestRect
.right
= MAX(DestRect
.right
, Points
[CurrentPoint
].x
);
780 DestRect
.top
= MIN(DestRect
.top
, Points
[CurrentPoint
].y
);
781 DestRect
.bottom
= MAX(DestRect
.bottom
, Points
[CurrentPoint
].y
);
784 // Draw the Polygon Edges with the current pen
785 for (CurrentPoint
= 0; CurrentPoint
< Count
; ++CurrentPoint
)
787 POINT To
, From
; //, Next;
789 /* Let CurrentPoint be i
790 * if i+1 > Count, Draw a line from Points[i] to Points[0]
791 * Draw a line from Points[i] to Points[i+1]
793 From
= Points
[CurrentPoint
];
794 if ( CurrentPoint
+ 1 >= Count
)
800 To
= Points
[CurrentPoint
+ 1];
803 DPRINT1("Polygon Making line from (%d,%d) to (%d,%d)\n", From
.x
, From
.y
, To
.x
, To
.y
);
804 IntEngLineTo(SurfObj
,
812 EDGE_CHAR
); /* MIX */
814 /* determine the fill mode to fill the polygon. */
815 ret
= FillPolygon(&dc
, SurfObj
, OutBrushObj
, FILL_CHAR
, Points
, Count
, DestRect
);
824 memset ( screen
, ' ', sizeof(screen
) );
857 const int pts_count
= sizeof(pts
)/sizeof(pts
[0]);
859 // use ALTERNATE or WINDING for 3rd param
860 Polygon ( pts
, pts_count
, ALTERNATE
);
862 // print out our "screen"
863 for ( int y
= 0; y
< SCREENY
; y
++ )
865 for ( int x
= 0; x
< SCREENX
; x
++ )
867 printf("%c", screen
[y
][x
] );