From e765178153e1ca5d7478bc3e5e9ad5ece990ad81 Mon Sep 17 00:00:00 2001 From: Thomas Bluemel Date: Tue, 18 May 2004 15:25:25 +0000 Subject: [PATCH] ported CreatePolyPolygonRgn() and CreatePolygonRgn() from wine svn path=/trunk/; revision=9438 --- reactos/subsys/win32k/objects/region.c | 1250 ++++++++++++++++++++---- 1 file changed, 1084 insertions(+), 166 deletions(-) diff --git a/reactos/subsys/win32k/objects/region.c b/reactos/subsys/win32k/objects/region.c index dd4b5bb9ac7..303c5d77483 100644 --- a/reactos/subsys/win32k/objects/region.c +++ b/reactos/subsys/win32k/objects/region.c @@ -16,7 +16,104 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: region.c,v 1.55 2004/05/18 14:06:42 weiden Exp $ */ + +/* + * GDI region objects. Shamelessly ripped out from the X11 distribution + * Thanks for the nice licence. + * + * Copyright 1993, 1994, 1995 Alexandre Julliard + * Modifications and additions: Copyright 1998 Huw Davies + * 1999 Alex Korobka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/************************************************************************ + +Copyright (c) 1987, 1988 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* + * The functions in this file implement the Region abstraction, similar to one + * used in the X11 sample server. A Region is simply an area, as the name + * implies, and is implemented as a "y-x-banded" array of rectangles. To + * explain: Each Region is made up of a certain number of rectangles sorted + * by y coordinate first, and then by x coordinate. + * + * Furthermore, the rectangles are banded such that every rectangle with a + * given upper-left y coordinate (y1) will have the same lower-right y + * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it + * will span the entire vertical distance of the band. This means that some + * areas that could be merged into a taller rectangle will be represented as + * several shorter rectangles to account for shorter rectangles to its left + * or right but within its "vertical scope". + * + * An added constraint on the rectangles is that they must cover as much + * horizontal area as possible. E.g. no two rectangles in a band are allowed + * to touch. + * + * Whenever possible, bands will be merged together to cover a greater vertical + * distance (and thus reduce the number of rectangles). Two bands can be merged + * only if the bottom of one touches the top of the other and they have + * rectangles in the same places (of the same width, of course). This maintains + * the y-x-banding that's so nice to have... + */ + +/* $Id: region.c,v 1.56 2004/05/18 15:25:25 weiden Exp $ */ #include #include @@ -50,6 +147,247 @@ IntEngPaint(IN SURFOBJ *Surface,IN CLIPOBJ *ClipRegion,IN BRUSHOBJ *Brush,IN POI (r1)->bottom > (r2)->top && \ (r1)->top < (r2)->bottom) +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + INT minor_axis; /* minor axis */ + INT d; /* decision variable */ + INT m, m1; /* slope and slope+1 */ + INT incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2) + + + +/* + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counter-clockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* + * for the winding number rule + */ +#define CLOCKWISE 1 +#define COUNTERCLOCKWISE -1 + +typedef struct _EdgeTableEntry { + INT ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + INT scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + INT ymax; /* ymax for the polygon */ + INT ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + +/************************************************************************** + * + * Poly Regions + * + *************************************************************************/ + +#define LARGE_COORDINATE 0x7fffffff /* FIXME */ +#define SMALL_COORDINATE 0x80000000 + + + /* * Check to see if there is enough memory in the present region. */ @@ -1692,171 +2030,6 @@ NtGdiCreateEllipticRgnIndirect(CONST PRECT Rect) SafeRect.right - SafeRect.left, SafeRect.bottom - SafeRect.top); } -HRGN FASTCALL -IntCreatePolyPolgonRgn(PPOINT pt, - PINT PolyCounts, - INT Count, - INT PolyFillMode) -{ - return (HRGN)0; -} - -HRGN -STDCALL -NtGdiCreatePolygonRgn(CONST PPOINT pt, - INT Count, - INT PolyFillMode) -{ - POINT *SafePoints; - NTSTATUS Status; - HRGN hRgn; - - - if (pt == NULL || Count == 0 || - (PolyFillMode != WINDING && PolyFillMode != ALTERNATE)) - { - /* Windows doesn't set a last error here */ - return (HRGN)0; - } - - if (Count == 1) - { - /* can't create a region with only one point! */ - SetLastWin32Error(ERROR_INVALID_PARAMETER); - return (HRGN)0; - } - - if (Count == 2) - { - /* Windows creates an empty region! */ - ROSRGNDATA *rgn; - - if(!(hRgn = RGNDATA_AllocRgn(1))) - { - return (HRGN)0; - } - if(!(rgn = RGNDATA_LockRgn(hRgn))) - { - NtGdiDeleteObject(hRgn); - return (HRGN)0; - } - - EMPTY_REGION(rgn); - - RGNDATA_UnlockRgn(hRgn); - return hRgn; - } - - if (!(SafePoints = ExAllocatePool(PagedPool, Count * sizeof(POINT)))) - { - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return (HRGN)0; - } - - Status = MmCopyFromCaller(SafePoints, pt, Count * sizeof(POINT)); - if (!NT_SUCCESS(Status)) - { - ExFreePool(SafePoints); - SetLastNtError(Status); - return (HRGN)0; - } - - hRgn = IntCreatePolyPolgonRgn(SafePoints, &Count, 1, PolyFillMode); - - ExFreePool(SafePoints); - return hRgn; -} - -HRGN -STDCALL -NtGdiCreatePolyPolygonRgn(CONST PPOINT pt, - CONST PINT PolyCounts, - INT Count, - INT PolyFillMode) -{ - POINT *Safept; - INT *SafePolyCounts; - INT nPoints, nEmpty, nInvalid, i; - HRGN hRgn; - NTSTATUS Status; - - if (pt == NULL || PolyCounts == NULL || Count == 0 || - (PolyFillMode != WINDING && PolyFillMode != ALTERNATE)) - { - /* Windows doesn't set a last error here */ - return (HRGN)0; - } - - if (!(SafePolyCounts = ExAllocatePool(PagedPool, Count * sizeof(INT)))) - { - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return (HRGN)0; - } - - Status = MmCopyFromCaller(SafePolyCounts, PolyCounts, Count * sizeof(INT)); - if (!NT_SUCCESS(Status)) - { - ExFreePool(SafePolyCounts); - SetLastNtError(Status); - return (HRGN)0; - } - - /* validate poligons */ - nPoints = 0; - nEmpty = 0; - nInvalid = 0; - for (i = 0; i < Count; i++) - { - if (SafePolyCounts[i] == 0) - { - nEmpty++; - } - if (SafePolyCounts[i] == 1) - { - nInvalid++; - } - nPoints += SafePolyCounts[i]; - } - - if (nEmpty == Count) - { - /* if all polygon counts are zero, return without setting a last error code. */ - ExFreePool(SafePolyCounts); - return (HRGN)0; - } - if (nInvalid != 0) - { - /* if at least one poly count is 1, fail */ - ExFreePool(SafePolyCounts); - SetLastWin32Error(ERROR_INVALID_PARAMETER); - return (HRGN)0; - } - - /* copy points */ - if (!(Safept = ExAllocatePool(PagedPool, nPoints * sizeof(POINT)))) - { - ExFreePool(SafePolyCounts); - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return (HRGN)0; - } - - Status = MmCopyFromCaller(Safept, pt, nPoints * sizeof(POINT)); - if (!NT_SUCCESS(Status)) - { - ExFreePool(Safept); - ExFreePool(SafePolyCounts); - SetLastNtError(Status); - return (HRGN)0; - } - - /* now we're ready to calculate the region safely */ - hRgn = IntCreatePolyPolgonRgn(Safept, SafePolyCounts, Count, PolyFillMode); - - ExFreePool(Safept); - ExFreePool(SafePolyCounts); - return hRgn; -} - HRGN STDCALL NtGdiCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect) { @@ -2463,4 +2636,749 @@ DWORD STDCALL NtGdiGetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata) RGNDATA_UnlockRgn( hrgn ); return size + sizeof(RGNDATAHEADER); } + + +/*********************************************************************** + * REGION_InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +static void REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, + INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock) + +{ + EdgeTableEntry *start, *prev; + ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) + { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) + { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = ExAllocatePoolWithTag( PagedPool, sizeof(ScanLineListBlock), TAG_REGION); + if(!tmpSLLBlock) + { + DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n"); + /* FIXME - free resources? */ + return; + } + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = (ScanLineListBlock *)NULL; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = (EdgeTableEntry *)NULL; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = (EdgeTableEntry *)NULL; + start = pSLL->edgelist; + while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) + { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; +} + +/*********************************************************************** + * REGION_loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ +static void REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs) +{ + EdgeTableEntry *pPrevAET; + EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) + { + while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) + { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/*********************************************************************** + * REGION_computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +static void REGION_computeWAET(EdgeTableEntry *AET) +{ + register EdgeTableEntry *pWETE; + register int inside = 1; + register int isInside = 0; + + AET->nextWETE = (EdgeTableEntry *)NULL; + pWETE = AET; + AET = AET->next; + while (AET) + { + if (AET->ClockWise) + isInside++; + else + isInside--; + + if ((!inside && !isInside) || + ( inside && isInside)) + { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = (EdgeTableEntry *)NULL; +} + +/*********************************************************************** + * REGION_InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ +static BOOL REGION_InsertionSort(EdgeTableEntry *AET) +{ + EdgeTableEntry *pETEchase; + EdgeTableEntry *pETEinsert; + EdgeTableEntry *pETEchaseBackTMP; + BOOL changed = FALSE; + + AET = AET->next; + while (AET) + { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) + { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = TRUE; + } + } + return changed; +} + +/*********************************************************************** + * REGION_FreeStorage + * + * Clean up our act. + */ +static void REGION_FreeStorage(ScanLineListBlock *pSLLBlock) +{ + ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) + { + tmpSLLBlock = pSLLBlock->next; + ExFreePool( pSLLBlock ); + pSLLBlock = tmpSLLBlock; + } +} + + +/*********************************************************************** + * REGION_PtsToRegion + * + * Create an array of rectangles from a list of points. + */ +static int REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock, + POINTBLOCK *FirstPtBlock, ROSRGNDATA *reg) +{ + RECT *rects; + POINT *pts; + POINTBLOCK *CurPtBlock; + int i; + RECT *extents, *temp; + INT numRects; + + extents = ®->rdh.rcBound; + + numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1; + + if(!(temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION))) + { + return 0; + } + if(reg->Buffer != NULL) + { + RtlCopyMemory(temp, reg->Buffer, reg->rdh.nCount * sizeof(RECT)); + if(reg->Buffer != ®->BuiltInRect) + ExFreePool(reg->Buffer); + } + reg->Buffer = temp; + + reg->rdh.nCount = numRects; + CurPtBlock = FirstPtBlock; + rects = reg->Buffer - 1; + numRects = 0; + extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE; + + for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) { + /* the loop uses 2 points per iteration */ + i = NUMPTSTOBUFFER >> 1; + if (!numFullPtBlocks) + i = iCurPtBlock >> 1; + for (pts = CurPtBlock->pts; i--; pts += 2) { + if (pts->x == pts[1].x) + continue; + if (numRects && pts->x == rects->left && pts->y == rects->bottom && + pts[1].x == rects->right && + (numRects == 1 || rects[-1].top != rects->top) && + (i && pts[2].y > pts[1].y)) { + rects->bottom = pts[1].y + 1; + continue; + } + numRects++; + rects++; + rects->left = pts->x; rects->top = pts->y; + rects->right = pts[1].x; rects->bottom = pts[1].y + 1; + if (rects->left < extents->left) + extents->left = rects->left; + if (rects->right > extents->right) + extents->right = rects->right; + } + CurPtBlock = CurPtBlock->next; + } + + if (numRects) { + extents->top = reg->Buffer->top; + extents->bottom = rects->bottom; + } else { + extents->left = 0; + extents->top = 0; + extents->right = 0; + extents->bottom = 0; + } + reg->rdh.nCount = numRects; + + return(TRUE); +} + +/*********************************************************************** + * REGION_CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ +static void REGION_CreateETandAET(const INT *Count, INT nbpolygons, + const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET, + EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock) +{ + const POINT *top, *bottom; + const POINT *PrevPt, *CurrPt, *EndPt; + INT poly, count; + int iSLLBlock = 0; + int dy; + + + /* + * initialize the Active Edge Table + */ + AET->next = (EdgeTableEntry *)NULL; + AET->back = (EdgeTableEntry *)NULL; + AET->nextWETE = (EdgeTableEntry *)NULL; + AET->bres.minor_axis = SMALL_COORDINATE; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = (ScanLineList *)NULL; + ET->ymax = SMALL_COORDINATE; + ET->ymin = LARGE_COORDINATE; + pSLLBlock->next = (ScanLineListBlock *)NULL; + + EndPt = pts - 1; + for(poly = 0; poly < nbpolygons; poly++) + { + count = Count[poly]; + EndPt += count; + if(count < 2) + continue; + + PrevPt = EndPt; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) + { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y > CurrPt->y) + { + bottom = PrevPt, top = CurrPt; + pETEs->ClockWise = 0; + } + else + { + bottom = CurrPt, top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y != top->y) + { + pETEs->ymax = bottom->y-1; + /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y - top->y; + BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres); + + REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, + &iSLLBlock); + + if (PrevPt->y > ET->ymax) + ET->ymax = PrevPt->y; + if (PrevPt->y < ET->ymin) + ET->ymin = PrevPt->y; + pETEs++; + } + + PrevPt = CurrPt; + } + } +} + +HRGN FASTCALL +IntCreatePolyPolgonRgn(POINT *Pts, + INT *Count, + INT nbpolygons, + INT mode) +{ + HRGN hrgn; + ROSRGNDATA *region; + EdgeTableEntry *pAET; /* Active Edge Table */ + INT y; /* current scanline */ + int iPts = 0; /* number of pts in buffer */ + EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/ + ScanLineList *pSLL; /* current scanLineList */ + POINT *pts; /* output buffer */ + EdgeTableEntry *pPrevAET; /* ptr to previous AET */ + EdgeTable ET; /* header node for ET */ + EdgeTableEntry AET; /* header node for AET */ + EdgeTableEntry *pETEs; /* EdgeTableEntries pool */ + ScanLineListBlock SLLBlock; /* header for scanlinelist */ + int fixWAET = FALSE; + POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */ + POINTBLOCK *tmpPtBlock; + int numFullPtBlocks = 0; + INT poly, total; + + if(!(hrgn = RGNDATA_AllocRgn(nbpolygons))) + return 0; + if(!(region = RGNDATA_LockRgn(hrgn))) + { + NtGdiDeleteObject(hrgn); + return 0; + } + + /* special case a rectangle */ + + if (((nbpolygons == 1) && ((*Count == 4) || + ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) && + (((Pts[0].y == Pts[1].y) && + (Pts[1].x == Pts[2].x) && + (Pts[2].y == Pts[3].y) && + (Pts[3].x == Pts[0].x)) || + ((Pts[0].x == Pts[1].x) && + (Pts[1].y == Pts[2].y) && + (Pts[2].x == Pts[3].x) && + (Pts[3].y == Pts[0].y)))) + { + RGNDATA_UnlockRgn( hrgn ); + NtGdiSetRectRgn( hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y), + max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y) ); + return hrgn; + } + + for(poly = total = 0; poly < nbpolygons; poly++) + total += Count[poly]; + if (! (pETEs = ExAllocatePoolWithTag( PagedPool, sizeof(EdgeTableEntry) * total, TAG_REGION ))) + { + NtGdiDeleteObject( hrgn ); + return 0; + } + pts = FirstPtBlock.pts; + REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock); + pSLL = ET.scanlines.next; + curPtBlock = &FirstPtBlock; + + if (mode != WINDING) { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL != NULL && y == pSLL->scanline) { + REGION_loadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) { + pts->x = pAET->bres.minor_axis, pts->y = y; + pts++, iPts++; + + /* + * send out the buffer + */ + if (iPts == NUMPTSTOBUFFER) { + tmpPtBlock = ExAllocatePoolWithTag( PagedPool, sizeof(POINTBLOCK), TAG_REGION); + if(!tmpPtBlock) { + DPRINT1("Can't alloc tPB\n"); + /* FIXME - free memory */ + return 0; + } + curPtBlock->next = tmpPtBlock; + curPtBlock = tmpPtBlock; + pts = curPtBlock->pts; + numFullPtBlocks++; + iPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y); + } + REGION_InsertionSort(&AET); + } + } + else { + /* + * for each scanline + */ + for (y = ET.ymin; y < ET.ymax; y++) { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL != NULL && y == pSLL->scanline) { + REGION_loadAET(&AET, pSLL->edgelist); + REGION_computeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) { + /* + * add to the buffer only those edges that + * are in the Winding active edge table. + */ + if (pWETE == pAET) { + pts->x = pAET->bres.minor_axis, pts->y = y; + pts++, iPts++; + + /* + * send out the buffer + */ + if (iPts == NUMPTSTOBUFFER) { + tmpPtBlock = ExAllocatePoolWithTag( PagedPool, + sizeof(POINTBLOCK), TAG_REGION ); + if(!tmpPtBlock) { + DPRINT1("Can't alloc tPB\n"); + /* FIXME - free resources */ + NtGdiDeleteObject( hrgn ); + return 0; + } + curPtBlock->next = tmpPtBlock; + curPtBlock = tmpPtBlock; + pts = curPtBlock->pts; + numFullPtBlocks++; iPts = 0; + } + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET); + } + + /* + * recompute the winding active edge table if + * we just resorted or have exited an edge. + */ + if (REGION_InsertionSort(&AET) || fixWAET) { + REGION_computeWAET(&AET); + fixWAET = FALSE; + } + } + } + REGION_FreeStorage(SLLBlock.next); + REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region); + + for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) { + tmpPtBlock = curPtBlock->next; + ExFreePool( curPtBlock ); + curPtBlock = tmpPtBlock; + } + ExFreePool( pETEs ); + RGNDATA_UnlockRgn( hrgn ); + return hrgn; +} + +HRGN +STDCALL +NtGdiCreatePolygonRgn(CONST PPOINT pt, + INT Count, + INT PolyFillMode) +{ + POINT *SafePoints; + NTSTATUS Status; + HRGN hRgn; + + + if (pt == NULL || Count == 0 || + (PolyFillMode != WINDING && PolyFillMode != ALTERNATE)) + { + /* Windows doesn't set a last error here */ + return (HRGN)0; + } + + if (Count == 1) + { + /* can't create a region with only one point! */ + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return (HRGN)0; + } + + if (Count == 2) + { + /* Windows creates an empty region! */ + ROSRGNDATA *rgn; + + if(!(hRgn = RGNDATA_AllocRgn(1))) + { + return (HRGN)0; + } + if(!(rgn = RGNDATA_LockRgn(hRgn))) + { + NtGdiDeleteObject(hRgn); + return (HRGN)0; + } + + EMPTY_REGION(rgn); + + RGNDATA_UnlockRgn(hRgn); + return hRgn; + } + + if (!(SafePoints = ExAllocatePoolWithTag(PagedPool, Count * sizeof(POINT), TAG_REGION))) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return (HRGN)0; + } + + Status = MmCopyFromCaller(SafePoints, pt, Count * sizeof(POINT)); + if (!NT_SUCCESS(Status)) + { + ExFreePool(SafePoints); + SetLastNtError(Status); + return (HRGN)0; + } + + hRgn = IntCreatePolyPolgonRgn(SafePoints, &Count, 1, PolyFillMode); + + ExFreePool(SafePoints); + return hRgn; +} + +HRGN +STDCALL +NtGdiCreatePolyPolygonRgn(CONST PPOINT pt, + CONST PINT PolyCounts, + INT Count, + INT PolyFillMode) +{ + POINT *Safept; + INT *SafePolyCounts; + INT nPoints, nEmpty, nInvalid, i; + HRGN hRgn; + NTSTATUS Status; + + if (pt == NULL || PolyCounts == NULL || Count == 0 || + (PolyFillMode != WINDING && PolyFillMode != ALTERNATE)) + { + /* Windows doesn't set a last error here */ + return (HRGN)0; + } + + if (!(SafePolyCounts = ExAllocatePoolWithTag(PagedPool, Count * sizeof(INT), TAG_REGION))) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return (HRGN)0; + } + + Status = MmCopyFromCaller(SafePolyCounts, PolyCounts, Count * sizeof(INT)); + if (!NT_SUCCESS(Status)) + { + ExFreePool(SafePolyCounts); + SetLastNtError(Status); + return (HRGN)0; + } + + /* validate poligons */ + nPoints = 0; + nEmpty = 0; + nInvalid = 0; + for (i = 0; i < Count; i++) + { + if (SafePolyCounts[i] == 0) + { + nEmpty++; + } + if (SafePolyCounts[i] == 1) + { + nInvalid++; + } + nPoints += SafePolyCounts[i]; + } + + if (nEmpty == Count) + { + /* if all polygon counts are zero, return without setting a last error code. */ + ExFreePool(SafePolyCounts); + return (HRGN)0; + } + if (nInvalid != 0) + { + /* if at least one poly count is 1, fail */ + ExFreePool(SafePolyCounts); + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return (HRGN)0; + } + + /* copy points */ + if (!(Safept = ExAllocatePoolWithTag(PagedPool, nPoints * sizeof(POINT), TAG_REGION))) + { + ExFreePool(SafePolyCounts); + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return (HRGN)0; + } + + Status = MmCopyFromCaller(Safept, pt, nPoints * sizeof(POINT)); + if (!NT_SUCCESS(Status)) + { + ExFreePool(Safept); + ExFreePool(SafePolyCounts); + SetLastNtError(Status); + return (HRGN)0; + } + + /* now we're ready to calculate the region safely */ + hRgn = IntCreatePolyPolgonRgn(Safept, SafePolyCounts, Count, PolyFillMode); + + ExFreePool(Safept); + ExFreePool(SafePolyCounts); + return hRgn; +} + /* EOF */