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