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