/* * ReactOS W32 Subsystem * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id$ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * PURPOSE: Various Polygon Filling routines for Polygon() * FILE: subsys/win32k/objects/polyfill.c * PROGRAMER: Mark Tempel * REVISION HISTORY: * 21/2/2003: Created */ #undef WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #undef NDEBUG #include INT abs(INT nm); #define FILL_EDGE_ALLOC_TAG 0x45465044 /* ** This struct is used for book keeping during polygon filling routines. */ typedef struct _tagFILL_EDGE { /*Basic line information*/ int FromX; int FromY; int ToX; int ToY; int dx; int dy; int absdx, absdy; int x, y; int xmajor; /*Active Edge List information*/ int XIntercept[2]; int Error; int ErrorMax; int XDirection, YDirection; /* The next edge in the active Edge List*/ struct _tagFILL_EDGE * pNext; } FILL_EDGE; typedef struct _FILL_EDGE_LIST { int Count; FILL_EDGE** Edges; } FILL_EDGE_LIST; #if 0 static void DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE* list ) { FILL_EDGE* pThis = list; if (0 == list) { DPRINT1("List is NULL\n"); return; } while(0 != pThis) { //DPRINT1("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY); DPRINT1("EDGE: [%d,%d]\n", pThis->XIntercept[0], pThis->XIntercept[1] ); pThis = pThis->pNext; } } #else #define DEBUG_PRINT_ACTIVE_EDGELIST(x) #endif /* ** Hide memory clean up. */ static void FASTCALL POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST* list) { int i; if ( list ) { if ( list->Edges ) { for ( i = 0; i < list->Count; i++ ) { if ( list->Edges[i] ) EngFreeMem ( list->Edges[i] ); } EngFreeMem ( list->Edges ); } EngFreeMem ( list ); } } /* ** This makes and initiaizes an Edge struct for a line between two points. */ static FILL_EDGE* FASTCALL POLYGONFILL_MakeEdge(POINT From, POINT To) { FILL_EDGE* rc = (FILL_EDGE*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE), FILL_EDGE_ALLOC_TAG); if (0 == rc) return NULL; //DPRINT1("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y); //Now Fill the struct. if ( To.y < From.y ) { rc->FromX = To.x; rc->FromY = To.y; rc->ToX = From.x; rc->ToY = From.y; rc->YDirection = -1; // lines that go up get walked backwards, so need to be offset // by -1 in order to make the walk identically on a pixel-level rc->Error = -1; } else { rc->FromX = From.x; rc->FromY = From.y; rc->ToX = To.x; rc->ToY = To.y; rc->YDirection = 1; rc->Error = 0; } rc->x = rc->FromX; rc->y = rc->FromY; rc->dx = rc->ToX - rc->FromX; rc->dy = rc->ToY - rc->FromY; rc->absdx = abs(rc->dx); rc->absdy = abs(rc->dy); rc->xmajor = rc->absdx > rc->absdy; rc->ErrorMax = MAX(rc->absdx,rc->absdy); rc->Error += rc->ErrorMax / 2; rc->XDirection = (rc->dx < 0)?(-1):(1); rc->pNext = 0; //DPRINT("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n", // From.x, From.y, To.x, To.y, rc->dx, rc->dy, rc->XDirection, rc->YDirection, rc->Error, rc->ErrorMax ); return rc; } /* ** My Edge comparison routine. ** This is for scan converting polygon fill. ** First sort by MinY, then Minx, then slope. ** ** This comparison will help us determine which ** lines will become active first when scanning from ** top (min y) to bottom (max y). ** ** Return Value Meaning ** Negative integer element1 < element2 ** Zero element1 = element2 ** Positive integer element1 > element2 */ static INT FASTCALL FILL_EDGE_Compare(FILL_EDGE* Edge1, FILL_EDGE* Edge2) { int e1 = Edge1->XIntercept[0] + Edge1->XIntercept[1]; int e2 = Edge2->XIntercept[0] + Edge2->XIntercept[1]; return e1 - e2; } /* ** Insert an edge into a list keeping the list in order. */ static void FASTCALL POLYGONFILL_ActiveListInsert(FILL_EDGE** activehead, FILL_EDGE* NewEdge ) { FILL_EDGE *pPrev, *pThis; //DPRINT1("In POLYGONFILL_ActiveListInsert()\n"); ASSERT ( activehead && NewEdge ); if ( !*activehead ) { NewEdge->pNext = NULL; *activehead = NewEdge; return; } /* ** First lets check to see if we have a new smallest value. */ if (FILL_EDGE_Compare(NewEdge, *activehead) <= 0) { NewEdge->pNext = *activehead; *activehead = NewEdge; return; } /* ** Ok, now scan to the next spot to put this item. */ pThis = *activehead; pPrev = NULL; while ( pThis && FILL_EDGE_Compare(pThis, NewEdge) < 0 ) { pPrev = pThis; pThis = pThis->pNext; } ASSERT(pPrev); NewEdge->pNext = pPrev->pNext; pPrev->pNext = NewEdge; //DEBUG_PRINT_ACTIVE_EDGELIST(*activehead); } /* ** Create a list of edges for a list of points. */ static FILL_EDGE_LIST* FASTCALL POLYGONFILL_MakeEdgeList(PPOINT Points, int Count) { int CurPt = 0; FILL_EDGE_LIST* list = 0; FILL_EDGE* e = 0; if ( 0 == Points || 2 > Count ) return 0; list = (FILL_EDGE_LIST*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE_LIST), FILL_EDGE_ALLOC_TAG); if ( 0 == list ) goto fail; list->Count = 0; list->Edges = (FILL_EDGE**)EngAllocMem(FL_ZERO_MEMORY, Count*sizeof(FILL_EDGE*), FILL_EDGE_ALLOC_TAG); if ( !list->Edges ) goto fail; memset ( list->Edges, 0, Count * sizeof(FILL_EDGE*) ); for ( CurPt = 1; CurPt < Count; ++CurPt ) { e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[CurPt] ); if ( !e ) goto fail; // if a straight horizontal line - who cares? if ( !e->absdy ) EngFreeMem ( e ); else list->Edges[list->Count++] = e; } e = POLYGONFILL_MakeEdge ( Points[CurPt-1], Points[0] ); if ( !e ) goto fail; if ( !e->absdy ) EngFreeMem ( e ); else list->Edges[list->Count++] = e; return list; fail: DPRINT1("Out Of MEMORY!!\n"); POLYGONFILL_DestroyEdgeList ( list ); return 0; } /* ** This slow routine uses the data stored in the edge list to ** calculate the x intercepts for each line in the edge list ** for scanline Scanline. **TODO: Get rid of this floating point arithmetic */ static void FASTCALL POLYGONFILL_UpdateScanline(FILL_EDGE* pEdge, int Scanline) { if ( 0 == pEdge->dy ) return; ASSERT ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline ); if ( pEdge->xmajor ) { int steps; ASSERT ( pEdge->y == Scanline ); // now shoot to end of scanline collision steps = (pEdge->ErrorMax-pEdge->Error-1)/pEdge->absdy; if ( steps ) { // record first collision with scanline int x1 = pEdge->x; pEdge->x += steps * pEdge->XDirection; pEdge->Error += steps * pEdge->absdy; ASSERT ( pEdge->Error < pEdge->ErrorMax ); pEdge->XIntercept[0] = MIN(x1,pEdge->x); pEdge->XIntercept[1] = MAX(x1,pEdge->x); } else { pEdge->XIntercept[0] = pEdge->x; pEdge->XIntercept[1] = pEdge->x; } // we should require exactly 1 step to step onto next scanline... ASSERT ( (pEdge->ErrorMax-pEdge->Error-1) / pEdge->absdy == 0 ); pEdge->x += pEdge->XDirection; pEdge->Error += pEdge->absdy; ASSERT ( pEdge->Error >= pEdge->ErrorMax ); // now step onto next scanline... pEdge->Error -= pEdge->absdx; pEdge->y++; } else // then this is a y-major line { pEdge->XIntercept[0] = pEdge->x; pEdge->XIntercept[1] = pEdge->x; pEdge->Error += pEdge->absdx; pEdge->y++; if ( pEdge->Error >= pEdge->ErrorMax ) { pEdge->Error -= pEdge->ErrorMax; pEdge->x += pEdge->XDirection; ASSERT ( pEdge->Error < pEdge->ErrorMax ); } } //DPRINT("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n", // pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, pEdge->XIntercept[0], pEdge->XIntercept[1] ); } /* ** This method updates the Active edge collection for the scanline Scanline. */ static void STDCALL POLYGONFILL_BuildActiveList ( int Scanline, FILL_EDGE_LIST* list, FILL_EDGE** ActiveHead ) { int i; ASSERT ( list && ActiveHead ); *ActiveHead = 0; for ( i = 0; i < list->Count; i++ ) { FILL_EDGE* pEdge = list->Edges[i]; ASSERT(pEdge); if ( pEdge->FromY <= Scanline && pEdge->ToY > Scanline ) { POLYGONFILL_UpdateScanline ( pEdge, Scanline ); POLYGONFILL_ActiveListInsert ( ActiveHead, pEdge ); } } } /* ** This method fills the portion of the polygon that intersects with the scanline ** Scanline. */ static void STDCALL POLYGONFILL_FillScanLineAlternate( PDC dc, int ScanLine, FILL_EDGE* ActiveHead, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode ) { FILL_EDGE *pLeft, *pRight; if ( !ActiveHead ) return; pLeft = ActiveHead; pRight = pLeft->pNext; ASSERT(pRight); while ( NULL != pRight ) { int x1 = pLeft->XIntercept[0]; int x2 = pRight->XIntercept[1]; if ( x2 > x1 ) { RECTL BoundRect; BoundRect.top = ScanLine; BoundRect.bottom = ScanLine + 1; BoundRect.left = x1; BoundRect.right = x2; //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine); IntEngLineTo( SurfObj, dc->CombinedClip, BrushObj, x1, ScanLine, x2, ScanLine, &BoundRect, // Bounding rectangle RopMode); // MIX } pLeft = pRight->pNext; pRight = pLeft ? pLeft->pNext : NULL; } } static void STDCALL POLYGONFILL_FillScanLineWinding( PDC dc, int ScanLine, FILL_EDGE* ActiveHead, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode ) { FILL_EDGE *pLeft, *pRight; int x1, x2, winding = 0; RECTL BoundRect; if ( !ActiveHead ) return; BoundRect.top = ScanLine; BoundRect.bottom = ScanLine + 1; pLeft = ActiveHead; winding = pLeft->YDirection; pRight = pLeft->pNext; ASSERT(pRight); // setup first line... x1 = pLeft->XIntercept[0]; x2 = pRight->XIntercept[1]; pLeft = pRight; pRight = pLeft->pNext; winding += pLeft->YDirection; while ( NULL != pRight ) { int newx1 = pLeft->XIntercept[0]; int newx2 = pRight->XIntercept[1]; if ( winding ) { // check and see if this new line touches the previous... if ( (newx1 >= x1 && newx1 <= x2) || (newx2 >= x1 && newx2 <= x2) || (x1 >= newx1 && x1 <= newx2) || (x2 >= newx2 && x2 <= newx2) ) { // yup, just tack it on to our existing line x1 = MIN(x1,newx1); x2 = MAX(x2,newx2); } else { // nope - render the old line.. BoundRect.left = x1; BoundRect.right = x2; //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine); IntEngLineTo( SurfObj, dc->CombinedClip, BrushObj, x1, ScanLine, x2, ScanLine, &BoundRect, // Bounding rectangle RopMode); // MIX x1 = newx1; x2 = newx2; } } pLeft = pRight; pRight = pLeft->pNext; winding += pLeft->YDirection; } // there will always be a line left-over, render it now... BoundRect.left = x1; BoundRect.right = x2; //DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine); IntEngLineTo( SurfObj, dc->CombinedClip, BrushObj, x1, ScanLine, x2, ScanLine, &BoundRect, // Bounding rectangle RopMode); // MIX } //When the fill mode is ALTERNATE, GDI fills the area between odd-numbered and //even-numbered polygon sides on each scan line. That is, GDI fills the area between the //first and second side, between the third and fourth side, and so on. //WINDING Selects winding mode (fills any region with a nonzero winding value). //When the fill mode is WINDING, GDI fills any region that has a nonzero winding value. //This value is defined as the number of times a pen used to draw the polygon would go around the region. //The direction of each edge of the polygon is important. BOOL STDCALL FillPolygon( PDC dc, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect ) { FILL_EDGE_LIST *list = 0; FILL_EDGE *ActiveHead = 0; int ScanLine; void STDCALL (*FillScanLine)( PDC dc, int ScanLine, FILL_EDGE* ActiveHead, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode ); //DPRINT("FillPolygon\n"); /* Create Edge List. */ list = POLYGONFILL_MakeEdgeList(Points, Count); /* DEBUG_PRINT_EDGELIST(list); */ if (NULL == list) return FALSE; if ( WINDING == dc->w.polyFillMode ) FillScanLine = POLYGONFILL_FillScanLineWinding; else /* default */ FillScanLine = POLYGONFILL_FillScanLineAlternate; /* For each Scanline from BoundRect.bottom to BoundRect.top, * determine line segments to draw */ for ( ScanLine = BoundRect.top; ScanLine < BoundRect.bottom; ++ScanLine ) { POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead); //DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead); FillScanLine ( dc, ScanLine, ActiveHead, SurfObj, BrushObj, RopMode ); } /* Free Edge List. If any are left. */ POLYGONFILL_DestroyEdgeList(list); return TRUE; } /* EOF */