Exclusively lock surface bits before reading or writing them
[reactos.git] / reactos / subsys / win32k / objects / region.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /*
21 * GDI region objects. Shamelessly ripped out from the X11 distribution
22 * Thanks for the nice licence.
23 *
24 * Copyright 1993, 1994, 1995 Alexandre Julliard
25 * Modifications and additions: Copyright 1998 Huw Davies
26 * 1999 Alex Korobka
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Lesser General Public
30 * License as published by the Free Software Foundation; either
31 * version 2.1 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Lesser General Public License for more details.
37 *
38 * You should have received a copy of the GNU Lesser General Public
39 * License along with this library; if not, write to the Free Software
40 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
41 */
42
43 /************************************************************************
44
45 Copyright (c) 1987, 1988 X Consortium
46
47 Permission is hereby granted, free of charge, to any person obtaining a copy
48 of this software and associated documentation files (the "Software"), to deal
49 in the Software without restriction, including without limitation the rights
50 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51 copies of the Software, and to permit persons to whom the Software is
52 furnished to do so, subject to the following conditions:
53
54 The above copyright notice and this permission notice shall be included in
55 all copies or substantial portions of the Software.
56
57 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
61 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
62 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
64 Except as contained in this notice, the name of the X Consortium shall not be
65 used in advertising or otherwise to promote the sale, use or other dealings
66 in this Software without prior written authorization from the X Consortium.
67
68
69 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
70
71 All Rights Reserved
72
73 Permission to use, copy, modify, and distribute this software and its
74 documentation for any purpose and without fee is hereby granted,
75 provided that the above copyright notice appear in all copies and that
76 both that copyright notice and this permission notice appear in
77 supporting documentation, and that the name of Digital not be
78 used in advertising or publicity pertaining to distribution of the
79 software without specific, written prior permission.
80
81 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
82 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
83 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
84 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
85 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
86 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
87 SOFTWARE.
88
89 ************************************************************************/
90 /*
91 * The functions in this file implement the Region abstraction, similar to one
92 * used in the X11 sample server. A Region is simply an area, as the name
93 * implies, and is implemented as a "y-x-banded" array of rectangles. To
94 * explain: Each Region is made up of a certain number of rectangles sorted
95 * by y coordinate first, and then by x coordinate.
96 *
97 * Furthermore, the rectangles are banded such that every rectangle with a
98 * given upper-left y coordinate (y1) will have the same lower-right y
99 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
100 * will span the entire vertical distance of the band. This means that some
101 * areas that could be merged into a taller rectangle will be represented as
102 * several shorter rectangles to account for shorter rectangles to its left
103 * or right but within its "vertical scope".
104 *
105 * An added constraint on the rectangles is that they must cover as much
106 * horizontal area as possible. E.g. no two rectangles in a band are allowed
107 * to touch.
108 *
109 * Whenever possible, bands will be merged together to cover a greater vertical
110 * distance (and thus reduce the number of rectangles). Two bands can be merged
111 * only if the bottom of one touches the top of the other and they have
112 * rectangles in the same places (of the same width, of course). This maintains
113 * the y-x-banding that's so nice to have...
114 */
115
116 /* $Id$ */
117 #include <w32k.h>
118 #include <win32k/float.h>
119
120 // Internal Functions
121
122 #if 1
123 #define COPY_RECTS(dest, src, nRects) \
124 do { \
125 PRECT xDest = (dest); \
126 PRECT xSrc = (src); \
127 UINT xRects = (nRects); \
128 while(xRects-- > 0) { \
129 *(xDest++) = *(xSrc++); \
130 } \
131 } while(0)
132 #else
133 #define COPY_RECTS(dest, src, nRects) RtlCopyMemory(dest, src, (nRects) * sizeof(RECT))
134 #endif
135
136 #define EMPTY_REGION(pReg) { \
137 (pReg)->rdh.nCount = 0; \
138 (pReg)->rdh.rcBound.left = (pReg)->rdh.rcBound.top = 0; \
139 (pReg)->rdh.rcBound.right = (pReg)->rdh.rcBound.bottom = 0; \
140 (pReg)->rdh.iType = NULLREGION; \
141 }
142
143 #define REGION_NOT_EMPTY(pReg) pReg->rdh.nCount
144
145 #define INRECT(r, x, y) \
146 ( ( ((r).right > x)) && \
147 ( ((r).left <= x)) && \
148 ( ((r).bottom > y)) && \
149 ( ((r).top <= y)) )
150
151 /* 1 if two RECTs overlap.
152 * 0 if two RECTs do not overlap.
153 */
154 #define EXTENTCHECK(r1, r2) \
155 ((r1)->right > (r2)->left && \
156 (r1)->left < (r2)->right && \
157 (r1)->bottom > (r2)->top && \
158 (r1)->top < (r2)->bottom)
159
160 /*
161 * In scan converting polygons, we want to choose those pixels
162 * which are inside the polygon. Thus, we add .5 to the starting
163 * x coordinate for both left and right edges. Now we choose the
164 * first pixel which is inside the pgon for the left edge and the
165 * first pixel which is outside the pgon for the right edge.
166 * Draw the left pixel, but not the right.
167 *
168 * How to add .5 to the starting x coordinate:
169 * If the edge is moving to the right, then subtract dy from the
170 * error term from the general form of the algorithm.
171 * If the edge is moving to the left, then add dy to the error term.
172 *
173 * The reason for the difference between edges moving to the left
174 * and edges moving to the right is simple: If an edge is moving
175 * to the right, then we want the algorithm to flip immediately.
176 * If it is moving to the left, then we don't want it to flip until
177 * we traverse an entire pixel.
178 */
179 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
180 int dx; /* local storage */ \
181 \
182 /* \
183 * if the edge is horizontal, then it is ignored \
184 * and assumed not to be processed. Otherwise, do this stuff. \
185 */ \
186 if ((dy) != 0) { \
187 xStart = (x1); \
188 dx = (x2) - xStart; \
189 if (dx < 0) { \
190 m = dx / (dy); \
191 m1 = m - 1; \
192 incr1 = -2 * dx + 2 * (dy) * m1; \
193 incr2 = -2 * dx + 2 * (dy) * m; \
194 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
195 } else { \
196 m = dx / (dy); \
197 m1 = m + 1; \
198 incr1 = 2 * dx - 2 * (dy) * m1; \
199 incr2 = 2 * dx - 2 * (dy) * m; \
200 d = -2 * m * (dy) + 2 * dx; \
201 } \
202 } \
203 }
204
205 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
206 if (m1 > 0) { \
207 if (d > 0) { \
208 minval += m1; \
209 d += incr1; \
210 } \
211 else { \
212 minval += m; \
213 d += incr2; \
214 } \
215 } else {\
216 if (d >= 0) { \
217 minval += m1; \
218 d += incr1; \
219 } \
220 else { \
221 minval += m; \
222 d += incr2; \
223 } \
224 } \
225 }
226
227 /*
228 * This structure contains all of the information needed
229 * to run the bresenham algorithm.
230 * The variables may be hardcoded into the declarations
231 * instead of using this structure to make use of
232 * register declarations.
233 */
234 typedef struct {
235 INT minor_axis; /* minor axis */
236 INT d; /* decision variable */
237 INT m, m1; /* slope and slope+1 */
238 INT incr1, incr2; /* error increments */
239 } BRESINFO;
240
241
242 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
243 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
244 bres.m, bres.m1, bres.incr1, bres.incr2)
245
246 #define BRESINCRPGONSTRUCT(bres) \
247 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
248
249
250
251 /*
252 * These are the data structures needed to scan
253 * convert regions. Two different scan conversion
254 * methods are available -- the even-odd method, and
255 * the winding number method.
256 * The even-odd rule states that a point is inside
257 * the polygon if a ray drawn from that point in any
258 * direction will pass through an odd number of
259 * path segments.
260 * By the winding number rule, a point is decided
261 * to be inside the polygon if a ray drawn from that
262 * point in any direction passes through a different
263 * number of clockwise and counter-clockwise path
264 * segments.
265 *
266 * These data structures are adapted somewhat from
267 * the algorithm in (Foley/Van Dam) for scan converting
268 * polygons.
269 * The basic algorithm is to start at the top (smallest y)
270 * of the polygon, stepping down to the bottom of
271 * the polygon by incrementing the y coordinate. We
272 * keep a list of edges which the current scanline crosses,
273 * sorted by x. This list is called the Active Edge Table (AET)
274 * As we change the y-coordinate, we update each entry in
275 * in the active edge table to reflect the edges new xcoord.
276 * This list must be sorted at each scanline in case
277 * two edges intersect.
278 * We also keep a data structure known as the Edge Table (ET),
279 * which keeps track of all the edges which the current
280 * scanline has not yet reached. The ET is basically a
281 * list of ScanLineList structures containing a list of
282 * edges which are entered at a given scanline. There is one
283 * ScanLineList per scanline at which an edge is entered.
284 * When we enter a new edge, we move it from the ET to the AET.
285 *
286 * From the AET, we can implement the even-odd rule as in
287 * (Foley/Van Dam).
288 * The winding number rule is a little trickier. We also
289 * keep the EdgeTableEntries in the AET linked by the
290 * nextWETE (winding EdgeTableEntry) link. This allows
291 * the edges to be linked just as before for updating
292 * purposes, but only uses the edges linked by the nextWETE
293 * link as edges representing spans of the polygon to
294 * drawn (as with the even-odd rule).
295 */
296
297 /*
298 * for the winding number rule
299 */
300 #define CLOCKWISE 1
301 #define COUNTERCLOCKWISE -1
302
303 typedef struct _EdgeTableEntry {
304 INT ymax; /* ycoord at which we exit this edge. */
305 BRESINFO bres; /* Bresenham info to run the edge */
306 struct _EdgeTableEntry *next; /* next in the list */
307 struct _EdgeTableEntry *back; /* for insertion sort */
308 struct _EdgeTableEntry *nextWETE; /* for winding num rule */
309 int ClockWise; /* flag for winding number rule */
310 } EdgeTableEntry;
311
312
313 typedef struct _ScanLineList{
314 INT scanline; /* the scanline represented */
315 EdgeTableEntry *edgelist; /* header node */
316 struct _ScanLineList *next; /* next in the list */
317 } ScanLineList;
318
319
320 typedef struct {
321 INT ymax; /* ymax for the polygon */
322 INT ymin; /* ymin for the polygon */
323 ScanLineList scanlines; /* header node */
324 } EdgeTable;
325
326
327 /*
328 * Here is a struct to help with storage allocation
329 * so we can allocate a big chunk at a time, and then take
330 * pieces from this heap when we need to.
331 */
332 #define SLLSPERBLOCK 25
333
334 typedef struct _ScanLineListBlock {
335 ScanLineList SLLs[SLLSPERBLOCK];
336 struct _ScanLineListBlock *next;
337 } ScanLineListBlock;
338
339
340 /*
341 *
342 * a few macros for the inner loops of the fill code where
343 * performance considerations don't allow a procedure call.
344 *
345 * Evaluate the given edge at the given scanline.
346 * If the edge has expired, then we leave it and fix up
347 * the active edge table; otherwise, we increment the
348 * x value to be ready for the next scanline.
349 * The winding number rule is in effect, so we must notify
350 * the caller when the edge has been removed so he
351 * can reorder the Winding Active Edge Table.
352 */
353 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
354 if (pAET->ymax == y) { /* leaving this edge */ \
355 pPrevAET->next = pAET->next; \
356 pAET = pPrevAET->next; \
357 fixWAET = 1; \
358 if (pAET) \
359 pAET->back = pPrevAET; \
360 } \
361 else { \
362 BRESINCRPGONSTRUCT(pAET->bres); \
363 pPrevAET = pAET; \
364 pAET = pAET->next; \
365 } \
366 }
367
368
369 /*
370 * Evaluate the given edge at the given scanline.
371 * If the edge has expired, then we leave it and fix up
372 * the active edge table; otherwise, we increment the
373 * x value to be ready for the next scanline.
374 * The even-odd rule is in effect.
375 */
376 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
377 if (pAET->ymax == y) { /* leaving this edge */ \
378 pPrevAET->next = pAET->next; \
379 pAET = pPrevAET->next; \
380 if (pAET) \
381 pAET->back = pPrevAET; \
382 } \
383 else { \
384 BRESINCRPGONSTRUCT(pAET->bres); \
385 pPrevAET = pAET; \
386 pAET = pAET->next; \
387 } \
388 }
389
390 /**************************************************************************
391 *
392 * Poly Regions
393 *
394 *************************************************************************/
395
396 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
397 #define SMALL_COORDINATE 0x80000000
398
399 /*
400 * Check to see if there is enough memory in the present region.
401 */
402 static inline int xmemcheck(ROSRGNDATA *reg, PRECT *rect, PRECT *firstrect ) {
403 if ( (reg->rdh.nCount+1)*sizeof( RECT ) >= reg->rdh.nRgnSize ) {
404 PRECT temp;
405 temp = ExAllocatePoolWithTag( PagedPool, (2 * (reg->rdh.nRgnSize)), TAG_REGION);
406
407 if (temp == 0)
408 return 0;
409
410 /* copy the rectangles */
411 COPY_RECTS(temp, *firstrect, reg->rdh.nCount);
412
413 reg->rdh.nRgnSize *= 2;
414 if (*firstrect != &reg->rdh.rcBound)
415 ExFreePool( *firstrect );
416 *firstrect = temp;
417 *rect = (*firstrect)+reg->rdh.nCount;
418 }
419 return 1;
420 }
421
422 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),(LPRECT *)&(firstrect))
423
424 typedef void FASTCALL (*overlapProcp)(PROSRGNDATA, PRECT, PRECT, PRECT, PRECT, INT, INT);
425 typedef void FASTCALL (*nonOverlapProcp)(PROSRGNDATA, PRECT, PRECT, INT, INT);
426
427 // Number of points to buffer before sending them off to scanlines() : Must be an even number
428 #define NUMPTSTOBUFFER 200
429
430 #define RGN_DEFAULT_RECTS 2
431
432 // used to allocate buffers for points and link the buffers together
433
434 typedef struct _POINTBLOCK {
435 POINT pts[NUMPTSTOBUFFER];
436 struct _POINTBLOCK *next;
437 } POINTBLOCK;
438
439 #ifndef NDEBUG
440 /*
441 * This function is left there for debugging purposes.
442 */
443
444 VOID FASTCALL
445 IntDumpRegion(HRGN hRgn)
446 {
447 ROSRGNDATA *Data;
448
449 Data = RGNDATA_LockRgn(hRgn);
450 if (Data == NULL)
451 {
452 DbgPrint("IntDumpRegion called with invalid region!\n");
453 return;
454 }
455
456 DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
457 hRgn,
458 Data->rdh.rcBound.left,
459 Data->rdh.rcBound.top,
460 Data->rdh.rcBound.right,
461 Data->rdh.rcBound.bottom,
462 Data->rdh.iType);
463
464 RGNDATA_UnlockRgn(Data);
465 }
466 #endif /* NDEBUG */
467
468 static BOOL FASTCALL REGION_CopyRegion(PROSRGNDATA dst, PROSRGNDATA src)
469 {
470 if(dst != src) // don't want to copy to itself
471 {
472 if (dst->rdh.nRgnSize < src->rdh.nCount * sizeof(RECT))
473 {
474 PRECT temp;
475
476 temp = ExAllocatePoolWithTag(PagedPool, src->rdh.nCount * sizeof(RECT), TAG_REGION );
477 if( !temp )
478 return FALSE;
479
480 if( dst->Buffer && dst->Buffer != &dst->rdh.rcBound )
481 ExFreePool( dst->Buffer ); //free the old buffer
482 dst->Buffer = temp;
483 dst->rdh.nRgnSize = src->rdh.nCount * sizeof(RECT); //size of region buffer
484 }
485 dst->rdh.nCount = src->rdh.nCount; //number of rectangles present in Buffer
486 dst->rdh.rcBound.left = src->rdh.rcBound.left;
487 dst->rdh.rcBound.top = src->rdh.rcBound.top;
488 dst->rdh.rcBound.right = src->rdh.rcBound.right;
489 dst->rdh.rcBound.bottom = src->rdh.rcBound.bottom;
490 dst->rdh.iType = src->rdh.iType;
491 COPY_RECTS(dst->Buffer, src->Buffer, src->rdh.nCount);
492 }
493 return TRUE;
494 }
495
496 static void FASTCALL REGION_SetExtents (ROSRGNDATA *pReg)
497 {
498 RECT *pRect, *pRectEnd, *pExtents;
499
500 if (pReg->rdh.nCount == 0)
501 {
502 pReg->rdh.rcBound.left = 0;
503 pReg->rdh.rcBound.top = 0;
504 pReg->rdh.rcBound.right = 0;
505 pReg->rdh.rcBound.bottom = 0;
506 pReg->rdh.iType = NULLREGION;
507 return;
508 }
509
510 pExtents = &pReg->rdh.rcBound;
511 pRect = (PRECT)pReg->Buffer;
512 pRectEnd = (PRECT)pReg->Buffer + pReg->rdh.nCount - 1;
513
514 /*
515 * Since pRect is the first rectangle in the region, it must have the
516 * smallest top and since pRectEnd is the last rectangle in the region,
517 * it must have the largest bottom, because of banding. Initialize left and
518 * right from pRect and pRectEnd, resp., as good things to initialize them
519 * to...
520 */
521 pExtents->left = pRect->left;
522 pExtents->top = pRect->top;
523 pExtents->right = pRectEnd->right;
524 pExtents->bottom = pRectEnd->bottom;
525
526 while (pRect <= pRectEnd)
527 {
528 if (pRect->left < pExtents->left)
529 pExtents->left = pRect->left;
530 if (pRect->right > pExtents->right)
531 pExtents->right = pRect->right;
532 pRect++;
533 }
534 pReg->rdh.iType = (1 == pReg->rdh.nCount ? SIMPLEREGION : COMPLEXREGION);
535 }
536
537
538 /***********************************************************************
539 * REGION_CropAndOffsetRegion
540 */
541 static BOOL FASTCALL REGION_CropAndOffsetRegion(const PPOINT off, const PRECT rect, PROSRGNDATA rgnSrc, PROSRGNDATA rgnDst)
542 {
543 if(!rect) // just copy and offset
544 {
545 PRECT xrect;
546 if(rgnDst == rgnSrc)
547 {
548 if(off->x || off->y)
549 xrect = (PRECT)rgnDst->Buffer;
550 else
551 return TRUE;
552 }
553 else{
554 xrect = ExAllocatePoolWithTag(PagedPool, rgnSrc->rdh.nCount * sizeof(RECT), TAG_REGION);
555 if( rgnDst->Buffer && rgnDst->Buffer != &rgnDst->rdh.rcBound )
556 ExFreePool( rgnDst->Buffer ); //free the old buffer. will be assigned to xrect below.
557 }
558
559 if(xrect)
560 {
561 ULONG i;
562
563 if(rgnDst != rgnSrc)
564 {
565 *rgnDst = *rgnSrc;
566 }
567
568 if(off->x || off->y)
569 {
570 for(i = 0; i < rgnDst->rdh.nCount; i++)
571 {
572 xrect[i].left = ((PRECT)rgnSrc->Buffer + i)->left + off->x;
573 xrect[i].right = ((PRECT)rgnSrc->Buffer + i)->right + off->x;
574 xrect[i].top = ((PRECT)rgnSrc->Buffer + i)->top + off->y;
575 xrect[i].bottom = ((PRECT)rgnSrc->Buffer + i)->bottom + off->y;
576 }
577 rgnDst->rdh.rcBound.left += off->x;
578 rgnDst->rdh.rcBound.right += off->x;
579 rgnDst->rdh.rcBound.top += off->y;
580 rgnDst->rdh.rcBound.bottom += off->y;
581 }
582 else
583 {
584 COPY_RECTS(xrect, rgnSrc->Buffer, rgnDst->rdh.nCount);
585 }
586
587 rgnDst->Buffer = xrect;
588 } else
589 return FALSE;
590 }
591 else if ((rect->left >= rect->right) ||
592 (rect->top >= rect->bottom) ||
593 !EXTENTCHECK(rect, &rgnSrc->rdh.rcBound))
594 {
595 goto empty;
596 }
597 else // region box and clipping rect appear to intersect
598 {
599 PRECT lpr, rpr;
600 ULONG i, j, clipa, clipb;
601 INT left = rgnSrc->rdh.rcBound.right + off->x;
602 INT right = rgnSrc->rdh.rcBound.left + off->x;
603
604 for(clipa = 0; ((PRECT)rgnSrc->Buffer + clipa)->bottom <= rect->top; clipa++)
605 //region and rect intersect so we stop before clipa > rgnSrc->rdh.nCount
606 ; // skip bands above the clipping rectangle
607
608 for(clipb = clipa; clipb < rgnSrc->rdh.nCount; clipb++)
609 if(((PRECT)rgnSrc->Buffer + clipb)->top >= rect->bottom)
610 break; // and below it
611
612 // clipa - index of the first rect in the first intersecting band
613 // clipb - index of the last rect in the last intersecting band
614
615 if((rgnDst != rgnSrc) && (rgnDst->rdh.nCount < (i = (clipb - clipa))))
616 {
617 PRECT temp;
618 temp = ExAllocatePoolWithTag( PagedPool, i * sizeof(RECT), TAG_REGION );
619 if(!temp)
620 return FALSE;
621
622 if( rgnDst->Buffer && rgnDst->Buffer != &rgnDst->rdh.rcBound )
623 ExFreePool( rgnDst->Buffer ); //free the old buffer
624 rgnDst->Buffer = temp;
625 rgnDst->rdh.nCount = i;
626 rgnDst->rdh.nRgnSize = i * sizeof(RECT);
627 }
628
629 for(i = clipa, j = 0; i < clipb ; i++)
630 {
631 // i - src index, j - dst index, j is always <= i for obvious reasons
632
633 lpr = (PRECT)rgnSrc->Buffer + i;
634
635 if(lpr->left < rect->right && lpr->right > rect->left)
636 {
637 rpr = (PRECT)rgnDst->Buffer + j;
638
639 rpr->top = lpr->top + off->y;
640 rpr->bottom = lpr->bottom + off->y;
641 rpr->left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
642 rpr->right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
643
644 if(rpr->left < left) left = rpr->left;
645 if(rpr->right > right) right = rpr->right;
646
647 j++;
648 }
649 }
650
651 if(j == 0) goto empty;
652
653 rgnDst->rdh.rcBound.left = left;
654 rgnDst->rdh.rcBound.right = right;
655
656 left = rect->top + off->y;
657 right = rect->bottom + off->y;
658
659 rgnDst->rdh.nCount = j--;
660 for(i = 0; i <= j; i++) // fixup top band
661 if((rgnDst->Buffer + i)->top < left)
662 (rgnDst->Buffer + i)->top = left;
663 else
664 break;
665
666 for(i = j; i >= 0; i--) // fixup bottom band
667 if(((PRECT)rgnDst->Buffer + i)->bottom > right)
668 ((PRECT)rgnDst->Buffer + i)->bottom = right;
669 else
670 break;
671
672 rgnDst->rdh.rcBound.top = ((PRECT)rgnDst->Buffer)->top;
673 rgnDst->rdh.rcBound.bottom = ((PRECT)rgnDst->Buffer + j)->bottom;
674
675 rgnDst->rdh.iType = (j >= 1) ? COMPLEXREGION : SIMPLEREGION;
676 }
677
678 return TRUE;
679
680 empty:
681 if(!rgnDst->Buffer)
682 {
683 rgnDst->Buffer = (PRECT)ExAllocatePoolWithTag(PagedPool, RGN_DEFAULT_RECTS * sizeof(RECT), TAG_REGION);
684 if(rgnDst->Buffer){
685 rgnDst->rdh.nCount = RGN_DEFAULT_RECTS;
686 rgnDst->rdh.nRgnSize = RGN_DEFAULT_RECTS * sizeof(RECT);
687 }
688 else
689 return FALSE;
690 }
691 EMPTY_REGION(rgnDst);
692 return TRUE;
693 }
694
695 /*!
696 * \param
697 * hSrc: Region to crop and offset.
698 * lpRect: Clipping rectangle. Can be NULL (no clipping).
699 * lpPt: Points to offset the cropped region. Can be NULL (no offset).
700 *
701 * hDst: Region to hold the result (a new region is created if it's 0).
702 * Allowed to be the same region as hSrc in which case everything
703 * will be done in place, with no memory reallocations.
704 *
705 * \return hDst if success, 0 otherwise.
706 */
707 HRGN FASTCALL REGION_CropRgn(HRGN hDst, HRGN hSrc, const PRECT lpRect, PPOINT lpPt)
708 {
709 PROSRGNDATA objSrc, rgnDst;
710 HRGN hRet = NULL;
711 POINT pt = { 0, 0 };
712
713 if( !hDst )
714 {
715 if( !( hDst = RGNDATA_AllocRgn(1) ) )
716 {
717 return 0;
718 }
719 }
720
721 rgnDst = RGNDATA_LockRgn(hDst);
722 if(rgnDst == NULL)
723 {
724 return NULL;
725 }
726
727 objSrc = RGNDATA_LockRgn(hSrc);
728 if(objSrc == NULL)
729 {
730 RGNDATA_UnlockRgn(rgnDst);
731 return NULL;
732 }
733 if(!lpPt)
734 lpPt = &pt;
735
736 if(REGION_CropAndOffsetRegion(lpPt, lpRect, objSrc, rgnDst) == FALSE)
737 { // ve failed cleanup and return
738 hRet = NULL;
739 }
740 else{ // ve are fine. unlock the correct pointer and return correct handle
741 hRet = hDst;
742 }
743
744 RGNDATA_UnlockRgn(objSrc);
745 RGNDATA_UnlockRgn(rgnDst);
746
747 return hRet;
748 }
749
750 /*!
751 * Attempt to merge the rects in the current band with those in the
752 * previous one. Used only by REGION_RegionOp.
753 *
754 * Results:
755 * The new index for the previous band.
756 *
757 * \note Side Effects:
758 * If coalescing takes place:
759 * - rectangles in the previous band will have their bottom fields
760 * altered.
761 * - pReg->numRects will be decreased.
762 *
763 */
764 static INT FASTCALL REGION_Coalesce (
765 PROSRGNDATA pReg, /* Region to coalesce */
766 INT prevStart, /* Index of start of previous band */
767 INT curStart /* Index of start of current band */
768 ) {
769 RECT *pPrevRect; /* Current rect in previous band */
770 RECT *pCurRect; /* Current rect in current band */
771 RECT *pRegEnd; /* End of region */
772 INT curNumRects; /* Number of rectangles in current band */
773 INT prevNumRects; /* Number of rectangles in previous band */
774 INT bandtop; /* top coordinate for current band */
775
776 pRegEnd = (PRECT)pReg->Buffer + pReg->rdh.nCount;
777 pPrevRect = (PRECT)pReg->Buffer + prevStart;
778 prevNumRects = curStart - prevStart;
779
780 /*
781 * Figure out how many rectangles are in the current band. Have to do
782 * this because multiple bands could have been added in REGION_RegionOp
783 * at the end when one region has been exhausted.
784 */
785 pCurRect = (PRECT)pReg->Buffer + curStart;
786 bandtop = pCurRect->top;
787 for (curNumRects = 0;
788 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
789 curNumRects++)
790 {
791 pCurRect++;
792 }
793
794 if (pCurRect != pRegEnd)
795 {
796 /*
797 * If more than one band was added, we have to find the start
798 * of the last band added so the next coalescing job can start
799 * at the right place... (given when multiple bands are added,
800 * this may be pointless -- see above).
801 */
802 pRegEnd--;
803 while ((pRegEnd-1)->top == pRegEnd->top)
804 {
805 pRegEnd--;
806 }
807 curStart = pRegEnd - (PRECT)pReg->Buffer;
808 pRegEnd = (PRECT)pReg->Buffer + pReg->rdh.nCount;
809 }
810
811 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
812 pCurRect -= curNumRects;
813 /*
814 * The bands may only be coalesced if the bottom of the previous
815 * matches the top scanline of the current.
816 */
817 if (pPrevRect->bottom == pCurRect->top)
818 {
819 /*
820 * Make sure the bands have rects in the same places. This
821 * assumes that rects have been added in such a way that they
822 * cover the most area possible. I.e. two rects in a band must
823 * have some horizontal space between them.
824 */
825 do
826 {
827 if ((pPrevRect->left != pCurRect->left) ||
828 (pPrevRect->right != pCurRect->right))
829 {
830 /*
831 * The bands don't line up so they can't be coalesced.
832 */
833 return (curStart);
834 }
835 pPrevRect++;
836 pCurRect++;
837 prevNumRects -= 1;
838 } while (prevNumRects != 0);
839
840 pReg->rdh.nCount -= curNumRects;
841 pCurRect -= curNumRects;
842 pPrevRect -= curNumRects;
843
844 /*
845 * The bands may be merged, so set the bottom of each rect
846 * in the previous band to that of the corresponding rect in
847 * the current band.
848 */
849 do
850 {
851 pPrevRect->bottom = pCurRect->bottom;
852 pPrevRect++;
853 pCurRect++;
854 curNumRects -= 1;
855 } while (curNumRects != 0);
856
857 /*
858 * If only one band was added to the region, we have to backup
859 * curStart to the start of the previous band.
860 *
861 * If more than one band was added to the region, copy the
862 * other bands down. The assumption here is that the other bands
863 * came from the same region as the current one and no further
864 * coalescing can be done on them since it's all been done
865 * already... curStart is already in the right place.
866 */
867 if (pCurRect == pRegEnd)
868 {
869 curStart = prevStart;
870 }
871 else
872 {
873 do
874 {
875 *pPrevRect++ = *pCurRect++;
876 } while (pCurRect != pRegEnd);
877 }
878 }
879 }
880 return (curStart);
881 }
882
883 /*!
884 * Apply an operation to two regions. Called by REGION_Union,
885 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
886 *
887 * Results:
888 * None.
889 *
890 * Side Effects:
891 * The new region is overwritten.
892 *
893 *\note The idea behind this function is to view the two regions as sets.
894 * Together they cover a rectangle of area that this function divides
895 * into horizontal bands where points are covered only by one region
896 * or by both. For the first case, the nonOverlapFunc is called with
897 * each the band and the band's upper and lower extents. For the
898 * second, the overlapFunc is called to process the entire band. It
899 * is responsible for clipping the rectangles in the band, though
900 * this function provides the boundaries.
901 * At the end of each band, the new region is coalesced, if possible,
902 * to reduce the number of rectangles in the region.
903 *
904 */
905 static void FASTCALL
906 REGION_RegionOp(
907 ROSRGNDATA *newReg, /* Place to store result */
908 ROSRGNDATA *reg1, /* First region in operation */
909 ROSRGNDATA *reg2, /* 2nd region in operation */
910 overlapProcp overlapFunc, /* Function to call for over-lapping bands */
911 nonOverlapProcp nonOverlap1Func, /* Function to call for non-overlapping bands in region 1 */
912 nonOverlapProcp nonOverlap2Func /* Function to call for non-overlapping bands in region 2 */
913 )
914 {
915 RECT *r1; /* Pointer into first region */
916 RECT *r2; /* Pointer into 2d region */
917 RECT *r1End; /* End of 1st region */
918 RECT *r2End; /* End of 2d region */
919 INT ybot; /* Bottom of intersection */
920 INT ytop; /* Top of intersection */
921 RECT *oldRects; /* Old rects for newReg */
922 ULONG prevBand; /* Index of start of
923 * previous band in newReg */
924 ULONG curBand; /* Index of start of current band in newReg */
925 RECT *r1BandEnd; /* End of current band in r1 */
926 RECT *r2BandEnd; /* End of current band in r2 */
927 ULONG top; /* Top of non-overlapping band */
928 ULONG bot; /* Bottom of non-overlapping band */
929
930 /*
931 * Initialization:
932 * set r1, r2, r1End and r2End appropriately, preserve the important
933 * parts of the destination region until the end in case it's one of
934 * the two source regions, then mark the "new" region empty, allocating
935 * another array of rectangles for it to use.
936 */
937 r1 = (PRECT)reg1->Buffer;
938 r2 = (PRECT)reg2->Buffer;
939 r1End = r1 + reg1->rdh.nCount;
940 r2End = r2 + reg2->rdh.nCount;
941
942
943 /*
944 * newReg may be one of the src regions so we can't empty it. We keep a
945 * note of its rects pointer (so that we can free them later), preserve its
946 * extents and simply set numRects to zero.
947 */
948
949 oldRects = (PRECT)newReg->Buffer;
950 newReg->rdh.nCount = 0;
951
952 /*
953 * Allocate a reasonable number of rectangles for the new region. The idea
954 * is to allocate enough so the individual functions don't need to
955 * reallocate and copy the array, which is time consuming, yet we don't
956 * have to worry about using too much memory. I hope to be able to
957 * nuke the Xrealloc() at the end of this function eventually.
958 */
959 newReg->rdh.nRgnSize = max(reg1->rdh.nCount,reg2->rdh.nCount) * 2 * sizeof(RECT);
960
961 if (! (newReg->Buffer = ExAllocatePoolWithTag( PagedPool, newReg->rdh.nRgnSize, TAG_REGION )))
962 {
963 newReg->rdh.nRgnSize = 0;
964 return;
965 }
966
967 /*
968 * Initialize ybot and ytop.
969 * In the upcoming loop, ybot and ytop serve different functions depending
970 * on whether the band being handled is an overlapping or non-overlapping
971 * band.
972 * In the case of a non-overlapping band (only one of the regions
973 * has points in the band), ybot is the bottom of the most recent
974 * intersection and thus clips the top of the rectangles in that band.
975 * ytop is the top of the next intersection between the two regions and
976 * serves to clip the bottom of the rectangles in the current band.
977 * For an overlapping band (where the two regions intersect), ytop clips
978 * the top of the rectangles of both regions and ybot clips the bottoms.
979 */
980 if (reg1->rdh.rcBound.top < reg2->rdh.rcBound.top)
981 ybot = reg1->rdh.rcBound.top;
982 else
983 ybot = reg2->rdh.rcBound.top;
984
985 /*
986 * prevBand serves to mark the start of the previous band so rectangles
987 * can be coalesced into larger rectangles. qv. miCoalesce, above.
988 * In the beginning, there is no previous band, so prevBand == curBand
989 * (curBand is set later on, of course, but the first band will always
990 * start at index 0). prevBand and curBand must be indices because of
991 * the possible expansion, and resultant moving, of the new region's
992 * array of rectangles.
993 */
994 prevBand = 0;
995
996 do
997 {
998 curBand = newReg->rdh.nCount;
999
1000 /*
1001 * This algorithm proceeds one source-band (as opposed to a
1002 * destination band, which is determined by where the two regions
1003 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1004 * rectangle after the last one in the current band for their
1005 * respective regions.
1006 */
1007 r1BandEnd = r1;
1008 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1009 {
1010 r1BandEnd++;
1011 }
1012
1013 r2BandEnd = r2;
1014 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1015 {
1016 r2BandEnd++;
1017 }
1018
1019 /*
1020 * First handle the band that doesn't intersect, if any.
1021 *
1022 * Note that attention is restricted to one band in the
1023 * non-intersecting region at once, so if a region has n
1024 * bands between the current position and the next place it overlaps
1025 * the other, this entire loop will be passed through n times.
1026 */
1027 if (r1->top < r2->top)
1028 {
1029 top = max(r1->top,ybot);
1030 bot = min(r1->bottom,r2->top);
1031
1032 if ((top != bot) && (nonOverlap1Func != NULL))
1033 {
1034 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1035 }
1036
1037 ytop = r2->top;
1038 }
1039 else if (r2->top < r1->top)
1040 {
1041 top = max(r2->top,ybot);
1042 bot = min(r2->bottom,r1->top);
1043
1044 if ((top != bot) && (nonOverlap2Func != NULL))
1045 {
1046 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1047 }
1048
1049 ytop = r1->top;
1050 }
1051 else
1052 {
1053 ytop = r1->top;
1054 }
1055
1056 /*
1057 * If any rectangles got added to the region, try and coalesce them
1058 * with rectangles from the previous band. Note we could just do
1059 * this test in miCoalesce, but some machines incur a not
1060 * inconsiderable cost for function calls, so...
1061 */
1062 if (newReg->rdh.nCount != curBand)
1063 {
1064 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1065 }
1066
1067 /*
1068 * Now see if we've hit an intersecting band. The two bands only
1069 * intersect if ybot > ytop
1070 */
1071 ybot = min(r1->bottom, r2->bottom);
1072 curBand = newReg->rdh.nCount;
1073 if (ybot > ytop)
1074 {
1075 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1076 }
1077
1078 if (newReg->rdh.nCount != curBand)
1079 {
1080 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1081 }
1082
1083 /*
1084 * If we've finished with a band (bottom == ybot) we skip forward
1085 * in the region to the next band.
1086 */
1087 if (r1->bottom == ybot)
1088 {
1089 r1 = r1BandEnd;
1090 }
1091 if (r2->bottom == ybot)
1092 {
1093 r2 = r2BandEnd;
1094 }
1095 } while ((r1 != r1End) && (r2 != r2End));
1096
1097 /*
1098 * Deal with whichever region still has rectangles left.
1099 */
1100 curBand = newReg->rdh.nCount;
1101 if (r1 != r1End)
1102 {
1103 if (nonOverlap1Func != NULL)
1104 {
1105 do
1106 {
1107 r1BandEnd = r1;
1108 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1109 {
1110 r1BandEnd++;
1111 }
1112 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1113 max(r1->top,ybot), r1->bottom);
1114 r1 = r1BandEnd;
1115 } while (r1 != r1End);
1116 }
1117 }
1118 else if ((r2 != r2End) && (nonOverlap2Func != NULL))
1119 {
1120 do
1121 {
1122 r2BandEnd = r2;
1123 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1124 {
1125 r2BandEnd++;
1126 }
1127 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1128 max(r2->top,ybot), r2->bottom);
1129 r2 = r2BandEnd;
1130 } while (r2 != r2End);
1131 }
1132
1133 if (newReg->rdh.nCount != curBand)
1134 {
1135 (void) REGION_Coalesce (newReg, prevBand, curBand);
1136 }
1137
1138 /*
1139 * A bit of cleanup. To keep regions from growing without bound,
1140 * we shrink the array of rectangles to match the new number of
1141 * rectangles in the region. This never goes to 0, however...
1142 *
1143 * Only do this stuff if the number of rectangles allocated is more than
1144 * twice the number of rectangles in the region (a simple optimization...).
1145 */
1146 if ((2 * newReg->rdh.nCount*sizeof(RECT) < newReg->rdh.nRgnSize && (newReg->rdh.nCount > 2)))
1147 {
1148 if (REGION_NOT_EMPTY(newReg))
1149 {
1150 RECT *prev_rects = (PRECT)newReg->Buffer;
1151 newReg->Buffer = ExAllocatePoolWithTag( PagedPool, newReg->rdh.nCount*sizeof(RECT), TAG_REGION );
1152
1153 if (! newReg->Buffer)
1154 newReg->Buffer = prev_rects;
1155 else{
1156 newReg->rdh.nRgnSize = newReg->rdh.nCount*sizeof(RECT);
1157 COPY_RECTS(newReg->Buffer, prev_rects, newReg->rdh.nCount);
1158 if (prev_rects != &newReg->rdh.rcBound)
1159 ExFreePool( prev_rects );
1160 }
1161 }
1162 else
1163 {
1164 /*
1165 * No point in doing the extra work involved in an Xrealloc if
1166 * the region is empty
1167 */
1168 newReg->rdh.nRgnSize = sizeof(RECT);
1169 if (newReg->Buffer != &newReg->rdh.rcBound)
1170 ExFreePool( newReg->Buffer );
1171 newReg->Buffer = ExAllocatePoolWithTag( PagedPool, sizeof(RECT), TAG_REGION );
1172 ASSERT( newReg->Buffer );
1173 }
1174 }
1175
1176 if( newReg->rdh.nCount == 0 )
1177 newReg->rdh.iType = NULLREGION;
1178 else
1179 newReg->rdh.iType = (newReg->rdh.nCount > 1)? COMPLEXREGION : SIMPLEREGION;
1180
1181 if (oldRects != &newReg->rdh.rcBound)
1182 ExFreePool( oldRects );
1183 return;
1184 }
1185
1186 /***********************************************************************
1187 * Region Intersection
1188 ***********************************************************************/
1189
1190
1191 /*!
1192 * Handle an overlapping band for REGION_Intersect.
1193 *
1194 * Results:
1195 * None.
1196 *
1197 * \note Side Effects:
1198 * Rectangles may be added to the region.
1199 *
1200 */
1201 static void FASTCALL
1202 REGION_IntersectO (
1203 PROSRGNDATA pReg,
1204 PRECT r1,
1205 PRECT r1End,
1206 PRECT r2,
1207 PRECT r2End,
1208 INT top,
1209 INT bottom
1210 )
1211 {
1212 INT left, right;
1213 RECT *pNextRect;
1214
1215 pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1216
1217 while ((r1 != r1End) && (r2 != r2End))
1218 {
1219 left = max(r1->left, r2->left);
1220 right = min(r1->right, r2->right);
1221
1222 /*
1223 * If there's any overlap between the two rectangles, add that
1224 * overlap to the new region.
1225 * There's no need to check for subsumption because the only way
1226 * such a need could arise is if some region has two rectangles
1227 * right next to each other. Since that should never happen...
1228 */
1229 if (left < right)
1230 {
1231 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1232 pNextRect->left = left;
1233 pNextRect->top = top;
1234 pNextRect->right = right;
1235 pNextRect->bottom = bottom;
1236 pReg->rdh.nCount += 1;
1237 pNextRect++;
1238 }
1239
1240 /*
1241 * Need to advance the pointers. Shift the one that extends
1242 * to the right the least, since the other still has a chance to
1243 * overlap with that region's next rectangle, if you see what I mean.
1244 */
1245 if (r1->right < r2->right)
1246 {
1247 r1++;
1248 }
1249 else if (r2->right < r1->right)
1250 {
1251 r2++;
1252 }
1253 else
1254 {
1255 r1++;
1256 r2++;
1257 }
1258 }
1259 return;
1260 }
1261
1262 /***********************************************************************
1263 * REGION_IntersectRegion
1264 */
1265 static void FASTCALL REGION_IntersectRegion(ROSRGNDATA *newReg, ROSRGNDATA *reg1,
1266 ROSRGNDATA *reg2)
1267 {
1268 /* check for trivial reject */
1269 if ( (!(reg1->rdh.nCount)) || (!(reg2->rdh.nCount)) ||
1270 (!EXTENTCHECK(&reg1->rdh.rcBound, &reg2->rdh.rcBound)))
1271 newReg->rdh.nCount = 0;
1272 else
1273 REGION_RegionOp (newReg, reg1, reg2,
1274 REGION_IntersectO, NULL, NULL);
1275
1276 /*
1277 * Can't alter newReg's extents before we call miRegionOp because
1278 * it might be one of the source regions and miRegionOp depends
1279 * on the extents of those regions being the same. Besides, this
1280 * way there's no checking against rectangles that will be nuked
1281 * due to coalescing, so we have to examine fewer rectangles.
1282 */
1283
1284 REGION_SetExtents(newReg);
1285 }
1286
1287 /***********************************************************************
1288 * Region Union
1289 ***********************************************************************/
1290
1291 /*!
1292 * Handle a non-overlapping band for the union operation. Just
1293 * Adds the rectangles into the region. Doesn't have to check for
1294 * subsumption or anything.
1295 *
1296 * Results:
1297 * None.
1298 *
1299 * \note Side Effects:
1300 * pReg->numRects is incremented and the final rectangles overwritten
1301 * with the rectangles we're passed.
1302 *
1303 */
1304 static void FASTCALL
1305 REGION_UnionNonO (
1306 PROSRGNDATA pReg,
1307 PRECT r,
1308 PRECT rEnd,
1309 INT top,
1310 INT bottom
1311 )
1312 {
1313 RECT *pNextRect;
1314
1315 pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1316
1317 while (r != rEnd)
1318 {
1319 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1320 pNextRect->left = r->left;
1321 pNextRect->top = top;
1322 pNextRect->right = r->right;
1323 pNextRect->bottom = bottom;
1324 pReg->rdh.nCount += 1;
1325 pNextRect++;
1326 r++;
1327 }
1328 return;
1329 }
1330
1331 /*!
1332 * Handle an overlapping band for the union operation. Picks the
1333 * left-most rectangle each time and merges it into the region.
1334 *
1335 * Results:
1336 * None.
1337 *
1338 * \note Side Effects:
1339 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1340 * be changed.
1341 *
1342 */
1343 static void FASTCALL
1344 REGION_UnionO (
1345 PROSRGNDATA pReg,
1346 PRECT r1,
1347 PRECT r1End,
1348 PRECT r2,
1349 PRECT r2End,
1350 INT top,
1351 INT bottom
1352 )
1353 {
1354 RECT *pNextRect;
1355
1356 pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1357
1358 #define MERGERECT(r) \
1359 if ((pReg->rdh.nCount != 0) && \
1360 ((pNextRect-1)->top == top) && \
1361 ((pNextRect-1)->bottom == bottom) && \
1362 ((pNextRect-1)->right >= r->left)) \
1363 { \
1364 if ((pNextRect-1)->right < r->right) \
1365 { \
1366 (pNextRect-1)->right = r->right; \
1367 } \
1368 } \
1369 else \
1370 { \
1371 MEMCHECK(pReg, pNextRect, pReg->Buffer); \
1372 pNextRect->top = top; \
1373 pNextRect->bottom = bottom; \
1374 pNextRect->left = r->left; \
1375 pNextRect->right = r->right; \
1376 pReg->rdh.nCount += 1; \
1377 pNextRect += 1; \
1378 } \
1379 r++;
1380
1381 while ((r1 != r1End) && (r2 != r2End))
1382 {
1383 if (r1->left < r2->left)
1384 {
1385 MERGERECT(r1);
1386 }
1387 else
1388 {
1389 MERGERECT(r2);
1390 }
1391 }
1392
1393 if (r1 != r1End)
1394 {
1395 do
1396 {
1397 MERGERECT(r1);
1398 } while (r1 != r1End);
1399 }
1400 else while (r2 != r2End)
1401 {
1402 MERGERECT(r2);
1403 }
1404 return;
1405 }
1406
1407 /***********************************************************************
1408 * REGION_UnionRegion
1409 */
1410 static void FASTCALL REGION_UnionRegion(ROSRGNDATA *newReg, ROSRGNDATA *reg1,
1411 ROSRGNDATA *reg2)
1412 {
1413 /* checks all the simple cases */
1414
1415 /*
1416 * Region 1 and 2 are the same or region 1 is empty
1417 */
1418 if (reg1 == reg2 || 0 == reg1->rdh.nCount ||
1419 reg1->rdh.rcBound.right <= reg1->rdh.rcBound.left ||
1420 reg1->rdh.rcBound.bottom <= reg1->rdh.rcBound.top)
1421 {
1422 if (newReg != reg2)
1423 {
1424 REGION_CopyRegion(newReg, reg2);
1425 }
1426 return;
1427 }
1428
1429 /*
1430 * if nothing to union (region 2 empty)
1431 */
1432 if (0 == reg2->rdh.nCount ||
1433 reg2->rdh.rcBound.right <= reg2->rdh.rcBound.left ||
1434 reg2->rdh.rcBound.bottom <= reg2->rdh.rcBound.top)
1435 {
1436 if (newReg != reg1)
1437 {
1438 REGION_CopyRegion(newReg, reg1);
1439 }
1440 return;
1441 }
1442
1443 /*
1444 * Region 1 completely subsumes region 2
1445 */
1446 if (1 == reg1->rdh.nCount &&
1447 reg1->rdh.rcBound.left <= reg2->rdh.rcBound.left &&
1448 reg1->rdh.rcBound.top <= reg2->rdh.rcBound.top &&
1449 reg2->rdh.rcBound.right <= reg1->rdh.rcBound.right &&
1450 reg2->rdh.rcBound.bottom <= reg1->rdh.rcBound.bottom)
1451 {
1452 if (newReg != reg1)
1453 {
1454 REGION_CopyRegion(newReg, reg1);
1455 }
1456 return;
1457 }
1458
1459 /*
1460 * Region 2 completely subsumes region 1
1461 */
1462 if (1 == reg2->rdh.nCount &&
1463 reg2->rdh.rcBound.left <= reg1->rdh.rcBound.left &&
1464 reg2->rdh.rcBound.top <= reg1->rdh.rcBound.top &&
1465 reg1->rdh.rcBound.right <= reg2->rdh.rcBound.right &&
1466 reg1->rdh.rcBound.bottom <= reg2->rdh.rcBound.bottom)
1467 {
1468 if (newReg != reg2)
1469 {
1470 REGION_CopyRegion(newReg, reg2);
1471 }
1472 return;
1473 }
1474
1475 REGION_RegionOp ( newReg, reg1, reg2, REGION_UnionO,
1476 REGION_UnionNonO, REGION_UnionNonO );
1477 newReg->rdh.rcBound.left = min(reg1->rdh.rcBound.left, reg2->rdh.rcBound.left);
1478 newReg->rdh.rcBound.top = min(reg1->rdh.rcBound.top, reg2->rdh.rcBound.top);
1479 newReg->rdh.rcBound.right = max(reg1->rdh.rcBound.right, reg2->rdh.rcBound.right);
1480 newReg->rdh.rcBound.bottom = max(reg1->rdh.rcBound.bottom, reg2->rdh.rcBound.bottom);
1481 }
1482
1483 /***********************************************************************
1484 * Region Subtraction
1485 ***********************************************************************/
1486
1487 /*!
1488 * Deal with non-overlapping band for subtraction. Any parts from
1489 * region 2 we discard. Anything from region 1 we add to the region.
1490 *
1491 * Results:
1492 * None.
1493 *
1494 * \note Side Effects:
1495 * pReg may be affected.
1496 *
1497 */
1498 static void FASTCALL
1499 REGION_SubtractNonO1 (
1500 PROSRGNDATA pReg,
1501 PRECT r,
1502 PRECT rEnd,
1503 INT top,
1504 INT bottom
1505 )
1506 {
1507 RECT *pNextRect;
1508
1509 pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1510
1511 while (r != rEnd)
1512 {
1513 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1514 pNextRect->left = r->left;
1515 pNextRect->top = top;
1516 pNextRect->right = r->right;
1517 pNextRect->bottom = bottom;
1518 pReg->rdh.nCount += 1;
1519 pNextRect++;
1520 r++;
1521 }
1522 return;
1523 }
1524
1525
1526 /*!
1527 * Overlapping band subtraction. x1 is the left-most point not yet
1528 * checked.
1529 *
1530 * Results:
1531 * None.
1532 *
1533 * \note Side Effects:
1534 * pReg may have rectangles added to it.
1535 *
1536 */
1537 static void FASTCALL
1538 REGION_SubtractO (
1539 PROSRGNDATA pReg,
1540 PRECT r1,
1541 PRECT r1End,
1542 PRECT r2,
1543 PRECT r2End,
1544 INT top,
1545 INT bottom
1546 )
1547 {
1548 RECT *pNextRect;
1549 INT left;
1550
1551 left = r1->left;
1552 pNextRect = (PRECT)pReg->Buffer + pReg->rdh.nCount;
1553
1554 while ((r1 != r1End) && (r2 != r2End))
1555 {
1556 if (r2->right <= left)
1557 {
1558 /*
1559 * Subtrahend missed the boat: go to next subtrahend.
1560 */
1561 r2++;
1562 }
1563 else if (r2->left <= left)
1564 {
1565 /*
1566 * Subtrahend preceeds minuend: nuke left edge of minuend.
1567 */
1568 left = r2->right;
1569 if (left >= r1->right)
1570 {
1571 /*
1572 * Minuend completely covered: advance to next minuend and
1573 * reset left fence to edge of new minuend.
1574 */
1575 r1++;
1576 if (r1 != r1End)
1577 left = r1->left;
1578 }
1579 else
1580 {
1581 /*
1582 * Subtrahend now used up since it doesn't extend beyond
1583 * minuend
1584 */
1585 r2++;
1586 }
1587 }
1588 else if (r2->left < r1->right)
1589 {
1590 /*
1591 * Left part of subtrahend covers part of minuend: add uncovered
1592 * part of minuend to region and skip to next subtrahend.
1593 */
1594 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1595 pNextRect->left = left;
1596 pNextRect->top = top;
1597 pNextRect->right = r2->left;
1598 pNextRect->bottom = bottom;
1599 pReg->rdh.nCount += 1;
1600 pNextRect++;
1601 left = r2->right;
1602 if (left >= r1->right)
1603 {
1604 /*
1605 * Minuend used up: advance to new...
1606 */
1607 r1++;
1608 if (r1 != r1End)
1609 left = r1->left;
1610 }
1611 else
1612 {
1613 /*
1614 * Subtrahend used up
1615 */
1616 r2++;
1617 }
1618 }
1619 else
1620 {
1621 /*
1622 * Minuend used up: add any remaining piece before advancing.
1623 */
1624 if (r1->right > left)
1625 {
1626 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1627 pNextRect->left = left;
1628 pNextRect->top = top;
1629 pNextRect->right = r1->right;
1630 pNextRect->bottom = bottom;
1631 pReg->rdh.nCount += 1;
1632 pNextRect++;
1633 }
1634 r1++;
1635 left = r1->left;
1636 }
1637 }
1638
1639 /*
1640 * Add remaining minuend rectangles to region.
1641 */
1642 while (r1 != r1End)
1643 {
1644 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1645 pNextRect->left = left;
1646 pNextRect->top = top;
1647 pNextRect->right = r1->right;
1648 pNextRect->bottom = bottom;
1649 pReg->rdh.nCount += 1;
1650 pNextRect++;
1651 r1++;
1652 if (r1 != r1End)
1653 {
1654 left = r1->left;
1655 }
1656 }
1657 return;
1658 }
1659
1660 /*!
1661 * Subtract regS from regM and leave the result in regD.
1662 * S stands for subtrahend, M for minuend and D for difference.
1663 *
1664 * Results:
1665 * TRUE.
1666 *
1667 * \note Side Effects:
1668 * regD is overwritten.
1669 *
1670 */
1671 static void FASTCALL REGION_SubtractRegion(ROSRGNDATA *regD, ROSRGNDATA *regM,
1672 ROSRGNDATA *regS )
1673 {
1674 /* check for trivial reject */
1675 if ( (!(regM->rdh.nCount)) || (!(regS->rdh.nCount)) ||
1676 (!EXTENTCHECK(&regM->rdh.rcBound, &regS->rdh.rcBound)) )
1677 {
1678 REGION_CopyRegion(regD, regM);
1679 return;
1680 }
1681
1682 REGION_RegionOp (regD, regM, regS, REGION_SubtractO,
1683 REGION_SubtractNonO1, NULL);
1684
1685 /*
1686 * Can't alter newReg's extents before we call miRegionOp because
1687 * it might be one of the source regions and miRegionOp depends
1688 * on the extents of those regions being the unaltered. Besides, this
1689 * way there's no checking against rectangles that will be nuked
1690 * due to coalescing, so we have to examine fewer rectangles.
1691 */
1692 REGION_SetExtents (regD);
1693 }
1694
1695 /***********************************************************************
1696 * REGION_XorRegion
1697 */
1698 static void FASTCALL REGION_XorRegion(ROSRGNDATA *dr, ROSRGNDATA *sra,
1699 ROSRGNDATA *srb)
1700 {
1701 HRGN htra, htrb;
1702 ROSRGNDATA *tra, *trb;
1703
1704 if(!(htra = RGNDATA_AllocRgn(sra->rdh.nCount + 1)))
1705 return;
1706 if(!(htrb = RGNDATA_AllocRgn(srb->rdh.nCount + 1)))
1707 {
1708 NtGdiDeleteObject( htra );
1709 return;
1710 }
1711 tra = RGNDATA_LockRgn( htra );
1712 if(!tra ){
1713 NtGdiDeleteObject( htra );
1714 NtGdiDeleteObject( htrb );
1715 return;
1716 }
1717
1718 trb = RGNDATA_LockRgn( htrb );
1719 if( !trb ){
1720 RGNDATA_UnlockRgn( tra );
1721 NtGdiDeleteObject( htra );
1722 NtGdiDeleteObject( htrb );
1723 return;
1724 }
1725
1726 REGION_SubtractRegion(tra,sra,srb);
1727 REGION_SubtractRegion(trb,srb,sra);
1728 REGION_UnionRegion(dr,tra,trb);
1729 RGNDATA_UnlockRgn( tra );
1730 RGNDATA_UnlockRgn( trb );
1731
1732 NtGdiDeleteObject( htra );
1733 NtGdiDeleteObject( htrb );
1734 return;
1735 }
1736
1737
1738 /*!
1739 * Adds a rectangle to a REGION
1740 */
1741 void FASTCALL REGION_UnionRectWithRegion(const RECT *rect, ROSRGNDATA *rgn)
1742 {
1743 ROSRGNDATA region;
1744
1745 region.Buffer = &region.rdh.rcBound;
1746 region.rdh.nCount = 1;
1747 region.rdh.nRgnSize = sizeof( RECT );
1748 region.rdh.rcBound = *rect;
1749 REGION_UnionRegion(rgn, rgn, &region);
1750 }
1751
1752 BOOL FASTCALL REGION_CreateFrameRgn(HRGN hDest, HRGN hSrc, INT x, INT y)
1753 {
1754 PROSRGNDATA srcObj, destObj;
1755 PRECT rc;
1756 ULONG i;
1757
1758 if (!(srcObj = (PROSRGNDATA)RGNDATA_LockRgn(hSrc)))
1759 {
1760 return FALSE;
1761 }
1762 if (!REGION_NOT_EMPTY(srcObj))
1763 {
1764 RGNDATA_UnlockRgn(srcObj);
1765 return FALSE;
1766 }
1767 if (!(destObj = (PROSRGNDATA)RGNDATA_LockRgn(hDest)))
1768 {
1769 RGNDATA_UnlockRgn(srcObj);
1770 return FALSE;
1771 }
1772
1773 EMPTY_REGION(destObj);
1774 if (!REGION_CopyRegion(destObj, srcObj))
1775 {
1776 RGNDATA_UnlockRgn(destObj);
1777 RGNDATA_UnlockRgn(srcObj);
1778 return FALSE;
1779 }
1780
1781 /* Original region moved to right */
1782 rc = (PRECT)srcObj->Buffer;
1783 for (i = 0; i < srcObj->rdh.nCount; i++)
1784 {
1785 rc->left += x;
1786 rc->right += x;
1787 rc++;
1788 }
1789 REGION_IntersectRegion(destObj, destObj, srcObj);
1790
1791 /* Original region moved to left */
1792 rc = (PRECT)srcObj->Buffer;
1793 for (i = 0; i < srcObj->rdh.nCount; i++)
1794 {
1795 rc->left -= 2 * x;
1796 rc->right -= 2 * x;
1797 rc++;
1798 }
1799 REGION_IntersectRegion(destObj, destObj, srcObj);
1800
1801 /* Original region moved down */
1802 rc = (PRECT)srcObj->Buffer;
1803 for (i = 0; i < srcObj->rdh.nCount; i++)
1804 {
1805 rc->left += x;
1806 rc->right += x;
1807 rc->top += y;
1808 rc->bottom += y;
1809 rc++;
1810 }
1811 REGION_IntersectRegion(destObj, destObj, srcObj);
1812
1813 /* Original region moved up */
1814 rc = (PRECT)srcObj->Buffer;
1815 for (i = 0; i < srcObj->rdh.nCount; i++)
1816 {
1817 rc->top -= 2 * y;
1818 rc->bottom -= 2 * y;
1819 rc++;
1820 }
1821 REGION_IntersectRegion(destObj, destObj, srcObj);
1822
1823 /* Restore the original region */
1824 rc = (PRECT)srcObj->Buffer;
1825 for (i = 0; i < srcObj->rdh.nCount; i++)
1826 {
1827 rc->top += y;
1828 rc->bottom += y;
1829 rc++;
1830 }
1831 REGION_SubtractRegion(destObj, srcObj, destObj);
1832
1833 RGNDATA_UnlockRgn(destObj);
1834 RGNDATA_UnlockRgn(srcObj);
1835 return TRUE;
1836 }
1837
1838
1839 BOOL FASTCALL REGION_LPTODP(HDC hdc, HRGN hDest, HRGN hSrc)
1840 {
1841 RECT *pCurRect, *pEndRect;
1842 PROSRGNDATA srcObj = NULL;
1843 PROSRGNDATA destObj = NULL;
1844
1845 DC * dc = DC_LockDc(hdc);
1846 RECT tmpRect;
1847 BOOL ret = FALSE;
1848
1849 if(!dc)
1850 return ret;
1851
1852 if(dc->w.MapMode == MM_TEXT) // Requires only a translation
1853 {
1854 if(NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
1855 goto done;
1856
1857 NtGdiOffsetRgn(hDest, dc->vportOrgX - dc->wndOrgX, dc->vportOrgY - dc->wndOrgY);
1858 ret = TRUE;
1859 goto done;
1860 }
1861
1862 if(!( srcObj = (PROSRGNDATA) RGNDATA_LockRgn( hSrc ) ))
1863 goto done;
1864 if(!( destObj = (PROSRGNDATA) RGNDATA_LockRgn( hDest ) ))
1865 {
1866 RGNDATA_UnlockRgn( srcObj );
1867 goto done;
1868 }
1869 EMPTY_REGION(destObj);
1870
1871 pEndRect = (PRECT)srcObj->Buffer + srcObj->rdh.nCount;
1872 for(pCurRect = (PRECT)srcObj->Buffer; pCurRect < pEndRect; pCurRect++)
1873 {
1874 tmpRect = *pCurRect;
1875 tmpRect.left = XLPTODP(dc, tmpRect.left);
1876 tmpRect.top = YLPTODP(dc, tmpRect.top);
1877 tmpRect.right = XLPTODP(dc, tmpRect.right);
1878 tmpRect.bottom = YLPTODP(dc, tmpRect.bottom);
1879
1880 if(tmpRect.left > tmpRect.right)
1881 { INT tmp = tmpRect.left; tmpRect.left = tmpRect.right; tmpRect.right = tmp; }
1882 if(tmpRect.top > tmpRect.bottom)
1883 { INT tmp = tmpRect.top; tmpRect.top = tmpRect.bottom; tmpRect.bottom = tmp; }
1884
1885 REGION_UnionRectWithRegion(&tmpRect, destObj);
1886 }
1887 ret = TRUE;
1888
1889 RGNDATA_UnlockRgn( srcObj );
1890 RGNDATA_UnlockRgn( destObj );
1891
1892 done:
1893 DC_UnlockDc( dc );
1894 return ret;
1895 }
1896
1897 HRGN FASTCALL RGNDATA_AllocRgn(INT n)
1898 {
1899 HRGN hReg;
1900 PROSRGNDATA pReg;
1901
1902 if ((hReg = (HRGN) GDIOBJ_AllocObj(GDI_OBJECT_TYPE_REGION)))
1903 {
1904 if (NULL != (pReg = RGNDATA_LockRgn(hReg)))
1905 {
1906 if (1 == n)
1907 {
1908 /* Testing shows that > 95% of all regions have only 1 rect.
1909 Including that here saves us from having to do another
1910 allocation */
1911 pReg->Buffer = &pReg->rdh.rcBound;
1912 }
1913 else
1914 {
1915 pReg->Buffer = ExAllocatePoolWithTag(PagedPool, n * sizeof(RECT), TAG_REGION);
1916 }
1917 if (NULL != pReg->Buffer)
1918 {
1919 EMPTY_REGION(pReg);
1920 pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
1921 pReg->rdh.nCount = n;
1922 pReg->rdh.nRgnSize = n*sizeof(RECT);
1923
1924 RGNDATA_UnlockRgn(pReg);
1925
1926 return hReg;
1927 }
1928 }
1929 else
1930 {
1931 RGNDATA_FreeRgn(hReg);
1932 }
1933 }
1934
1935 return NULL;
1936 }
1937
1938 BOOL INTERNAL_CALL
1939 RGNDATA_Cleanup(PVOID ObjectBody)
1940 {
1941 PROSRGNDATA pRgn = (PROSRGNDATA)ObjectBody;
1942 if(pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
1943 ExFreePool(pRgn->Buffer);
1944 return TRUE;
1945 }
1946
1947 // NtGdi Exported Functions
1948 INT
1949 STDCALL
1950 NtGdiCombineRgn(HRGN hDest,
1951 HRGN hSrc1,
1952 HRGN hSrc2,
1953 INT CombineMode)
1954 {
1955 INT result = ERROR;
1956 PROSRGNDATA destRgn, src1Rgn, src2Rgn;
1957
1958 destRgn = RGNDATA_LockRgn(hDest);
1959 if( destRgn )
1960 {
1961 src1Rgn = RGNDATA_LockRgn(hSrc1);
1962 if( src1Rgn )
1963 {
1964 if (CombineMode == RGN_COPY)
1965 {
1966 if( !REGION_CopyRegion(destRgn, src1Rgn) )
1967 return ERROR;
1968 result = destRgn->rdh.iType;
1969 }
1970 else
1971 {
1972 src2Rgn = RGNDATA_LockRgn(hSrc2);
1973 if( src2Rgn )
1974 {
1975 switch (CombineMode)
1976 {
1977 case RGN_AND:
1978 REGION_IntersectRegion(destRgn, src1Rgn, src2Rgn);
1979 break;
1980 case RGN_OR:
1981 REGION_UnionRegion(destRgn, src1Rgn, src2Rgn);
1982 break;
1983 case RGN_XOR:
1984 REGION_XorRegion(destRgn, src1Rgn, src2Rgn);
1985 break;
1986 case RGN_DIFF:
1987 REGION_SubtractRegion(destRgn, src1Rgn, src2Rgn);
1988 break;
1989 }
1990 RGNDATA_UnlockRgn(src2Rgn);
1991 result = destRgn->rdh.iType;
1992 }
1993 else if(hSrc2 == NULL)
1994 {
1995 DPRINT1("NtGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", CombineMode);
1996 }
1997 }
1998
1999 RGNDATA_UnlockRgn(src1Rgn);
2000 }
2001
2002 RGNDATA_UnlockRgn(destRgn);
2003 }
2004 else
2005 {
2006 DPRINT("NtGdiCombineRgn: hDest unavailable\n");
2007 result = ERROR;
2008 }
2009
2010 return result;
2011 }
2012
2013 HRGN
2014 STDCALL
2015 NtGdiCreateEllipticRgn(INT Left,
2016 INT Top,
2017 INT Right,
2018 INT Bottom)
2019 {
2020 return NtGdiCreateRoundRectRgn(Left, Top, Right, Bottom,
2021 Right - Left, Bottom - Top);
2022 }
2023
2024 HRGN
2025 STDCALL
2026 NtGdiCreateEllipticRgnIndirect(CONST PRECT Rect)
2027 {
2028 RECT SafeRect;
2029 NTSTATUS Status;
2030
2031 Status = MmCopyFromCaller(&SafeRect, Rect, sizeof(RECT));
2032 if(!NT_SUCCESS(Status))
2033 {
2034 SetLastNtError(Status);
2035 return NULL;
2036 }
2037
2038 return NtGdiCreateRoundRectRgn(SafeRect.left, SafeRect.top, SafeRect.right, SafeRect.bottom,
2039 SafeRect.right - SafeRect.left, SafeRect.bottom - SafeRect.top);
2040 }
2041
2042 HRGN STDCALL
2043 NtGdiCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
2044 {
2045 HRGN hRgn;
2046
2047 /* Allocate region data structure with space for 1 RECT */
2048 if ((hRgn = RGNDATA_AllocRgn(1)))
2049 {
2050 if (NtGdiSetRectRgn(hRgn, LeftRect, TopRect, RightRect, BottomRect))
2051 return hRgn;
2052 NtGdiDeleteObject(hRgn);
2053 }
2054
2055 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2056 return NULL;
2057 }
2058
2059 HRGN STDCALL
2060 NtGdiCreateRectRgnIndirect(CONST PRECT rc)
2061 {
2062 RECT SafeRc;
2063 NTSTATUS Status;
2064
2065 Status = MmCopyFromCaller(&SafeRc, rc, sizeof(RECT));
2066 if (!NT_SUCCESS(Status))
2067 {
2068 return(NULL);
2069 }
2070 return(UnsafeIntCreateRectRgnIndirect(&SafeRc));
2071 }
2072
2073 HRGN
2074 STDCALL
2075 NtGdiCreateRoundRectRgn(INT left, INT top, INT right, INT bottom,
2076 INT ellipse_width, INT ellipse_height)
2077 {
2078 PROSRGNDATA obj;
2079 HRGN hrgn;
2080 int asq, bsq, d, xd, yd;
2081 RECT rect;
2082
2083 /* Make the dimensions sensible */
2084
2085 if (left > right) { INT tmp = left; left = right; right = tmp; }
2086 if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
2087
2088 ellipse_width = abs(ellipse_width);
2089 ellipse_height = abs(ellipse_height);
2090
2091 /* Check parameters */
2092
2093 if (ellipse_width > right-left) ellipse_width = right-left;
2094 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
2095
2096 /* Check if we can do a normal rectangle instead */
2097
2098 if ((ellipse_width < 2) || (ellipse_height < 2))
2099 return NtGdiCreateRectRgn( left, top, right, bottom );
2100
2101 /* Create region */
2102
2103 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
2104 if (!(hrgn = RGNDATA_AllocRgn(d))) return 0;
2105 if (!(obj = RGNDATA_LockRgn(hrgn))) return 0;
2106
2107 /* Ellipse algorithm, based on an article by K. Porter */
2108 /* in DDJ Graphics Programming Column, 8/89 */
2109
2110 asq = ellipse_width * ellipse_width / 4; /* a^2 */
2111 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
2112 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
2113 xd = 0;
2114 yd = asq * ellipse_height; /* 2a^2b */
2115
2116 rect.left = left + ellipse_width / 2;
2117 rect.right = right - ellipse_width / 2;
2118
2119 /* Loop to draw first half of quadrant */
2120
2121 while (xd < yd)
2122 {
2123 if (d > 0) /* if nearest pixel is toward the center */
2124 {
2125 /* move toward center */
2126 rect.top = top++;
2127 rect.bottom = rect.top + 1;
2128 UnsafeIntUnionRectWithRgn( obj, &rect );
2129 rect.top = --bottom;
2130 rect.bottom = rect.top + 1;
2131 UnsafeIntUnionRectWithRgn( obj, &rect );
2132 yd -= 2*asq;
2133 d -= yd;
2134 }
2135 rect.left--; /* next horiz point */
2136 rect.right++;
2137 xd += 2*bsq;
2138 d += bsq + xd;
2139 }
2140 /* Loop to draw second half of quadrant */
2141
2142 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
2143 while (yd >= 0)
2144 {
2145 /* next vertical point */
2146 rect.top = top++;
2147 rect.bottom = rect.top + 1;
2148 UnsafeIntUnionRectWithRgn( obj, &rect );
2149 rect.top = --bottom;
2150 rect.bottom = rect.top + 1;
2151 UnsafeIntUnionRectWithRgn( obj, &rect );
2152 if (d < 0) /* if nearest pixel is outside ellipse */
2153 {
2154 rect.left--; /* move away from center */
2155 rect.right++;
2156 xd += 2*bsq;
2157 d += xd;
2158 }
2159 yd -= 2*asq;
2160 d += asq - yd;
2161 }
2162 /* Add the inside rectangle */
2163
2164 if (top <= bottom)
2165 {
2166 rect.top = top;
2167 rect.bottom = bottom;
2168 UnsafeIntUnionRectWithRgn( obj, &rect );
2169 }
2170 RGNDATA_UnlockRgn( obj );
2171 return hrgn;
2172 }
2173
2174 BOOL
2175 STDCALL
2176 NtGdiEqualRgn(HRGN hSrcRgn1,
2177 HRGN hSrcRgn2)
2178 {
2179 PROSRGNDATA rgn1, rgn2;
2180 PRECT tRect1, tRect2;
2181 ULONG i;
2182 BOOL bRet = FALSE;
2183
2184 if( !(rgn1 = RGNDATA_LockRgn(hSrcRgn1)))
2185 return ERROR;
2186
2187 if( !(rgn2 = RGNDATA_LockRgn(hSrcRgn2))){
2188 RGNDATA_UnlockRgn( rgn1 );
2189 return ERROR;
2190 }
2191
2192 if(rgn1->rdh.nCount != rgn2->rdh.nCount ||
2193 rgn1->rdh.nCount == 0 ||
2194 rgn1->rdh.rcBound.left != rgn2->rdh.rcBound.left ||
2195 rgn1->rdh.rcBound.right != rgn2->rdh.rcBound.right ||
2196 rgn1->rdh.rcBound.top != rgn2->rdh.rcBound.top ||
2197 rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom)
2198 goto exit;
2199
2200 tRect1 = (PRECT)rgn1->Buffer;
2201 tRect2 = (PRECT)rgn2->Buffer;
2202
2203 if( !tRect1 || !tRect2 )
2204 goto exit;
2205
2206 for(i=0; i < rgn1->rdh.nCount; i++)
2207 {
2208 if(tRect1[i].left != tRect2[i].left ||
2209 tRect1[i].right != tRect2[i].right ||
2210 tRect1[i].top != tRect2[i].top ||
2211 tRect1[i].bottom != tRect2[i].bottom)
2212 goto exit;
2213 }
2214 bRet = TRUE;
2215
2216 exit:
2217 RGNDATA_UnlockRgn( rgn1 );
2218 RGNDATA_UnlockRgn( rgn2 );
2219 return bRet;
2220 }
2221
2222 HRGN
2223 STDCALL
2224 NtGdiExtCreateRegion(CONST XFORM *Xform,
2225 DWORD Count,
2226 CONST RGNDATA *RgnData)
2227 {
2228 HRGN hRgn;
2229 RGNDATA SafeRgnData;
2230 PROSRGNDATA Region;
2231 NTSTATUS Status;
2232
2233 if (Count < FIELD_OFFSET(RGNDATA, Buffer))
2234 {
2235 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2236 return NULL;
2237 }
2238
2239 Status = MmCopyFromCaller(&SafeRgnData, RgnData, min(Count, sizeof(RGNDATA)));
2240 if (!NT_SUCCESS(Status))
2241 {
2242 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2243 return NULL;
2244 }
2245
2246 hRgn = RGNDATA_AllocRgn(SafeRgnData.rdh.nCount);
2247 if (hRgn == NULL)
2248 {
2249 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2250 return NULL;
2251 }
2252
2253 Region = RGNDATA_LockRgn(hRgn);
2254 if (Region == NULL)
2255 {
2256 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2257 return FALSE;
2258 }
2259
2260 RtlCopyMemory(&Region->rdh, &SafeRgnData, FIELD_OFFSET(RGNDATA, Buffer));
2261
2262 Status = MmCopyFromCaller(Region->Buffer, RgnData->Buffer,
2263 Count - FIELD_OFFSET(RGNDATA, Buffer));
2264 if (!NT_SUCCESS(Status))
2265 {
2266 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2267 RGNDATA_UnlockRgn(Region);
2268 NtGdiDeleteObject(hRgn);
2269 return NULL;
2270 }
2271
2272 RGNDATA_UnlockRgn(Region);
2273
2274 return hRgn;
2275 }
2276
2277 BOOL
2278 STDCALL
2279 NtGdiFillRgn(HDC hDC, HRGN hRgn, HBRUSH hBrush)
2280 {
2281 HBRUSH oldhBrush;
2282 PROSRGNDATA rgn;
2283 PRECT r;
2284
2285 if (NULL == (rgn = RGNDATA_LockRgn(hRgn)))
2286 {
2287 return FALSE;
2288 }
2289
2290 if (NULL == (oldhBrush = NtGdiSelectObject(hDC, hBrush)))
2291 {
2292 RGNDATA_UnlockRgn(rgn);
2293 return FALSE;
2294 }
2295
2296 for (r = rgn->Buffer; r < rgn->Buffer + rgn->rdh.nCount; r++)
2297 {
2298 NtGdiPatBlt(hDC, r->left, r->top, r->right - r->left, r->bottom - r->top, PATCOPY);
2299 }
2300
2301 RGNDATA_UnlockRgn( rgn );
2302 NtGdiSelectObject(hDC, oldhBrush);
2303
2304 return TRUE;
2305 }
2306
2307 BOOL
2308 STDCALL
2309 NtGdiFrameRgn(HDC hDC, HRGN hRgn, HBRUSH hBrush, INT Width, INT Height)
2310 {
2311 HRGN FrameRgn;
2312 BOOL Ret;
2313
2314 if(!(FrameRgn = NtGdiCreateRectRgn(0, 0, 0, 0)))
2315 {
2316 return FALSE;
2317 }
2318 if(!REGION_CreateFrameRgn(FrameRgn, hRgn, Width, Height))
2319 {
2320 NtGdiDeleteObject(FrameRgn);
2321 return FALSE;
2322 }
2323
2324 Ret = NtGdiFillRgn(hDC, FrameRgn, hBrush);
2325
2326 NtGdiDeleteObject(FrameRgn);
2327 return Ret;
2328 }
2329
2330 INT FASTCALL
2331 UnsafeIntGetRgnBox(PROSRGNDATA Rgn,
2332 LPRECT pRect)
2333 {
2334 DWORD ret;
2335
2336 if (Rgn)
2337 {
2338 *pRect = Rgn->rdh.rcBound;
2339 ret = Rgn->rdh.iType;
2340
2341 return ret;
2342 }
2343 return 0; //if invalid region return zero
2344 }
2345
2346
2347 INT STDCALL
2348 NtGdiGetRgnBox(HRGN hRgn,
2349 LPRECT pRect)
2350 {
2351 PROSRGNDATA Rgn;
2352 RECT SafeRect;
2353 DWORD ret;
2354
2355 if (!(Rgn = RGNDATA_LockRgn(hRgn)))
2356 {
2357 return ERROR;
2358 }
2359
2360 ret = UnsafeIntGetRgnBox(Rgn, &SafeRect);
2361 RGNDATA_UnlockRgn(Rgn);
2362 if (ERROR == ret)
2363 {
2364 return ret;
2365 }
2366
2367 if (!NT_SUCCESS(MmCopyToCaller(pRect, &SafeRect, sizeof(RECT))))
2368 {
2369 return ERROR;
2370 }
2371
2372 return ret;
2373 }
2374
2375 BOOL
2376 STDCALL
2377 NtGdiInvertRgn(HDC hDC,
2378 HRGN hRgn)
2379 {
2380 PROSRGNDATA RgnData;
2381 ULONG i;
2382 PRECT rc;
2383
2384 if(!(RgnData = RGNDATA_LockRgn(hRgn)))
2385 {
2386 SetLastWin32Error(ERROR_INVALID_HANDLE);
2387 return FALSE;
2388 }
2389
2390 rc = (PRECT)RgnData->Buffer;
2391 for(i = 0; i < RgnData->rdh.nCount; i++)
2392 {
2393
2394 if(!NtGdiPatBlt(hDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, DSTINVERT))
2395 {
2396 RGNDATA_UnlockRgn(RgnData);
2397 return FALSE;
2398 }
2399 rc++;
2400 }
2401
2402 RGNDATA_UnlockRgn(RgnData);
2403 return TRUE;
2404 }
2405
2406 INT
2407 STDCALL
2408 NtGdiOffsetRgn(HRGN hRgn,
2409 INT XOffset,
2410 INT YOffset)
2411 {
2412 PROSRGNDATA rgn = RGNDATA_LockRgn(hRgn);
2413 INT ret;
2414
2415 DPRINT("NtGdiOffsetRgn: hRgn %d Xoffs %d Yoffs %d rgn %x\n", hRgn, XOffset, YOffset, rgn );
2416
2417 if( !rgn ){
2418 DPRINT("NtGdiOffsetRgn: hRgn error\n");
2419 return ERROR;
2420 }
2421
2422 if(XOffset || YOffset) {
2423 int nbox = rgn->rdh.nCount;
2424 PRECT pbox = (PRECT)rgn->Buffer;
2425
2426 if(nbox && pbox) {
2427 while(nbox--) {
2428 pbox->left += XOffset;
2429 pbox->right += XOffset;
2430 pbox->top += YOffset;
2431 pbox->bottom += YOffset;
2432 pbox++;
2433 }
2434 if (rgn->Buffer != &rgn->rdh.rcBound)
2435 {
2436 rgn->rdh.rcBound.left += XOffset;
2437 rgn->rdh.rcBound.right += XOffset;
2438 rgn->rdh.rcBound.top += YOffset;
2439 rgn->rdh.rcBound.bottom += YOffset;
2440 }
2441 }
2442 }
2443 ret = rgn->rdh.iType;
2444 RGNDATA_UnlockRgn( rgn );
2445 return ret;
2446 }
2447
2448 BOOL
2449 STDCALL
2450 NtGdiPaintRgn(HDC hDC,
2451 HRGN hRgn)
2452 {
2453 //RECT box;
2454 HRGN tmpVisRgn; //, prevVisRgn;
2455 DC *dc = DC_LockDc(hDC);
2456 PROSRGNDATA visrgn;
2457 CLIPOBJ* ClipRegion;
2458 BOOL bRet = FALSE;
2459 PGDIBRUSHOBJ pBrush;
2460 GDIBRUSHINST BrushInst;
2461 POINTL BrushOrigin;
2462 BITMAPOBJ *BitmapObj;
2463
2464 if( !dc )
2465 return FALSE;
2466
2467 if(!(tmpVisRgn = NtGdiCreateRectRgn(0, 0, 0, 0))){
2468 DC_UnlockDc( dc );
2469 return FALSE;
2470 }
2471
2472 /* ei enable later
2473 // Transform region into device co-ords
2474 if(!REGION_LPTODP(hDC, tmpVisRgn, hRgn) || NtGdiOffsetRgn(tmpVisRgn, dc->w.DCOrgX, dc->w.DCOrgY) == ERROR) {
2475 NtGdiDeleteObject( tmpVisRgn );
2476 DC_UnlockDc( dc );
2477 return FALSE;
2478 }
2479 */
2480 /* enable when clipping is implemented
2481 NtGdiCombineRgn(tmpVisRgn, tmpVisRgn, dc->w.hGCClipRgn, RGN_AND);
2482 */
2483
2484 //visrgn = RGNDATA_LockRgn(tmpVisRgn);
2485 visrgn = RGNDATA_LockRgn(hRgn);
2486 if (visrgn == NULL)
2487 {
2488 DC_UnlockDc( dc );
2489 return FALSE;
2490 }
2491
2492 ClipRegion = IntEngCreateClipRegion (
2493 visrgn->rdh.nCount, (PRECTL)visrgn->Buffer, (PRECTL)&visrgn->rdh.rcBound );
2494 ASSERT( ClipRegion );
2495 pBrush = BRUSHOBJ_LockBrush(dc->w.hBrush);
2496 ASSERT(pBrush);
2497 IntGdiInitBrushInstance(&BrushInst, pBrush, dc->XlateBrush);
2498
2499 BrushOrigin.x = dc->w.brushOrgX;
2500 BrushOrigin.y = dc->w.brushOrgY;
2501 BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
2502 /* FIXME - Handle BitmapObj == NULL !!!! */
2503
2504 bRet = IntEngPaint(&BitmapObj->SurfObj,
2505 ClipRegion,
2506 &BrushInst.BrushObject,
2507 &BrushOrigin,
2508 0xFFFF);//FIXME:don't know what to put here
2509
2510 BITMAPOBJ_UnlockBitmap(BitmapObj);
2511 RGNDATA_UnlockRgn( visrgn );
2512
2513 // Fill the region
2514 DC_UnlockDc( dc );
2515 return TRUE;
2516 }
2517
2518 BOOL
2519 STDCALL
2520 NtGdiPtInRegion(HRGN hRgn,
2521 INT X,
2522 INT Y)
2523 {
2524 PROSRGNDATA rgn;
2525 ULONG i;
2526 PRECT r;
2527
2528 if(!(rgn = RGNDATA_LockRgn(hRgn) ) )
2529 return FALSE;
2530
2531 if(rgn->rdh.nCount > 0 && INRECT(rgn->rdh.rcBound, X, Y)){
2532 r = (PRECT) rgn->Buffer;
2533 for(i = 0; i < rgn->rdh.nCount; i++) {
2534 if(INRECT(*r, X, Y)){
2535 RGNDATA_UnlockRgn(rgn);
2536 return TRUE;
2537 }
2538 r++;
2539 }
2540 }
2541 RGNDATA_UnlockRgn(rgn);
2542 return FALSE;
2543 }
2544
2545 BOOL
2546 FASTCALL
2547 UnsafeIntRectInRegion(PROSRGNDATA Rgn,
2548 CONST LPRECT rc)
2549 {
2550 PRECT pCurRect, pRectEnd;
2551
2552 // this is (just) a useful optimization
2553 if((Rgn->rdh.nCount > 0) && EXTENTCHECK(&Rgn->rdh.rcBound, rc))
2554 {
2555 for (pCurRect = (PRECT)Rgn->Buffer, pRectEnd = pCurRect + Rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
2556 {
2557 if (pCurRect->bottom <= rc->top) continue; // not far enough down yet
2558 if (pCurRect->top >= rc->bottom) break; // too far down
2559 if (pCurRect->right <= rc->left) continue; // not far enough over yet
2560 if (pCurRect->left >= rc->right) continue;
2561
2562 return TRUE;
2563 }
2564 }
2565 return FALSE;
2566 }
2567
2568 BOOL
2569 STDCALL
2570 NtGdiRectInRegion(HRGN hRgn,
2571 CONST LPRECT unsaferc)
2572 {
2573 PROSRGNDATA Rgn;
2574 RECT rc;
2575 BOOL Ret;
2576
2577 if(!(Rgn = RGNDATA_LockRgn(hRgn)))
2578 {
2579 return ERROR;
2580 }
2581
2582 if (!NT_SUCCESS(MmCopyFromCaller(&rc, unsaferc, sizeof(RECT))))
2583 {
2584 RGNDATA_UnlockRgn(Rgn);
2585 DPRINT1("NtGdiRectInRegion: bogus rc\n");
2586 return ERROR;
2587 }
2588
2589 Ret = UnsafeIntRectInRegion(Rgn, &rc);
2590 RGNDATA_UnlockRgn(Rgn);
2591 return Ret;
2592 }
2593
2594 BOOL
2595 STDCALL
2596 NtGdiSetRectRgn(HRGN hRgn,
2597 INT LeftRect,
2598 INT TopRect,
2599 INT RightRect,
2600 INT BottomRect)
2601 {
2602 PROSRGNDATA rgn;
2603 PRECT firstRect;
2604
2605
2606
2607 if( !( rgn = RGNDATA_LockRgn(hRgn) ) )
2608 return 0; //per documentation
2609
2610 if (LeftRect > RightRect) { INT tmp = LeftRect; LeftRect = RightRect; RightRect = tmp; }
2611 if (TopRect > BottomRect) { INT tmp = TopRect; TopRect = BottomRect; BottomRect = tmp; }
2612
2613 if((LeftRect != RightRect) && (TopRect != BottomRect))
2614 {
2615 firstRect = (PRECT)rgn->Buffer;
2616 ASSERT( firstRect );
2617 firstRect->left = rgn->rdh.rcBound.left = LeftRect;
2618 firstRect->top = rgn->rdh.rcBound.top = TopRect;
2619 firstRect->right = rgn->rdh.rcBound.right = RightRect;
2620 firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
2621 rgn->rdh.nCount = 1;
2622 rgn->rdh.iType = SIMPLEREGION;
2623 } else
2624 EMPTY_REGION(rgn);
2625
2626 RGNDATA_UnlockRgn( rgn );
2627 return TRUE;
2628 }
2629
2630 HRGN STDCALL
2631 NtGdiUnionRectWithRgn(HRGN hDest, CONST PRECT UnsafeRect)
2632 {
2633 RECT SafeRect;
2634 PROSRGNDATA Rgn;
2635
2636 if(!(Rgn = (PROSRGNDATA)RGNDATA_LockRgn(hDest)))
2637 {
2638 SetLastWin32Error(ERROR_INVALID_HANDLE);
2639 return NULL;
2640 }
2641
2642 if (! NT_SUCCESS(MmCopyFromCaller(&SafeRect, UnsafeRect, sizeof(RECT))))
2643 {
2644 RGNDATA_UnlockRgn(Rgn);
2645 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2646 return NULL;
2647 }
2648
2649 UnsafeIntUnionRectWithRgn(Rgn, &SafeRect);
2650 RGNDATA_UnlockRgn(Rgn);
2651 return hDest;
2652 }
2653
2654 /*!
2655 * MSDN: GetRegionData, Return Values:
2656 *
2657 * "If the function succeeds and dwCount specifies an adequate number of bytes,
2658 * the return value is always dwCount. If dwCount is too small or the function
2659 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
2660 * required number of bytes.
2661 *
2662 * If the function fails, the return value is zero."
2663 */
2664 DWORD STDCALL NtGdiGetRegionData(HRGN hrgn, DWORD count, LPRGNDATA rgndata)
2665 {
2666 DWORD size;
2667 PROSRGNDATA obj = RGNDATA_LockRgn( hrgn );
2668
2669 if(!obj)
2670 return 0;
2671
2672 size = obj->rdh.nCount * sizeof(RECT);
2673 if(count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
2674 {
2675 RGNDATA_UnlockRgn( obj );
2676 if (rgndata) /* buffer is too small, signal it by return 0 */
2677 return 0;
2678 else /* user requested buffer size with rgndata NULL */
2679 return size + sizeof(RGNDATAHEADER);
2680 }
2681
2682 //first we copy the header then we copy buffer
2683 if( !NT_SUCCESS( MmCopyToCaller( rgndata, obj, sizeof( RGNDATAHEADER )))){
2684 RGNDATA_UnlockRgn( obj );
2685 return 0;
2686 }
2687 if( !NT_SUCCESS( MmCopyToCaller( (char*)rgndata+sizeof( RGNDATAHEADER ), obj->Buffer, size ))){
2688 RGNDATA_UnlockRgn( obj );
2689 return 0;
2690 }
2691
2692 RGNDATA_UnlockRgn( obj );
2693 return size + sizeof(RGNDATAHEADER);
2694 }
2695
2696
2697 /***********************************************************************
2698 * REGION_InsertEdgeInET
2699 *
2700 * Insert the given edge into the edge table.
2701 * First we must find the correct bucket in the
2702 * Edge table, then find the right slot in the
2703 * bucket. Finally, we can insert it.
2704 *
2705 */
2706 static void FASTCALL REGION_InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE,
2707 INT scanline, ScanLineListBlock **SLLBlock, INT *iSLLBlock)
2708
2709 {
2710 EdgeTableEntry *start, *prev;
2711 ScanLineList *pSLL, *pPrevSLL;
2712 ScanLineListBlock *tmpSLLBlock;
2713
2714 /*
2715 * find the right bucket to put the edge into
2716 */
2717 pPrevSLL = &ET->scanlines;
2718 pSLL = pPrevSLL->next;
2719 while (pSLL && (pSLL->scanline < scanline))
2720 {
2721 pPrevSLL = pSLL;
2722 pSLL = pSLL->next;
2723 }
2724
2725 /*
2726 * reassign pSLL (pointer to ScanLineList) if necessary
2727 */
2728 if ((!pSLL) || (pSLL->scanline > scanline))
2729 {
2730 if (*iSLLBlock > SLLSPERBLOCK-1)
2731 {
2732 tmpSLLBlock = ExAllocatePoolWithTag( PagedPool, sizeof(ScanLineListBlock), TAG_REGION);
2733 if(!tmpSLLBlock)
2734 {
2735 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2736 /* FIXME - free resources? */
2737 return;
2738 }
2739 (*SLLBlock)->next = tmpSLLBlock;
2740 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
2741 *SLLBlock = tmpSLLBlock;
2742 *iSLLBlock = 0;
2743 }
2744 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2745
2746 pSLL->next = pPrevSLL->next;
2747 pSLL->edgelist = (EdgeTableEntry *)NULL;
2748 pPrevSLL->next = pSLL;
2749 }
2750 pSLL->scanline = scanline;
2751
2752 /*
2753 * now insert the edge in the right bucket
2754 */
2755 prev = (EdgeTableEntry *)NULL;
2756 start = pSLL->edgelist;
2757 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2758 {
2759 prev = start;
2760 start = start->next;
2761 }
2762 ETE->next = start;
2763
2764 if (prev)
2765 prev->next = ETE;
2766 else
2767 pSLL->edgelist = ETE;
2768 }
2769
2770 /***********************************************************************
2771 * REGION_loadAET
2772 *
2773 * This routine moves EdgeTableEntries from the
2774 * EdgeTable into the Active Edge Table,
2775 * leaving them sorted by smaller x coordinate.
2776 *
2777 */
2778 static void FASTCALL REGION_loadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs)
2779 {
2780 EdgeTableEntry *pPrevAET;
2781 EdgeTableEntry *tmp;
2782
2783 pPrevAET = AET;
2784 AET = AET->next;
2785 while (ETEs)
2786 {
2787 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2788 {
2789 pPrevAET = AET;
2790 AET = AET->next;
2791 }
2792 tmp = ETEs->next;
2793 ETEs->next = AET;
2794 if (AET)
2795 AET->back = ETEs;
2796 ETEs->back = pPrevAET;
2797 pPrevAET->next = ETEs;
2798 pPrevAET = ETEs;
2799
2800 ETEs = tmp;
2801 }
2802 }
2803
2804 /***********************************************************************
2805 * REGION_computeWAET
2806 *
2807 * This routine links the AET by the
2808 * nextWETE (winding EdgeTableEntry) link for
2809 * use by the winding number rule. The final
2810 * Active Edge Table (AET) might look something
2811 * like:
2812 *
2813 * AET
2814 * ---------- --------- ---------
2815 * |ymax | |ymax | |ymax |
2816 * | ... | |... | |... |
2817 * |next |->|next |->|next |->...
2818 * |nextWETE| |nextWETE| |nextWETE|
2819 * --------- --------- ^--------
2820 * | | |
2821 * V-------------------> V---> ...
2822 *
2823 */
2824 static void FASTCALL REGION_computeWAET(EdgeTableEntry *AET)
2825 {
2826 register EdgeTableEntry *pWETE;
2827 register int inside = 1;
2828 register int isInside = 0;
2829
2830 AET->nextWETE = (EdgeTableEntry *)NULL;
2831 pWETE = AET;
2832 AET = AET->next;
2833 while (AET)
2834 {
2835 if (AET->ClockWise)
2836 isInside++;
2837 else
2838 isInside--;
2839
2840 if ((!inside && !isInside) ||
2841 ( inside && isInside))
2842 {
2843 pWETE->nextWETE = AET;
2844 pWETE = AET;
2845 inside = !inside;
2846 }
2847 AET = AET->next;
2848 }
2849 pWETE->nextWETE = (EdgeTableEntry *)NULL;
2850 }
2851
2852 /***********************************************************************
2853 * REGION_InsertionSort
2854 *
2855 * Just a simple insertion sort using
2856 * pointers and back pointers to sort the Active
2857 * Edge Table.
2858 *
2859 */
2860 static BOOL FASTCALL REGION_InsertionSort(EdgeTableEntry *AET)
2861 {
2862 EdgeTableEntry *pETEchase;
2863 EdgeTableEntry *pETEinsert;
2864 EdgeTableEntry *pETEchaseBackTMP;
2865 BOOL changed = FALSE;
2866
2867 AET = AET->next;
2868 while (AET)
2869 {
2870 pETEinsert = AET;
2871 pETEchase = AET;
2872 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2873 pETEchase = pETEchase->back;
2874
2875 AET = AET->next;
2876 if (pETEchase != pETEinsert)
2877 {
2878 pETEchaseBackTMP = pETEchase->back;
2879 pETEinsert->back->next = AET;
2880 if (AET)
2881 AET->back = pETEinsert->back;
2882 pETEinsert->next = pETEchase;
2883 pETEchase->back->next = pETEinsert;
2884 pETEchase->back = pETEinsert;
2885 pETEinsert->back = pETEchaseBackTMP;
2886 changed = TRUE;
2887 }
2888 }
2889 return changed;
2890 }
2891
2892 /***********************************************************************
2893 * REGION_FreeStorage
2894 *
2895 * Clean up our act.
2896 */
2897 static void FASTCALL REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
2898 {
2899 ScanLineListBlock *tmpSLLBlock;
2900
2901 while (pSLLBlock)
2902 {
2903 tmpSLLBlock = pSLLBlock->next;
2904 ExFreePool( pSLLBlock );
2905 pSLLBlock = tmpSLLBlock;
2906 }
2907 }
2908
2909
2910 /***********************************************************************
2911 * REGION_PtsToRegion
2912 *
2913 * Create an array of rectangles from a list of points.
2914 */
2915 static int FASTCALL REGION_PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2916 POINTBLOCK *FirstPtBlock, ROSRGNDATA *reg)
2917 {
2918 RECT *rects;
2919 POINT *pts;
2920 POINTBLOCK *CurPtBlock;
2921 int i;
2922 RECT *extents, *temp;
2923 INT numRects;
2924
2925 extents = &reg->rdh.rcBound;
2926
2927 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2928
2929 if(!(temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION)))
2930 {
2931 return 0;
2932 }
2933 if(reg->Buffer != NULL)
2934 {
2935 COPY_RECTS(temp, reg->Buffer, reg->rdh.nCount);
2936 if(reg->Buffer != &reg->rdh.rcBound)
2937 ExFreePool(reg->Buffer);
2938 }
2939 reg->Buffer = temp;
2940
2941 reg->rdh.nCount = numRects;
2942 CurPtBlock = FirstPtBlock;
2943 rects = reg->Buffer - 1;
2944 numRects = 0;
2945 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2946
2947 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
2948 /* the loop uses 2 points per iteration */
2949 i = NUMPTSTOBUFFER >> 1;
2950 if (!numFullPtBlocks)
2951 i = iCurPtBlock >> 1;
2952 for (pts = CurPtBlock->pts; i--; pts += 2) {
2953 if (pts->x == pts[1].x)
2954 continue;
2955 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
2956 pts[1].x == rects->right &&
2957 (numRects == 1 || rects[-1].top != rects->top) &&
2958 (i && pts[2].y > pts[1].y)) {
2959 rects->bottom = pts[1].y + 1;
2960 continue;
2961 }
2962 numRects++;
2963 rects++;
2964 rects->left = pts->x; rects->top = pts->y;
2965 rects->right = pts[1].x; rects->bottom = pts[1].y + 1;
2966 if (rects->left < extents->left)
2967 extents->left = rects->left;
2968 if (rects->right > extents->right)
2969 extents->right = rects->right;
2970 }
2971 CurPtBlock = CurPtBlock->next;
2972 }
2973
2974 if (numRects) {
2975 extents->top = reg->Buffer->top;
2976 extents->bottom = rects->bottom;
2977 } else {
2978 extents->left = 0;
2979 extents->top = 0;
2980 extents->right = 0;
2981 extents->bottom = 0;
2982 }
2983 reg->rdh.nCount = numRects;
2984
2985 return(TRUE);
2986 }
2987
2988 /***********************************************************************
2989 * REGION_CreateEdgeTable
2990 *
2991 * This routine creates the edge table for
2992 * scan converting polygons.
2993 * The Edge Table (ET) looks like:
2994 *
2995 * EdgeTable
2996 * --------
2997 * | ymax | ScanLineLists
2998 * |scanline|-->------------>-------------->...
2999 * -------- |scanline| |scanline|
3000 * |edgelist| |edgelist|
3001 * --------- ---------
3002 * | |
3003 * | |
3004 * V V
3005 * list of ETEs list of ETEs
3006 *
3007 * where ETE is an EdgeTableEntry data structure,
3008 * and there is one ScanLineList per scanline at
3009 * which an edge is initially entered.
3010 *
3011 */
3012 static void FASTCALL REGION_CreateETandAET(const INT *Count, INT nbpolygons,
3013 const POINT *pts, EdgeTable *ET, EdgeTableEntry *AET,
3014 EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock)
3015 {
3016 const POINT *top, *bottom;
3017 const POINT *PrevPt, *CurrPt, *EndPt;
3018 INT poly, count;
3019 int iSLLBlock = 0;
3020 int dy;
3021
3022
3023 /*
3024 * initialize the Active Edge Table
3025 */
3026 AET->next = (EdgeTableEntry *)NULL;
3027 AET->back = (EdgeTableEntry *)NULL;
3028 AET->nextWETE = (EdgeTableEntry *)NULL;
3029 AET->bres.minor_axis = SMALL_COORDINATE;
3030
3031 /*
3032 * initialize the Edge Table.
3033 */
3034 ET->scanlines.next = (ScanLineList *)NULL;
3035 ET->ymax = SMALL_COORDINATE;
3036 ET->ymin = LARGE_COORDINATE;
3037 pSLLBlock->next = (ScanLineListBlock *)NULL;
3038
3039 EndPt = pts - 1;
3040 for(poly = 0; poly < nbpolygons; poly++)
3041 {
3042 count = Count[poly];
3043 EndPt += count;
3044 if(count < 2)
3045 continue;
3046
3047 PrevPt = EndPt;
3048
3049 /*
3050 * for each vertex in the array of points.
3051 * In this loop we are dealing with two vertices at
3052 * a time -- these make up one edge of the polygon.
3053 */
3054 while (count--)
3055 {
3056 CurrPt = pts++;
3057
3058 /*
3059 * find out which point is above and which is below.
3060 */
3061 if (PrevPt->y > CurrPt->y)
3062 {
3063 bottom = PrevPt, top = CurrPt;
3064 pETEs->ClockWise = 0;
3065 }
3066 else
3067 {
3068 bottom = CurrPt, top = PrevPt;
3069 pETEs->ClockWise = 1;
3070 }
3071
3072 /*
3073 * don't add horizontal edges to the Edge table.
3074 */
3075 if (bottom->y != top->y)
3076 {
3077 pETEs->ymax = bottom->y-1;
3078 /* -1 so we don't get last scanline */
3079
3080 /*
3081 * initialize integer edge algorithm
3082 */
3083 dy = bottom->y - top->y;
3084 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
3085
3086 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
3087 &iSLLBlock);
3088
3089 if (PrevPt->y > ET->ymax)
3090 ET->ymax = PrevPt->y;
3091 if (PrevPt->y < ET->ymin)
3092 ET->ymin = PrevPt->y;
3093 pETEs++;
3094 }
3095
3096 PrevPt = CurrPt;
3097 }
3098 }
3099 }
3100
3101 HRGN FASTCALL
3102 IntCreatePolyPolgonRgn(POINT *Pts,
3103 INT *Count,
3104 INT nbpolygons,
3105 INT mode)
3106 {
3107 HRGN hrgn;
3108 ROSRGNDATA *region;
3109 EdgeTableEntry *pAET; /* Active Edge Table */
3110 INT y; /* current scanline */
3111 int iPts = 0; /* number of pts in buffer */
3112 EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
3113 ScanLineList *pSLL; /* current scanLineList */
3114 POINT *pts; /* output buffer */
3115 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
3116 EdgeTable ET; /* header node for ET */
3117 EdgeTableEntry AET; /* header node for AET */
3118 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
3119 ScanLineListBlock SLLBlock; /* header for scanlinelist */
3120 int fixWAET = FALSE;
3121 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
3122 POINTBLOCK *tmpPtBlock;
3123 int numFullPtBlocks = 0;
3124 INT poly, total;
3125
3126 if(!(hrgn = RGNDATA_AllocRgn(nbpolygons)))
3127 return 0;
3128 if(!(region = RGNDATA_LockRgn(hrgn)))
3129 {
3130 NtGdiDeleteObject(hrgn);
3131 return 0;
3132 }
3133
3134 /* special case a rectangle */
3135
3136 if (((nbpolygons == 1) && ((*Count == 4) ||
3137 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
3138 (((Pts[0].y == Pts[1].y) &&
3139 (Pts[1].x == Pts[2].x) &&
3140 (Pts[2].y == Pts[3].y) &&
3141 (Pts[3].x == Pts[0].x)) ||
3142 ((Pts[0].x == Pts[1].x) &&
3143 (Pts[1].y == Pts[2].y) &&
3144 (Pts[2].x == Pts[3].x) &&
3145 (Pts[3].y == Pts[0].y))))
3146 {
3147 RGNDATA_UnlockRgn( region );
3148 NtGdiSetRectRgn( hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y),
3149 max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y) );
3150 return hrgn;
3151 }
3152
3153 for(poly = total = 0; poly < nbpolygons; poly++)
3154 total += Count[poly];
3155 if (! (pETEs = ExAllocatePoolWithTag( PagedPool, sizeof(EdgeTableEntry) * total, TAG_REGION )))
3156 {
3157 NtGdiDeleteObject( hrgn );
3158 return 0;
3159 }
3160 pts = FirstPtBlock.pts;
3161 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
3162 pSLL = ET.scanlines.next;
3163 curPtBlock = &FirstPtBlock;
3164
3165 if (mode != WINDING) {
3166 /*
3167 * for each scanline
3168 */
3169 for (y = ET.ymin; y < ET.ymax; y++) {
3170 /*
3171 * Add a new edge to the active edge table when we
3172 * get to the next edge.
3173 */
3174 if (pSLL != NULL && y == pSLL->scanline) {
3175 REGION_loadAET(&AET, pSLL->edgelist);
3176 pSLL = pSLL->next;
3177 }
3178 pPrevAET = &AET;
3179 pAET = AET.next;
3180
3181 /*
3182 * for each active edge
3183 */
3184 while (pAET) {
3185 pts->x = pAET->bres.minor_axis, pts->y = y;
3186 pts++, iPts++;
3187
3188 /*
3189 * send out the buffer
3190 */
3191 if (iPts == NUMPTSTOBUFFER) {
3192 tmpPtBlock = ExAllocatePoolWithTag( PagedPool, sizeof(POINTBLOCK), TAG_REGION);
3193 if(!tmpPtBlock) {
3194 DPRINT1("Can't alloc tPB\n");
3195 ExFreePool(pETEs);
3196 return 0;
3197 }
3198 curPtBlock->next = tmpPtBlock;
3199 curPtBlock = tmpPtBlock;
3200 pts = curPtBlock->pts;
3201 numFullPtBlocks++;
3202 iPts = 0;
3203 }
3204 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
3205 }
3206 REGION_InsertionSort(&AET);
3207 }
3208 }
3209 else {
3210 /*
3211 * for each scanline
3212 */
3213 for (y = ET.ymin; y < ET.ymax; y++) {
3214 /*
3215 * Add a new edge to the active edge table when we
3216 * get to the next edge.
3217 */
3218 if (pSLL != NULL && y == pSLL->scanline) {
3219 REGION_loadAET(&AET, pSLL->edgelist);
3220 REGION_computeWAET(&AET);
3221 pSLL = pSLL->next;
3222 }
3223 pPrevAET = &AET;
3224 pAET = AET.next;
3225 pWETE = pAET;
3226
3227 /*
3228 * for each active edge
3229 */
3230 while (pAET) {
3231 /*
3232 * add to the buffer only those edges that
3233 * are in the Winding active edge table.
3234 */
3235 if (pWETE == pAET) {
3236 pts->x = pAET->bres.minor_axis, pts->y = y;
3237 pts++, iPts++;
3238
3239 /*
3240 * send out the buffer
3241 */
3242 if (iPts == NUMPTSTOBUFFER) {
3243 tmpPtBlock = ExAllocatePoolWithTag( PagedPool,
3244 sizeof(POINTBLOCK), TAG_REGION );
3245 if(!tmpPtBlock) {
3246 DPRINT1("Can't alloc tPB\n");
3247 ExFreePool(pETEs);
3248 NtGdiDeleteObject( hrgn );
3249 return 0;
3250 }
3251 curPtBlock->next = tmpPtBlock;
3252 curPtBlock = tmpPtBlock;
3253 pts = curPtBlock->pts;
3254 numFullPtBlocks++; iPts = 0;
3255 }
3256 pWETE = pWETE->nextWETE;
3257 }
3258 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
3259 }
3260
3261 /*
3262 * recompute the winding active edge table if
3263 * we just resorted or have exited an edge.
3264 */
3265 if (REGION_InsertionSort(&AET) || fixWAET) {
3266 REGION_computeWAET(&AET);
3267 fixWAET = FALSE;
3268 }
3269 }
3270 }
3271 REGION_FreeStorage(SLLBlock.next);
3272 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
3273
3274 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
3275 tmpPtBlock = curPtBlock->next;
3276 ExFreePool( curPtBlock );
3277 curPtBlock = tmpPtBlock;
3278 }
3279 ExFreePool( pETEs );
3280 RGNDATA_UnlockRgn( region );
3281 return hrgn;
3282 }
3283
3284 HRGN
3285 STDCALL
3286 NtGdiCreatePolygonRgn(CONST PPOINT pt,
3287 INT Count,
3288 INT PolyFillMode)
3289 {
3290 POINT *SafePoints;
3291 NTSTATUS Status;
3292 HRGN hRgn;
3293
3294
3295 if (pt == NULL || Count == 0 ||
3296 (PolyFillMode != WINDING && PolyFillMode != ALTERNATE))
3297 {
3298 /* Windows doesn't set a last error here */
3299 return (HRGN)0;
3300 }
3301
3302 if (Count == 1)
3303 {
3304 /* can't create a region with only one point! */
3305 SetLastWin32Error(ERROR_INVALID_PARAMETER);
3306 return (HRGN)0;
3307 }
3308
3309 if (Count == 2)
3310 {
3311 /* Windows creates an empty region! */
3312 ROSRGNDATA *rgn;
3313
3314 if(!(hRgn = RGNDATA_AllocRgn(1)))
3315 {
3316 return (HRGN)0;
3317 }
3318 if(!(rgn = RGNDATA_LockRgn(hRgn)))
3319 {
3320 NtGdiDeleteObject(hRgn);
3321 return (HRGN)0;
3322 }
3323
3324 EMPTY_REGION(rgn);
3325
3326 RGNDATA_UnlockRgn(rgn);
3327 return hRgn;
3328 }
3329
3330 if (!(SafePoints = ExAllocatePoolWithTag(PagedPool, Count * sizeof(POINT), TAG_REGION)))
3331 {
3332 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
3333 return (HRGN)0;
3334 }
3335
3336 Status = MmCopyFromCaller(SafePoints, pt, Count * sizeof(POINT));
3337 if (!NT_SUCCESS(Status))
3338 {
3339 ExFreePool(SafePoints);
3340 SetLastNtError(Status);
3341 return (HRGN)0;
3342 }
3343
3344 hRgn = IntCreatePolyPolgonRgn(SafePoints, &Count, 1, PolyFillMode);
3345
3346 ExFreePool(SafePoints);
3347 return hRgn;
3348 }
3349
3350 HRGN
3351 STDCALL
3352 NtGdiCreatePolyPolygonRgn(CONST PPOINT pt,
3353 CONST PINT PolyCounts,
3354 INT Count,
3355 INT PolyFillMode)
3356 {
3357 POINT *Safept;
3358 INT *SafePolyCounts;
3359 INT nPoints, nEmpty, nInvalid, i;
3360 HRGN hRgn;
3361 NTSTATUS Status;
3362
3363 if (pt == NULL || PolyCounts == NULL || Count == 0 ||
3364 (PolyFillMode != WINDING && PolyFillMode != ALTERNATE))
3365 {
3366 /* Windows doesn't set a last error here */
3367 return (HRGN)0;
3368 }
3369
3370 if (!(SafePolyCounts = ExAllocatePoolWithTag(PagedPool, Count * sizeof(INT), TAG_REGION)))
3371 {
3372 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
3373 return (HRGN)0;
3374 }
3375
3376 Status = MmCopyFromCaller(SafePolyCounts, PolyCounts, Count * sizeof(INT));
3377 if (!NT_SUCCESS(Status))
3378 {
3379 ExFreePool(SafePolyCounts);
3380 SetLastNtError(Status);
3381 return (HRGN)0;
3382 }
3383
3384 /* validate poligons */
3385 nPoints = 0;
3386 nEmpty = 0;
3387 nInvalid = 0;
3388 for (i = 0; i < Count; i++)
3389 {
3390 if (SafePolyCounts[i] == 0)
3391 {
3392 nEmpty++;
3393 }
3394 if (SafePolyCounts[i] == 1)
3395 {
3396 nInvalid++;
3397 }
3398 nPoints += SafePolyCounts[i];
3399 }
3400
3401 if (nEmpty == Count)
3402 {
3403 /* if all polygon counts are zero, return without setting a last error code. */
3404 ExFreePool(SafePolyCounts);
3405 return (HRGN)0;
3406 }
3407 if (nInvalid != 0)
3408 {
3409 /* if at least one poly count is 1, fail */
3410 ExFreePool(SafePolyCounts);
3411 SetLastWin32Error(ERROR_INVALID_PARAMETER);
3412 return (HRGN)0;
3413 }
3414
3415 /* copy points */
3416 if (!(Safept = ExAllocatePoolWithTag(PagedPool, nPoints * sizeof(POINT), TAG_REGION)))
3417 {
3418 ExFreePool(SafePolyCounts);
3419 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
3420 return (HRGN)0;
3421 }
3422
3423 Status = MmCopyFromCaller(Safept, pt, nPoints * sizeof(POINT));
3424 if (!NT_SUCCESS(Status))
3425 {
3426 ExFreePool(Safept);
3427 ExFreePool(SafePolyCounts);
3428 SetLastNtError(Status);
3429 return (HRGN)0;
3430 }
3431
3432 /* now we're ready to calculate the region safely */
3433 hRgn = IntCreatePolyPolgonRgn(Safept, SafePolyCounts, Count, PolyFillMode);
3434
3435 ExFreePool(Safept);
3436 ExFreePool(SafePolyCounts);
3437 return hRgn;
3438 }
3439
3440 /* EOF */