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