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