xref: /reactos/win32ss/gdi/ntgdi/polyfill.c (revision 4f0b8d3d)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS Win32k subsystem
4  * PURPOSE:           Various Polygon Filling routines for Polygon()
5  * FILE:              subsystems/win32/win32k/objects/polyfill.c
6  * PROGRAMER:         Mark Tempel
7  */
8 
9 #include <win32k.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 #define FILL_EDGE_ALLOC_TAG 0x45465044
15 
16 /*
17 ** This struct is used for book keeping during polygon filling routines.
18 */
19 typedef struct _tagFILL_EDGE
20 {
21   /* Basic line information */
22   int FromX;
23   int FromY;
24   int ToX;
25   int ToY;
26   int dx;
27   int dy;
28   int absdx, absdy;
29   int x, y;
30   int xmajor;
31 
32   /* Active Edge List information */
33   int XIntercept[2];
34   int Error;
35   int ErrorMax;
36   int XDirection, YDirection;
37 
38   /* The next edge in the active Edge List */
39   struct _tagFILL_EDGE * pNext;
40 } FILL_EDGE;
41 
42 typedef struct _FILL_EDGE_LIST
43 {
44   int Count;
45   FILL_EDGE** Edges;
46 } FILL_EDGE_LIST;
47 
48 #if 0
49 static
50 void
51 DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE* list )
52 {
53   FILL_EDGE* pThis = list;
54   if (0 == list)
55   {
56     DPRINT1("List is NULL\n");
57     return;
58   }
59 
60   while(0 != pThis)
61   {
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] );
64     pThis = pThis->pNext;
65   }
66 }
67 #else
68 #define DEBUG_PRINT_ACTIVE_EDGELIST(x)
69 #endif
70 
71 /*
72 **  Hide memory clean up.
73 */
74 static
75 void
76 FASTCALL
77 POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST* list)
78 {
79   int i;
80   if ( list )
81   {
82     if ( list->Edges )
83     {
84       for ( i = 0; i < list->Count; i++ )
85       {
86 	if ( list->Edges[i] )
87 	  EngFreeMem ( list->Edges[i] );
88       }
89       EngFreeMem ( list->Edges );
90     }
91     EngFreeMem ( list );
92   }
93 }
94 
95 /*
96 ** This makes and initiaizes an Edge struct for a line between two points.
97 */
98 static
99 FILL_EDGE*
100 FASTCALL
101 POLYGONFILL_MakeEdge(POINT From, POINT To)
102 {
103   FILL_EDGE* rc = (FILL_EDGE*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE), FILL_EDGE_ALLOC_TAG);
104 
105   if (0 == rc)
106     return NULL;
107 
108   //DPRINT1("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y);
109   // Now fill the struct.
110   if ( To.y < From.y )
111   {
112     rc->FromX = To.x;
113     rc->FromY = To.y;
114     rc->ToX = From.x;
115     rc->ToY = From.y;
116     rc->YDirection = -1;
117 
118     // Lines that go up get walked backwards, so need to be offset
119     // by -1 in order to make the walk identically on a pixel-level
120     rc->Error = -1;
121   }
122   else
123   {
124     rc->FromX = From.x;
125     rc->FromY = From.y;
126     rc->ToX = To.x;
127     rc->ToY = To.y;
128     rc->YDirection = 1;
129 
130     rc->Error = 0;
131   }
132 
133   rc->x = rc->FromX;
134   rc->y = rc->FromY;
135   rc->dx   = rc->ToX - rc->FromX;
136   rc->dy   = rc->ToY - rc->FromY;
137   rc->absdx = abs(rc->dx);
138   rc->absdy = abs(rc->dy);
139 
140   rc->xmajor = rc->absdx > rc->absdy;
141 
142   rc->ErrorMax = max(rc->absdx,rc->absdy);
143 
144   rc->Error += rc->ErrorMax / 2;
145 
146   rc->XDirection = (rc->dx < 0)?(-1):(1);
147 
148   rc->pNext = 0;
149 
150   //DPRINT("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n",
151   //  From.x, From.y, To.x, To.y, rc->dx, rc->dy, rc->XDirection, rc->YDirection, rc->Error, rc->ErrorMax );
152 
153   return rc;
154 }
155 /*
156 ** My Edge comparison routine.
157 ** This is for scan converting polygon fill.
158 ** First sort by MinY, then Minx, then slope.
159 **
160 ** This comparison will help us determine which
161 ** lines will become active first when scanning from
162 ** top (min y) to bottom (max y).
163 **
164 ** Return Value Meaning
165 ** Negative integer element1 < element2
166 ** Zero element1 = element2
167 ** Positive integer element1 > element2
168 */
169 static
170 INT
171 FASTCALL
172 FILL_EDGE_Compare(FILL_EDGE* Edge1, FILL_EDGE* Edge2)
173 {
174   int e1 = Edge1->XIntercept[0] + Edge1->XIntercept[1];
175   int e2 = Edge2->XIntercept[0] + Edge2->XIntercept[1];
176 
177   return e1 - e2;
178 }
179 
180 
181 /*
182 ** Insert an edge into a list keeping the list in order.
183 */
184 static
185 void
186 FASTCALL
187 POLYGONFILL_ActiveListInsert(FILL_EDGE** activehead, FILL_EDGE* NewEdge )
188 {
189   FILL_EDGE *pPrev, *pThis;
190   //DPRINT1("In POLYGONFILL_ActiveListInsert()\n");
191   ASSERT ( activehead && NewEdge );
192   if ( !*activehead )
193   {
194     NewEdge->pNext = NULL;
195     *activehead = NewEdge;
196     return;
197   }
198   /*
199   ** First lets check to see if we have a new smallest value.
200   */
201   if (FILL_EDGE_Compare(NewEdge, *activehead) <= 0)
202   {
203     NewEdge->pNext = *activehead;
204     *activehead = NewEdge;
205     return;
206   }
207   /*
208   ** Ok, now scan to the next spot to put this item.
209   */
210   pThis = *activehead;
211   pPrev = NULL;
212   while ( pThis && FILL_EDGE_Compare(pThis, NewEdge) < 0 )
213   {
214     pPrev = pThis;
215     pThis = pThis->pNext;
216   }
217 
218   ASSERT(pPrev);
219   NewEdge->pNext = pPrev->pNext;
220   pPrev->pNext = NewEdge;
221   //DEBUG_PRINT_ACTIVE_EDGELIST(*activehead);
222 }
223 
224 /*
225 ** Create a list of edges for a list of points.
226 */
227 static
228 FILL_EDGE_LIST*
229 FASTCALL
230 POLYGONFILL_MakeEdgeList(PPOINT Points, int Count)
231 {
232   int CurPt = 0;
233   FILL_EDGE_LIST* list = 0;
234   FILL_EDGE* e = 0;
235 
236   if ( 0 == Points || 2 > Count )
237     return 0;
238 
239   list = (FILL_EDGE_LIST*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE_LIST), FILL_EDGE_ALLOC_TAG);
240   if ( 0 == list )
241     goto fail;
242   list->Count = 0;
243   list->Edges = (FILL_EDGE**)EngAllocMem(FL_ZERO_MEMORY, Count*sizeof(FILL_EDGE*), FILL_EDGE_ALLOC_TAG);
244   if ( !list->Edges )
245     goto fail;
246 
247   memset ( list->Edges, 0, Count * sizeof(FILL_EDGE*) );
248 
249   for ( CurPt = 1; CurPt < Count; ++CurPt )
250   {
251     e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[CurPt] );
252     if ( !e )
253       goto fail;
254 
255     // If a straight horizontal line - who cares?
256     if ( !e->absdy )
257       EngFreeMem ( e );
258     else
259       list->Edges[list->Count++] = e;
260   }
261   e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[0] );
262   if ( !e )
263     goto fail;
264 
265   if ( !e->absdy )
266     EngFreeMem ( e );
267   else
268     list->Edges[list->Count++] = e;
269   return list;
270 
271 fail:
272 
273   DPRINT1("Out Of MEMORY!!\n");
274   POLYGONFILL_DestroyEdgeList ( list );
275   return 0;
276 }
277 
278 
279 /*
280 ** This slow routine uses the data stored in the edge list to
281 ** calculate the x intercepts for each line in the edge list
282 ** for scanline Scanline.
283 **TODO: Get rid of this floating point arithmetic
284 */
285 static
286 void
287 FASTCALL
288 POLYGONFILL_UpdateScanline(FILL_EDGE* pEdge, int Scanline)
289 {
290   if ( 0 == pEdge->dy )
291     return;
292 
293   ASSERT ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline );
294 
295   if ( pEdge->xmajor )
296   {
297     int steps;
298 
299     ASSERT ( pEdge->y == Scanline );
300 
301     // Now shoot to end of scanline collision
302     steps = (pEdge->ErrorMax-pEdge->Error-1)/pEdge->absdy;
303     if ( steps )
304     {
305       // Record first collision with scanline
306       int x1 = pEdge->x;
307       pEdge->x += steps * pEdge->XDirection;
308       pEdge->Error += steps * pEdge->absdy;
309       ASSERT ( pEdge->Error < pEdge->ErrorMax );
310       pEdge->XIntercept[0] = min(x1,pEdge->x);
311       pEdge->XIntercept[1] = max(x1,pEdge->x);
312     }
313     else
314     {
315       pEdge->XIntercept[0] = pEdge->x;
316       pEdge->XIntercept[1] = pEdge->x;
317     }
318 
319     // We should require exactly 1 step to step onto next scanline...
320     ASSERT ( (pEdge->ErrorMax-pEdge->Error-1) / pEdge->absdy == 0 );
321     pEdge->x += pEdge->XDirection;
322     pEdge->Error += pEdge->absdy;
323     ASSERT ( pEdge->Error >= pEdge->ErrorMax );
324 
325     // Now step onto next scanline...
326     pEdge->Error -= pEdge->absdx;
327     pEdge->y++;
328   }
329   else // Then this is a y-major line
330   {
331     pEdge->XIntercept[0] = pEdge->x;
332     pEdge->XIntercept[1] = pEdge->x;
333 
334     pEdge->Error += pEdge->absdx;
335     pEdge->y++;
336 
337     if ( pEdge->Error >= pEdge->ErrorMax )
338     {
339       pEdge->Error -= pEdge->ErrorMax;
340       pEdge->x += pEdge->XDirection;
341       ASSERT ( pEdge->Error < pEdge->ErrorMax );
342     }
343   }
344 
345   //DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n",
346   //        pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, pEdge->XIntercept[0], pEdge->XIntercept[1] );
347 }
348 
349 /*
350  * This method updates the Active edge collection for the scanline Scanline.
351  */
352 static
353 void
354 APIENTRY
355 POLYGONFILL_BuildActiveList ( int Scanline, FILL_EDGE_LIST* list, FILL_EDGE** ActiveHead )
356 {
357   int i;
358 
359   ASSERT ( list && ActiveHead );
360   *ActiveHead = 0;
361   for ( i = 0; i < list->Count; i++ )
362   {
363     FILL_EDGE* pEdge = list->Edges[i];
364     ASSERT(pEdge);
365     if ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline )
366     {
367       POLYGONFILL_UpdateScanline ( pEdge, Scanline );
368       POLYGONFILL_ActiveListInsert ( ActiveHead, pEdge );
369     }
370   }
371 }
372 
373 /*
374 ** This method fills the portion of the polygon that intersects with the scanline
375 ** Scanline.
376 */
377 static
378 void
379 APIENTRY
380 POLYGONFILL_FillScanLineAlternate(
381   PDC dc,
382   int ScanLine,
383   FILL_EDGE* ActiveHead,
384   SURFACE *psurf,
385   BRUSHOBJ *BrushObj,
386   MIX RopMode )
387 {
388   FILL_EDGE *pLeft, *pRight;
389 
390   if ( !ActiveHead )
391     return;
392 
393   pLeft = ActiveHead;
394   pRight = pLeft->pNext;
395   ASSERT(pRight);
396 
397   while ( NULL != pRight )
398   {
399     int x1 = pLeft->XIntercept[0];
400     int x2 = pRight->XIntercept[1];
401     if ( x2 > x1 )
402     {
403       RECTL BoundRect;
404       BoundRect.top = ScanLine;
405       BoundRect.bottom = ScanLine + 1;
406       BoundRect.left = x1;
407       BoundRect.right = x2;
408 
409       //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
410       IntEngLineTo(&psurf->SurfObj,
411                    dc->rosdc.CombinedClip,
412                    BrushObj,
413                    x1,
414                    ScanLine,
415                    x2,
416                    ScanLine,
417                    &BoundRect,  // Bounding rectangle
418                    RopMode);    // MIX
419     }
420     pLeft = pRight->pNext;
421     pRight = pLeft ? pLeft->pNext : NULL;
422   }
423 }
424 
425 static
426 void
427 APIENTRY
428 POLYGONFILL_FillScanLineWinding(
429   PDC dc,
430   int ScanLine,
431   FILL_EDGE* ActiveHead,
432   SURFACE *psurf,
433   BRUSHOBJ *BrushObj,
434   MIX RopMode )
435 {
436   FILL_EDGE *pLeft, *pRight;
437   int x1, x2, winding = 0;
438   RECTL BoundRect;
439 
440   if ( !ActiveHead )
441     return;
442 
443   BoundRect.top = ScanLine;
444   BoundRect.bottom = ScanLine + 1;
445 
446   pLeft = ActiveHead;
447   winding = pLeft->YDirection;
448   pRight = pLeft->pNext;
449   ASSERT(pRight);
450 
451   // Setup first line...
452   x1 = pLeft->XIntercept[0];
453   x2 = pRight->XIntercept[1];
454 
455   pLeft = pRight;
456   pRight = pLeft->pNext;
457   winding += pLeft->YDirection;
458 
459   while ( NULL != pRight )
460   {
461     int newx1 = pLeft->XIntercept[0];
462     int newx2 = pRight->XIntercept[1];
463     if ( winding )
464     {
465       // Check and see if this new line touches the previous...
466       if ( (newx1 >= x1 && newx1 <= x2)
467 	|| (newx2 >= x1 && newx2 <= x2)
468 	|| (x1 >= newx1 && x1 <= newx2)
469 	|| (x2 >= newx2 && x2 <= newx2)
470 	)
471       {
472 	// Yup, just tack it on to our existing line
473 	x1 = min(x1,newx1);
474 	x2 = max(x2,newx2);
475       }
476       else
477       {
478 	// Nope - render the old line..
479 	BoundRect.left = x1;
480 	BoundRect.right = x2;
481 
482 	//DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
483 	IntEngLineTo(&psurf->SurfObj,
484                      dc->rosdc.CombinedClip,
485                      BrushObj,
486                      x1,
487                      ScanLine,
488                      x2,
489                      ScanLine,
490                      &BoundRect,  // Bounding rectangle
491                      RopMode);    // MIX
492 
493 	x1 = newx1;
494 	x2 = newx2;
495       }
496     }
497     pLeft = pRight;
498     pRight = pLeft->pNext;
499     winding += pLeft->YDirection;
500   }
501   // There will always be a line left-over, render it now...
502   BoundRect.left = x1;
503   BoundRect.right = x2;
504 
505   //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
506   IntEngLineTo(&psurf->SurfObj,
507                dc->rosdc.CombinedClip,
508                BrushObj,
509                x1,
510                ScanLine,
511                x2,
512                ScanLine,
513                &BoundRect,  // Bounding rectangle
514                RopMode);    // MIX
515 }
516 
517 // When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and
518 // even-numbered polygon sides on each scan line. That is, GDI fills the area between the
519 // first and second side, between the third and fourth side, and so on.
520 
521 // WINDING Selects winding mode (fills any region with a nonzero winding value).
522 // When the fill mode is WINDING, GDI fills any region that has a nonzero winding value.
523 // This value is defined as the number of times a pen used to draw the polygon would go around the region.
524 // The direction of each edge of the polygon is important.
525 
526 BOOL
527 APIENTRY
528 FillPolygon(
529   PDC dc,
530   SURFACE *psurf,
531   BRUSHOBJ *BrushObj,
532   MIX RopMode,
533   CONST PPOINT Points,
534   int Count,
535   RECTL BoundRect )
536 {
537   FILL_EDGE_LIST *list = 0;
538   FILL_EDGE *ActiveHead = 0;
539   int ScanLine;
540   PDC_ATTR pdcattr = dc->pdcattr;
541   void
542   (APIENTRY *FillScanLine)(
543     PDC dc,
544     int ScanLine,
545     FILL_EDGE* ActiveHead,
546     SURFACE *psurf,
547     BRUSHOBJ *BrushObj,
548     MIX RopMode );
549 
550   //DPRINT("FillPolygon\n");
551 
552   /* Create Edge List. */
553   list = POLYGONFILL_MakeEdgeList(Points, Count);
554   /* DEBUG_PRINT_EDGELIST(list); */
555   if (NULL == list)
556     return FALSE;
557 
558   if ( WINDING == pdcattr->jFillMode )
559     FillScanLine = POLYGONFILL_FillScanLineWinding;
560   else /* Default */
561     FillScanLine = POLYGONFILL_FillScanLineAlternate;
562 
563   /* For each Scanline from BoundRect.bottom to BoundRect.top,
564    * determine line segments to draw
565    */
566   for ( ScanLine = BoundRect.top; ScanLine < BoundRect.bottom; ++ScanLine )
567   {
568     POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead);
569     //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
570     FillScanLine ( dc, ScanLine, ActiveHead, psurf, BrushObj, RopMode );
571   }
572 
573   /* Free Edge List. If any are left. */
574   POLYGONFILL_DestroyEdgeList(list);
575 
576   return TRUE;
577 }
578 
579 BOOL FASTCALL
580 IntFillPolygon(
581     PDC dc,
582     SURFACE *psurf,
583     BRUSHOBJ *BrushObj,
584     CONST PPOINT Points,
585     int Count,
586     RECTL DestRect,
587     POINTL *BrushOrigin)
588 {
589     FILL_EDGE_LIST *list = 0;
590     FILL_EDGE *ActiveHead = 0;
591     FILL_EDGE *pLeft, *pRight;
592     int ScanLine;
593 
594     //DPRINT("IntFillPolygon\n");
595 
596     /* Create Edge List. */
597     list = POLYGONFILL_MakeEdgeList(Points, Count);
598     /* DEBUG_PRINT_EDGELIST(list); */
599     if (NULL == list)
600         return FALSE;
601 
602     /* For each Scanline from DestRect.top to DestRect.bottom, determine line segments to draw */
603     for ( ScanLine = DestRect.top; ScanLine < DestRect.bottom; ++ScanLine )
604     {
605         POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead);
606         //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
607 
608         if ( !ActiveHead )
609           return FALSE;
610 
611         pLeft = ActiveHead;
612         pRight = pLeft->pNext;
613         ASSERT(pRight);
614 
615         while ( NULL != pRight )
616         {
617             int x1 = pLeft->XIntercept[0];
618             int x2 = pRight->XIntercept[1];
619             if ( x2 > x1 )
620             {
621                 RECTL LineRect;
622                 LineRect.top = ScanLine;
623                 LineRect.bottom = ScanLine + 1;
624                 LineRect.left = x1;
625                 LineRect.right = x2;
626 
627                 IntEngBitBlt(&psurf->SurfObj,
628                                  NULL,
629                                  NULL,
630                                  dc->rosdc.CombinedClip,
631                                  NULL,
632                                  &LineRect,
633                                  NULL,
634                                  NULL,
635                                  BrushObj,
636                                  BrushOrigin,
637                                  ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
638             }
639             pLeft = pRight->pNext;
640             pRight = pLeft ? pLeft->pNext : NULL;
641         }
642     }
643 
644     /* Free Edge List. If any are left. */
645     POLYGONFILL_DestroyEdgeList(list);
646 
647     return TRUE;
648 }
649 
650 /* EOF */
651