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