[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 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_CropAndOffsetRegion
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 BOOL
1842 FASTCALL
1843 REGION_CreateFrameRgn(
1844 HRGN hDest,
1845 HRGN hSrc,
1846 INT x,
1847 INT y)
1848 {
1849 PREGION srcObj, destObj;
1850 PRECTL rc;
1851 ULONG i;
1852
1853 srcObj = RGNOBJAPI_Lock(hSrc, NULL);
1854 if (srcObj == NULL)
1855 {
1856 return FALSE;
1857 }
1858
1859 if (!REGION_NOT_EMPTY(srcObj))
1860 {
1861 RGNOBJAPI_Unlock(srcObj);
1862 return FALSE;
1863 }
1864
1865 destObj = RGNOBJAPI_Lock(hDest, NULL);
1866 if (destObj == NULL)
1867 {
1868 RGNOBJAPI_Unlock(srcObj);
1869 return FALSE;
1870 }
1871
1872 EMPTY_REGION(destObj);
1873 if (!REGION_CopyRegion(destObj, srcObj))
1874 {
1875 RGNOBJAPI_Unlock(destObj);
1876 RGNOBJAPI_Unlock(srcObj);
1877 return FALSE;
1878 }
1879
1880 if (REGION_Complexity(srcObj) == SIMPLEREGION)
1881 {
1882 if (!REGION_CreateSimpleFrameRgn(destObj, x, y))
1883 {
1884 EMPTY_REGION(destObj);
1885 RGNOBJAPI_Unlock(destObj);
1886 RGNOBJAPI_Unlock(srcObj);
1887 return FALSE;
1888 }
1889 }
1890 else
1891 {
1892 /* Original region moved to right */
1893 rc = srcObj->Buffer;
1894 for (i = 0; i < srcObj->rdh.nCount; i++)
1895 {
1896 rc->left += x;
1897 rc->right += x;
1898 rc++;
1899 }
1900
1901 REGION_IntersectRegion(destObj, destObj, srcObj);
1902
1903 /* Original region moved to left */
1904 rc = srcObj->Buffer;
1905 for (i = 0; i < srcObj->rdh.nCount; i++)
1906 {
1907 rc->left -= 2 * x;
1908 rc->right -= 2 * x;
1909 rc++;
1910 }
1911
1912 REGION_IntersectRegion(destObj, destObj, srcObj);
1913
1914 /* Original region moved down */
1915 rc = srcObj->Buffer;
1916 for (i = 0; i < srcObj->rdh.nCount; i++)
1917 {
1918 rc->left += x;
1919 rc->right += x;
1920 rc->top += y;
1921 rc->bottom += y;
1922 rc++;
1923 }
1924
1925 REGION_IntersectRegion(destObj, destObj, srcObj);
1926
1927 /* Original region moved up */
1928 rc = srcObj->Buffer;
1929 for (i = 0; i < srcObj->rdh.nCount; i++)
1930 {
1931 rc->top -= 2 * y;
1932 rc->bottom -= 2 * y;
1933 rc++;
1934 }
1935
1936 REGION_IntersectRegion(destObj, destObj, srcObj);
1937
1938 /* Restore the original region */
1939 rc = srcObj->Buffer;
1940 for (i = 0; i < srcObj->rdh.nCount; i++)
1941 {
1942 rc->top += y;
1943 rc->bottom += y;
1944 rc++;
1945 }
1946
1947 REGION_SubtractRegion(destObj, srcObj, destObj);
1948 }
1949
1950 RGNOBJAPI_Unlock(destObj);
1951 RGNOBJAPI_Unlock(srcObj);
1952 return TRUE;
1953 }
1954
1955
1956 static
1957 BOOL
1958 FASTCALL
1959 REGION_LPTODP(
1960 _In_ PDC dc,
1961 _Inout_ PREGION RgnDest,
1962 _In_ PREGION RgnSrc)
1963 {
1964 RECTL *pCurRect, *pEndRect;
1965 RECTL tmpRect;
1966 PDC_ATTR pdcattr;
1967
1968 if (dc == NULL)
1969 return FALSE;
1970 pdcattr = dc->pdcattr;
1971
1972 if (pdcattr->iMapMode == MM_TEXT) // Requires only a translation
1973 {
1974 if (IntGdiCombineRgn(RgnDest, RgnSrc, 0, RGN_COPY) == ERROR)
1975 return FALSE;
1976
1977 IntGdiOffsetRgn(RgnDest,
1978 pdcattr->ptlViewportOrg.x - pdcattr->ptlWindowOrg.x,
1979 pdcattr->ptlViewportOrg.y - pdcattr->ptlWindowOrg.y);
1980 return TRUE;
1981 }
1982
1983 EMPTY_REGION(RgnDest);
1984
1985 pEndRect = RgnSrc->Buffer + RgnSrc->rdh.nCount;
1986 for (pCurRect = RgnSrc->Buffer; pCurRect < pEndRect; pCurRect++)
1987 {
1988 tmpRect = *pCurRect;
1989 tmpRect.left = XLPTODP(pdcattr, tmpRect.left);
1990 tmpRect.top = YLPTODP(pdcattr, tmpRect.top);
1991 tmpRect.right = XLPTODP(pdcattr, tmpRect.right);
1992 tmpRect.bottom = YLPTODP(pdcattr, tmpRect.bottom);
1993
1994 if (tmpRect.left > tmpRect.right)
1995 {
1996 INT tmp = tmpRect.left;
1997 tmpRect.left = tmpRect.right;
1998 tmpRect.right = tmp;
1999 }
2000
2001 if (tmpRect.top > tmpRect.bottom)
2002 {
2003 INT tmp = tmpRect.top;
2004 tmpRect.top = tmpRect.bottom;
2005 tmpRect.bottom = tmp;
2006 }
2007
2008 REGION_UnionRectWithRgn(RgnDest, &tmpRect);
2009 }
2010
2011 return TRUE;
2012 }
2013
2014 PREGION
2015 FASTCALL
2016 REGION_AllocRgnWithHandle(
2017 INT nReg)
2018 {
2019 //HRGN hReg;
2020 PREGION pReg;
2021
2022 pReg = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE,
2023 sizeof(REGION),
2024 BASEFLAG_LOOKASIDE);
2025 if (pReg == NULL)
2026 {
2027 DPRINT1("Could not allocate a palette.\n");
2028 return NULL;
2029 }
2030
2031 if (!GDIOBJ_hInsertObject(&pReg->BaseObject, GDI_OBJ_HMGR_POWNED))
2032 {
2033 DPRINT1("Could not insert palette into handle table.\n");
2034 GDIOBJ_vFreeObject(&pReg->BaseObject);
2035 return NULL;
2036 }
2037
2038 //hReg = pReg->BaseObject.hHmgr;
2039
2040 if ((nReg == 0) || (nReg == 1))
2041 {
2042 /* Testing shows that > 95% of all regions have only 1 rect.
2043 Including that here saves us from having to do another allocation */
2044 pReg->Buffer = &pReg->rdh.rcBound;
2045 }
2046 else
2047 {
2048 pReg->Buffer = ExAllocatePoolWithTag(PagedPool,
2049 nReg * sizeof(RECT),
2050 TAG_REGION);
2051 if (pReg->Buffer == NULL)
2052 {
2053 DPRINT1("Could not allocate region buffer\n");
2054 GDIOBJ_vDeleteObject(&pReg->BaseObject);
2055 return NULL;
2056 }
2057 }
2058
2059 EMPTY_REGION(pReg);
2060 pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
2061 pReg->rdh.nCount = nReg;
2062 pReg->rdh.nRgnSize = nReg * sizeof(RECT);
2063 pReg->prgnattr = &pReg->rgnattr;
2064
2065 return pReg;
2066 }
2067
2068 BOOL
2069 NTAPI
2070 REGION_bAllocRgnAttr(
2071 PREGION prgn)
2072 {
2073 PPROCESSINFO ppi;
2074 PRGN_ATTR prgnattr;
2075
2076 ppi = PsGetCurrentProcessWin32Process();
2077 ASSERT(ppi);
2078
2079 prgnattr = GdiPoolAllocate(ppi->pPoolRgnAttr);
2080 if (prgnattr == NULL)
2081 {
2082 DPRINT1("Could not allocate RGN attr\n");
2083 return FALSE;
2084 }
2085
2086 /* Set the object attribute in the handle table */
2087 prgn->prgnattr = prgnattr;
2088 GDIOBJ_vSetObjectAttr(&prgn->BaseObject, prgnattr);
2089
2090 return TRUE;
2091 }
2092
2093
2094 //
2095 // Allocate User Space Region Handle.
2096 //
2097 PREGION
2098 FASTCALL
2099 REGION_AllocUserRgnWithHandle(
2100 INT nRgn)
2101 {
2102 PREGION prgn;
2103
2104 prgn = REGION_AllocRgnWithHandle(nRgn);
2105 if (prgn == NULL)
2106 {
2107 return NULL;
2108 }
2109
2110 if (!REGION_bAllocRgnAttr(prgn))
2111 {
2112 ASSERT(FALSE);
2113 }
2114
2115 return prgn;
2116 }
2117
2118 VOID
2119 NTAPI
2120 REGION_vSyncRegion(
2121 PREGION pRgn)
2122 {
2123 PRGN_ATTR pRgn_Attr = NULL;
2124
2125 if (pRgn && pRgn->prgnattr != &pRgn->rgnattr)
2126 {
2127 pRgn_Attr = GDIOBJ_pvGetObjectAttr(&pRgn->BaseObject);
2128
2129 if ( pRgn_Attr )
2130 {
2131 _SEH2_TRY
2132 {
2133 if ( !(pRgn_Attr->AttrFlags & ATTR_CACHED) )
2134 {
2135 if ( pRgn_Attr->AttrFlags & (ATTR_RGN_VALID|ATTR_RGN_DIRTY) )
2136 {
2137 switch (pRgn_Attr->iComplexity)
2138 {
2139 case NULLREGION:
2140 EMPTY_REGION( pRgn );
2141 break;
2142
2143 case SIMPLEREGION:
2144 REGION_SetRectRgn( pRgn,
2145 pRgn_Attr->Rect.left,
2146 pRgn_Attr->Rect.top,
2147 pRgn_Attr->Rect.right,
2148 pRgn_Attr->Rect.bottom );
2149 break;
2150 }
2151 pRgn_Attr->AttrFlags &= ~ATTR_RGN_DIRTY;
2152 }
2153 }
2154 }
2155 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2156 {
2157 (VOID)0;
2158 }
2159 _SEH2_END;
2160 }
2161 }
2162
2163 }
2164
2165 PREGION
2166 FASTCALL
2167 RGNOBJAPI_Lock(
2168 HRGN hRgn,
2169 PRGN_ATTR *ppRgn_Attr)
2170 {
2171 PREGION pRgn;
2172
2173 pRgn = REGION_LockRgn(hRgn);
2174 if (pRgn == NULL)
2175 return NULL;
2176
2177 REGION_vSyncRegion(pRgn);
2178
2179 if (ppRgn_Attr)
2180 *ppRgn_Attr = pRgn->prgnattr;
2181
2182 return pRgn;
2183 }
2184
2185 VOID
2186 FASTCALL
2187 RGNOBJAPI_Unlock(
2188 PREGION pRgn)
2189 {
2190 PRGN_ATTR pRgn_Attr;
2191
2192 if (pRgn && GreGetObjectOwner(pRgn->BaseObject.hHmgr) == GDI_OBJ_HMGR_POWNED)
2193 {
2194 pRgn_Attr = GDIOBJ_pvGetObjectAttr(&pRgn->BaseObject);
2195
2196 if ( pRgn_Attr )
2197 {
2198 _SEH2_TRY
2199 {
2200 if ( pRgn_Attr->AttrFlags & ATTR_RGN_VALID )
2201 {
2202 pRgn_Attr->iComplexity = REGION_Complexity( pRgn );
2203 pRgn_Attr->Rect.left = pRgn->rdh.rcBound.left;
2204 pRgn_Attr->Rect.top = pRgn->rdh.rcBound.top;
2205 pRgn_Attr->Rect.right = pRgn->rdh.rcBound.right;
2206 pRgn_Attr->Rect.bottom = pRgn->rdh.rcBound.bottom;
2207 }
2208 }
2209 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2210 {
2211 (VOID)0;
2212 }
2213 _SEH2_END;
2214 }
2215 }
2216 REGION_UnlockRgn(pRgn);
2217 }
2218
2219 /*
2220 System Regions:
2221 These regions do not use attribute sections and when allocated, use gdiobj
2222 level functions.
2223 */
2224 //
2225 // System Region Functions
2226 //
2227 PREGION
2228 FASTCALL
2229 IntSysCreateRectpRgn(
2230 INT LeftRect,
2231 INT TopRect,
2232 INT RightRect,
2233 INT BottomRect)
2234 {
2235 PREGION prgn;
2236
2237 /* Allocate a region, witout a handle */
2238 prgn = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE, sizeof(REGION), BASEFLAG_LOOKASIDE);
2239 if (prgn == NULL)
2240 {
2241 return NULL;
2242 }
2243
2244 /* Initialize it */
2245 prgn->Buffer = &prgn->rdh.rcBound;
2246 prgn->prgnattr = &prgn->rgnattr;
2247 REGION_SetRectRgn(prgn, LeftRect, TopRect, RightRect, BottomRect);
2248
2249 return prgn;
2250 }
2251
2252 VOID
2253 NTAPI
2254 REGION_vCleanup(PVOID ObjectBody)
2255 {
2256 PREGION pRgn = (PREGION)ObjectBody;
2257 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
2258 ASSERT(ppi);
2259
2260 ASSERT(pRgn->prgnattr);
2261 if (pRgn->prgnattr != &pRgn->rgnattr)
2262 GdiPoolFree(ppi->pPoolRgnAttr, pRgn->prgnattr);
2263
2264 if (pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
2265 ExFreePoolWithTag(pRgn->Buffer, TAG_REGION);
2266 }
2267
2268 VOID
2269 FASTCALL
2270 REGION_Delete(PREGION pRgn)
2271 {
2272 if (pRgn == prgnDefault)
2273 return;
2274
2275 GDIOBJ_vDeleteObject(&pRgn->BaseObject);
2276 }
2277
2278 VOID
2279 FASTCALL
2280 IntGdiReleaseRaoRgn(PDC pDC)
2281 {
2282 INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
2283 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
2284 pDC->fs |= DC_FLAG_DIRTY_RAO;
2285 Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
2286 RECTL_vSetEmptyRect(&pDC->erclClip);
2287 REGION_Delete(pDC->prgnRao);
2288 pDC->prgnRao = NULL;
2289 }
2290
2291 VOID
2292 FASTCALL
2293 IntGdiReleaseVisRgn(PDC pDC)
2294 {
2295 INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
2296 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
2297 pDC->fs |= DC_FLAG_DIRTY_RAO;
2298 Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
2299 RECTL_vSetEmptyRect(&pDC->erclClip);
2300 REGION_Delete(pDC->prgnVis);
2301 pDC->prgnVis = prgnDefault;
2302 }
2303
2304 VOID
2305 FASTCALL
2306 IntUpdateVisRectRgn(PDC pDC, PREGION pRgn)
2307 {
2308 INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
2309 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
2310 PDC_ATTR pdcattr;
2311 RECTL rcl;
2312
2313 if (Entry->Flags & GDI_ENTRY_VALIDATE_VIS)
2314 {
2315 pdcattr = pDC->pdcattr;
2316
2317 pdcattr->VisRectRegion.iComplexity = REGION_Complexity(pRgn);
2318
2319 if (pRgn && pdcattr->VisRectRegion.iComplexity != NULLREGION)
2320 {
2321 rcl.left = pRgn->rdh.rcBound.left;
2322 rcl.top = pRgn->rdh.rcBound.top;
2323 rcl.right = pRgn->rdh.rcBound.right;
2324 rcl.bottom = pRgn->rdh.rcBound.bottom;
2325
2326 rcl.left -= pDC->erclWindow.left;
2327 rcl.top -= pDC->erclWindow.top;
2328 rcl.right -= pDC->erclWindow.left;
2329 rcl.bottom -= pDC->erclWindow.top;
2330 }
2331 else
2332 {
2333 RECTL_vSetEmptyRect(&rcl);
2334 }
2335
2336 pdcattr->VisRectRegion.Rect = rcl;
2337
2338 Entry->Flags &= ~GDI_ENTRY_VALIDATE_VIS;
2339 }
2340 }
2341
2342 BOOL
2343 FASTCALL
2344 IntGdiSetRegionOwner(HRGN hRgn, DWORD OwnerMask)
2345 {
2346 PREGION prgn;
2347 PRGN_ATTR prgnattr;
2348 PPROCESSINFO ppi;
2349
2350 prgn = RGNOBJAPI_Lock(hRgn, &prgnattr);
2351 if (prgn == NULL)
2352 {
2353 return FALSE;
2354 }
2355
2356 if (prgnattr != &prgn->rgnattr)
2357 {
2358 GDIOBJ_vSetObjectAttr(&prgn->BaseObject, NULL);
2359 prgn->prgnattr = &prgn->rgnattr;
2360 ppi = PsGetCurrentProcessWin32Process();
2361 GdiPoolFree(ppi->pPoolRgnAttr, prgnattr);
2362 }
2363
2364 RGNOBJAPI_Unlock(prgn);
2365
2366 return GreSetObjectOwner(hRgn, OwnerMask);
2367 }
2368
2369 INT
2370 FASTCALL
2371 IntGdiCombineRgn(
2372 PREGION prgnDest,
2373 PREGION prgnSrc1,
2374 PREGION prgnSrc2,
2375 INT iCombineMode)
2376 {
2377
2378 if (prgnDest == NULL)
2379 {
2380 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2381 return ERROR;
2382 }
2383
2384 if (prgnSrc1 == NULL)
2385 {
2386 DPRINT("IntGdiCombineRgn: hSrc1 unavailable\n");
2387 return ERROR;
2388 }
2389
2390 if (iCombineMode == RGN_COPY)
2391 {
2392 if (!REGION_CopyRegion(prgnDest, prgnSrc1))
2393 return ERROR;
2394
2395 return REGION_Complexity(prgnDest);
2396 }
2397
2398 if (prgnSrc2 == NULL)
2399 {
2400 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", iCombineMode);
2401 ASSERT(FALSE);
2402 return ERROR;
2403 }
2404
2405 switch (iCombineMode)
2406 {
2407 case RGN_AND:
2408 REGION_IntersectRegion(prgnDest, prgnSrc1, prgnSrc2);
2409 break;
2410 case RGN_OR:
2411 REGION_UnionRegion(prgnDest, prgnSrc1, prgnSrc2);
2412 break;
2413 case RGN_XOR:
2414 REGION_XorRegion(prgnDest, prgnSrc1, prgnSrc2);
2415 break;
2416 case RGN_DIFF:
2417 REGION_SubtractRegion(prgnDest, prgnSrc1, prgnSrc2);
2418 break;
2419 }
2420
2421 return REGION_Complexity(prgnDest);
2422 }
2423
2424 INT
2425 FASTCALL
2426 REGION_GetRgnBox(
2427 PREGION Rgn,
2428 PRECTL pRect)
2429 {
2430 DWORD ret;
2431
2432 if (Rgn != NULL)
2433 {
2434 *pRect = Rgn->rdh.rcBound;
2435 ret = REGION_Complexity(Rgn);
2436
2437 return ret;
2438 }
2439 return 0; // If invalid region return zero
2440 }
2441
2442 INT
2443 APIENTRY
2444 IntGdiGetRgnBox(
2445 HRGN hRgn,
2446 PRECTL pRect)
2447 {
2448 PREGION Rgn;
2449 DWORD ret;
2450
2451 Rgn = RGNOBJAPI_Lock(hRgn, NULL);
2452 if (Rgn == NULL)
2453 {
2454 return ERROR;
2455 }
2456
2457 ret = REGION_GetRgnBox(Rgn, pRect);
2458 RGNOBJAPI_Unlock(Rgn);
2459
2460 return ret;
2461 }
2462
2463 BOOL
2464 FASTCALL
2465 IntGdiPaintRgn(
2466 PDC dc,
2467 PREGION Rgn)
2468 {
2469 PREGION VisRgn;
2470 XCLIPOBJ ClipRegion;
2471 BOOL bRet = FALSE;
2472 POINTL BrushOrigin;
2473 SURFACE *psurf;
2474 PDC_ATTR pdcattr;
2475
2476 if ((dc == NULL) || (Rgn == NULL))
2477 return FALSE;
2478
2479 pdcattr = dc->pdcattr;
2480
2481 ASSERT(!(pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY)));
2482
2483 VisRgn = IntSysCreateRectpRgn(0, 0, 0, 0);
2484 if (VisRgn == NULL)
2485 {
2486 return FALSE;
2487 }
2488
2489 // Transform region into device co-ords
2490 if (!REGION_LPTODP(dc, VisRgn, Rgn) ||
2491 IntGdiOffsetRgn(VisRgn, dc->ptlDCOrig.x, dc->ptlDCOrig.y) == ERROR)
2492 {
2493 REGION_Delete(VisRgn);
2494 return FALSE;
2495 }
2496
2497 if (dc->prgnRao)
2498 IntGdiCombineRgn(VisRgn, VisRgn, dc->prgnRao, RGN_AND);
2499
2500 IntEngInitClipObj(&ClipRegion);
2501 IntEngUpdateClipRegion(&ClipRegion,
2502 VisRgn->rdh.nCount,
2503 VisRgn->Buffer,
2504 &VisRgn->rdh.rcBound );
2505
2506 BrushOrigin.x = pdcattr->ptlBrushOrigin.x;
2507 BrushOrigin.y = pdcattr->ptlBrushOrigin.y;
2508 psurf = dc->dclevel.pSurface;
2509 /* FIXME: Handle psurf == NULL !!!! */
2510
2511 bRet = IntEngPaint(&psurf->SurfObj,
2512 &ClipRegion.ClipObj,
2513 &dc->eboFill.BrushObject,
2514 &BrushOrigin,
2515 0xFFFF); // FIXME: Don't know what to put here
2516
2517 REGION_Delete(VisRgn);
2518 IntEngFreeClipResources(&ClipRegion);
2519
2520 // Fill the region
2521 return bRet;
2522 }
2523
2524 BOOL
2525 FASTCALL
2526 REGION_PtInRegion(
2527 PREGION prgn,
2528 INT X,
2529 INT Y)
2530 {
2531 ULONG i;
2532 PRECT r;
2533
2534 if (prgn->rdh.nCount > 0 && INRECT(prgn->rdh.rcBound, X, Y))
2535 {
2536 r = prgn->Buffer;
2537 for (i = 0; i < prgn->rdh.nCount; i++)
2538 {
2539 if (INRECT(r[i], X, Y))
2540 return TRUE;
2541 }
2542 }
2543
2544 return FALSE;
2545 }
2546
2547 BOOL
2548 FASTCALL
2549 REGION_RectInRegion(
2550 PREGION Rgn,
2551 const RECTL *rect)
2552 {
2553 PRECTL pCurRect, pRectEnd;
2554 RECT rc;
2555
2556 /* Swap the coordinates to make right >= left and bottom >= top */
2557 /* (region building rectangles are normalized the same way) */
2558 if (rect->top > rect->bottom)
2559 {
2560 rc.top = rect->bottom;
2561 rc.bottom = rect->top;
2562 }
2563 else
2564 {
2565 rc.top = rect->top;
2566 rc.bottom = rect->bottom;
2567 }
2568
2569 if (rect->right < rect->left)
2570 {
2571 rc.right = rect->left;
2572 rc.left = rect->right;
2573 }
2574 else
2575 {
2576 rc.right = rect->right;
2577 rc.left = rect->left;
2578 }
2579
2580 /* This is (just) a useful optimization */
2581 if ((Rgn->rdh.nCount > 0) && EXTENTCHECK(&Rgn->rdh.rcBound, &rc))
2582 {
2583 for (pCurRect = Rgn->Buffer, pRectEnd = pCurRect +
2584 Rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
2585 {
2586 if (pCurRect->bottom <= rc.top)
2587 continue; /* Not far enough down yet */
2588
2589 if (pCurRect->top >= rc.bottom)
2590 break; /* Too far down */
2591
2592 if (pCurRect->right <= rc.left)
2593 continue; /* Not far enough over yet */
2594
2595 if (pCurRect->left >= rc.right)
2596 {
2597 continue;
2598 }
2599
2600 return TRUE;
2601 }
2602 }
2603
2604 return FALSE;
2605 }
2606
2607 VOID
2608 FASTCALL
2609 REGION_SetRectRgn(
2610 PREGION rgn,
2611 INT LeftRect,
2612 INT TopRect,
2613 INT RightRect,
2614 INT BottomRect)
2615 {
2616 PRECTL firstRect;
2617
2618 if (LeftRect > RightRect)
2619 {
2620 INT tmp = LeftRect;
2621 LeftRect = RightRect;
2622 RightRect = tmp;
2623 }
2624
2625 if (TopRect > BottomRect)
2626 {
2627 INT tmp = TopRect;
2628 TopRect = BottomRect;
2629 BottomRect = tmp;
2630 }
2631
2632 if ((LeftRect != RightRect) && (TopRect != BottomRect))
2633 {
2634 firstRect = rgn->Buffer;
2635 ASSERT(firstRect);
2636 firstRect->left = rgn->rdh.rcBound.left = LeftRect;
2637 firstRect->top = rgn->rdh.rcBound.top = TopRect;
2638 firstRect->right = rgn->rdh.rcBound.right = RightRect;
2639 firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
2640 rgn->rdh.nCount = 1;
2641 rgn->rdh.iType = RDH_RECTANGLES;
2642 }
2643 else
2644 {
2645 EMPTY_REGION(rgn);
2646 }
2647 }
2648
2649 INT
2650 FASTCALL
2651 IntGdiOffsetRgn(
2652 PREGION rgn,
2653 INT XOffset,
2654 INT YOffset)
2655 {
2656 if (XOffset || YOffset)
2657 {
2658 int nbox = rgn->rdh.nCount;
2659 PRECTL pbox = rgn->Buffer;
2660
2661 if (nbox && pbox)
2662 {
2663 while (nbox--)
2664 {
2665 pbox->left += XOffset;
2666 pbox->right += XOffset;
2667 pbox->top += YOffset;
2668 pbox->bottom += YOffset;
2669 pbox++;
2670 }
2671
2672 if (rgn->Buffer != &rgn->rdh.rcBound)
2673 {
2674 rgn->rdh.rcBound.left += XOffset;
2675 rgn->rdh.rcBound.right += XOffset;
2676 rgn->rdh.rcBound.top += YOffset;
2677 rgn->rdh.rcBound.bottom += YOffset;
2678 }
2679 }
2680 }
2681
2682 return REGION_Complexity(rgn);
2683 }
2684
2685 /***********************************************************************
2686 * REGION_InsertEdgeInET
2687 *
2688 * Insert the given edge into the edge table.
2689 * First we must find the correct bucket in the
2690 * Edge table, then find the right slot in the
2691 * bucket. Finally, we can insert it.
2692 *
2693 */
2694 static
2695 VOID
2696 FASTCALL
2697 REGION_InsertEdgeInET(
2698 EDGE_TABLE *ET,
2699 EDGE_TABLE_ENTRY *ETE,
2700 INT scanline,
2701 SCANLINE_LISTBLOCK **SLLBlock,
2702 INT *iSLLBlock)
2703 {
2704 EDGE_TABLE_ENTRY *start, *prev;
2705 SCANLINE_LIST *pSLL, *pPrevSLL;
2706 SCANLINE_LISTBLOCK *tmpSLLBlock;
2707
2708 /* Find the right bucket to put the edge into */
2709 pPrevSLL = &ET->scanlines;
2710 pSLL = pPrevSLL->next;
2711 while (pSLL && (pSLL->scanline < scanline))
2712 {
2713 pPrevSLL = pSLL;
2714 pSLL = pSLL->next;
2715 }
2716
2717 /* Reassign pSLL (pointer to SCANLINE_LIST) if necessary */
2718 if ((!pSLL) || (pSLL->scanline > scanline))
2719 {
2720 if (*iSLLBlock > SLLSPERBLOCK-1)
2721 {
2722 tmpSLLBlock = ExAllocatePoolWithTag(PagedPool,
2723 sizeof(SCANLINE_LISTBLOCK),
2724 TAG_REGION);
2725 if (tmpSLLBlock == NULL)
2726 {
2727 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
2728 /* FIXME: Free resources? */
2729 return;
2730 }
2731
2732 (*SLLBlock)->next = tmpSLLBlock;
2733 tmpSLLBlock->next = (SCANLINE_LISTBLOCK *)NULL;
2734 *SLLBlock = tmpSLLBlock;
2735 *iSLLBlock = 0;
2736 }
2737
2738 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2739
2740 pSLL->next = pPrevSLL->next;
2741 pSLL->edgelist = (EDGE_TABLE_ENTRY *)NULL;
2742 pPrevSLL->next = pSLL;
2743 }
2744
2745 pSLL->scanline = scanline;
2746
2747 /* Now insert the edge in the right bucket */
2748 prev = (EDGE_TABLE_ENTRY *)NULL;
2749 start = pSLL->edgelist;
2750 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
2751 {
2752 prev = start;
2753 start = start->next;
2754 }
2755
2756 ETE->next = start;
2757
2758 if (prev)
2759 prev->next = ETE;
2760 else
2761 pSLL->edgelist = ETE;
2762 }
2763
2764 /***********************************************************************
2765 * REGION_loadAET
2766 *
2767 * This routine moves EDGE_TABLEEntries from the
2768 * EDGE_TABLE into the Active Edge Table,
2769 * leaving them sorted by smaller x coordinate.
2770 *
2771 */
2772 static
2773 VOID
2774 FASTCALL
2775 REGION_loadAET(
2776 EDGE_TABLE_ENTRY *AET,
2777 EDGE_TABLE_ENTRY *ETEs)
2778 {
2779 EDGE_TABLE_ENTRY *pPrevAET;
2780 EDGE_TABLE_ENTRY *tmp;
2781
2782 pPrevAET = AET;
2783 AET = AET->next;
2784 while (ETEs)
2785 {
2786 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
2787 {
2788 pPrevAET = AET;
2789 AET = AET->next;
2790 }
2791
2792 tmp = ETEs->next;
2793 ETEs->next = AET;
2794 if (AET)
2795 AET->back = ETEs;
2796
2797 ETEs->back = pPrevAET;
2798 pPrevAET->next = ETEs;
2799 pPrevAET = ETEs;
2800
2801 ETEs = tmp;
2802 }
2803 }
2804
2805 /***********************************************************************
2806 * REGION_computeWAET
2807 *
2808 * This routine links the AET by the
2809 * nextWETE (winding EDGE_TABLE_ENTRY) link for
2810 * use by the winding number rule. The final
2811 * Active Edge Table (AET) might look something
2812 * like:
2813 *
2814 * AET
2815 * ---------- --------- ---------
2816 * |ymax | |ymax | |ymax |
2817 * | ... | |... | |... |
2818 * |next |->|next |->|next |->...
2819 * |nextWETE| |nextWETE| |nextWETE|
2820 * --------- --------- ^--------
2821 * | | |
2822 * V-------------------> V---> ...
2823 *
2824 */
2825 static
2826 VOID
2827 FASTCALL
2828 REGION_computeWAET(
2829 EDGE_TABLE_ENTRY *AET)
2830 {
2831 register EDGE_TABLE_ENTRY *pWETE;
2832 register INT inside = 1;
2833 register INT isInside = 0;
2834
2835 AET->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
2836 pWETE = AET;
2837 AET = AET->next;
2838 while (AET)
2839 {
2840 if (AET->ClockWise)
2841 isInside++;
2842 else
2843 isInside--;
2844
2845 if ((!inside && !isInside) ||
2846 ( inside && isInside))
2847 {
2848 pWETE->nextWETE = AET;
2849 pWETE = AET;
2850 inside = !inside;
2851 }
2852 AET = AET->next;
2853 }
2854
2855 pWETE->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
2856 }
2857
2858 /***********************************************************************
2859 * REGION_InsertionSort
2860 *
2861 * Just a simple insertion sort using
2862 * pointers and back pointers to sort the Active
2863 * Edge Table.
2864 *
2865 */
2866 static
2867 BOOL
2868 FASTCALL
2869 REGION_InsertionSort(
2870 EDGE_TABLE_ENTRY *AET)
2871 {
2872 EDGE_TABLE_ENTRY *pETEchase;
2873 EDGE_TABLE_ENTRY *pETEinsert;
2874 EDGE_TABLE_ENTRY *pETEchaseBackTMP;
2875 BOOL changed = FALSE;
2876
2877 AET = AET->next;
2878 while (AET)
2879 {
2880 pETEinsert = AET;
2881 pETEchase = AET;
2882 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2883 pETEchase = pETEchase->back;
2884
2885 AET = AET->next;
2886 if (pETEchase != pETEinsert)
2887 {
2888 pETEchaseBackTMP = pETEchase->back;
2889 pETEinsert->back->next = AET;
2890 if (AET)
2891 AET->back = pETEinsert->back;
2892
2893 pETEinsert->next = pETEchase;
2894 pETEchase->back->next = pETEinsert;
2895 pETEchase->back = pETEinsert;
2896 pETEinsert->back = pETEchaseBackTMP;
2897 changed = TRUE;
2898 }
2899 }
2900
2901 return changed;
2902 }
2903
2904 /***********************************************************************
2905 * REGION_FreeStorage
2906 *
2907 * Clean up our act.
2908 */
2909 static
2910 VOID
2911 FASTCALL
2912 REGION_FreeStorage(
2913 SCANLINE_LISTBLOCK *pSLLBlock)
2914 {
2915 SCANLINE_LISTBLOCK *tmpSLLBlock;
2916
2917 while (pSLLBlock)
2918 {
2919 tmpSLLBlock = pSLLBlock->next;
2920 ExFreePoolWithTag(pSLLBlock, TAG_REGION);
2921 pSLLBlock = tmpSLLBlock;
2922 }
2923 }
2924
2925
2926 /***********************************************************************
2927 * REGION_PtsToRegion
2928 *
2929 * Create an array of rectangles from a list of points.
2930 */
2931 static
2932 INT
2933 FASTCALL
2934 REGION_PtsToRegion(
2935 INT numFullPtBlocks,
2936 INT iCurPtBlock,
2937 POINTBLOCK *FirstPtBlock,
2938 PREGION reg)
2939 {
2940 RECTL *rects;
2941 POINT *pts;
2942 POINTBLOCK *CurPtBlock;
2943 INT i;
2944 RECTL *extents, *temp;
2945 INT numRects;
2946
2947 extents = &reg->rdh.rcBound;
2948
2949 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2950
2951 /* Make sure, we have at least one rect */
2952 if (numRects == 0)
2953 {
2954 numRects = 1;
2955 }
2956
2957 temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION);
2958 if (temp == NULL)
2959 {
2960 return 0;
2961 }
2962
2963 if (reg->Buffer != NULL)
2964 {
2965 COPY_RECTS(temp, reg->Buffer, reg->rdh.nCount);
2966 if (reg->Buffer != &reg->rdh.rcBound)
2967 ExFreePoolWithTag(reg->Buffer, TAG_REGION);
2968 }
2969 reg->Buffer = temp;
2970
2971 reg->rdh.nCount = numRects;
2972 CurPtBlock = FirstPtBlock;
2973 rects = reg->Buffer - 1;
2974 numRects = 0;
2975 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
2976
2977 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--)
2978 {
2979 /* The loop uses 2 points per iteration */
2980 i = NUMPTSTOBUFFER >> 1;
2981 if (numFullPtBlocks == 0)
2982 i = iCurPtBlock >> 1;
2983
2984 for (pts = CurPtBlock->pts; i--; pts += 2)
2985 {
2986 if (pts->x == pts[1].x)
2987 continue;
2988
2989 if ((numRects && pts->x == rects->left) &&
2990 (pts->y == rects->bottom) &&
2991 (pts[1].x == rects->right) &&
2992 ((numRects == 1) || (rects[-1].top != rects->top)) &&
2993 (i && pts[2].y > pts[1].y))
2994 {
2995 rects->bottom = pts[1].y + 1;
2996 continue;
2997 }
2998
2999 numRects++;
3000 rects++;
3001 rects->left = pts->x;
3002 rects->top = pts->y;
3003 rects->right = pts[1].x;
3004 rects->bottom = pts[1].y + 1;
3005
3006 if (rects->left < extents->left)
3007 extents->left = rects->left;
3008 if (rects->right > extents->right)
3009 extents->right = rects->right;
3010 }
3011
3012 CurPtBlock = CurPtBlock->next;
3013 }
3014
3015 if (numRects)
3016 {
3017 extents->top = reg->Buffer->top;
3018 extents->bottom = rects->bottom;
3019 }
3020 else
3021 {
3022 extents->left = 0;
3023 extents->top = 0;
3024 extents->right = 0;
3025 extents->bottom = 0;
3026 }
3027
3028 reg->rdh.nCount = numRects;
3029
3030 return(TRUE);
3031 }
3032
3033 /***********************************************************************
3034 * REGION_CreateEDGE_TABLE
3035 *
3036 * This routine creates the edge table for
3037 * scan converting polygons.
3038 * The Edge Table (ET) looks like:
3039 *
3040 * EDGE_TABLE
3041 * --------
3042 * | ymax | SCANLINE_LISTs
3043 * |scanline|-->------------>-------------->...
3044 * -------- |scanline| |scanline|
3045 * |edgelist| |edgelist|
3046 * --------- ---------
3047 * | |
3048 * | |
3049 * V V
3050 * list of ETEs list of ETEs
3051 *
3052 * where ETE is an EDGE_TABLE_ENTRY data structure,
3053 * and there is one SCANLINE_LIST per scanline at
3054 * which an edge is initially entered.
3055 *
3056 */
3057 static
3058 VOID
3059 FASTCALL
3060 REGION_CreateETandAET(
3061 const ULONG *Count,
3062 INT nbpolygons,
3063 const POINT *pts,
3064 EDGE_TABLE *ET,
3065 EDGE_TABLE_ENTRY *AET,
3066 EDGE_TABLE_ENTRY *pETEs,
3067 SCANLINE_LISTBLOCK *pSLLBlock)
3068 {
3069 const POINT *top, *bottom;
3070 const POINT *PrevPt, *CurrPt, *EndPt;
3071 INT poly, count;
3072 INT iSLLBlock = 0;
3073 INT dy;
3074
3075 /* Initialize the Active Edge Table */
3076 AET->next = (EDGE_TABLE_ENTRY *)NULL;
3077 AET->back = (EDGE_TABLE_ENTRY *)NULL;
3078 AET->nextWETE = (EDGE_TABLE_ENTRY *)NULL;
3079 AET->bres.minor_axis = SMALL_COORDINATE;
3080
3081 /* Initialize the Edge Table. */
3082 ET->scanlines.next = (SCANLINE_LIST *)NULL;
3083 ET->ymax = SMALL_COORDINATE;
3084 ET->ymin = LARGE_COORDINATE;
3085 pSLLBlock->next = (SCANLINE_LISTBLOCK *)NULL;
3086
3087 EndPt = pts - 1;
3088 for (poly = 0; poly < nbpolygons; poly++)
3089 {
3090 count = Count[poly];
3091 EndPt += count;
3092 if (count < 2)
3093 continue;
3094
3095 PrevPt = EndPt;
3096
3097 /* For each vertex in the array of points.
3098 * In this loop we are dealing with two vertices at
3099 * a time -- these make up one edge of the polygon. */
3100 while (count--)
3101 {
3102 CurrPt = pts++;
3103
3104 /* Find out which point is above and which is below. */
3105 if (PrevPt->y > CurrPt->y)
3106 {
3107 bottom = PrevPt, top = CurrPt;
3108 pETEs->ClockWise = 0;
3109 }
3110 else
3111 {
3112 bottom = CurrPt, top = PrevPt;
3113 pETEs->ClockWise = 1;
3114 }
3115
3116 /* Don't add horizontal edges to the Edge table. */
3117 if (bottom->y != top->y)
3118 {
3119 /* -1 so we don't get last scanline */
3120 pETEs->ymax = bottom->y - 1;
3121
3122 /* Initialize integer edge algorithm */
3123 dy = bottom->y - top->y;
3124 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
3125
3126 REGION_InsertEdgeInET(ET,
3127 pETEs,
3128 top->y,
3129 &pSLLBlock,
3130 &iSLLBlock);
3131
3132 if (PrevPt->y > ET->ymax)
3133 ET->ymax = PrevPt->y;
3134 if (PrevPt->y < ET->ymin)
3135 ET->ymin = PrevPt->y;
3136 pETEs++;
3137 }
3138
3139 PrevPt = CurrPt;
3140 }
3141 }
3142 }
3143
3144 BOOL
3145 FASTCALL
3146 IntSetPolyPolygonRgn(
3147 POINT *Pts,
3148 PULONG Count,
3149 INT nbpolygons,
3150 INT mode,
3151 PREGION Rgn)
3152 {
3153 EDGE_TABLE_ENTRY *pAET; /* Active Edge Table */
3154 INT y; /* Current scanline */
3155 INT iPts = 0; /* Number of pts in buffer */
3156 EDGE_TABLE_ENTRY *pWETE; /* Winding Edge Table Entry */
3157 SCANLINE_LIST *pSLL; /* Current SCANLINE_LIST */
3158 POINT *pts; /* Output buffer */
3159 EDGE_TABLE_ENTRY *pPrevAET; /* Pointer to previous AET */
3160 EDGE_TABLE ET; /* Header node for ET */
3161 EDGE_TABLE_ENTRY AET; /* Header node for AET */
3162 EDGE_TABLE_ENTRY *pETEs; /* EDGE_TABLEEntries pool */
3163 SCANLINE_LISTBLOCK SLLBlock; /* Header for SCANLINE_LIST */
3164 INT fixWAET = FALSE;
3165 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
3166 POINTBLOCK *tmpPtBlock;
3167 INT numFullPtBlocks = 0;
3168 INT poly, total;
3169
3170 if (mode == 0 || mode > 2) return 0;
3171
3172 /* Special case a rectangle */
3173 if (((nbpolygons == 1) && ((*Count == 4) ||
3174 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
3175 (((Pts[0].y == Pts[1].y) &&
3176 (Pts[1].x == Pts[2].x) &&
3177 (Pts[2].y == Pts[3].y) &&
3178 (Pts[3].x == Pts[0].x)) ||
3179 ((Pts[0].x == Pts[1].x) &&
3180 (Pts[1].y == Pts[2].y) &&
3181 (Pts[2].x == Pts[3].x) &&
3182 (Pts[3].y == Pts[0].y))))
3183 {
3184 REGION_SetRectRgn(Rgn,
3185 min(Pts[0].x, Pts[2].x),
3186 min(Pts[0].y, Pts[2].y),
3187 max(Pts[0].x, Pts[2].x),
3188 max(Pts[0].y, Pts[2].y));
3189 return TRUE;
3190 }
3191
3192 for (poly = total = 0; poly < nbpolygons; poly++)
3193 total += Count[poly];
3194
3195 pETEs = ExAllocatePoolWithTag(PagedPool,
3196 sizeof(EDGE_TABLE_ENTRY) * total,
3197 TAG_REGION);
3198 if (pETEs == NULL)
3199 {
3200 return FALSE;
3201 }
3202
3203 pts = FirstPtBlock.pts;
3204 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
3205 pSLL = ET.scanlines.next;
3206 curPtBlock = &FirstPtBlock;
3207
3208 if (mode != WINDING)
3209 {
3210 /* For each scanline */
3211 for (y = ET.ymin; y < ET.ymax; y++)
3212 {
3213 /* Add a new edge to the active edge table when we
3214 * get to the next edge. */
3215 if (pSLL != NULL && y == pSLL->scanline)
3216 {
3217 REGION_loadAET(&AET, pSLL->edgelist);
3218 pSLL = pSLL->next;
3219 }
3220 pPrevAET = &AET;
3221 pAET = AET.next;
3222
3223 /* For each active edge */
3224 while (pAET)
3225 {
3226 pts->x = pAET->bres.minor_axis, pts->y = y;
3227 pts++, iPts++;
3228
3229 /* Send out the buffer */
3230 if (iPts == NUMPTSTOBUFFER)
3231 {
3232 tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3233 sizeof(POINTBLOCK),
3234 TAG_REGION);
3235 if (tmpPtBlock == NULL)
3236 {
3237 DPRINT1("Can't alloc tPB\n");
3238 ExFreePoolWithTag(pETEs, TAG_REGION);
3239 return FALSE;
3240 }
3241
3242 curPtBlock->next = tmpPtBlock;
3243 curPtBlock = tmpPtBlock;
3244 pts = curPtBlock->pts;
3245 numFullPtBlocks++;
3246 iPts = 0;
3247 }
3248
3249 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
3250 }
3251
3252 REGION_InsertionSort(&AET);
3253 }
3254 }
3255 else
3256 {
3257 /* For each scanline */
3258 for (y = ET.ymin; y < ET.ymax; y++)
3259 {
3260 /* Add a new edge to the active edge table when we
3261 * get to the next edge. */
3262 if (pSLL != NULL && y == pSLL->scanline)
3263 {
3264 REGION_loadAET(&AET, pSLL->edgelist);
3265 REGION_computeWAET(&AET);
3266 pSLL = pSLL->next;
3267 }
3268
3269 pPrevAET = &AET;
3270 pAET = AET.next;
3271 pWETE = pAET;
3272
3273 /* For each active edge */
3274 while (pAET)
3275 {
3276 /* Add to the buffer only those edges that
3277 * are in the Winding active edge table. */
3278 if (pWETE == pAET)
3279 {
3280 pts->x = pAET->bres.minor_axis, pts->y = y;
3281 pts++, iPts++;
3282
3283 /* Send out the buffer */
3284 if (iPts == NUMPTSTOBUFFER)
3285 {
3286 tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3287 sizeof(POINTBLOCK),
3288 TAG_REGION);
3289 if (tmpPtBlock == NULL)
3290 {
3291 DPRINT1("Can't alloc tPB\n");
3292 ExFreePoolWithTag(pETEs, TAG_REGION);
3293 return FALSE;
3294 }
3295 curPtBlock->next = tmpPtBlock;
3296 curPtBlock = tmpPtBlock;
3297 pts = curPtBlock->pts;
3298 numFullPtBlocks++;
3299 iPts = 0;
3300 }
3301
3302 pWETE = pWETE->nextWETE;
3303 }
3304
3305 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
3306 }
3307
3308 /* Recompute the winding active edge table if
3309 * we just resorted or have exited an edge. */
3310 if (REGION_InsertionSort(&AET) || fixWAET)
3311 {
3312 REGION_computeWAET(&AET);
3313 fixWAET = FALSE;
3314 }
3315 }
3316 }
3317
3318 REGION_FreeStorage(SLLBlock.next);
3319 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, Rgn);
3320
3321 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;)
3322 {
3323 tmpPtBlock = curPtBlock->next;
3324 ExFreePoolWithTag(curPtBlock, TAG_REGION);
3325 curPtBlock = tmpPtBlock;
3326 }
3327
3328 ExFreePoolWithTag(pETEs, TAG_REGION);
3329 return TRUE;
3330 }
3331
3332 BOOL
3333 FASTCALL
3334 IntRectInRegion(
3335 HRGN hRgn,
3336 LPRECTL rc)
3337 {
3338 PREGION Rgn;
3339 BOOL Ret;
3340
3341 Rgn = RGNOBJAPI_Lock(hRgn, NULL);
3342 if (Rgn == NULL)
3343 {
3344 return ERROR;
3345 }
3346
3347 Ret = REGION_RectInRegion(Rgn, rc);
3348 RGNOBJAPI_Unlock(Rgn);
3349 return Ret;
3350 }
3351
3352
3353 //
3354 // NtGdi Exported Functions
3355 //
3356 INT
3357 APIENTRY
3358 NtGdiCombineRgn(
3359 IN HRGN hrgnDst,
3360 IN HRGN hrgnSrc1,
3361 IN HRGN hrgnSrc2,
3362 IN INT iMode)
3363 {
3364 HRGN ahrgn[3];
3365 PREGION aprgn[3];
3366 INT iResult;
3367
3368 /* Validate the combine mode */
3369 if ((iMode < RGN_AND) || (iMode > RGN_COPY))
3370 {
3371 return ERROR;
3372 }
3373
3374 /* Validate that we have the required regions */
3375 if ((hrgnDst == NULL) ||
3376 (hrgnSrc1 == NULL) ||
3377 ((iMode != RGN_COPY) && (hrgnSrc2 == NULL)))
3378 {
3379 DPRINT1("NtGdiCombineRgn: %p, %p, %p, %d\n",
3380 hrgnDst, hrgnSrc1, hrgnSrc2, iMode);
3381 return ERROR;
3382 }
3383
3384 /* Lock all regions */
3385 ahrgn[0] = hrgnDst;
3386 ahrgn[1] = hrgnSrc1;
3387 ahrgn[2] = iMode != RGN_COPY ? hrgnSrc2 : NULL;
3388 if (!GDIOBJ_bLockMultipleObjects(3, (HGDIOBJ*)ahrgn, (PVOID*)aprgn, GDIObjType_RGN_TYPE))
3389 {
3390 DPRINT1("NtGdiCombineRgn: %p, %p, %p, %d\n",
3391 hrgnDst, hrgnSrc1, hrgnSrc2, iMode);
3392 return ERROR;
3393 }
3394
3395 /* HACK: Sync usermode attributes */
3396 REGION_vSyncRegion(aprgn[0]);
3397 REGION_vSyncRegion(aprgn[1]);
3398 if (aprgn[2]) REGION_vSyncRegion(aprgn[2]);
3399
3400 /* Call the internal function */
3401 iResult = IntGdiCombineRgn(aprgn[0], aprgn[1], aprgn[2], iMode);
3402
3403 /// FIXME: need to sync user attr back
3404
3405 /* Cleanup and return */
3406 REGION_UnlockRgn(aprgn[0]);
3407 REGION_UnlockRgn(aprgn[1]);
3408 if (aprgn[2])
3409 REGION_UnlockRgn(aprgn[2]);
3410
3411 return iResult;
3412 }
3413
3414 HRGN
3415 APIENTRY
3416 NtGdiCreateEllipticRgn(
3417 INT Left,
3418 INT Top,
3419 INT Right,
3420 INT Bottom)
3421 {
3422 return NtGdiCreateRoundRectRgn(Left,
3423 Top,
3424 Right, Bottom,
3425 Right - Left,
3426 Bottom - Top);
3427 }
3428
3429 HRGN
3430 APIENTRY
3431 NtGdiCreateRectRgn(
3432 INT LeftRect,
3433 INT TopRect,
3434 INT RightRect,
3435 INT BottomRect)
3436 {
3437 PREGION pRgn;
3438 HRGN hRgn;
3439
3440 /* Allocate region data structure with space for 1 RECTL */
3441 pRgn = REGION_AllocUserRgnWithHandle(1);
3442 if (pRgn == NULL)
3443 {
3444 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3445 return NULL;
3446 }
3447
3448 hRgn = pRgn->BaseObject.hHmgr;
3449
3450 REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
3451 RGNOBJAPI_Unlock(pRgn);
3452
3453 DPRINT("Returning %p.\n", hRgn);
3454
3455 return hRgn;
3456 }
3457
3458 HRGN
3459 APIENTRY
3460 NtGdiCreateRoundRectRgn(
3461 INT left,
3462 INT top,
3463 INT right,
3464 INT bottom,
3465 INT ellipse_width,
3466 INT ellipse_height)
3467 {
3468 PREGION obj;
3469 HRGN hrgn;
3470 INT asq, bsq, d, xd, yd;
3471 RECTL rect;
3472
3473 /* Make the dimensions sensible */
3474 if (left > right)
3475 {
3476 INT tmp = left;
3477 left = right;
3478 right = tmp;
3479 }
3480
3481 if (top > bottom)
3482 {
3483 INT tmp = top;
3484 top = bottom;
3485 bottom = tmp;
3486 }
3487
3488 ellipse_width = abs(ellipse_width);
3489 ellipse_height = abs(ellipse_height);
3490
3491 /* Check parameters */
3492 if (ellipse_width > right-left)
3493 ellipse_width = right-left;
3494 if (ellipse_height > bottom-top)
3495 ellipse_height = bottom-top;
3496
3497 /* Check if we can do a normal rectangle instead */
3498 if ((ellipse_width < 2) || (ellipse_height < 2))
3499 return NtGdiCreateRectRgn(left, top, right, bottom);
3500
3501 /* Create region */
3502 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
3503 obj = REGION_AllocUserRgnWithHandle(d);
3504 if (obj == NULL)
3505 return 0;
3506
3507 hrgn = obj->BaseObject.hHmgr;
3508
3509 /* Ellipse algorithm, based on an article by K. Porter
3510 in DDJ Graphics Programming Column, 8/89 */
3511 asq = ellipse_width * ellipse_width / 4; /* a^2 */
3512 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
3513 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
3514 xd = 0;
3515 yd = asq * ellipse_height; /* 2a^2b */
3516
3517 rect.left = left + ellipse_width / 2;
3518 rect.right = right - ellipse_width / 2;
3519
3520 /* Loop to draw first half of quadrant */
3521 while (xd < yd)
3522 {
3523 /* If nearest pixel is toward the center */
3524 if (d > 0)
3525 {
3526 /* Move toward center */
3527 rect.top = top++;
3528 rect.bottom = rect.top + 1;
3529 REGION_UnionRectWithRgn(obj, &rect);
3530 rect.top = --bottom;
3531 rect.bottom = rect.top + 1;
3532 REGION_UnionRectWithRgn(obj, &rect);
3533 yd -= 2*asq;
3534 d -= yd;
3535 }
3536
3537 /* Next horiz point */
3538 rect.left--;
3539 rect.right++;
3540 xd += 2*bsq;
3541 d += bsq + xd;
3542 }
3543
3544 /* Loop to draw second half of quadrant */
3545 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
3546 while (yd >= 0)
3547 {
3548 /* next vertical point */
3549 rect.top = top++;
3550 rect.bottom = rect.top + 1;
3551 REGION_UnionRectWithRgn(obj, &rect);
3552 rect.top = --bottom;
3553 rect.bottom = rect.top + 1;
3554 REGION_UnionRectWithRgn(obj, &rect);
3555
3556 /* If nearest pixel is outside ellipse */
3557 if (d < 0)
3558 {
3559 /* Move away from center */
3560 rect.left--;
3561 rect.right++;
3562 xd += 2*bsq;
3563 d += xd;
3564 }
3565
3566 yd -= 2*asq;
3567 d += asq - yd;
3568 }
3569
3570 /* Add the inside rectangle */
3571 if (top <= bottom)
3572 {
3573 rect.top = top;
3574 rect.bottom = bottom;
3575 REGION_UnionRectWithRgn(obj, &rect);
3576 }
3577
3578 RGNOBJAPI_Unlock(obj);
3579 return hrgn;
3580 }
3581
3582 BOOL
3583 APIENTRY
3584 NtGdiEqualRgn(
3585 HRGN hSrcRgn1,
3586 HRGN hSrcRgn2)
3587 {
3588 PREGION rgn1, rgn2;
3589 PRECTL tRect1, tRect2;
3590 ULONG i;
3591 BOOL bRet = FALSE;
3592
3593 rgn1 = RGNOBJAPI_Lock(hSrcRgn1, NULL);
3594 if (rgn1 == NULL)
3595 return ERROR;
3596
3597 rgn2 = RGNOBJAPI_Lock(hSrcRgn2, NULL);
3598 if (rgn2 == NULL)
3599 {
3600 RGNOBJAPI_Unlock(rgn1);
3601 return ERROR;
3602 }
3603
3604 if (rgn1->rdh.nCount != rgn2->rdh.nCount)
3605 goto exit;
3606
3607 if (rgn1->rdh.nCount == 0)
3608 {
3609 bRet = TRUE;
3610 goto exit;
3611 }
3612
3613 if ((rgn1->rdh.rcBound.left != rgn2->rdh.rcBound.left) ||
3614 (rgn1->rdh.rcBound.right != rgn2->rdh.rcBound.right) ||
3615 (rgn1->rdh.rcBound.top != rgn2->rdh.rcBound.top) ||
3616 (rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom))
3617 goto exit;
3618
3619 tRect1 = rgn1->Buffer;
3620 tRect2 = rgn2->Buffer;
3621
3622 if ((tRect1 == NULL) || (tRect2 == NULL))
3623 goto exit;
3624
3625 for (i=0; i < rgn1->rdh.nCount; i++)
3626 {
3627 if ((tRect1[i].left != tRect2[i].left) ||
3628 (tRect1[i].right != tRect2[i].right) ||
3629 (tRect1[i].top != tRect2[i].top) ||
3630 (tRect1[i].bottom != tRect2[i].bottom))
3631 goto exit;
3632 }
3633
3634 bRet = TRUE;
3635
3636 exit:
3637 RGNOBJAPI_Unlock(rgn1);
3638 RGNOBJAPI_Unlock(rgn2);
3639 return bRet;
3640 }
3641
3642 HRGN
3643 APIENTRY
3644 NtGdiExtCreateRegion(
3645 OPTIONAL LPXFORM Xform,
3646 DWORD Count,
3647 LPRGNDATA RgnData)
3648 {
3649 HRGN hRgn;
3650 PREGION Region;
3651 DWORD nCount = 0;
3652 DWORD iType = 0;
3653 DWORD dwSize = 0;
3654 UINT i;
3655 RECT* rects;
3656 NTSTATUS Status = STATUS_SUCCESS;
3657 MATRIX matrix;
3658 XFORMOBJ xo;
3659
3660 DPRINT("NtGdiExtCreateRegion\n");
3661 _SEH2_TRY
3662 {
3663 ProbeForRead(RgnData, Count, 1);
3664 nCount = RgnData->rdh.nCount;
3665 iType = RgnData->rdh.iType;
3666 dwSize = RgnData->rdh.dwSize;
3667 rects = (RECT*)RgnData->Buffer;
3668 }
3669 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3670 {
3671 Status = _SEH2_GetExceptionCode();
3672 }
3673 _SEH2_END;
3674
3675 if (!NT_SUCCESS(Status))
3676 {
3677 SetLastNtError(Status);
3678 return NULL;
3679 }
3680
3681 /* Check parameters, but don't set last error here */
3682 if ((Count < sizeof(RGNDATAHEADER) + nCount * sizeof(RECT)) ||
3683 (iType != RDH_RECTANGLES) ||
3684 (dwSize != sizeof(RGNDATAHEADER)))
3685 {
3686 return NULL;
3687 }
3688
3689 Region = REGION_AllocUserRgnWithHandle(nCount);
3690
3691 if (Region == NULL)
3692 {
3693 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3694 return FALSE;
3695 }
3696 hRgn = Region->BaseObject.hHmgr;
3697
3698 _SEH2_TRY
3699 {
3700 /* Insert the rectangles one by one */
3701 for(i=0; i<nCount; i++)
3702 {
3703 REGION_UnionRectWithRgn(Region, &rects[i]);
3704 }
3705
3706 if (Xform != NULL)
3707 {
3708 ULONG ret;
3709
3710 /* Init the XFORMOBJ from the Xform struct */
3711 Status = STATUS_INVALID_PARAMETER;
3712 XFORMOBJ_vInit(&xo, &matrix);
3713 ret = XFORMOBJ_iSetXform(&xo, (XFORML*)Xform);
3714
3715 /* Check for error */
3716 if (ret != DDI_ERROR)
3717 {
3718 /* Apply the coordinate transformation on the rects */
3719 if (XFORMOBJ_bApplyXform(&xo,
3720 XF_LTOL,
3721 Region->rdh.nCount * 2,
3722 Region->Buffer,
3723 Region->Buffer))
3724 {
3725 Status = STATUS_SUCCESS;
3726 }
3727 }
3728 }
3729 }
3730 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3731 {
3732 Status = _SEH2_GetExceptionCode();
3733 }
3734 _SEH2_END;
3735 if (!NT_SUCCESS(Status))
3736 {
3737 EngSetLastError(ERROR_INVALID_PARAMETER);
3738 RGNOBJAPI_Unlock(Region);
3739 GreDeleteObject(hRgn);
3740 return NULL;
3741 }
3742
3743 RGNOBJAPI_Unlock(Region);
3744
3745 return hRgn;
3746 }
3747
3748 BOOL
3749 APIENTRY
3750 NtGdiFillRgn(
3751 HDC hDC,
3752 HRGN hRgn,
3753 HBRUSH hBrush)
3754 {
3755 HBRUSH oldhBrush;
3756 PREGION rgn;
3757 PRECTL r;
3758
3759 rgn = RGNOBJAPI_Lock(hRgn, NULL);
3760 if (rgn == NULL)
3761 {
3762 return FALSE;
3763 }
3764
3765 oldhBrush = NtGdiSelectBrush(hDC, hBrush);
3766 if (oldhBrush == NULL)
3767 {
3768 RGNOBJAPI_Unlock(rgn);
3769 return FALSE;
3770 }
3771
3772 for (r = rgn->Buffer; r < rgn->Buffer + rgn->rdh.nCount; r++)
3773 {
3774 NtGdiPatBlt(hDC, r->left, r->top, r->right - r->left, r->bottom - r->top, PATCOPY);
3775 }
3776
3777 RGNOBJAPI_Unlock(rgn);
3778 NtGdiSelectBrush(hDC, oldhBrush);
3779
3780 return TRUE;
3781 }
3782
3783 BOOL
3784 APIENTRY
3785 NtGdiFrameRgn(
3786 HDC hDC,
3787 HRGN hRgn,
3788 HBRUSH hBrush,
3789 INT Width,
3790 INT Height)
3791 {
3792 HRGN FrameRgn;
3793 BOOL Ret;
3794
3795 FrameRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
3796 if (FrameRgn == NULL)
3797 {
3798 return FALSE;
3799 }
3800
3801 if (!REGION_CreateFrameRgn(FrameRgn, hRgn, Width, Height))
3802 {
3803 GreDeleteObject(FrameRgn);
3804 return FALSE;
3805 }
3806
3807 Ret = NtGdiFillRgn(hDC, FrameRgn, hBrush);
3808
3809 GreDeleteObject(FrameRgn);
3810 return Ret;
3811 }
3812
3813
3814 INT
3815 APIENTRY
3816 NtGdiGetRgnBox(
3817 HRGN hRgn,
3818 PRECTL pRect)
3819 {
3820 PREGION Rgn;
3821 RECTL SafeRect;
3822 DWORD ret;
3823 NTSTATUS Status = STATUS_SUCCESS;
3824
3825 Rgn = RGNOBJAPI_Lock(hRgn, NULL);
3826 if (Rgn == NULL)
3827 {
3828 return ERROR;
3829 }
3830
3831 ret = REGION_GetRgnBox(Rgn, &SafeRect);
3832 RGNOBJAPI_Unlock(Rgn);
3833 if (ret == ERROR)
3834 {
3835 return ret;
3836 }
3837
3838 _SEH2_TRY
3839 {
3840 ProbeForWrite(pRect, sizeof(RECT), 1);
3841 *pRect = SafeRect;
3842 }
3843 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3844 {
3845 Status = _SEH2_GetExceptionCode();
3846 }
3847 _SEH2_END;
3848 if (!NT_SUCCESS(Status))
3849 {
3850 return ERROR;
3851 }
3852
3853 return ret;
3854 }
3855
3856 BOOL
3857 APIENTRY
3858 NtGdiInvertRgn(
3859 HDC hDC,
3860 HRGN hRgn)
3861 {
3862 PREGION RgnData;
3863 ULONG i;
3864 PRECTL rc;
3865
3866 RgnData = RGNOBJAPI_Lock(hRgn, NULL);
3867 if (RgnData == NULL)
3868 {
3869 EngSetLastError(ERROR_INVALID_HANDLE);
3870 return FALSE;
3871 }
3872
3873 rc = RgnData->Buffer;
3874 for (i = 0; i < RgnData->rdh.nCount; i++)
3875 {
3876
3877 if (!NtGdiPatBlt(hDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, DSTINVERT))
3878 {
3879 RGNOBJAPI_Unlock(RgnData);
3880 return FALSE;
3881 }
3882 rc++;
3883 }
3884
3885 RGNOBJAPI_Unlock(RgnData);
3886 return TRUE;
3887 }
3888
3889 INT
3890 APIENTRY
3891 NtGdiOffsetRgn(
3892 HRGN hRgn,
3893 INT XOffset,
3894 INT YOffset)
3895 {
3896 PREGION rgn;
3897 INT ret;
3898
3899 DPRINT("NtGdiOffsetRgn: hRgn %p Xoffs %d Yoffs %d rgn %p\n", hRgn, XOffset, YOffset, rgn );
3900
3901 rgn = RGNOBJAPI_Lock(hRgn, NULL);
3902 if (rgn == NULL)
3903 {
3904 DPRINT("NtGdiOffsetRgn: hRgn error\n");
3905 return ERROR;
3906 }
3907
3908 ret = IntGdiOffsetRgn(rgn, XOffset, YOffset);
3909
3910 RGNOBJAPI_Unlock(rgn);
3911 return ret;
3912 }
3913
3914 BOOL
3915 APIENTRY
3916 NtGdiPtInRegion(
3917 HRGN hRgn,
3918 INT X,
3919 INT Y)
3920 {
3921 PREGION prgn;
3922 BOOL ret;
3923
3924 prgn = RGNOBJAPI_Lock(hRgn, NULL);
3925 if (prgn == NULL)
3926 return FALSE;
3927
3928 ret = REGION_PtInRegion(prgn, X, Y);
3929
3930 RGNOBJAPI_Unlock(prgn);
3931 return ret;
3932 }
3933
3934 BOOL
3935 APIENTRY
3936 NtGdiRectInRegion(
3937 HRGN hRgn,
3938 LPRECTL unsaferc)
3939 {
3940 RECTL rc = { 0 };
3941 NTSTATUS Status = STATUS_SUCCESS;
3942
3943 _SEH2_TRY
3944 {
3945 ProbeForRead(unsaferc, sizeof(RECT), 1);
3946 rc = *unsaferc;
3947 }
3948 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3949 {
3950 Status = _SEH2_GetExceptionCode();
3951 }
3952 _SEH2_END;
3953
3954 if (!NT_SUCCESS(Status))
3955 {
3956 SetLastNtError(Status);
3957 DPRINT1("NtGdiRectInRegion: Bogus rc\n");
3958 return ERROR;
3959 }
3960
3961 return IntRectInRegion(hRgn, &rc);
3962 }
3963
3964 BOOL
3965 APIENTRY
3966 NtGdiSetRectRgn(
3967 HRGN hRgn,
3968 INT LeftRect,
3969 INT TopRect,
3970 INT RightRect,
3971 INT BottomRect)
3972 {
3973 PREGION rgn;
3974
3975 rgn = RGNOBJAPI_Lock(hRgn, NULL);
3976 if (rgn == NULL)
3977 {
3978 return 0; // Per documentation
3979 }
3980
3981 REGION_SetRectRgn(rgn, LeftRect, TopRect, RightRect, BottomRect);
3982
3983 RGNOBJAPI_Unlock(rgn);
3984 return TRUE;
3985 }
3986
3987 HRGN
3988 APIENTRY
3989 NtGdiUnionRectWithRgn(
3990 HRGN hDest,
3991 const RECTL *UnsafeRect)
3992 {
3993 RECTL SafeRect = { 0 };
3994 PREGION Rgn;
3995 NTSTATUS Status = STATUS_SUCCESS;
3996
3997 Rgn = RGNOBJAPI_Lock(hDest, NULL);
3998 if (Rgn == NULL)
3999 {
4000 EngSetLastError(ERROR_INVALID_HANDLE);
4001 return NULL;
4002 }
4003
4004 _SEH2_TRY
4005 {
4006 ProbeForRead(UnsafeRect, sizeof(RECT), 1);
4007 SafeRect = *UnsafeRect;
4008 }
4009 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4010 {
4011 Status = _SEH2_GetExceptionCode();
4012 }
4013 _SEH2_END;
4014
4015 if (!NT_SUCCESS(Status))
4016 {
4017 RGNOBJAPI_Unlock(Rgn);
4018 SetLastNtError(Status);
4019 return NULL;
4020 }
4021
4022 REGION_UnionRectWithRgn(Rgn, &SafeRect);
4023 RGNOBJAPI_Unlock(Rgn);
4024 return hDest;
4025 }
4026
4027 /*!
4028 * MSDN: GetRegionData, Return Values:
4029 *
4030 * "If the function succeeds and dwCount specifies an adequate number of bytes,
4031 * the return value is always dwCount. If dwCount is too small or the function
4032 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
4033 * required number of bytes.
4034 *
4035 * If the function fails, the return value is zero."
4036 */
4037 _Success_(return!=0)
4038 ULONG
4039 APIENTRY
4040 NtGdiGetRegionData(
4041 _In_ HRGN hrgn,
4042 _In_ ULONG cjBuffer,
4043 _Out_opt_bytecap_(cjBuffer) LPRGNDATA lpRgnData)
4044 {
4045 ULONG cjRects, cjSize;
4046 PREGION prgn;
4047
4048 /* Lock the region */
4049 prgn = RGNOBJAPI_Lock(hrgn, NULL);
4050 if (prgn == NULL)
4051 {
4052 EngSetLastError(ERROR_INVALID_HANDLE);
4053 return 0;
4054 }
4055
4056 /* Calculate the region sizes */
4057 cjRects = prgn->rdh.nCount * sizeof(RECT);
4058 cjSize = cjRects + sizeof(RGNDATAHEADER);
4059
4060 /* Check if region data is requested */
4061 if (lpRgnData)
4062 {
4063 /* Check if the buffer is large enough */
4064 if (cjBuffer >= cjSize)
4065 {
4066 /* Probe the buffer and copy the data */
4067 _SEH2_TRY
4068 {
4069 ProbeForWrite(lpRgnData, cjSize, sizeof(ULONG));
4070 RtlCopyMemory(lpRgnData, &prgn->rdh, sizeof(RGNDATAHEADER));
4071 RtlCopyMemory(lpRgnData->Buffer, prgn->Buffer, cjRects);
4072 lpRgnData->rdh.iType = RDH_RECTANGLES;
4073 }
4074 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4075 {
4076 EngSetLastError(ERROR_INVALID_PARAMETER);
4077 cjSize = 0;
4078 }
4079 _SEH2_END;
4080 }
4081 else
4082 {
4083 /* Buffer is too small */
4084 EngSetLastError(ERROR_INVALID_PARAMETER);
4085 cjSize = 0;
4086 }
4087 }
4088
4089 /* Unlock the region and return the size */
4090 RGNOBJAPI_Unlock(prgn);
4091 return cjSize;
4092 }
4093
4094 /* EOF */