Reactos
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.
4//
5// Royce3
6
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
10// real stuff.
11
12#include <memory.h>
13#include <malloc.h>
14#include <stdio.h>
15#include <string.h>
16#include <conio.h>
17#include <assert.h>
18
19#define FASTCALL
20#define INT int
21#define CLIPOBJ int
22#define SURFOBJ int
23#define PBRUSHOBJ int
24#define MIX char
25#define BOOL bool
26#define TRUE true
27#define FALSE false
28#define CONST const
29#define MmCopyFromCaller memmove
30#define ALTERNATE 0
31#define WINDING 1
32
33#define ASSERT assert
34
35typedef struct W
36{
37 int polyFillMode;
38} W;
39
40typedef struct DC
41{
42 CLIPOBJ CombinedClip;
43 W w;
44} DC, *PDC;
45
46typedef struct tagPOINT
47{
48 long x, y;
49} POINT, *PPOINT, *LPPOINT;
50
51typedef struct RECTL
52{
53 long left, top, right, bottom;
54} RECTL, *PRECTL;
55
56#define EngFreeMem free
57
58#define FL_ZERO_MEMORY 1
59
60#define DPRINT1 printf("%i:",__LINE__);printf
61inline void DPRINT(...){}
62
63#define SCREENX 25
64#define SCREENY 15
65char screen[SCREENY][SCREENX];
66
67#define EDGE_CHAR '*'
68#define FILL_CHAR 'o'
69
70void* EngAllocMem ( int zero, unsigned long size, int tag=0 )
71{
72 void* p = malloc ( size );
73 if ( zero )
74 memset ( p, 0, size );
75 return p;
76}
77
78template <class T>
79inline T MIN ( T a, T b )
80{
81 return a < b ? a : b;
82}
83
84template <class T>
85inline T MAX ( T a, T b )
86{
87 return a > b ? a : b;
88}
89
90template <class T>
91inline T abs ( T t )
92{
93 return t < 0 ? -t : t;
94}
95
96void putpixel ( int x, int y, char c )
97{
98 ASSERT( x >= 0 && x < SCREENX && y >= 0 && y < SCREENY );
99 if ( screen[y][x] == c )
100 return;
101 if ( screen[y][x] == ' ' )
102 screen[y][x] = c;
103 else
104 screen[y][x] = '#';
105}
106
107void IntEngLineTo (
108 SURFOBJ*,
109 CLIPOBJ,
110 PBRUSHOBJ,
111 int x1, int y1, int x2, int y2,
112 RECTL*,
113 MIX mix )
114{
115 int dx = x2 - x1;
116 int dy = y2 - y1;
117 int absdx = abs(dx);
118 int absdy = abs(dy);
119 int EMax = MAX(absdx,absdy);
120 int E = EMax/2;
121 int xinc = dx < 0 ? -1 : 1,
122 yinc = dy < 0 ? -1 : 1;
123 if ( !dy )
124 {
125 while ( x1 != x2 )
126 {
127 putpixel ( x1, y1, mix );
128 x1 += xinc;
129 }
130 return;
131 }
132 if ( !dx )
133 {
134 while ( y1 != y2 )
135 {
136 putpixel ( x1, y1, mix );
137 y1 += yinc;
138 }
139 return;
140 }
141 for ( int i = 0; i < EMax; i++ )
142 {
143 putpixel ( x1, y1, mix );
144 if ( absdy > absdx )
145 {
146 y1 += yinc;
147 E += absdx;
148 if ( E >= EMax )
149 {
150 E -= absdy;
151 x1 += xinc;
152 }
153 }
154 else
155 {
156 x1 += xinc;
157 E += absdy;
158 if ( E >= EMax )
159 {
160 E -= absdx;
161 y1 += yinc;
162 }
163 }
164 }
165}
166
167#define FILL_EDGE_ALLOC_TAG 0x45465044
168
169/*
170** This struct is used for book keeping during polygon filling routines.
171*/
172typedef struct _tagFILL_EDGE
173{
174 /*Basic line information*/
175 int FromX;
176 int FromY;
177 int ToX;
178 int ToY;
179 int dx;
180 int dy;
181 int absdx, absdy;
182 int x, y;
183 int xmajor;
184
185 /*Active Edge List information*/
186 int XIntercept[2];
187 int Error;
188 int ErrorMax;
189 int XDirection, YDirection;
190
191 /* The next edge in the active Edge List*/
192 struct _tagFILL_EDGE * pNext;
193} FILL_EDGE;
194
195typedef struct _FILL_EDGE_LIST
196{
197 int Count;
198 FILL_EDGE** Edges;
199} FILL_EDGE_LIST;
200
201#if 0
202static
203void
204DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE* list )
205{
206 FILL_EDGE* pThis = list;
207 if (0 == list)
208 {
209 DPRINT1("List is NULL\n");
210 return;
211 }
212
213 while(0 != pThis)
214 {
215 //DPRINT1("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY);
216 DPRINT1("EDGE: [%d,%d]\n", pThis->XIntercept[0], pThis->XIntercept[1] );
217 pThis = pThis->pNext;
218 }
219}
220#else
221#define DEBUG_PRINT_ACTIVE_EDGELIST(x)
222#endif
223
224/*
225** Hide memory clean up.
226*/
227static
228void
229FASTCALL
230POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST* list)
231{
232 int i;
233 if ( list )
234 {
235 if ( list->Edges )
236 {
237 for ( i = 0; i < list->Count; i++ )
238 {
239 if ( list->Edges[i] )
240 EngFreeMem ( list->Edges[i] );
241 }
242 EngFreeMem ( list->Edges );
243 }
244 EngFreeMem ( list );
245 }
246}
247
248/*
249** This makes and initiaizes an Edge struct for a line between two points.
250*/
251static
252FILL_EDGE*
253FASTCALL
254POLYGONFILL_MakeEdge(POINT From, POINT To)
255{
256 FILL_EDGE* rc = (FILL_EDGE*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE), FILL_EDGE_ALLOC_TAG);
257
258 if (0 == rc)
259 return NULL;
260
261 //DPRINT1("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y);
262 //Now Fill the struct.
263 if ( To.y < From.y )
264 {
265 rc->FromX = To.x;
266 rc->FromY = To.y;
267 rc->ToX = From.x;
268 rc->ToY = From.y;
269 rc->YDirection = -1;
270
271 // lines that go up get walked backwards, so need to be offset
272 // by -1 in order to make the walk identically on a pixel-level
273 rc->Error = -1;
274 }
275 else
276 {
277 rc->FromX = From.x;
278 rc->FromY = From.y;
279 rc->ToX = To.x;
280 rc->ToY = To.y;
281 rc->YDirection = 1;
282
283 rc->Error = 0;
284 }
285
286 rc->x = rc->FromX;
287 rc->y = rc->FromY;
288 rc->dx = rc->ToX - rc->FromX;
289 rc->dy = rc->ToY - rc->FromY;
290 rc->absdx = abs(rc->dx);
291 rc->absdy = abs(rc->dy);
292
293 rc->xmajor = rc->absdx > rc->absdy;
294
295 rc->ErrorMax = MAX(rc->absdx,rc->absdy);
296
297 rc->Error += rc->ErrorMax / 2;
298
299 rc->XDirection = (rc->dx < 0)?(-1):(1);
300
301 rc->pNext = 0;
302
303 DPRINT("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n",
304 From.x, From.y, To.x, To.y, rc->dx, rc->dy, rc->XDirection, rc->YDirection, rc->Error, rc->ErrorMax );
305
306 return rc;
307}
308/*
309** My Edge comparison routine.
310** This is for scan converting polygon fill.
311** First sort by MinY, then Minx, then slope.
312**
313** This comparison will help us determine which
314** lines will become active first when scanning from
315** top (min y) to bottom (max y).
316**
317** Return Value Meaning
318** Negative integer element1 < element2
319** Zero element1 = element2
320** Positive integer element1 > element2
321*/
322static
323INT
324FASTCALL
325FILL_EDGE_Compare(FILL_EDGE* Edge1, FILL_EDGE* Edge2)
326{
327 int e1 = Edge1->XIntercept[0] + Edge1->XIntercept[1];
328 int e2 = Edge2->XIntercept[0] + Edge2->XIntercept[1];
329
330 return e1 - e2;
331}
332
333
334/*
335** Insert an edge into a list keeping the list in order.
336*/
337static
338void
339FASTCALL
340POLYGONFILL_ActiveListInsert(FILL_EDGE** activehead, FILL_EDGE* NewEdge )
341{
342 FILL_EDGE *pPrev, *pThis;
343 //DPRINT1("In POLYGONFILL_ActiveListInsert()\n");
344 ASSERT ( activehead && NewEdge );
345 if ( !*activehead )
346 {
347 NewEdge->pNext = NULL;
348 *activehead = NewEdge;
349 return;
350 }
351 /*
352 ** First lets check to see if we have a new smallest value.
353 */
354 if (FILL_EDGE_Compare(NewEdge, *activehead) <= 0)
355 {
356 NewEdge->pNext = *activehead;
357 *activehead = NewEdge;
358 return;
359 }
360 /*
361 ** Ok, now scan to the next spot to put this item.
362 */
363 pThis = *activehead;
364 pPrev = NULL;
365 while ( pThis && FILL_EDGE_Compare(pThis, NewEdge) < 0 )
366 {
367 pPrev = pThis;
368 pThis = pThis->pNext;
369 }
370
371 ASSERT(pPrev);
372 NewEdge->pNext = pPrev->pNext;
373 pPrev->pNext = NewEdge;
374 //DEBUG_PRINT_ACTIVE_EDGELIST(*activehead);
375}
376
377/*
378** Create a list of edges for a list of points.
379*/
380static
381FILL_EDGE_LIST*
382FASTCALL
383POLYGONFILL_MakeEdgeList(PPOINT Points, int Count)
384{
385 int CurPt = 0;
386 FILL_EDGE_LIST* list = 0;
387 FILL_EDGE* e = 0;
388
389 if ( 0 == Points || 2 > Count )
390 return 0;
391
392 list = (FILL_EDGE_LIST*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE_LIST), FILL_EDGE_ALLOC_TAG);
393 if ( 0 == list )
394 goto fail;
395 list->Count = 0;
396 list->Edges = (FILL_EDGE**)EngAllocMem(FL_ZERO_MEMORY, Count*sizeof(FILL_EDGE*), FILL_EDGE_ALLOC_TAG);
397 if ( !list->Edges )
398 goto fail;
399 memset ( list->Edges, 0, Count * sizeof(FILL_EDGE*) );
400
401 for ( CurPt = 1; CurPt < Count; ++CurPt )
402 {
403 e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[CurPt] );
404 if ( !e )
405 goto fail;
406 // if a straight horizontal line - who cares?
407 if ( !e->absdy )
408 EngFreeMem ( e );
409 else
410 list->Edges[list->Count++] = e;
411 }
412 e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[0] );
413 if ( !e )
414 goto fail;
415 if ( !e->absdy )
416 EngFreeMem ( e );
417 else
418 list->Edges[list->Count++] = e;
419 return list;
420
421fail:
422 DPRINT1("Out Of MEMORY!!\n");
423 POLYGONFILL_DestroyEdgeList ( list );
424 return 0;
425}
426
427
428/*
429** This slow routine uses the data stored in the edge list to
430** calculate the x intercepts for each line in the edge list
431** for scanline Scanline.
432**TODO: Get rid of this floating point arithmetic
433*/
434static
435void
436FASTCALL
437POLYGONFILL_UpdateScanline(FILL_EDGE* pEdge, int Scanline)
438{
439 if ( 0 == pEdge->dy )
440 return;
441
442 ASSERT ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline );
443
444 if ( pEdge->xmajor )
445 {
446 int steps;
447
448 ASSERT ( pEdge->y == Scanline );
449
450 // now shoot to end of scanline collision
451 steps = (pEdge->ErrorMax-pEdge->Error-1)/pEdge->absdy;
452 if ( steps )
453 {
454 // record first collision with scanline
455 int x1 = pEdge->x;
456 pEdge->x += steps * pEdge->XDirection;
457 pEdge->Error += steps * pEdge->absdy;
458 ASSERT ( pEdge->Error < pEdge->ErrorMax );
459 pEdge->XIntercept[0] = MIN(x1,pEdge->x);
460 pEdge->XIntercept[1] = MAX(x1,pEdge->x);
461 }
462 else
463 {
464 pEdge->XIntercept[0] = pEdge->x;
465 pEdge->XIntercept[1] = pEdge->x;
466 }
467
468 // we should require exactly 1 step to step onto next scanline...
469 ASSERT ( (pEdge->ErrorMax-pEdge->Error-1) / pEdge->absdy == 0 );
470 pEdge->x += pEdge->XDirection;
471 pEdge->Error += pEdge->absdy;
472 ASSERT ( pEdge->Error >= pEdge->ErrorMax );
473
474 // now step onto next scanline...
475 pEdge->Error -= pEdge->absdx;
476 pEdge->y++;
477 }
478 else // then this is a y-major line
479 {
480 pEdge->XIntercept[0] = pEdge->x;
481 pEdge->XIntercept[1] = pEdge->x;
482
483 pEdge->Error += pEdge->absdx;
484 pEdge->y++;
485
486 if ( pEdge->Error >= pEdge->ErrorMax )
487 {
488 pEdge->Error -= pEdge->ErrorMax;
489 pEdge->x += pEdge->XDirection;
490 ASSERT ( pEdge->Error < pEdge->ErrorMax );
491 }
492 }
493
494 DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n",
495 pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, pEdge->XIntercept[0], pEdge->XIntercept[1] );
496}
497
498/*
499** This method updates the Active edge collection for the scanline Scanline.
500*/
501static
502void
503POLYGONFILL_BuildActiveList ( int Scanline, FILL_EDGE_LIST* list, FILL_EDGE** ActiveHead )
504{
505 int i;
506
507 ASSERT ( list && ActiveHead );
508 *ActiveHead = 0;
509 for ( i = 0; i < list->Count; i++ )
510 {
511 FILL_EDGE* pEdge = list->Edges[i];
512 ASSERT(pEdge);
513 if ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline )
514 {
515 POLYGONFILL_UpdateScanline ( pEdge, Scanline );
516 POLYGONFILL_ActiveListInsert ( ActiveHead, pEdge );
517 }
518 }
519}
520
521/*
522** This method fills the portion of the polygon that intersects with the scanline
523** Scanline.
524*/
525static
526void
527POLYGONFILL_FillScanLineAlternate(
528 PDC dc,
529 int ScanLine,
530 FILL_EDGE* ActiveHead,
531 SURFOBJ *SurfObj,
532 PBRUSHOBJ BrushObj,
533 MIX RopMode )
534{
535 FILL_EDGE *pLeft, *pRight;
536
537 if ( !ActiveHead )
538 return;
539
540 pLeft = ActiveHead;
541 pRight = pLeft->pNext;
542 ASSERT(pRight);
543
544 while ( NULL != pRight )
545 {
546 int x1 = pLeft->XIntercept[0];
547 int x2 = pRight->XIntercept[1];
548 if ( x2 > x1 )
549 {
550 RECTL BoundRect;
551 BoundRect.top = ScanLine;
552 BoundRect.bottom = ScanLine + 1;
553 BoundRect.left = x1;
554 BoundRect.right = x2;
555
556 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
557 IntEngLineTo( SurfObj,
558 dc->CombinedClip,
559 BrushObj,
560 x1,
561 ScanLine,
562 x2,
563 ScanLine,
564 &BoundRect, // Bounding rectangle
565 RopMode); // MIX
566 }
567 pLeft = pRight->pNext;
568 pRight = pLeft ? pLeft->pNext : NULL;
569 }
570}
571
572static
573void
574POLYGONFILL_FillScanLineWinding(
575 PDC dc,
576 int ScanLine,
577 FILL_EDGE* ActiveHead,
578 SURFOBJ *SurfObj,
579 PBRUSHOBJ BrushObj,
580 MIX RopMode )
581{
582 FILL_EDGE *pLeft, *pRight;
583 int x1, x2, winding = 0;
584 RECTL BoundRect;
585
586 if ( !ActiveHead )
587 return;
588
589 BoundRect.top = ScanLine;
590 BoundRect.bottom = ScanLine + 1;
591
592 pLeft = ActiveHead;
593 winding = pLeft->YDirection;
594 pRight = pLeft->pNext;
595 ASSERT(pRight);
596
597 // setup first line...
598 x1 = pLeft->XIntercept[0];
599 x2 = pRight->XIntercept[1];
600
601 pLeft = pRight;
602 pRight = pLeft->pNext;
603 winding += pLeft->YDirection;
604
605 while ( NULL != pRight )
606 {
607 int newx1 = pLeft->XIntercept[0];
608 int newx2 = pRight->XIntercept[1];
609 if ( winding )
610 {
611 // check and see if this new line touches the previous...
612 if ( (newx1 >= x1 && newx1 <= x2)
613 || (newx2 >= x1 && newx2 <= x2)
614 || (x1 >= newx1 && x1 <= newx2)
615 || (x2 >= newx2 && x2 <= newx2)
616 )
617 {
618 // yup, just tack it on to our existing line
619 x1 = MIN(x1,newx1);
620 x2 = MAX(x2,newx2);
621 }
622 else
623 {
624 // nope - render the old line..
625 BoundRect.left = x1;
626 BoundRect.right = x2;
627
628 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
629 IntEngLineTo( SurfObj,
630 dc->CombinedClip,
631 BrushObj,
632 x1,
633 ScanLine,
634 x2,
635 ScanLine,
636 &BoundRect, // Bounding rectangle
637 RopMode); // MIX
638
639 x1 = newx1;
640 x2 = newx2;
641 }
642 }
643 pLeft = pRight;
644 pRight = pLeft->pNext;
645 winding += pLeft->YDirection;
646 }
647 // there will always be a line left-over, render it now...
648 BoundRect.left = x1;
649 BoundRect.right = x2;
650
651 DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
652 IntEngLineTo( SurfObj,
653 dc->CombinedClip,
654 BrushObj,
655 x1,
656 ScanLine,
657 x2,
658 ScanLine,
659 &BoundRect, // Bounding rectangle
660 RopMode); // MIX
661}
662
663//When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
664//even-numbered polygon sides on each scan line. That is, GDI fills the area between the
665//first and second side, between the third and fourth side, and so on.
666
667//WINDING Selects winding mode (fills any region with a nonzero winding value).
668//When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
669//This value is defined as the number of times a pen used to draw the polygon would go around the region.
670//The direction of each edge of the polygon is important.
671
672BOOL
673FillPolygon(
674 PDC dc,
675 SURFOBJ *SurfObj,
676 PBRUSHOBJ BrushObj,
677 MIX RopMode,
678 CONST PPOINT Points,
679 int Count,
680 RECTL BoundRect )
681{
682 FILL_EDGE_LIST *list = 0;
683 FILL_EDGE *ActiveHead = 0;
684 int ScanLine;
685
686 void
687 (*FillScanLine)(
688 PDC dc,
689 int ScanLine,
690 FILL_EDGE* ActiveHead,
691 SURFOBJ *SurfObj,
692 PBRUSHOBJ BrushObj,
693 MIX RopMode );
694
695 DPRINT("FillPolygon\n");
696
697 /* Create Edge List. */
698 list = POLYGONFILL_MakeEdgeList(Points, Count);
699 /* DEBUG_PRINT_EDGELIST(list); */
700 if (NULL == list)
701 return FALSE;
702
703 if ( WINDING == dc->w.polyFillMode )
704 FillScanLine = POLYGONFILL_FillScanLineWinding;
705 else /* default */
706 FillScanLine = POLYGONFILL_FillScanLineAlternate;
707
708 /* For each Scanline from BoundRect.bottom to BoundRect.top,
709 * determine line segments to draw
710 */
711 for ( ScanLine = BoundRect.top; ScanLine < BoundRect.bottom; ++ScanLine )
712 {
713 POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead);
714 //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
715 FillScanLine ( dc, ScanLine, ActiveHead, SurfObj, BrushObj, RopMode );
716 }
717
718 /* Free Edge List. If any are left. */
719 POLYGONFILL_DestroyEdgeList(list);
720
721 return TRUE;
722}
723
724
725
726
727
728// this is highly hacked from W32kPolygon...
729BOOL
730Polygon ( CONST PPOINT UnsafePoints, int Count, int polyFillMode )
731{
732 BOOL ret;
733 RECTL DestRect;
734 int CurrentPoint;
735 PPOINT Points;
736 SURFOBJ* SurfObj = 0;
737 DC dc;
738 PBRUSHOBJ OutBrushObj = 0;
739
740 dc.CombinedClip = 0;
741 dc.w.polyFillMode = polyFillMode;
742
743 DPRINT1("In W32kPolygon()\n");
744
745 if ( NULL == UnsafePoints || Count < 2)
746 {
747 DPRINT1("ERROR_INVALID_PARAMETER\n");
748 return FALSE;
749 }
750
751 /* Copy points from userspace to kernelspace */
752 Points = (PPOINT)EngAllocMem(0, Count * sizeof(POINT));
753 if (NULL == Points)
754 {
755 DPRINT1("ERROR_NOT_ENOUGH_MEMORY\n");
756 return FALSE;
757 }
758 MmCopyFromCaller(Points, UnsafePoints, Count * sizeof(POINT));
759 if ( memcmp ( Points, UnsafePoints, Count * sizeof(POINT) ) )
760 {
761 free(Points);
762 return FALSE;
763 }
764
765 DestRect.left = Points[0].x;
766 DestRect.right = Points[0].x;
767 DestRect.top = Points[0].y;
768 DestRect.bottom = Points[0].y;
769
770 for (CurrentPoint = 1; CurrentPoint < Count; ++CurrentPoint)
771 {
772 DestRect.left = MIN(DestRect.left, Points[CurrentPoint].x);
773 DestRect.right = MAX(DestRect.right, Points[CurrentPoint].x);
774 DestRect.top = MIN(DestRect.top, Points[CurrentPoint].y);
775 DestRect.bottom = MAX(DestRect.bottom, Points[CurrentPoint].y);
776 }
777
778 // Draw the Polygon Edges with the current pen
779 for (CurrentPoint = 0; CurrentPoint < Count; ++CurrentPoint)
780 {
781 POINT To, From; //, Next;
782
783 /* Let CurrentPoint be i
784 * if i+1 > Count, Draw a line from Points[i] to Points[0]
785 * Draw a line from Points[i] to Points[i+1]
786 */
787 From = Points[CurrentPoint];
788 if ( CurrentPoint + 1 >= Count)
789 {
790 To = Points[0];
791 }
792 else
793 {
794 To = Points[CurrentPoint + 1];
795 }
796
797 DPRINT1("Polygon Making line from (%ld,%ld) to (%ld,%ld)\n", From.x, From.y, To.x, To.y );
798 IntEngLineTo(SurfObj,
799 dc.CombinedClip,
800 OutBrushObj,
801 From.x,
802 From.y,
803 To.x,
804 To.y,
805 &DestRect,
806 EDGE_CHAR); /* MIX */
807 }
808 /* determine the fill mode to fill the polygon. */
809 ret = FillPolygon(&dc, SurfObj, OutBrushObj, FILL_CHAR, Points, Count, DestRect );
810 free(Points);
811
812 return ret;
813}
814
815
816int main()
817{
818 memset ( screen, ' ', sizeof(screen) );
819 POINT pts[] =
820 {
821#if 0
822 { 0, 0 },
823 { 12, 4 },
824 { 4, 8 },
825#elif 0
826 { 3, 0 },
827 { 0, 3 },
828 { 3, 6 },
829#elif 0
830 { 1, 1 },
831 { 3, 1 },
832 { 3, 3 },
833 { 1, 3 }
834#elif 0
835 { 0, 0 },
836 { 4, 0 },
837 { 4, 4 },
838 { 8, 4 },
839 { 8, 8 },
840 { 4, 8 },
841 { 4, 4 },
842 { 0, 4 },
843#else
844 { 4, 12 },
845 { 12, 0 },
846 { 18, 12 },
847 { 4, 4 },
848 { 20, 4 }
849#endif
850 };
851 const int pts_count = sizeof(pts)/sizeof(pts[0]);
852
853 // use ALTERNATE or WINDING for 3rd param
854 Polygon ( pts, pts_count, ALTERNATE );
855
856 // print out our "screen"
857 for ( int y = 0; y < SCREENY; y++ )
858 {
859 for ( int x = 0; x < SCREENX; x++ )
860 {
861 printf("%c", screen[y][x] );
862 }
863 printf("\n");
864 }
865 DPRINT1("Done!\n");
866 (void)_getch();
867}
868/* EOF */