Polygon now works flawlessly ( with possible exception to the issue mentioned on the list earlier this evening ). Scrollbar arrows are now perfect.

svn path=/trunk/; revision=5595
This commit is contained in:
Royce Mitchell III 2003-08-16 04:47:41 +00:00
parent 58d4ce9842
commit 321a71f1dd
4 changed files with 439 additions and 551 deletions

View file

@ -19,24 +19,50 @@
#define FASTCALL
#define STDCALL
#define INT int
#define CLIPOBJ int
#define SURFOBJ int
#define PBRUSHOBJ int
#define MIX char
#define BOOL bool
#define TRUE true
#define FALSE false
#define CONST const
#define MmCopyFromCaller memmove
#define ALTERNATE 0
#define WINDING 1
#define ASSERT assert
#define EngFreeMem free
typedef struct W
{
int polyFillMode;
} W;
#define FILL_EDGE_ALLOC_TAG 0x45465044
typedef struct DC
{
CLIPOBJ CombinedClip;
W w;
} DC, *PDC;
typedef struct tagPOINT
{
long x, y;
} POINT, *PPOINT, *LPPOINT;
typedef struct RECTL
{
long left, top, right, bottom;
} RECTL, *PRECTL;
#define EngFreeMem free
#define FL_ZERO_MEMORY 1
#define DPRINT1 printf("%i:",__LINE__);printf
inline void DPRINT(...){}
#define SCREENX 16
#define SCREENY 16
#define SCREENX 25
#define SCREENY 25
char screen[SCREENY][SCREENX];
#define EDGE_CHAR '*'
@ -79,7 +105,13 @@ void putpixel ( int x, int y, char c )
screen[y][x] = '#';
}
void Line ( int x1, int y1, int x2, int y2, char c )
void IntEngLineTo (
SURFOBJ*,
CLIPOBJ,
PBRUSHOBJ,
int x1, int y1, int x2, int y2,
RECTL*,
MIX mix )
{
int dx = x2 - x1;
int dy = y2 - y1;
@ -93,7 +125,7 @@ void Line ( int x1, int y1, int x2, int y2, char c )
{
while ( x1 != x2 )
{
putpixel ( x1, y1, c );
putpixel ( x1, y1, mix );
x1 += xinc;
}
return;
@ -102,14 +134,14 @@ void Line ( int x1, int y1, int x2, int y2, char c )
{
while ( y1 != y2 )
{
putpixel ( x1, y1, c );
putpixel ( x1, y1, mix );
y1 += yinc;
}
return;
}
for ( int i = 0; i < EMax; i++ )
{
putpixel ( x1, y1, c );
putpixel ( x1, y1, mix );
if ( absdy > absdx )
{
y1 += yinc;
@ -133,15 +165,7 @@ void Line ( int x1, int y1, int x2, int y2, char c )
}
}
typedef struct tagPOINT
{
long x, y;
} POINT, *PPOINT, *LPPOINT;
typedef struct tagRECTL
{
long left, top, right, bottom;
} RECTL, *PRECTL;
#define FILL_EDGE_ALLOC_TAG 0x45465044
/*
** This struct is used for book keeping during polygon filling routines.
@ -288,7 +312,7 @@ POLYGONFILL_MakeEdge(POINT From, POINT To)
}
}
DPRINT1("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=(%i,%i) err=%i max=%i\n",
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;
@ -478,7 +502,7 @@ POLYGONFILL_UpdateScanline(FILL_EDGE* pEdge, int Scanline)
pEdge->XIntercept[1] = pEdge->x;
}
DPRINT1("Line (%d, %d) to (%d, %d) intersects scanline %d at (%d,%d)\n",
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] );
}
@ -513,7 +537,13 @@ POLYGONFILL_BuildActiveList ( int Scanline, FILL_EDGE_LIST* list, FILL_EDGE** Ac
static
void
STDCALL
POLYGONFILL_FillScanLineAlternate ( int ScanLine, FILL_EDGE* ActiveHead )
POLYGONFILL_FillScanLineAlternate(
PDC dc,
int ScanLine,
FILL_EDGE* ActiveHead,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode )
{
FILL_EDGE *pLeft, *pRight;
@ -536,17 +566,16 @@ POLYGONFILL_FillScanLineAlternate ( int ScanLine, FILL_EDGE* ActiveHead )
BoundRect.left = x1;
BoundRect.right = x2;
DPRINT1("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
Line ( x1, ScanLine, x2, ScanLine, FILL_CHAR );
/*ret = IntEngLineTo( SurfObj,
DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
IntEngLineTo( SurfObj,
dc->CombinedClip,
BrushObj,
x1,
ScanLine,
ScanLine,
x2,
ScanLine,
&BoundRect, // Bounding rectangle
RopMode);*/ // MIX
RopMode); // MIX
}
pLeft = pRight->pNext;
pRight = pLeft ? pLeft->pNext : NULL;
@ -556,7 +585,13 @@ POLYGONFILL_FillScanLineAlternate ( int ScanLine, FILL_EDGE* ActiveHead )
static
void
STDCALL
POLYGONFILL_FillScanLineWinding ( int ScanLine, FILL_EDGE* ActiveHead )
POLYGONFILL_FillScanLineWinding(
PDC dc,
int ScanLine,
FILL_EDGE* ActiveHead,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode )
{
FILL_EDGE *pLeft, *pRight;
int winding = 0;
@ -581,74 +616,58 @@ POLYGONFILL_FillScanLineWinding ( int ScanLine, FILL_EDGE* ActiveHead )
BoundRect.left = x1;
BoundRect.right = x2;
DPRINT1("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
Line ( x1, ScanLine, x2, ScanLine, FILL_CHAR );
/*ret = IntEngLineTo( SurfObj,
DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
IntEngLineTo( SurfObj,
dc->CombinedClip,
BrushObj,
x1,
ScanLine,
ScanLine,
x2,
ScanLine,
&BoundRect, // Bounding rectangle
RopMode);*/ // MIX
RopMode); // MIX
}
pLeft = pRight;
pRight = pLeft->pNext;
winding += pLeft->XDirection;
winding += pLeft->YDirection;
}
}
//ALTERNATE Selects alternate mode (fills the area between odd-numbered and even-numbered
//polygon sides on each scan line).
//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.
BOOL
STDCALL
FillPolygon_ALTERNATE(CONST PPOINT Points, int Count, RECTL BoundRect)
{
FILL_EDGE_LIST *list = 0;
FILL_EDGE *ActiveHead = 0;
int ScanLine;
DPRINT1("FillPolygon_ALTERNATE\n");
/* Create Edge List. */
list = POLYGONFILL_MakeEdgeList(Points, Count);
/* DEBUG_PRINT_EDGELIST(list); */
if (NULL == list)
return FALSE;
/* For each Scanline from BoundRect.bottom to BoundRect.top,
* determine line segments to draw
*/
for ( ScanLine = BoundRect.top + 1; ScanLine < BoundRect.bottom; ++ScanLine )
{
POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead);
//DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
POLYGONFILL_FillScanLineAlternate(ScanLine, ActiveHead);
}
/* Free Edge List. If any are left. */
POLYGONFILL_DestroyEdgeList(list);
return TRUE;
}
//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.
//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_WINDING(CONST PPOINT Points, int Count, RECTL BoundRect)
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;
DPRINT1("FillPolygon_WINDING\n");
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);
@ -656,6 +675,11 @@ FillPolygon_WINDING(CONST PPOINT Points, int Count, RECTL BoundRect)
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
*/
@ -663,7 +687,7 @@ FillPolygon_WINDING(CONST PPOINT Points, int Count, RECTL BoundRect)
{
POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead);
//DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
POLYGONFILL_FillScanLineWinding(ScanLine, ActiveHead);
FillScanLine ( dc, ScanLine, ActiveHead, SurfObj, BrushObj, RopMode );
}
/* Free Edge List. If any are left. */
@ -674,35 +698,22 @@ FillPolygon_WINDING(CONST PPOINT Points, int Count, RECTL BoundRect)
/*
* 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: polytest.cpp,v 1.1 2003/08/16 01:34:29 royce Exp $ */
//This implementation is blatantly ripped off from W32kRectangle
// this is highly hacked from W32kPolygon...
BOOL
Polygon ( CONST PPOINT UnsafePoints, int Count )
Polygon ( CONST PPOINT UnsafePoints, int Count, int polyFillMode )
{
BOOL ret;
RECTL DestRect;
int CurrentPoint;
PPOINT Points;
SURFOBJ* SurfObj = 0;
DC dc;
PBRUSHOBJ OutBrushObj = 0;
dc.CombinedClip = 0;
dc.w.polyFillMode = polyFillMode;
DPRINT1("In W32kPolygon()\n");
@ -759,20 +770,18 @@ Polygon ( CONST PPOINT UnsafePoints, int Count )
}
DPRINT1("Polygon Making line from (%d,%d) to (%d,%d)\n", From.x, From.y, To.x, To.y );
Line ( From.x, From.y, To.x, To.y, EDGE_CHAR );
/*ret = IntEngLineTo(SurfObj,
dc->CombinedClip,
OutBrushObj,
From.x,
From.y,
To.x,
To.y,
&DestRect,
dc->w.ROPmode);*/ /* MIX */
IntEngLineTo(SurfObj,
dc.CombinedClip,
OutBrushObj,
From.x,
From.y,
To.x,
To.y,
&DestRect,
EDGE_CHAR); /* MIX */
}
/* determine the fill mode to fill the polygon. */
ret = FillPolygon_ALTERNATE(Points, Count, DestRect);
ret = FillPolygon(&dc, SurfObj, OutBrushObj, FILL_CHAR, Points, Count, DestRect );
free(Points);
return ret;
@ -789,14 +798,19 @@ void main()
{ 12, 4 },
{ 4, 8 },
#else
{ 2, 8 },
{ 6, 2 },
{ 9, 8 },
{ 2, 4 },
{ 10, 4 }
{ 4, 16 },
{ 12, 4 },
{ 18, 16 },
{ 4, 8 },
{ 20, 8 }
#endif
};
Polygon ( pts,sizeof(pts)/sizeof(pts[0]) );
const int pts_count = sizeof(pts)/sizeof(pts[0]);
// use ALTERNATE or WINDING for 3rd param
Polygon ( pts, pts_count, ALTERNATE );
// print out our "screen"
for ( int y = 0; y < SCREENY; y++ )
{
for ( int x = 0; x < SCREENX; x++ )
@ -808,4 +822,4 @@ void main()
DPRINT1("Done!\n");
(void)getch();
}
/* EOF */

View file

@ -2,7 +2,6 @@
#define __WIN32K_PAINT_H
BOOL STDCALL FillSolid (PSURFOBJ Surface, PRECTL Dimensions, ULONG iColor);
BOOL STDCALL FillPolygon_ALTERNATE (PDC dc, PSURFOBJ SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, INT Count, RECTL BoundRect);
BOOL STDCALL FillPolygon_WINDING (PDC dc, PSURFOBJ SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, INT Count, RECTL BoundRect);
BOOL STDCALL FillPolygon ( PDC dc, PSURFOBJ SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, INT Count, RECTL BoundRect );
#endif /* __WIN32K_PAINT_H */

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: fillshap.c,v 1.22 2003/08/15 18:51:32 royce Exp $ */
/* $Id: fillshap.c,v 1.23 2003/08/16 04:47:41 royce Exp $ */
#undef WIN32_LEAN_AND_MEAN
#include <windows.h>
@ -77,12 +77,16 @@ W32kPie(HDC hDC,
#if 0
//ALTERNATE Selects alternate mode (fills the area between odd-numbered and even-numbered
//polygon sides on each scan line).
//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.
extern BOOL FillPolygon_ALTERNATE(PDC dc,
//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.
extern BOOL FillPolygon(PDC dc,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode,
@ -90,17 +94,6 @@ extern BOOL FillPolygon_ALTERNATE(PDC dc,
int Count,
RECTL BoundRect);
//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.
extern BOOL FillPolygon_WINDING(PDC dc,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,MIX RopMode,
CONST PPOINT Points,
int Count,
RECTL BoundRect);
#endif
//This implementation is blatantly ripped off from W32kRectangle
@ -181,16 +174,9 @@ W32kPolygon(HDC hDC,
/* Now fill the polygon with the current brush. */
FillBrushObj = (BRUSHOBJ*) GDIOBJ_LockObj(dc->w.hBrush, GO_BRUSH_MAGIC);
/* determine the fill mode to fill the polygon. */
#if 1
if (WINDING == dc->w.polyFillMode)
{
ret = FillPolygon_WINDING(dc, SurfObj, FillBrushObj, dc->w.ROPmode, Points, Count, DestRect);
}
else /* default */
{
ret = FillPolygon_ALTERNATE(dc, SurfObj, FillBrushObj, dc->w.ROPmode, Points, Count, DestRect);
}
ret = FillPolygon ( dc, SurfObj, FillBrushObj, dc->w.ROPmode, Points, Count, DestRect);
#endif
// Draw the Polygon Edges with the current pen
for (CurrentPoint = 0; CurrentPoint < Count; ++CurrentPoint)
@ -224,15 +210,7 @@ W32kPolygon(HDC hDC,
}
#if 0
/* determine the fill mode to fill the polygon. */
if (WINDING == dc->w.polyFillMode)
{
ret = FillPolygon_WINDING(dc, SurfObj, FillBrushObj, dc->w.ROPmode, Points, Count, DestRect);
}
else /* default */
{
ret = FillPolygon_ALTERNATE(dc, SurfObj, FillBrushObj, dc->w.ROPmode, Points, Count, DestRect);
}
ret = FillPolygon ( dc, SurfObj, FillBrushObj, dc->w.ROPmode, Points, Count, DestRect);
#endif
GDIOBJ_UnlockObj(dc->w.hBrush, GO_BRUSH_MAGIC);
}

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: polyfill.c,v 1.7 2003/08/15 18:51:32 royce Exp $
/* $Id: polyfill.c,v 1.8 2003/08/16 04:47:41 royce Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -40,12 +40,12 @@
#define NDEBUG
#include <win32k/debug1.h>
#define PFILL_EDGE_ALLOC_TAG 0x45465044
#define FILL_EDGE_ALLOC_TAG 0x45465044
/*
** This struct is used for book keeping during polygon filling routines.
*/
typedef struct _tagPFILL_EDGE
typedef struct _tagFILL_EDGE
{
/*Basic line information*/
int FromX;
@ -54,51 +54,48 @@ typedef struct _tagPFILL_EDGE
int ToY;
int dx;
int dy;
int MinX;
int MaxX;
int MinY;
int MaxY;
int absdx, absdy;
int x, y;
int xmajor;
/*Active Edge List information*/
int XIntercept;
int XIntercept[2];
int Error;
int ErrorInc;
int ErrorMax;
int XPerY;
int XDirection;
int YDirection; // used for Winding
int XDirection, YDirection;
/* The next edge in the Edge List*/
struct _tagPFILL_EDGE * pNext;
} PFILL_EDGE, *PPFILL_EDGE;
/* The next edge in the active Edge List*/
struct _tagFILL_EDGE * pNext;
} FILL_EDGE;
typedef PPFILL_EDGE PFILL_EDGE_LIST;
typedef struct _FILL_EDGE_LIST
{
int Count;
FILL_EDGE** Edges;
} FILL_EDGE_LIST;
#if 0
static
inline
int
abs ( int a )
{
return a < 0 ? -a : a;
}
/*static
void
DEBUG_PRINT_EDGELIST(PFILL_EDGE_LIST list)
DEBUG_PRINT_ACTIVE_EDGELIST ( FILL_EDGE* list )
{
PPFILL_EDGE pThis = list;
if (0 == list)
{
DPRINT("List is NULL\n");
return;
}
while(0 != pThis)
{
DPRINT("EDGE: (%d, %d) to (%d, %d)\n", pThis->FromX, pThis->FromY, pThis->ToX, pThis->ToY);
pThis = pThis->pNext;
}
}*/
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.
@ -106,30 +103,21 @@ DEBUG_PRINT_EDGELIST(PFILL_EDGE_LIST list)
static
void
FASTCALL
POLYGONFILL_DestroyEdge(PPFILL_EDGE pEdge)
POLYGONFILL_DestroyEdgeList(FILL_EDGE_LIST* list)
{
if (0 != pEdge)
EngFreeMem(pEdge);
}
/*
** Clean up a list.
*/
static
void
FASTCALL
POLYGONFILL_DestroyEdgeList(PFILL_EDGE_LIST list)
{
PPFILL_EDGE pThis = 0;
PPFILL_EDGE pNext = 0;
pThis = list;
while (0 != pThis)
int i;
if ( list )
{
//DPRINT("Destroying Edge\n");
pNext = pThis->pNext;
POLYGONFILL_DestroyEdge(pThis);
pThis = pNext;
if ( list->Edges )
{
for ( i = 0; i < list->Count; i++ )
{
if ( list->Edges[i] )
EngFreeMem ( list->Edges[i] );
}
EngFreeMem ( list->Edges );
}
EngFreeMem ( list );
}
}
@ -137,17 +125,16 @@ POLYGONFILL_DestroyEdgeList(PFILL_EDGE_LIST list)
** This makes and initiaizes an Edge struct for a line between two points.
*/
static
PPFILL_EDGE
FILL_EDGE*
FASTCALL
POLYGONFILL_MakeEdge(POINT From, POINT To)
{
int absdx, absdy;
PPFILL_EDGE rc = (PPFILL_EDGE)EngAllocMem(FL_ZERO_MEMORY, sizeof(PFILL_EDGE), PFILL_EDGE_ALLOC_TAG);
FILL_EDGE* rc = (FILL_EDGE*)EngAllocMem(FL_ZERO_MEMORY, sizeof(FILL_EDGE), FILL_EDGE_ALLOC_TAG);
if (0 == rc)
return NULL;
//DPRINT ("Making Edge: (%d, %d) to (%d, %d)\n", From.x, From.y, To.x, To.y);
//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 )
{
@ -166,22 +153,16 @@ POLYGONFILL_MakeEdge(POINT From, POINT To)
rc->YDirection = 1;
}
rc->x = rc->FromX;
rc->y = rc->FromY;
rc->dx = rc->ToX - rc->FromX;
rc->dy = rc->ToY - rc->FromY;
absdx = abs(rc->dx);
absdy = abs(rc->dy);
rc->MinX = MIN(To.x, From.x);
rc->MaxX = MAX(To.x, From.x);
rc->MinY = MIN(To.y, From.y);
rc->MaxY = MAX(To.y, From.y);
rc->absdx = abs(rc->dx);
rc->absdy = abs(rc->dy);
if (rc->MinY == To.y)
rc->XIntercept = To.x;
else
rc->XIntercept = From.x;
rc->xmajor = rc->absdx > rc->absdy;
rc->ErrorMax = absdy;
rc->ErrorInc = absdx;
rc->ErrorMax = MAX(rc->absdx,rc->absdy);
rc->Error = rc->ErrorMax / 2;
@ -189,8 +170,25 @@ POLYGONFILL_MakeEdge(POINT From, POINT To)
rc->pNext = 0;
DPRINT ("MakeEdge (%i,%i)->(%i,%i) d=(%i,%i) dir=%i err=%i max=%i\n",
From.x, From.y, To.x, To.y, rc->dx, rc->dy, rc->Direction, rc->Error, rc->ErrorMax );
rc->XIntercept[0] = rc->x;
rc->XIntercept[1] = rc->x;
if ( rc->xmajor && rc->absdy )
{
int x1 = rc->x;
int steps = (rc->ErrorMax-rc->Error-1) / rc->absdy;
if ( steps )
{
rc->x += steps * rc->XDirection;
rc->Error += steps * rc->absdy;
ASSERT ( rc->Error < rc->ErrorMax );
rc->XIntercept[0] = MIN(x1,rc->x);
rc->XIntercept[1] = MAX(x1,rc->x);
}
}
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;
}
@ -211,30 +209,12 @@ POLYGONFILL_MakeEdge(POINT From, POINT To)
static
INT
FASTCALL
PFILL_EDGE_Compare(PPFILL_EDGE Edge1, PPFILL_EDGE Edge2)
FILL_EDGE_Compare(FILL_EDGE* Edge1, FILL_EDGE* Edge2)
{
//DPRINT("In PFILL_EDGE_Compare()\n");
if (Edge1->MinY == Edge2->MinY)
{
//DPRINT("In PFILL_EDGE_Compare() MinYs are equal\n");
if (Edge1->MinX == Edge2->MinX)
{
if (0 == Edge2->dx || 0 == Edge1->dx)
{
return Edge1->dx - Edge2->dx;
}
else
{
return (Edge1->dy/Edge1->dx) - (Edge2->dy/Edge2->dx);
}
}
else
{
return Edge1->MinX - Edge2->MinX;
}
}
//DPRINT("In PFILL_EDGE_Compare() returning: %d\n",Edge1->MinY - Edge2->MinY);
return Edge1->MinY - Edge2->MinY;
int e1 = Edge1->XIntercept[0] + Edge1->XIntercept[1];
int e2 = Edge2->XIntercept[0] + Edge2->XIntercept[1];
return e1 - e2;
}
@ -244,79 +224,91 @@ PFILL_EDGE_Compare(PPFILL_EDGE Edge1, PPFILL_EDGE Edge2)
static
void
FASTCALL
POLYGONFILL_ListInsert(PFILL_EDGE_LIST *list, PPFILL_EDGE NewEdge)
POLYGONFILL_ActiveListInsert(FILL_EDGE** activehead, FILL_EDGE* NewEdge )
{
PPFILL_EDGE pThis;
if (0 != list && 0 != NewEdge)
FILL_EDGE *pPrev, *pThis;
//DPRINT1("In POLYGONFILL_ActiveListInsert()\n");
ASSERT ( activehead && NewEdge );
if ( !*activehead )
{
pThis = *list;
//DPRINT("In POLYGONFILL_ListInsert()\n");
/*
** First lets check to see if we have a new smallest value.
*/
if (0 < PFILL_EDGE_Compare(pThis, NewEdge))
{
NewEdge->pNext = pThis;
*list = NewEdge;
return;
}
/*
** Ok, now scan to the next spot to put this item.
*/
while (0 > PFILL_EDGE_Compare(pThis, NewEdge))
{
if (0 == pThis->pNext)
break;
pThis = pThis->pNext;
}
NewEdge->pNext = pThis->pNext;
pThis->pNext = NewEdge;
//DEBUG_PRINT_EDGELIST(*list);
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
PFILL_EDGE_LIST
FILL_EDGE_LIST*
FASTCALL
POLYGONFILL_MakeEdgeList(PPOINT Points, int Count)
{
int CurPt = 0;
int SeqNum = 0;
PPFILL_EDGE rc = 0;
PPFILL_EDGE NextEdge = 0;
FILL_EDGE_LIST* list = 0;
FILL_EDGE* e = 0;
if ( 0 == Points || 2 > Count )
return rc;
return 0;
//Establish the list with the first two points.
rc = POLYGONFILL_MakeEdge ( Points[0], Points[1] );
if (0 == rc) return rc;
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,++SeqNum )
for ( CurPt = 1; CurPt < Count; ++CurPt )
{
if (CurPt == Count - 1 )
{
NextEdge = POLYGONFILL_MakeEdge(Points[CurPt],Points[0]);
}
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
{
NextEdge = POLYGONFILL_MakeEdge(Points[CurPt],Points[CurPt + 1]);
}
if (0 != NextEdge)
{
POLYGONFILL_ListInsert(&rc, NextEdge);
}
else
{
DPRINT1("Out Of MEMORY!! NextEdge = 0\n");
}
list->Edges[list->Count++] = e;
}
return rc;
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;
}
@ -329,187 +321,88 @@ POLYGONFILL_MakeEdgeList(PPOINT Points, int Count)
static
void
FASTCALL
POLYGONFILL_UpdateScanline(PPFILL_EDGE pEdge, int Scanline)
POLYGONFILL_UpdateScanline(FILL_EDGE* pEdge, int Scanline)
{
if ( 0 == pEdge->dy )
return;
if ( pEdge->ErrorMax )
ASSERT ( pEdge->FromY < Scanline && pEdge->ToY >= Scanline );
if ( pEdge->xmajor )
{
pEdge->Error += pEdge->ErrorInc;
int steps;
// we should require exactly 1 step to step onto current 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 current scanline...
pEdge->Error -= pEdge->absdx;
pEdge->y++;
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;
}
}
else // then this is a y-major line
{
pEdge->Error += pEdge->absdx;
pEdge->y++;
if ( pEdge->Error >= pEdge->ErrorMax )
{
int steps = pEdge->Error / pEdge->ErrorMax;
pEdge->XIntercept += steps * pEdge->XDirection;
pEdge->Error -= steps * pEdge->ErrorMax;
pEdge->Error -= pEdge->ErrorMax;
pEdge->x += pEdge->XDirection;
ASSERT ( pEdge->Error < pEdge->ErrorMax );
}
pEdge->XIntercept[0] = pEdge->x;
pEdge->XIntercept[1] = pEdge->x;
}
DPRINT ("Line (%d, %d) to (%d, %d) intersects scanline %d at %d\n",
pEdge->FromX, pEdge->FromY, pEdge->ToX, pEdge->ToY, Scanline, pEdge->XIntercept );
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 routine removes an edge from the global edge list and inserts it into
** the active edge list (preserving the order).
** An edge is considered Active if the current scanline intersects it.
**
** Note: once an edge is no longer active, it is deleted.
*/
static
void
FASTCALL
POLYGONFILL_AECInsertInOrder(PFILL_EDGE_LIST *list, PPFILL_EDGE pEdge)
{
BOOL Done = FALSE;
PPFILL_EDGE pThis = 0;
PPFILL_EDGE pPrev = 0;
pThis = *list;
while(0 != pThis && !Done)
{
/*pEdge goes before pThis*/
if (pThis->XIntercept > pEdge->XIntercept)
{
if (*list == pThis)
{
*list = pEdge;
}
else
{
pPrev->pNext = pEdge;
}
pEdge->pNext = pThis;
Done = TRUE;
}
pPrev = pThis;
pThis = pThis->pNext;
}
}
/*
** This routine reorders the Active Edge collection (list) after all
** the now inactive edges have been removed.
*/
static
void
FASTCALL
POLYGONFILL_AECReorder(PFILL_EDGE_LIST *AEC)
{
PPFILL_EDGE pThis = 0;
PPFILL_EDGE pPrev = 0;
PPFILL_EDGE pTarg = 0;
pThis = *AEC;
while (0 != pThis)
{
/*We are at the end of the list*/
if ( 0 == pThis->pNext )
return;
/*If the next item is out of order, pull it from the list and
re-insert it, and don't advance pThis.*/
if (pThis->XIntercept > pThis->pNext->XIntercept)
{
pTarg = pThis->pNext;
pThis->pNext = pTarg->pNext;
pTarg->pNext = 0;
POLYGONFILL_AECInsertInOrder(AEC, pTarg);
}
else/*In order, advance pThis*/
{
pPrev = pThis;
pThis = pThis->pNext;
}
}
}
/*
** This method updates the Active edge collection for the scanline Scanline.
*/
static
void
STDCALL
POLYGONFILL_UpdateActiveEdges(int Scanline, PFILL_EDGE_LIST *GEC, PFILL_EDGE_LIST *AEC)
POLYGONFILL_BuildActiveList ( int Scanline, FILL_EDGE_LIST* list, FILL_EDGE** ActiveHead )
{
PPFILL_EDGE pThis = 0;
PPFILL_EDGE pAECLast = 0;
PPFILL_EDGE pPrev = 0;
DPRINT("In POLYGONFILL_UpdateActiveEdges() Scanline: %d\n", Scanline);
/*First scan through GEC and look for any edges that have become active*/
pThis = *GEC;
while (0 != pThis && pThis->MinY <= Scanline)
int i;
ASSERT ( list && ActiveHead );
*ActiveHead = 0;
for ( i = 0; i < list->Count; i++ )
{
//DPRINT("Moving Edge to AEC\n");
/*Remove the edge from GEC and put it into AEC*/
if (pThis->MinY <= Scanline)
FILL_EDGE* pEdge = list->Edges[i];
ASSERT(pEdge);
if ( pEdge->FromY < Scanline && pEdge->ToY >= Scanline )
{
/*Always peel off the front of the GEC*/
*GEC = pThis->pNext;
/*Now put this edge at the end of AEC*/
if (0 == *AEC)
{
*AEC = pThis;
pThis->pNext = 0;
pAECLast = pThis;
}
else if(0 == pAECLast)
{
pAECLast = *AEC;
while(0 != pAECLast->pNext)
{
pAECLast = pAECLast->pNext;
}
pAECLast->pNext = pThis;
pThis->pNext = 0;
pAECLast = pThis;
}
else
{
pAECLast->pNext = pThis;
pThis->pNext = 0;
pAECLast = pThis;
}
}
pThis = *GEC;
}
/*Now remove any edges in the AEC that are no longer active and Update the XIntercept in the AEC*/
pThis = *AEC;
while (0 != pThis)
{
/*First check to see if this item is deleted*/
if (pThis->MaxY <= Scanline)
{
//DPRINT("Removing Edge from AEC\n");
if (0 == pPrev)/*First element in the list*/
{
*AEC = pThis->pNext;
}
else
{
pPrev->pNext = pThis->pNext;
}
POLYGONFILL_DestroyEdge(pThis);
}
else/*Otherwise, update the scanline*/
{
POLYGONFILL_UpdateScanline(pThis, Scanline);
pPrev = pThis;
}
/*List Upkeep*/
if (0 == pPrev)/*First element in the list*/
{
pThis = *AEC;
}
else
{
pThis = pPrev->pNext;
POLYGONFILL_UpdateScanline ( pEdge, Scanline );
POLYGONFILL_ActiveListInsert ( ActiveHead, pEdge );
}
}
/*Last re Xintercept order the AEC*/
POLYGONFILL_AECReorder(AEC);
}
/*
@ -519,138 +412,137 @@ POLYGONFILL_UpdateActiveEdges(int Scanline, PFILL_EDGE_LIST *GEC, PFILL_EDGE_LIS
static
void
STDCALL
POLYGONFILL_FillScanLineAlternate(PDC dc, int ScanLine, PFILL_EDGE_LIST ActiveEdges, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode)
POLYGONFILL_FillScanLineAlternate(
PDC dc,
int ScanLine,
FILL_EDGE* ActiveHead,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode )
{
BOOL OnOdd = TRUE;
RECTL BoundRect;
int XInterceptOdd,XInterceptEven,ret;
PPFILL_EDGE pThis = ActiveEdges;
FILL_EDGE *pLeft, *pRight;
while (NULL != pThis)
if ( !ActiveHead )
return;
pLeft = ActiveHead;
pRight = pLeft->pNext;
ASSERT(pRight);
while ( NULL != pRight )
{
if (OnOdd)
int x1 = pLeft->XIntercept[1];
int x2 = pRight->XIntercept[0]+1;
if ( x2 > x1 )
{
XInterceptOdd = pThis->XIntercept;
OnOdd = FALSE;
}
else
{
XInterceptEven = pThis->XIntercept+1;
if ( XInterceptEven > XInterceptOdd )
{
BoundRect.top = ScanLine;
BoundRect.bottom = ScanLine + 1;
BoundRect.left = XInterceptOdd;
BoundRect.right = XInterceptEven;
RECTL BoundRect;
BoundRect.top = ScanLine;
BoundRect.bottom = ScanLine + 1;
BoundRect.left = x1;
BoundRect.right = x2;
DPRINT ("Fill Line (%d, %d) to (%d, %d)\n",XInterceptOdd, ScanLine, XInterceptEven, ScanLine);
ret = IntEngLineTo( SurfObj,
dc->CombinedClip,
BrushObj,
XInterceptOdd,
ScanLine,
XInterceptEven,
ScanLine,
&BoundRect, /* Bounding rectangle */
RopMode); /* MIX */
}
OnOdd = TRUE;
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
}
pThis = pThis->pNext;
pLeft = pRight->pNext;
pRight = pLeft ? pLeft->pNext : NULL;
}
}
static
void
STDCALL
POLYGONFILL_FillScanLineWinding(PDC dc, int ScanLine, PFILL_EDGE_LIST ActiveEdges, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode)
POLYGONFILL_FillScanLineWinding(
PDC dc,
int ScanLine,
FILL_EDGE* ActiveHead,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode )
{
RECTL BoundRect;
int XPrevIntercept,XIntercept,ret;
PPFILL_EDGE pThis = ActiveEdges;
FILL_EDGE *pLeft, *pRight;
int winding = 0;
XPrevIntercept = pThis->XIntercept;
winding += pThis->YDirection;
pThis = pThis->pNext;
if ( !ActiveHead )
return;
while (NULL != pThis)
pLeft = ActiveHead;
winding = pLeft->YDirection;
pRight = pLeft->pNext;
ASSERT(pRight);
while ( NULL != pRight )
{
XIntercept = pThis->XIntercept + 1;
if ( winding && XIntercept > XPrevIntercept )
int x1 = pLeft->XIntercept[1];
int x2 = pRight->XIntercept[0]+1;
if ( winding && x2 > x1 )
{
RECTL BoundRect;
BoundRect.top = ScanLine;
BoundRect.bottom = ScanLine + 1;
BoundRect.left = XPrevIntercept;
BoundRect.right = XIntercept;
BoundRect.left = x1;
BoundRect.right = x2;
DPRINT ("Fill Line (%d, %d) to (%d, %d)\n",XPrevIntercept, ScanLine, XIntercept, ScanLine);
ret = IntEngLineTo( SurfObj,
DPRINT("Fill Line (%d, %d) to (%d, %d)\n",x1, ScanLine, x2, ScanLine);
IntEngLineTo( SurfObj,
dc->CombinedClip,
BrushObj,
XPrevIntercept,
ScanLine,
XIntercept,
x1,
ScanLine,
x2,
ScanLine,
&BoundRect, // Bounding rectangle
RopMode); // MIX
}
XPrevIntercept = XIntercept - 1;
winding += pThis->YDirection;
pThis = pThis->pNext;
pLeft = pRight;
pRight = pLeft->pNext;
winding += pLeft->YDirection;
}
}
//ALTERNATE Selects alternate mode (fills the area between odd-numbered and even-numbered
//polygon sides on each scan line).
//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.
BOOL
STDCALL
FillPolygon_ALTERNATE(PDC dc, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj, MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect)
{
PFILL_EDGE_LIST list = 0;
PFILL_EDGE_LIST ActiveEdges = 0;
int ScanLine;
DPRINT("FillPolygon_ALTERNATE\n");
/* Create Edge List. */
list = POLYGONFILL_MakeEdgeList(Points, Count);
/* DEBUG_PRINT_EDGELIST(list); */
if (NULL == list)
return FALSE;
/* For each Scanline from BoundRect.bottom to BoundRect.top,
* determine line segments to draw
*/
for ( ScanLine = BoundRect.top + 1; ScanLine < BoundRect.bottom; ++ScanLine )
{
POLYGONFILL_UpdateActiveEdges(ScanLine, &list, &ActiveEdges);
/* DEBUG_PRINT_EDGELIST(ActiveEdges); */
POLYGONFILL_FillScanLineAlternate(dc, ScanLine, ActiveEdges, SurfObj, BrushObj, RopMode);
}
/* Free Edge List. If any are left. */
POLYGONFILL_DestroyEdgeList(list);
return TRUE;
}
//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.
//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_WINDING(PDC dc, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj,MIX RopMode, CONST PPOINT Points, int Count, RECTL BoundRect)
FillPolygon(
PDC dc,
SURFOBJ *SurfObj,
PBRUSHOBJ BrushObj,
MIX RopMode,
CONST PPOINT Points,
int Count,
RECTL BoundRect )
{
PFILL_EDGE_LIST list = 0;
PFILL_EDGE_LIST ActiveEdges = 0;
FILL_EDGE_LIST *list = 0;
FILL_EDGE *ActiveHead = 0;
int ScanLine;
DPRINT("FillPolygon_ALTERNATE\n");
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);
@ -658,14 +550,19 @@ FillPolygon_WINDING(PDC dc, SURFOBJ *SurfObj, PBRUSHOBJ BrushObj,MIX RopMode, CO
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 + 1; ScanLine < BoundRect.bottom; ++ScanLine )
{
POLYGONFILL_UpdateActiveEdges(ScanLine, &list, &ActiveEdges);
/* DEBUG_PRINT_EDGELIST(ActiveEdges); */
POLYGONFILL_FillScanLineWinding(dc, ScanLine, ActiveEdges, SurfObj, BrushObj, RopMode);
POLYGONFILL_BuildActiveList(ScanLine, list, &ActiveHead);
//DEBUG_PRINT_ACTIVE_EDGELIST(ActiveHead);
FillScanLine ( dc, ScanLine, ActiveHead, SurfObj, BrushObj, RopMode );
}
/* Free Edge List. If any are left. */