Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpolyfill.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS Win32k subsystem 00004 * PURPOSE: Various Polygon Filling routines for Polygon() 00005 * FILE: subsystems/win32/win32k/objects/polyfill.c 00006 * PROGRAMER: Mark Tempel 00007 */ 00008 00009 #include <win32k.h> 00010 00011 #define NDEBUG 00012 #include <debug.h> 00013 00014 #define FILL_EDGE_ALLOC_TAG 0x45465044 00015 00016 /* 00017 ** This struct is used for book keeping during polygon filling routines. 00018 */ 00019 typedef struct _tagFILL_EDGE 00020 { 00021 /* Basic line information */ 00022 int FromX; 00023 int FromY; 00024 int ToX; 00025 int ToY; 00026 int dx; 00027 int dy; 00028 int absdx, absdy; 00029 int x, y; 00030 int xmajor; 00031 00032 /* Active Edge List information */ 00033 int XIntercept[2]; 00034 int Error; 00035 int ErrorMax; 00036 int XDirection, YDirection; 00037 00038 /* The next edge in the active Edge List */ 00039 struct _tagFILL_EDGE * pNext; 00040 } FILL_EDGE; 00041 00042 typedef struct _FILL_EDGE_LIST 00043 { 00044 int Count; 00045 FILL_EDGE** Edges; 00046 } FILL_EDGE_LIST; 00047 00048 #if 0 00049 static 00050 void 00051 DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE* list ) 00052 { 00053 FILL_EDGE* pThis = list; 00054 if (0 == list) 00055 { 00056 DPRINT1("List is NULL\n"); 00057 return; 00058 } 00059 00060 while(0 != pThis) 00061 { 00062 //DPRINT1("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY); 00063 DPRINT1("EDGE: [%d,%d]\n", pThis->XIntercept[0], pThis->XIntercept[1] ); 00064 pThis = pThis->pNext; 00065 } 00066 } 00067 #else 00068 #define DEBUG_PRINT_ACTIVE_EDGELIST(x) 00069 #endif 00070 00071 /* 00072 ** Hide memory clean up. 00073 */ 00074 static 00075 void 00076 FASTCALL 00077 POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST* list) 00078 { 00079 int i; 00080 if ( list ) 00081 { 00082 if ( list->Edges ) 00083 { 00084 for ( i = 0; i < list->Count; i++ ) 00085 { 00086 if ( list->Edges[i] ) 00087 EngFreeMem ( list->Edges[i] ); 00088 } 00089 EngFreeMem ( list->Edges ); 00090 } 00091 EngFreeMem ( list ); 00092 } 00093 } 00094 00095 /* 00096 ** This makes and initiaizes an Edge struct for a line between two points. 00097 */ 00098 static 00099 FILL_EDGE* 00100 FASTCALL 00101 POLYGONFILL_MakeEdge(POINT From, POINT To) 00102 { 00103 FILL_EDGE* rc = (FILL_EDGE*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE), FILL_EDGE_ALLOC_TAG); 00104 00105 if (0 == rc) 00106 return NULL; 00107 00108 //DPRINT1("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y); 00109 // Now fill the struct. 00110 if ( To.y < From.y ) 00111 { 00112 rc->FromX = To.x; 00113 rc->FromY = To.y; 00114 rc->ToX = From.x; 00115 rc->ToY = From.y; 00116 rc->YDirection = -1; 00117 00118 // Lines that go up get walked backwards, so need to be offset 00119 // by -1 in order to make the walk identically on a pixel-level 00120 rc->Error = -1; 00121 } 00122 else 00123 { 00124 rc->FromX = From.x; 00125 rc->FromY = From.y; 00126 rc->ToX = To.x; 00127 rc->ToY = To.y; 00128 rc->YDirection = 1; 00129 00130 rc->Error = 0; 00131 } 00132 00133 rc->x = rc->FromX; 00134 rc->y = rc->FromY; 00135 rc->dx = rc->ToX - rc->FromX; 00136 rc->dy = rc->ToY - rc->FromY; 00137 rc->absdx = abs(rc->dx); 00138 rc->absdy = abs(rc->dy); 00139 00140 rc->xmajor = rc->absdx > rc->absdy; 00141 00142 rc->ErrorMax = max(rc->absdx,rc->absdy); 00143 00144 rc->Error += rc->ErrorMax / 2; 00145 00146 rc->XDirection = (rc->dx < 0)?(-1):(1); 00147 00148 rc->pNext = 0; 00149 00150 //DPRINT("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n", 00151 // From.x, From.y, To.x, To.y, rc->dx, rc->dy, rc->XDirection, rc->YDirection, rc->Error, rc->ErrorMax ); 00152 00153 return rc; 00154 } 00155 /* 00156 ** My Edge comparison routine. 00157 ** This is for scan converting polygon fill. 00158 ** First sort by MinY, then Minx, then slope. 00159 ** 00160 ** This comparison will help us determine which 00161 ** lines will become active first when scanning from 00162 ** top (min y) to bottom (max y). 00163 ** 00164 ** Return Value Meaning 00165 ** Negative integer element1 < element2 00166 ** Zero element1 = element2 00167 ** Positive integer element1 > element2 00168 */ 00169 static 00170 INT 00171 FASTCALL 00172 FILL_EDGE_Compare(FILL_EDGE* Edge1, FILL_EDGE* Edge2) 00173 { 00174 int e1 = Edge1->XIntercept[0] + Edge1->XIntercept[1]; 00175 int e2 = Edge2->XIntercept[0] + Edge2->XIntercept[1]; 00176 00177 return e1 - e2; 00178 } 00179 00180 00181 /* 00182 ** Insert an edge into a list keeping the list in order. 00183 */ 00184 static 00185 void 00186 FASTCALL 00187 POLYGONFILL_ActiveListInsert(FILL_EDGE** activehead, FILL_EDGE* NewEdge ) 00188 { 00189 FILL_EDGE *pPrev, *pThis; 00190 //DPRINT1("In POLYGONFILL_ActiveListInsert()\n"); 00191 ASSERT ( activehead && NewEdge ); 00192 if ( !*activehead ) 00193 { 00194 NewEdge->pNext = NULL; 00195 *activehead = NewEdge; 00196 return; 00197 } 00198 /* 00199 ** First lets check to see if we have a new smallest value. 00200 */ 00201 if (FILL_EDGE_Compare(NewEdge, *activehead) <= 0) 00202 { 00203 NewEdge->pNext = *activehead; 00204 *activehead = NewEdge; 00205 return; 00206 } 00207 /* 00208 ** Ok, now scan to the next spot to put this item. 00209 */ 00210 pThis = *activehead; 00211 pPrev = NULL; 00212 while ( pThis && FILL_EDGE_Compare(pThis, NewEdge) < 0 ) 00213 { 00214 pPrev = pThis; 00215 pThis = pThis->pNext; 00216 } 00217 00218 ASSERT(pPrev); 00219 NewEdge->pNext = pPrev->pNext; 00220 pPrev->pNext = NewEdge; 00221 //DEBUG_PRINT_ACTIVE_EDGELIST(*activehead); 00222 } 00223 00224 /* 00225 ** Create a list of edges for a list of points. 00226 */ 00227 static 00228 FILL_EDGE_LIST* 00229 FASTCALL 00230 POLYGONFILL_MakeEdgeList(PPOINT Points, int Count) 00231 { 00232 int CurPt = 0; 00233 FILL_EDGE_LIST* list = 0; 00234 FILL_EDGE* e = 0; 00235 00236 if ( 0 == Points || 2 > Count ) 00237 return 0; 00238 00239 list = (FILL_EDGE_LIST*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE_LIST), FILL_EDGE_ALLOC_TAG); 00240 if ( 0 == list ) 00241 goto fail; 00242 list->Count = 0; 00243 list->Edges = (FILL_EDGE**)EngAllocMem(FL_ZERO_MEMORY, Count*sizeof(FILL_EDGE*), FILL_EDGE_ALLOC_TAG); 00244 if ( !list->Edges ) 00245 goto fail; 00246 00247 memset ( list->Edges, 0, Count * sizeof(FILL_EDGE*) ); 00248 00249 for ( CurPt = 1; CurPt < Count; ++CurPt ) 00250 { 00251 e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[CurPt] ); 00252 if ( !e ) 00253 goto fail; 00254 00255 // If a straight horizontal line - who cares? 00256 if ( !e->absdy ) 00257 EngFreeMem ( e ); 00258 else 00259 list->Edges[list->Count++] = e; 00260 } 00261 e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[0] ); 00262 if ( !e ) 00263 goto fail; 00264 00265 if ( !e->absdy ) 00266 EngFreeMem ( e ); 00267 else 00268 list->Edges[list->Count++] = e; 00269 return list; 00270 00271 fail: 00272 00273 DPRINT1("Out Of MEMORY!!\n"); 00274 POLYGONFILL_DestroyEdgeList ( list ); 00275 return 0; 00276 } 00277 00278 00279 /* 00280 ** This slow routine uses the data stored in the edge list to 00281 ** calculate the x intercepts for each line in the edge list 00282 ** for scanline Scanline. 00283 **TODO: Get rid of this floating point arithmetic 00284 */ 00285 static 00286 void 00287 FASTCALL 00288 POLYGONFILL_UpdateScanline(FILL_EDGE* pEdge, int Scanline) 00289 { 00290 if ( 0 == pEdge->dy ) 00291 return; 00292 00293 ASSERT ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline ); 00294 00295 if ( pEdge->xmajor ) 00296 { 00297 int steps; 00298 00299 ASSERT ( pEdge->y == Scanline ); 00300 00301 // Now shoot to end of scanline collision 00302 steps = (pEdge->ErrorMax-pEdge->Error-1)/pEdge->absdy; 00303 if ( steps ) 00304 { 00305 // Record first collision with scanline 00306 int x1 = pEdge->x; 00307 pEdge->x += steps * pEdge->XDirection; 00308 pEdge->Error += steps * pEdge->absdy; 00309 ASSERT ( pEdge->Error < pEdge->ErrorMax ); 00310 pEdge->XIntercept[0] = min(x1,pEdge->x); 00311 pEdge->XIntercept[1] = max(x1,pEdge->x); 00312 } 00313 else 00314 { 00315 pEdge->XIntercept[0] = pEdge->x; 00316 pEdge->XIntercept[1] = pEdge->x; 00317 } 00318 00319 // We should require exactly 1 step to step onto next scanline... 00320 ASSERT ( (pEdge->ErrorMax-pEdge->Error-1) / pEdge->absdy == 0 ); 00321 pEdge->x += pEdge->XDirection; 00322 pEdge->Error += pEdge->absdy; 00323 ASSERT ( pEdge->Error >= pEdge->ErrorMax ); 00324 00325 // Now step onto next scanline... 00326 pEdge->Error -= pEdge->absdx; 00327 pEdge->y++; 00328 } 00329 else // Then this is a y-major line 00330 { 00331 pEdge->XIntercept[0] = pEdge->x; 00332 pEdge->XIntercept[1] = pEdge->x; 00333 00334 pEdge->Error += pEdge->absdx; 00335 pEdge->y++; 00336 00337 if ( pEdge->Error >= pEdge->ErrorMax ) 00338 { 00339 pEdge->Error -= pEdge->ErrorMax; 00340 pEdge->x += pEdge->XDirection; 00341 ASSERT ( pEdge->Error < pEdge->ErrorMax ); 00342 } 00343 } 00344 00345 //DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n", 00346 // pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, pEdge->XIntercept[0], pEdge->XIntercept[1] ); 00347 } 00348 00349 /* 00350 * This method updates the Active edge collection for the scanline Scanline. 00351 */ 00352 static 00353 void 00354 APIENTRY 00355 POLYGONFILL_BuildActiveList ( int Scanline, FILL_EDGE_LIST* list, FILL_EDGE** ActiveHead ) 00356 { 00357 int i; 00358 00359 ASSERT ( list && ActiveHead ); 00360 *ActiveHead = 0; 00361 for ( i = 0; i < list->Count; i++ ) 00362 { 00363 FILL_EDGE* pEdge = list->Edges[i]; 00364 ASSERT(pEdge); 00365 if ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline ) 00366 { 00367 POLYGONFILL_UpdateScanline ( pEdge, Scanline ); 00368 POLYGONFILL_ActiveListInsert ( ActiveHead, pEdge ); 00369 } 00370 } 00371 } 00372 00373 /* 00374 ** This method fills the portion of the polygon that intersects with the scanline 00375 ** Scanline. 00376 */ 00377 static 00378 void 00379 APIENTRY 00380 POLYGONFILL_FillScanLineAlternate( 00381 PDC dc, 00382 int ScanLine, 00383 FILL_EDGE* ActiveHead, 00384 SURFACE *psurf, 00385 BRUSHOBJ *BrushObj, 00386 MIX RopMode ) 00387 { 00388 FILL_EDGE *pLeft, *pRight; 00389 00390 if ( !ActiveHead ) 00391 return; 00392 00393 pLeft = ActiveHead; 00394 pRight = pLeft->pNext; 00395 ASSERT(pRight); 00396 00397 while ( NULL != pRight ) 00398 { 00399 int x1 = pLeft->XIntercept[0]; 00400 int x2 = pRight->XIntercept[1]; 00401 if ( x2 > x1 ) 00402 { 00403 RECTL BoundRect; 00404 BoundRect.top = ScanLine; 00405 BoundRect.bottom = ScanLine + 1; 00406 BoundRect.left = x1; 00407 BoundRect.right = x2; 00408 00409 //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine); 00410 IntEngLineTo(&psurf->SurfObj, 00411 dc->rosdc.CombinedClip, 00412 BrushObj, 00413 x1, 00414 ScanLine, 00415 x2, 00416 ScanLine, 00417 &BoundRect, // Bounding rectangle 00418 RopMode); // MIX 00419 } 00420 pLeft = pRight->pNext; 00421 pRight = pLeft ? pLeft->pNext : NULL; 00422 } 00423 } 00424 00425 static 00426 void 00427 APIENTRY 00428 POLYGONFILL_FillScanLineWinding( 00429 PDC dc, 00430 int ScanLine, 00431 FILL_EDGE* ActiveHead, 00432 SURFACE *psurf, 00433 BRUSHOBJ *BrushObj, 00434 MIX RopMode ) 00435 { 00436 FILL_EDGE *pLeft, *pRight; 00437 int x1, x2, winding = 0; 00438 RECTL BoundRect; 00439 00440 if ( !ActiveHead ) 00441 return; 00442 00443 BoundRect.top = ScanLine; 00444 BoundRect.bottom = ScanLine + 1; 00445 00446 pLeft = ActiveHead; 00447 winding = pLeft->YDirection; 00448 pRight = pLeft->pNext; 00449 ASSERT(pRight); 00450 00451 // Setup first line... 00452 x1 = pLeft->XIntercept[0]; 00453 x2 = pRight->XIntercept[1]; 00454 00455 pLeft = pRight; 00456 pRight = pLeft->pNext; 00457 winding += pLeft->YDirection; 00458 00459 while ( NULL != pRight ) 00460 { 00461 int newx1 = pLeft->XIntercept[0]; 00462 int newx2 = pRight->XIntercept[1]; 00463 if ( winding ) 00464 { 00465 // Check and see if this new line touches the previous... 00466 if ( (newx1 >= x1 && newx1 <= x2) 00467 || (newx2 >= x1 && newx2 <= x2) 00468 || (x1 >= newx1 && x1 <= newx2) 00469 || (x2 >= newx2 && x2 <= newx2) 00470 ) 00471 { 00472 // Yup, just tack it on to our existing line 00473 x1 = min(x1,newx1); 00474 x2 = max(x2,newx2); 00475 } 00476 else 00477 { 00478 // Nope - render the old line.. 00479 BoundRect.left = x1; 00480 BoundRect.right = x2; 00481 00482 //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine); 00483 IntEngLineTo(&psurf->SurfObj, 00484 dc->rosdc.CombinedClip, 00485 BrushObj, 00486 x1, 00487 ScanLine, 00488 x2, 00489 ScanLine, 00490 &BoundRect, // Bounding rectangle 00491 RopMode); // MIX 00492 00493 x1 = newx1; 00494 x2 = newx2; 00495 } 00496 } 00497 pLeft = pRight; 00498 pRight = pLeft->pNext; 00499 winding += pLeft->YDirection; 00500 } 00501 // There will always be a line left-over, render it now... 00502 BoundRect.left = x1; 00503 BoundRect.right = x2; 00504 00505 //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine); 00506 IntEngLineTo(&psurf->SurfObj, 00507 dc->rosdc.CombinedClip, 00508 BrushObj, 00509 x1, 00510 ScanLine, 00511 x2, 00512 ScanLine, 00513 &BoundRect, // Bounding rectangle 00514 RopMode); // MIX 00515 } 00516 00517 // When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and 00518 // even-numbered polygon sides on each scan line. That is, GDI fills the area between the 00519 // first and second side, between the third and fourth side, and so on. 00520 00521 // WINDING Selects winding mode (fills any region with a nonzero winding value). 00522 // When the fill mode is WINDING, GDI fills any region that has a nonzero winding value. 00523 // This value is defined as the number of times a pen used to draw the polygon would go around the region. 00524 // The direction of each edge of the polygon is important. 00525 00526 BOOL 00527 APIENTRY 00528 FillPolygon( 00529 PDC dc, 00530 SURFACE *psurf, 00531 BRUSHOBJ *BrushObj, 00532 MIX RopMode, 00533 CONST PPOINT Points, 00534 int Count, 00535 RECTL BoundRect ) 00536 { 00537 FILL_EDGE_LIST *list = 0; 00538 FILL_EDGE *ActiveHead = 0; 00539 int ScanLine; 00540 PDC_ATTR pdcattr = dc->pdcattr; 00541 void 00542 (APIENTRY *FillScanLine)( 00543 PDC dc, 00544 int ScanLine, 00545 FILL_EDGE* ActiveHead, 00546 SURFACE *psurf, 00547 BRUSHOBJ *BrushObj, 00548 MIX RopMode ); 00549 00550 //DPRINT("FillPolygon\n"); 00551 00552 /* Create Edge List. */ 00553 list = POLYGONFILL_MakeEdgeList(Points, Count); 00554 /* DEBUG_PRINT_EDGELIST(list); */ 00555 if (NULL == list) 00556 return FALSE; 00557 00558 if ( WINDING == pdcattr->jFillMode ) 00559 FillScanLine = POLYGONFILL_FillScanLineWinding; 00560 else /* Default */ 00561 FillScanLine = POLYGONFILL_FillScanLineAlternate; 00562 00563 /* For each Scanline from BoundRect.bottom to BoundRect.top, 00564 * determine line segments to draw 00565 */ 00566 for ( ScanLine = BoundRect.top; ScanLine < BoundRect.bottom; ++ScanLine ) 00567 { 00568 POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead); 00569 //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead); 00570 FillScanLine ( dc, ScanLine, ActiveHead, psurf, BrushObj, RopMode ); 00571 } 00572 00573 /* Free Edge List. If any are left. */ 00574 POLYGONFILL_DestroyEdgeList(list); 00575 00576 return TRUE; 00577 } 00578 00579 BOOL FASTCALL 00580 IntFillPolygon( 00581 PDC dc, 00582 SURFACE *psurf, 00583 BRUSHOBJ *BrushObj, 00584 CONST PPOINT Points, 00585 int Count, 00586 RECTL DestRect, 00587 POINTL *BrushOrigin) 00588 { 00589 FILL_EDGE_LIST *list = 0; 00590 FILL_EDGE *ActiveHead = 0; 00591 FILL_EDGE *pLeft, *pRight; 00592 int ScanLine; 00593 00594 //DPRINT("IntFillPolygon\n"); 00595 00596 /* Create Edge List. */ 00597 list = POLYGONFILL_MakeEdgeList(Points, Count); 00598 /* DEBUG_PRINT_EDGELIST(list); */ 00599 if (NULL == list) 00600 return FALSE; 00601 00602 /* For each Scanline from DestRect.top to DestRect.bottom, determine line segments to draw */ 00603 for ( ScanLine = DestRect.top; ScanLine < DestRect.bottom; ++ScanLine ) 00604 { 00605 POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead); 00606 //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead); 00607 00608 if ( !ActiveHead ) 00609 return FALSE; 00610 00611 pLeft = ActiveHead; 00612 pRight = pLeft->pNext; 00613 ASSERT(pRight); 00614 00615 while ( NULL != pRight ) 00616 { 00617 int x1 = pLeft->XIntercept[0]; 00618 int x2 = pRight->XIntercept[1]; 00619 if ( x2 > x1 ) 00620 { 00621 RECTL LineRect; 00622 LineRect.top = ScanLine; 00623 LineRect.bottom = ScanLine + 1; 00624 LineRect.left = x1; 00625 LineRect.right = x2; 00626 00627 IntEngBitBlt(&psurf->SurfObj, 00628 NULL, 00629 NULL, 00630 dc->rosdc.CombinedClip, 00631 NULL, 00632 &LineRect, 00633 NULL, 00634 NULL, 00635 BrushObj, 00636 BrushOrigin, 00637 ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY)); 00638 } 00639 pLeft = pRight->pNext; 00640 pRight = pLeft ? pLeft->pNext : NULL; 00641 } 00642 } 00643 00644 /* Free Edge List. If any are left. */ 00645 POLYGONFILL_DestroyEdgeList(list); 00646 00647 return TRUE; 00648 } 00649 00650 /* EOF */ Generated on Sat May 26 2012 04:37:15 for ReactOS by
1.7.6.1
|