[WIN32SS]
[reactos.git] / reactos / win32ss / gdi / ntgdi / polyfill.c
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 */