gdi32: Build the region object directly instead of making a copy in CreatePolyPolygonRgn.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2018-04-04 15:12:55 +02:00
parent 97f996744e
commit 99b64f7432
1 changed files with 74 additions and 95 deletions

View File

@ -2537,16 +2537,24 @@ static void REGION_computeWAET( struct list *AET, struct list *WETE )
}
/***********************************************************************
* REGION_InsertionSort
*
* Just a simple insertion sort to sort the Active Edge Table.
* next_scanline
*
* Update the Active Edge Table for the next scan line and sort it again.
*/
static BOOL REGION_InsertionSort( struct list *AET )
static inline BOOL next_scanline( struct list *AET, int y )
{
struct edge_table_entry *active, *next, *insert;
BOOL changed = FALSE;
LIST_FOR_EACH_ENTRY_SAFE( active, next, AET, struct edge_table_entry, entry )
{
if (active->ymax == y) /* leaving this edge */
{
list_remove( &active->entry );
changed = TRUE;
}
else bres_incr_polygon( &active->bres );
}
LIST_FOR_EACH_ENTRY_SAFE( active, next, AET, struct edge_table_entry, entry )
{
LIST_FOR_EACH_ENTRY( insert, AET, struct edge_table_entry, entry )
@ -2580,59 +2588,6 @@ static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
}
/***********************************************************************
* REGION_PtsToRegion
*
* Create an array of rectangles from a list of points.
*/
static WINEREGION *REGION_PtsToRegion( const POINT *pts, unsigned int size )
{
int cur_band = 0, prev_band = 0;
RECT *extents;
WINEREGION *reg;
if (!(reg = alloc_region( size / 2 ))) return NULL;
extents = &reg->extents;
extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
/* the loop uses 2 points per iteration */
for (; size > 0; pts += 2, size -= 2)
{
if (pts[0].x == pts[1].x)
continue;
if (reg->numRects && pts[0].y != reg->rects[cur_band].top)
{
prev_band = REGION_Coalesce( reg, prev_band, cur_band );
cur_band = reg->numRects;
}
add_rect( reg, pts[0].x, pts[0].y, pts[1].x, pts[1].y + 1 );
if (pts[0].x < extents->left)
extents->left = pts[0].x;
if (pts[1].x > extents->right)
extents->right = pts[1].x;
}
if (reg->numRects) {
REGION_Coalesce( reg, prev_band, cur_band );
extents->top = reg->rects[0].top;
extents->bottom = reg->rects[reg->numRects-1].bottom;
} else {
extents->left = 0;
extents->top = 0;
extents->right = 0;
extents->bottom = 0;
}
REGION_compact( reg );
return reg;
}
/* Number of points in the first point buffer. Must be an even number. */
#define NUMPTSTOBUFFER 200
/***********************************************************************
* CreatePolyPolygonRgn (GDI32.@)
*/
@ -2640,7 +2595,7 @@ HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
INT nbpolygons, INT mode)
{
HRGN hrgn = 0;
WINEREGION *obj;
WINEREGION *obj = NULL;
INT y; /* current scanline */
struct list WETE, *pWETE; /* Winding Edge Table */
ScanLineList *pSLL; /* current scanLineList */
@ -2648,11 +2603,10 @@ HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
struct list AET; /* header for AET */
EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
ScanLineListBlock SLLBlock; /* header for scanlinelist */
BOOL fixWAET = FALSE;
POINT buffer[NUMPTSTOBUFFER], *points = buffer;
struct edge_table_entry *active, *next;
unsigned int nb_points, pos = 0;
INT poly, total;
struct edge_table_entry *active;
unsigned int nb_points;
int cur_band = 0, prev_band = 0;
INT i, poly, total, first = 1;
TRACE("%p, count %d, polygons %d, mode %d\n", Pts, *Count, nbpolygons, mode);
@ -2677,11 +2631,9 @@ HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
return 0;
if (!(nb_points = REGION_CreateEdgeTable(Count, nbpolygons, Pts, &ET, pETEs, &SLLBlock))) goto done;
if (nb_points > NUMPTSTOBUFFER)
{
if (nb_points > INT_MAX / sizeof(*points)) goto done;
if (!(points = HeapAlloc( GetProcessHeap(), 0, nb_points * sizeof(*points) ))) goto done;
}
if (!(obj = alloc_region( nb_points / 2 )))
goto done;
list_init( &AET );
pSLL = ET.scanlines.next;
if (mode != WINDING) {
@ -2698,18 +2650,28 @@ HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
pSLL = pSLL->next;
}
LIST_FOR_EACH_ENTRY_SAFE( active, next, &AET, struct edge_table_entry, entry )
LIST_FOR_EACH_ENTRY( active, &AET, struct edge_table_entry, entry )
{
points[pos].x = active->bres.minor_axis;
points[pos].y = y;
pos++;
if (active->ymax == y) /* leaving this edge */
list_remove( &active->entry );
else
bres_incr_polygon( &active->bres );
if (first)
{
obj->rects[obj->numRects].left = active->bres.minor_axis;
obj->rects[obj->numRects].top = y;
}
else if (obj->rects[obj->numRects].left != active->bres.minor_axis)
{
obj->rects[obj->numRects].right = active->bres.minor_axis;
obj->rects[obj->numRects].bottom = y + 1;
obj->numRects++;
}
first = !first;
}
next_scanline( &AET, y );
if (obj->numRects)
{
prev_band = REGION_Coalesce( obj, prev_band, cur_band );
cur_band = obj->numRects;
}
REGION_InsertionSort(&AET);
}
}
else {
@ -2731,47 +2693,64 @@ HRGN WINAPI CreatePolyPolygonRgn(const POINT *Pts, const INT *Count,
/*
* for each active edge
*/
LIST_FOR_EACH_ENTRY_SAFE( active, next, &AET, struct edge_table_entry, entry )
LIST_FOR_EACH_ENTRY( active, &AET, struct edge_table_entry, entry )
{
/*
* add to the buffer only those edges that
* are in the Winding active edge table.
*/
if (pWETE == &active->winding_entry) {
points[pos].x = active->bres.minor_axis;
points[pos].y = y;
pos++;
if (first)
{
obj->rects[obj->numRects].left = active->bres.minor_axis;
obj->rects[obj->numRects].top = y;
}
else if (obj->rects[obj->numRects].left != active->bres.minor_axis)
{
obj->rects[obj->numRects].right = active->bres.minor_axis;
obj->rects[obj->numRects].bottom = y + 1;
obj->numRects++;
}
first = !first;
pWETE = list_next( &WETE, pWETE );
}
if (active->ymax == y) /* leaving this edge */
{
list_remove( &active->entry );
fixWAET = TRUE;
}
else
bres_incr_polygon( &active->bres );
}
/*
* recompute the winding active edge table if
* we just resorted or have exited an edge.
*/
if (REGION_InsertionSort(&AET) || fixWAET) {
REGION_computeWAET( &AET, &WETE );
fixWAET = FALSE;
if (next_scanline( &AET, y )) REGION_computeWAET( &AET, &WETE );
if (obj->numRects)
{
prev_band = REGION_Coalesce( obj, prev_band, cur_band );
cur_band = obj->numRects;
}
}
}
assert( pos == nb_points );
assert( obj->numRects <= nb_points / 2 );
if (obj->numRects)
{
obj->extents.left = INT_MAX;
obj->extents.right = INT_MIN;
obj->extents.top = obj->rects[0].top;
obj->extents.bottom = obj->rects[obj->numRects-1].bottom;
for (i = 0; i < obj->numRects; i++)
{
obj->extents.left = min( obj->extents.left, obj->rects[i].left );
obj->extents.right = max( obj->extents.right, obj->rects[i].right );
}
}
REGION_compact( obj );
if (!(obj = REGION_PtsToRegion( points, pos ))) goto done;
if (!(hrgn = alloc_gdi_handle( obj, OBJ_REGION, &region_funcs )))
free_region( obj );
done:
REGION_FreeStorage(SLLBlock.next);
if (points != buffer) HeapFree( GetProcessHeap(), 0, points );
HeapFree( GetProcessHeap(), 0, pETEs );
return hrgn;
}