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