ce79722837cb55c62b29a8ceaf288f8dea90bc3e
[reactos.git] / reactos / win32ss / gdi / ntgdi / region.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 * GDI region objects. Shamelessly ripped out from the X11 distribution
22 * Thanks for the nice licence.
23 *
24 * Copyright 1993, 1994, 1995 Alexandre Julliard
25 * Modifications and additions: Copyright 1998 Huw Davies
26 * 1999 Alex Korobka
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Lesser General Public
30 * License as published by the Free Software Foundation; either
31 * version 2.1 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Lesser General Public License for more details.
37 *
38 * You should have received a copy of the GNU Lesser General Public
39 * License along with this library; if not, write to the Free Software
40 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
41 */
42
43 /************************************************************************
44
45 Copyright (c) 1987, 1988 X Consortium
46
47 Permission is hereby granted, free of charge, to any person obtaining a copy
48 of this software and associated documentation files (the "Software"), to deal
49 in the Software without restriction, including without limitation the rights
50 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51 copies of the Software, and to permit persons to whom the Software is
52 furnished to do so, subject to the following conditions:
53
54 The above copyright notice and this permission notice shall be included in
55 all copies or substantial portions of the Software.
56
57 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
61 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
62 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
64 Except as contained in this notice, the name of the X Consortium shall not be
65 used in advertising or otherwise to promote the sale, use or other dealings
66 in this Software without prior written authorization from the X Consortium.
67
68
69 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
70
71 All Rights Reserved
72
73 Permission to use, copy, modify, and distribute this software and its
74 documentation for any purpose and without fee is hereby granted,
75 provided that the above copyright notice appear in all copies and that
76 both that copyright notice and this permission notice appear in
77 supporting documentation, and that the name of Digital not be
78 used in advertising or publicity pertaining to distribution of the
79 software without specific, written prior permission.
80
81 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
82 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
83 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
84 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
85 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
86 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
87 SOFTWARE.
88
89 ************************************************************************/
90 /*
91 * The functions in this file implement the Region abstraction, similar to one
92 * used in the X11 sample server. A Region is simply an area, as the name
93 * implies, and is implemented as a "y-x-banded" array of rectangles. To
94 * explain: Each Region is made up of a certain number of rectangles sorted
95 * by y coordinate first, and then by x coordinate.
96 *
97 * Furthermore, the rectangles are banded such that every rectangle with a
98 * given upper-left y coordinate (y1) will have the same lower-right y
99 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
100 * will span the entire vertical distance of the band. This means that some
101 * areas that could be merged into a taller rectangle will be represented as
102 * several shorter rectangles to account for shorter rectangles to its left
103 * or right but within its "vertical scope".
104 *
105 * An added constraint on the rectangles is that they must cover as much
106 * horizontal area as possible. E.g. no two rectangles in a band are allowed
107 * to touch.
108 *
109 * Whenever possible, bands will be merged together to cover a greater vertical
110 * distance (and thus reduce the number of rectangles). Two bands can be merged
111 * only if the bottom of one touches the top of the other and they have
112 * rectangles in the same places (of the same width, of course). This maintains
113 * the y-x-banding that's so nice to have...
114 */
115
116 #include <win32k.h>
117 #include <suppress.h>
118
119 #define NDEBUG
120 #include <debug.h>
121
122 PROSRGNDATA prgnDefault = NULL;
123 HRGN hrgnDefault = NULL;
124
125 // Internal Functions
126
127 #if 1
128 #define COPY_RECTS(dest, src, nRects) \
129 do { \
130 PRECTL xDest = (dest); \
131 PRECTL xSrc = (src); \
132 UINT xRects = (nRects); \
133 while(xRects-- > 0) { \
134 *(xDest++) = *(xSrc++); \
135 } \
136 } while(0)
137 #else
138 #define COPY_RECTS(dest, src, nRects) RtlCopyMemory(dest, src, (nRects) * sizeof(RECTL))
139 #endif
140
141 #define EMPTY_REGION(pReg) { \
142 (pReg)->rdh.nCount = 0; \
143 (pReg)->rdh.rcBound.left = (pReg)->rdh.rcBound.top = 0; \
144 (pReg)->rdh.rcBound.right = (pReg)->rdh.rcBound.bottom = 0; \
145 (pReg)->rdh.iType = RDH_RECTANGLES; \
146 }
147
148 #define REGION_NOT_EMPTY(pReg) pReg->rdh.nCount
149
150 #define INRECT(r, x, y) \
151 ( ( ((r).right > x)) && \
152 ( ((r).left <= x)) && \
153 ( ((r).bottom > y)) && \
154 ( ((r).top <= y)) )
155
156 /* 1 if two RECTs overlap.
157 * 0 if two RECTs do not overlap.
158 */
159 #define EXTENTCHECK(r1, r2) \
160 ((r1)->right > (r2)->left && \
161 (r1)->left < (r2)->right && \
162 (r1)->bottom > (r2)->top && \
163 (r1)->top < (r2)->bottom)
164
165 /*
166 * In scan converting polygons, we want to choose those pixels
167 * which are inside the polygon. Thus, we add .5 to the starting
168 * x coordinate for both left and right edges. Now we choose the
169 * first pixel which is inside the pgon for the left edge and the
170 * first pixel which is outside the pgon for the right edge.
171 * Draw the left pixel, but not the right.
172 *
173 * How to add .5 to the starting x coordinate:
174 * If the edge is moving to the right, then subtract dy from the
175 * error term from the general form of the algorithm.
176 * If the edge is moving to the left, then add dy to the error term.
177 *
178 * The reason for the difference between edges moving to the left
179 * and edges moving to the right is simple: If an edge is moving
180 * to the right, then we want the algorithm to flip immediately.
181 * If it is moving to the left, then we don't want it to flip until
182 * we traverse an entire pixel.
183 */
184 #define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
185 int dx; /* Local storage */ \
186 \
187 /* \
188 * If the edge is horizontal, then it is ignored \
189 * and assumed not to be processed. Otherwise, do this stuff. \
190 */ \
191 if ((dy) != 0) { \
192 xStart = (x1); \
193 dx = (x2) - xStart; \
194 if (dx < 0) { \
195 m = dx / (dy); \
196 m1 = m - 1; \
197 incr1 = -2 * dx + 2 * (dy) * m1; \
198 incr2 = -2 * dx + 2 * (dy) * m; \
199 d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
200 } else { \
201 m = dx / (dy); \
202 m1 = m + 1; \
203 incr1 = 2 * dx - 2 * (dy) * m1; \
204 incr2 = 2 * dx - 2 * (dy) * m; \
205 d = -2 * m * (dy) + 2 * dx; \
206 } \
207 } \
208 }
209
210 #define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
211 if (m1 > 0) { \
212 if (d > 0) { \
213 minval += m1; \
214 d += incr1; \
215 } \
216 else { \
217 minval += m; \
218 d += incr2; \
219 } \
220 } else {\
221 if (d >= 0) { \
222 minval += m1; \
223 d += incr1; \
224 } \
225 else { \
226 minval += m; \
227 d += incr2; \
228 } \
229 } \
230 }
231
232 /*
233 * This structure contains all of the information needed
234 * to run the bresenham algorithm.
235 * The variables may be hardcoded into the declarations
236 * instead of using this structure to make use of
237 * register declarations.
238 */
239 typedef struct
240 {
241 INT minor_axis; /* Minor axis */
242 INT d; /* Decision variable */
243 INT m, m1; /* Slope and slope+1 */
244 INT incr1, incr2; /* Error increments */
245 } BRESINFO;
246
247
248 #define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
249 BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
250 bres.m, bres.m1, bres.incr1, bres.incr2)
251
252 #define BRESINCRPGONSTRUCT(bres) \
253 BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
254
255
256
257 /*
258 * These are the data structures needed to scan
259 * convert regions. Two different scan conversion
260 * methods are available -- the even-odd method, and
261 * the winding number method.
262 * The even-odd rule states that a point is inside
263 * the polygon if a ray drawn from that point in any
264 * direction will pass through an odd number of
265 * path segments.
266 * By the winding number rule, a point is decided
267 * to be inside the polygon if a ray drawn from that
268 * point in any direction passes through a different
269 * number of clockwise and counter-clockwise path
270 * segments.
271 *
272 * These data structures are adapted somewhat from
273 * the algorithm in (Foley/Van Dam) for scan converting
274 * polygons.
275 * The basic algorithm is to start at the top (smallest y)
276 * of the polygon, stepping down to the bottom of
277 * the polygon by incrementing the y coordinate. We
278 * keep a list of edges which the current scanline crosses,
279 * sorted by x. This list is called the Active Edge Table (AET)
280 * As we change the y-coordinate, we update each entry in
281 * in the active edge table to reflect the edges new xcoord.
282 * This list must be sorted at each scanline in case
283 * two edges intersect.
284 * We also keep a data structure known as the Edge Table (ET),
285 * which keeps track of all the edges which the current
286 * scanline has not yet reached. The ET is basically a
287 * list of ScanLineList structures containing a list of
288 * edges which are entered at a given scanline. There is one
289 * ScanLineList per scanline at which an edge is entered.
290 * When we enter a new edge, we move it from the ET to the AET.
291 *
292 * From the AET, we can implement the even-odd rule as in
293 * (Foley/Van Dam).
294 * The winding number rule is a little trickier. We also
295 * keep the EdgeTableEntries in the AET linked by the
296 * nextWETE (winding EdgeTableEntry) link. This allows
297 * the edges to be linked just as before for updating
298 * purposes, but only uses the edges linked by the nextWETE
299 * link as edges representing spans of the polygon to
300 * drawn (as with the even-odd rule).
301 */
302
303 /*
304 * For the winding number rule
305 */
306 #define CLOCKWISE 1
307 #define COUNTERCLOCKWISE -1
308
309 typedef struct _EdgeTableEntry
310 {
311 INT ymax; /* ycoord at which we exit this edge. */
312 BRESINFO bres; /* Bresenham info to run the edge */
313 struct _EdgeTableEntry *next; /* Next in the list */
314 struct _EdgeTableEntry *back; /* For insertion sort */
315 struct _EdgeTableEntry *nextWETE; /* For winding num rule */
316 int ClockWise; /* Flag for winding number rule */
317 } EdgeTableEntry;
318
319
320 typedef struct _ScanLineList
321 {
322 INT scanline; /* The scanline represented */
323 EdgeTableEntry *edgelist; /* Header node */
324 struct _ScanLineList *next; /* Next in the list */
325 } ScanLineList;
326
327
328 typedef struct
329 {
330 INT ymax; /* ymax for the polygon */
331 INT ymin; /* ymin for the polygon */
332 ScanLineList scanlines; /* Header node */
333 } EdgeTable;
334
335
336 /*
337 * Here is a struct to help with storage allocation
338 * so we can allocate a big chunk at a time, and then take
339 * pieces from this heap when we need to.
340 */
341 #define SLLSPERBLOCK 25
342
343 typedef struct _ScanLineListBlock
344 {
345 ScanLineList SLLs[SLLSPERBLOCK];
346 struct _ScanLineListBlock *next;
347 } ScanLineListBlock;
348
349
350 /*
351 * A few macros for the inner loops of the fill code where
352 * performance considerations don't allow a procedure call.
353 *
354 * Evaluate the given edge at the given scanline.
355 * If the edge has expired, then we leave it and fix up
356 * the active edge table; otherwise, we increment the
357 * x value to be ready for the next scanline.
358 * The winding number rule is in effect, so we must notify
359 * the caller when the edge has been removed so he
360 * can reorder the Winding Active Edge Table.
361 */
362 #define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
363 if (pAET->ymax == y) { /* Leaving this edge */ \
364 pPrevAET->next = pAET->next; \
365 pAET = pPrevAET->next; \
366 fixWAET = 1; \
367 if (pAET) \
368 pAET->back = pPrevAET; \
369 } \
370 else { \
371 BRESINCRPGONSTRUCT(pAET->bres); \
372 pPrevAET = pAET; \
373 pAET = pAET->next; \
374 } \
375 }
376
377
378 /*
379 * Evaluate the given edge at the given scanline.
380 * If the edge has expired, then we leave it and fix up
381 * the active edge table; otherwise, we increment the
382 * x value to be ready for the next scanline.
383 * The even-odd rule is in effect.
384 */
385 #define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
386 if (pAET->ymax == y) { /* Leaving this edge */ \
387 pPrevAET->next = pAET->next; \
388 pAET = pPrevAET->next; \
389 if (pAET) \
390 pAET->back = pPrevAET; \
391 } \
392 else { \
393 BRESINCRPGONSTRUCT(pAET->bres); \
394 pPrevAET = pAET; \
395 pAET = pAET->next; \
396 } \
397 }
398
399 /**************************************************************************
400 *
401 * Poly Regions
402 *
403 *************************************************************************/
404
405 #define LARGE_COORDINATE 0x7fffffff /* FIXME */
406 #define SMALL_COORDINATE 0x80000000
407
408 /*
409 * Check to see if there is enough memory in the present region.
410 */
411 static __inline int xmemcheck(ROSRGNDATA *reg, PRECTL *rect, PRECTL *firstrect)
412 {
413 if ( (reg->rdh.nCount+1) * sizeof(RECT) >= reg->rdh.nRgnSize )
414 {
415 PRECTL temp;
416 DWORD NewSize = 2 * reg->rdh.nRgnSize;
417 if (NewSize < (reg->rdh.nCount + 1) * sizeof(RECT))
418 {
419 NewSize = (reg->rdh.nCount + 1) * sizeof(RECT);
420 }
421 temp = ExAllocatePoolWithTag(PagedPool, NewSize, TAG_REGION);
422
423 if (temp == NULL)
424 {
425 return 0;
426 }
427
428 /* Copy the rectangles */
429 COPY_RECTS(temp, *firstrect, reg->rdh.nCount);
430
431 reg->rdh.nRgnSize = NewSize;
432 if (*firstrect != &reg->rdh.rcBound)
433 {
434 ExFreePoolWithTag(*firstrect, TAG_REGION);
435 }
436 *firstrect = temp;
437 *rect = (*firstrect)+reg->rdh.nCount;
438 }
439 return 1;
440 }
441
442 #define MEMCHECK(reg, rect, firstrect) xmemcheck(reg,&(rect),(PRECTL *)&(firstrect))
443
444 typedef void (FASTCALL *overlapProcp)(PROSRGNDATA, PRECT, PRECT, PRECT, PRECT, INT, INT);
445 typedef void (FASTCALL *nonOverlapProcp)(PROSRGNDATA, PRECT, PRECT, INT, INT);
446
447 // Number of points to buffer before sending them off to scanlines() : Must be an even number
448 #define NUMPTSTOBUFFER 200
449
450 #define RGN_DEFAULT_RECTS 2
451
452 // Used to allocate buffers for points and link the buffers together
453
454 typedef struct _POINTBLOCK
455 {
456 POINT pts[NUMPTSTOBUFFER];
457 struct _POINTBLOCK *next;
458 } POINTBLOCK;
459
460 #ifndef NDEBUG
461 /*
462 * This function is left there for debugging purposes.
463 */
464
465 VOID FASTCALL
466 IntDumpRegion(HRGN hRgn)
467 {
468 ROSRGNDATA *Data;
469
470 Data = RGNOBJAPI_Lock(hRgn, NULL);
471 if (Data == NULL)
472 {
473 DbgPrint("IntDumpRegion called with invalid region!\n");
474 return;
475 }
476
477 DbgPrint("IntDumpRegion(%x): %d,%d-%d,%d %d\n",
478 hRgn,
479 Data->rdh.rcBound.left,
480 Data->rdh.rcBound.top,
481 Data->rdh.rcBound.right,
482 Data->rdh.rcBound.bottom,
483 Data->rdh.iType);
484
485 RGNOBJAPI_Unlock(Data);
486 }
487 #endif /* Not NDEBUG */
488
489
490 INT
491 FASTCALL
492 REGION_Complexity( PROSRGNDATA obj )
493 {
494 if (!obj) return NULLREGION;
495 switch(obj->rdh.nCount)
496 {
497 DPRINT("Region Complexity -> %lu",obj->rdh.nCount);
498 case 0: return NULLREGION;
499 case 1: return SIMPLEREGION;
500 default: return COMPLEXREGION;
501 }
502 }
503
504 static
505 BOOL
506 FASTCALL
507 REGION_CopyRegion(
508 PROSRGNDATA dst,
509 PROSRGNDATA src
510 )
511 {
512 if (dst != src) // Don't want to copy to itself
513 {
514 if (dst->rdh.nRgnSize < src->rdh.nCount * sizeof(RECT))
515 {
516 PRECTL temp;
517
518 temp = ExAllocatePoolWithTag(PagedPool, src->rdh.nCount * sizeof(RECT), TAG_REGION );
519 if (!temp)
520 return FALSE;
521
522 if (dst->Buffer && dst->Buffer != &dst->rdh.rcBound)
523 ExFreePoolWithTag(dst->Buffer, TAG_REGION); // Free the old buffer
524 dst->Buffer = temp;
525 dst->rdh.nRgnSize = src->rdh.nCount * sizeof(RECT); // Size of region buffer
526 }
527 dst->rdh.nCount = src->rdh.nCount; // Number of rectangles present in Buffer
528 dst->rdh.rcBound.left = src->rdh.rcBound.left;
529 dst->rdh.rcBound.top = src->rdh.rcBound.top;
530 dst->rdh.rcBound.right = src->rdh.rcBound.right;
531 dst->rdh.rcBound.bottom = src->rdh.rcBound.bottom;
532 dst->rdh.iType = src->rdh.iType;
533 COPY_RECTS(dst->Buffer, src->Buffer, src->rdh.nCount);
534 }
535 return TRUE;
536 }
537
538 static void FASTCALL
539 REGION_SetExtents(ROSRGNDATA *pReg)
540 {
541 RECTL *pRect, *pRectEnd, *pExtents;
542
543 if (pReg->rdh.nCount == 0)
544 {
545 pReg->rdh.rcBound.left = 0;
546 pReg->rdh.rcBound.top = 0;
547 pReg->rdh.rcBound.right = 0;
548 pReg->rdh.rcBound.bottom = 0;
549 pReg->rdh.iType = RDH_RECTANGLES;
550 return;
551 }
552
553 pExtents = &pReg->rdh.rcBound;
554 pRect = pReg->Buffer;
555 pRectEnd = pReg->Buffer + pReg->rdh.nCount - 1;
556
557 /*
558 * Since pRect is the first rectangle in the region, it must have the
559 * smallest top and since pRectEnd is the last rectangle in the region,
560 * it must have the largest bottom, because of banding. Initialize left and
561 * right from pRect and pRectEnd, resp., as good things to initialize them
562 * to...
563 */
564 pExtents->left = pRect->left;
565 pExtents->top = pRect->top;
566 pExtents->right = pRectEnd->right;
567 pExtents->bottom = pRectEnd->bottom;
568
569 while (pRect <= pRectEnd)
570 {
571 if (pRect->left < pExtents->left)
572 pExtents->left = pRect->left;
573 if (pRect->right > pExtents->right)
574 pExtents->right = pRect->right;
575 pRect++;
576 }
577 pReg->rdh.iType = RDH_RECTANGLES;
578 }
579
580 // FIXME: This seems to be wrong
581 /***********************************************************************
582 * REGION_CropAndOffsetRegion
583 */
584 BOOL FASTCALL
585 REGION_CropAndOffsetRegion(
586 PROSRGNDATA rgnDst,
587 PROSRGNDATA rgnSrc,
588 const RECTL *rect,
589 const POINTL *offset
590 )
591 {
592 POINT pt = {0,0};
593 const POINT *off = offset;
594
595 if (!off) off = &pt;
596
597 if (!rect) // Just copy and offset
598 {
599 PRECTL xrect;
600 if (rgnDst == rgnSrc)
601 {
602 if (off->x || off->y)
603 xrect = rgnDst->Buffer;
604 else
605 return TRUE;
606 }
607 else
608 {
609 xrect = ExAllocatePoolWithTag(PagedPool, rgnSrc->rdh.nCount * sizeof(RECT), TAG_REGION);
610 if(!xrect)
611 return FALSE;
612 if (rgnDst->Buffer && rgnDst->Buffer != &rgnDst->rdh.rcBound)
613 ExFreePoolWithTag(rgnDst->Buffer, TAG_REGION); // Free the old buffer. Will be assigned to xrect below.
614 }
615
616 if (rgnDst != rgnSrc)
617 {
618 *rgnDst = *rgnSrc;
619 }
620
621 if (off->x || off->y)
622 {
623 ULONG i;
624 for (i = 0; i < rgnDst->rdh.nCount; i++)
625 {
626 xrect[i].left = (rgnSrc->Buffer + i)->left + off->x;
627 xrect[i].right = (rgnSrc->Buffer + i)->right + off->x;
628 xrect[i].top = (rgnSrc->Buffer + i)->top + off->y;
629 xrect[i].bottom = (rgnSrc->Buffer + i)->bottom + off->y;
630 }
631 rgnDst->rdh.rcBound.left += off->x;
632 rgnDst->rdh.rcBound.right += off->x;
633 rgnDst->rdh.rcBound.top += off->y;
634 rgnDst->rdh.rcBound.bottom += off->y;
635 }
636 else
637 {
638 COPY_RECTS(xrect, rgnSrc->Buffer, rgnDst->rdh.nCount);
639 }
640
641 rgnDst->Buffer = xrect;
642 }
643 else if ((rect->left >= rect->right) ||
644 (rect->top >= rect->bottom) ||
645 !EXTENTCHECK(rect, &rgnSrc->rdh.rcBound))
646 {
647 goto empty;
648 }
649 else // Region box and clipping rect appear to intersect
650 {
651 PRECTL lpr, rpr;
652 ULONG i, j, clipa, clipb;
653 INT left = rgnSrc->rdh.rcBound.right + off->x;
654 INT right = rgnSrc->rdh.rcBound.left + off->x;
655
656 for (clipa = 0; (rgnSrc->Buffer + clipa)->bottom <= rect->top; clipa++)
657 // Region and rect intersect so we stop before clipa > rgnSrc->rdh.nCount
658 ; // skip bands above the clipping rectangle
659
660 for (clipb = clipa; clipb < rgnSrc->rdh.nCount; clipb++)
661 if ((rgnSrc->Buffer + clipb)->top >= rect->bottom)
662 break; // and below it
663
664 // clipa - index of the first rect in the first intersecting band
665 // clipb - index of the last rect in the last intersecting band
666
667 if ((rgnDst != rgnSrc) && (rgnDst->rdh.nCount < (i = (clipb - clipa))))
668 {
669 PRECTL temp;
670 temp = ExAllocatePoolWithTag(PagedPool, i * sizeof(RECT), TAG_REGION);
671 if (!temp)
672 return FALSE;
673
674 if (rgnDst->Buffer && rgnDst->Buffer != &rgnDst->rdh.rcBound)
675 ExFreePoolWithTag(rgnDst->Buffer, TAG_REGION); // free the old buffer
676 rgnDst->Buffer = temp;
677 rgnDst->rdh.nCount = i;
678 rgnDst->rdh.nRgnSize = i * sizeof(RECT);
679 }
680
681 for (i = clipa, j = 0; i < clipb ; i++)
682 {
683 // i - src index, j - dst index, j is always <= i for obvious reasons
684
685 lpr = rgnSrc->Buffer + i;
686
687 if (lpr->left < rect->right && lpr->right > rect->left)
688 {
689 rpr = rgnDst->Buffer + j;
690
691 rpr->top = lpr->top + off->y;
692 rpr->bottom = lpr->bottom + off->y;
693 rpr->left = ((lpr->left > rect->left) ? lpr->left : rect->left) + off->x;
694 rpr->right = ((lpr->right < rect->right) ? lpr->right : rect->right) + off->x;
695
696 if (rpr->left < left) left = rpr->left;
697 if (rpr->right > right) right = rpr->right;
698
699 j++;
700 }
701 }
702
703 if (j == 0) goto empty;
704
705 rgnDst->rdh.rcBound.left = left;
706 rgnDst->rdh.rcBound.right = right;
707
708 left = rect->top + off->y;
709 right = rect->bottom + off->y;
710
711 rgnDst->rdh.nCount = j--;
712 for (i = 0; i <= j; i++) // Fixup top band
713 if ((rgnDst->Buffer + i)->top < left)
714 (rgnDst->Buffer + i)->top = left;
715 else
716 break;
717
718 for (i = j; i > 0; i--) // Fixup bottom band
719 if ((rgnDst->Buffer + i)->bottom > right)
720 (rgnDst->Buffer + i)->bottom = right;
721 else
722 break;
723
724 rgnDst->rdh.rcBound.top = (rgnDst->Buffer)->top;
725 rgnDst->rdh.rcBound.bottom = (rgnDst->Buffer + j)->bottom;
726
727 rgnDst->rdh.iType = RDH_RECTANGLES;
728 }
729
730 return TRUE;
731
732 empty:
733 if (!rgnDst->Buffer)
734 {
735 rgnDst->Buffer = ExAllocatePoolWithTag(PagedPool, RGN_DEFAULT_RECTS * sizeof(RECT), TAG_REGION);
736 if (rgnDst->Buffer)
737 {
738 rgnDst->rdh.nCount = RGN_DEFAULT_RECTS;
739 rgnDst->rdh.nRgnSize = RGN_DEFAULT_RECTS * sizeof(RECT);
740 }
741 else
742 return FALSE;
743 }
744 EMPTY_REGION(rgnDst);
745 return TRUE;
746 }
747
748
749 /*!
750 * Attempt to merge the rects in the current band with those in the
751 * previous one. Used only by REGION_RegionOp.
752 *
753 * Results:
754 * The new index for the previous band.
755 *
756 * \note Side Effects:
757 * If coalescing takes place:
758 * - rectangles in the previous band will have their bottom fields
759 * altered.
760 * - pReg->numRects will be decreased.
761 *
762 */
763 static INT FASTCALL
764 REGION_Coalesce(
765 PROSRGNDATA pReg, /* Region to coalesce */
766 INT prevStart, /* Index of start of previous band */
767 INT curStart /* Index of start of current band */
768 )
769 {
770 RECTL *pPrevRect; /* Current rect in previous band */
771 RECTL *pCurRect; /* Current rect in current band */
772 RECTL *pRegEnd; /* End of region */
773 INT curNumRects; /* Number of rectangles in current band */
774 INT prevNumRects; /* Number of rectangles in previous band */
775 INT bandtop; /* Top coordinate for current band */
776
777 pRegEnd = pReg->Buffer + pReg->rdh.nCount;
778 pPrevRect = pReg->Buffer + prevStart;
779 prevNumRects = curStart - prevStart;
780
781 /*
782 * Figure out how many rectangles are in the current band. Have to do
783 * this because multiple bands could have been added in REGION_RegionOp
784 * at the end when one region has been exhausted.
785 */
786 pCurRect = pReg->Buffer + curStart;
787 bandtop = pCurRect->top;
788 for (curNumRects = 0;
789 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
790 curNumRects++)
791 {
792 pCurRect++;
793 }
794
795 if (pCurRect != pRegEnd)
796 {
797 /*
798 * If more than one band was added, we have to find the start
799 * of the last band added so the next coalescing job can start
800 * at the right place... (given when multiple bands are added,
801 * this may be pointless -- see above).
802 */
803 pRegEnd--;
804 while ((pRegEnd-1)->top == pRegEnd->top)
805 {
806 pRegEnd--;
807 }
808 curStart = pRegEnd - pReg->Buffer;
809 pRegEnd = pReg->Buffer + pReg->rdh.nCount;
810 }
811
812 if ((curNumRects == prevNumRects) && (curNumRects != 0))
813 {
814 pCurRect -= curNumRects;
815 /*
816 * The bands may only be coalesced if the bottom of the previous
817 * matches the top scanline of the current.
818 */
819 if (pPrevRect->bottom == pCurRect->top)
820 {
821 /*
822 * Make sure the bands have rects in the same places. This
823 * assumes that rects have been added in such a way that they
824 * cover the most area possible. I.e. two rects in a band must
825 * have some horizontal space between them.
826 */
827 do
828 {
829 if ((pPrevRect->left != pCurRect->left) ||
830 (pPrevRect->right != pCurRect->right))
831 {
832 /*
833 * The bands don't line up so they can't be coalesced.
834 */
835 return (curStart);
836 }
837 pPrevRect++;
838 pCurRect++;
839 prevNumRects -= 1;
840 }
841 while (prevNumRects != 0);
842
843 pReg->rdh.nCount -= curNumRects;
844 pCurRect -= curNumRects;
845 pPrevRect -= curNumRects;
846
847 /*
848 * The bands may be merged, so set the bottom of each rect
849 * in the previous band to that of the corresponding rect in
850 * the current band.
851 */
852 do
853 {
854 pPrevRect->bottom = pCurRect->bottom;
855 pPrevRect++;
856 pCurRect++;
857 curNumRects -= 1;
858 }
859 while (curNumRects != 0);
860
861 /*
862 * If only one band was added to the region, we have to backup
863 * curStart to the start of the previous band.
864 *
865 * If more than one band was added to the region, copy the
866 * other bands down. The assumption here is that the other bands
867 * came from the same region as the current one and no further
868 * coalescing can be done on them since it's all been done
869 * already... curStart is already in the right place.
870 */
871 if (pCurRect == pRegEnd)
872 {
873 curStart = prevStart;
874 }
875 else
876 {
877 do
878 {
879 *pPrevRect++ = *pCurRect++;
880 }
881 while (pCurRect != pRegEnd);
882 }
883 }
884 }
885 return (curStart);
886 }
887
888 /*!
889 * Apply an operation to two regions. Called by REGION_Union,
890 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
891 *
892 * Results:
893 * None.
894 *
895 * Side Effects:
896 * The new region is overwritten.
897 *
898 *\note The idea behind this function is to view the two regions as sets.
899 * Together they cover a rectangle of area that this function divides
900 * into horizontal bands where points are covered only by one region
901 * or by both. For the first case, the nonOverlapFunc is called with
902 * each the band and the band's upper and lower extents. For the
903 * second, the overlapFunc is called to process the entire band. It
904 * is responsible for clipping the rectangles in the band, though
905 * this function provides the boundaries.
906 * At the end of each band, the new region is coalesced, if possible,
907 * to reduce the number of rectangles in the region.
908 *
909 */
910 static void FASTCALL
911 REGION_RegionOp(
912 ROSRGNDATA *newReg, /* Place to store result */
913 ROSRGNDATA *reg1, /* First region in operation */
914 ROSRGNDATA *reg2, /* 2nd region in operation */
915 overlapProcp overlapFunc, /* Function to call for over-lapping bands */
916 nonOverlapProcp nonOverlap1Func, /* Function to call for non-overlapping bands in region 1 */
917 nonOverlapProcp nonOverlap2Func /* Function to call for non-overlapping bands in region 2 */
918 )
919 {
920 RECTL *r1; /* Pointer into first region */
921 RECTL *r2; /* Pointer into 2d region */
922 RECTL *r1End; /* End of 1st region */
923 RECTL *r2End; /* End of 2d region */
924 INT ybot; /* Bottom of intersection */
925 INT ytop; /* Top of intersection */
926 RECTL *oldRects; /* Old rects for newReg */
927 ULONG prevBand; /* Index of start of
928 * Previous band in newReg */
929 ULONG curBand; /* Index of start of current band in newReg */
930 RECTL *r1BandEnd; /* End of current band in r1 */
931 RECTL *r2BandEnd; /* End of current band in r2 */
932 ULONG top; /* Top of non-overlapping band */
933 ULONG bot; /* Bottom of non-overlapping band */
934
935 /*
936 * Initialization:
937 * set r1, r2, r1End and r2End appropriately, preserve the important
938 * parts of the destination region until the end in case it's one of
939 * the two source regions, then mark the "new" region empty, allocating
940 * another array of rectangles for it to use.
941 */
942 r1 = reg1->Buffer;
943 r2 = reg2->Buffer;
944 r1End = r1 + reg1->rdh.nCount;
945 r2End = r2 + reg2->rdh.nCount;
946
947
948 /*
949 * newReg may be one of the src regions so we can't empty it. We keep a
950 * note of its rects pointer (so that we can free them later), preserve its
951 * extents and simply set numRects to zero.
952 */
953
954 oldRects = newReg->Buffer;
955 newReg->rdh.nCount = 0;
956
957 /*
958 * Allocate a reasonable number of rectangles for the new region. The idea
959 * is to allocate enough so the individual functions don't need to
960 * reallocate and copy the array, which is time consuming, yet we don't
961 * have to worry about using too much memory. I hope to be able to
962 * nuke the Xrealloc() at the end of this function eventually.
963 */
964 newReg->rdh.nRgnSize = max(reg1->rdh.nCount + 1,reg2->rdh.nCount) * 2 * sizeof(RECT);
965
966 newReg->Buffer = ExAllocatePoolWithTag(PagedPool, newReg->rdh.nRgnSize, TAG_REGION);
967 if (!newReg->Buffer)
968 {
969 newReg->rdh.nRgnSize = 0;
970 return;
971 }
972
973 /*
974 * Initialize ybot and ytop.
975 * In the upcoming loop, ybot and ytop serve different functions depending
976 * on whether the band being handled is an overlapping or non-overlapping
977 * band.
978 * In the case of a non-overlapping band (only one of the regions
979 * has points in the band), ybot is the bottom of the most recent
980 * intersection and thus clips the top of the rectangles in that band.
981 * ytop is the top of the next intersection between the two regions and
982 * serves to clip the bottom of the rectangles in the current band.
983 * For an overlapping band (where the two regions intersect), ytop clips
984 * the top of the rectangles of both regions and ybot clips the bottoms.
985 */
986 if (reg1->rdh.rcBound.top < reg2->rdh.rcBound.top)
987 ybot = reg1->rdh.rcBound.top;
988 else
989 ybot = reg2->rdh.rcBound.top;
990
991 /*
992 * prevBand serves to mark the start of the previous band so rectangles
993 * can be coalesced into larger rectangles. qv. miCoalesce, above.
994 * In the beginning, there is no previous band, so prevBand == curBand
995 * (curBand is set later on, of course, but the first band will always
996 * start at index 0). prevBand and curBand must be indices because of
997 * the possible expansion, and resultant moving, of the new region's
998 * array of rectangles.
999 */
1000 prevBand = 0;
1001
1002 do
1003 {
1004 curBand = newReg->rdh.nCount;
1005
1006 /*
1007 * This algorithm proceeds one source-band (as opposed to a
1008 * destination band, which is determined by where the two regions
1009 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1010 * rectangle after the last one in the current band for their
1011 * respective regions.
1012 */
1013 r1BandEnd = r1;
1014 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top))
1015 {
1016 r1BandEnd++;
1017 }
1018
1019 r2BandEnd = r2;
1020 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top))
1021 {
1022 r2BandEnd++;
1023 }
1024
1025 /*
1026 * First handle the band that doesn't intersect, if any.
1027 *
1028 * Note that attention is restricted to one band in the
1029 * non-intersecting region at once, so if a region has n
1030 * bands between the current position and the next place it overlaps
1031 * the other, this entire loop will be passed through n times.
1032 */
1033 if (r1->top < r2->top)
1034 {
1035 top = max(r1->top,ybot);
1036 bot = min(r1->bottom,r2->top);
1037
1038 if ((top != bot) && (nonOverlap1Func != NULL))
1039 {
1040 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
1041 }
1042
1043 ytop = r2->top;
1044 }
1045 else if (r2->top < r1->top)
1046 {
1047 top = max(r2->top,ybot);
1048 bot = min(r2->bottom,r1->top);
1049
1050 if ((top != bot) && (nonOverlap2Func != NULL))
1051 {
1052 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
1053 }
1054
1055 ytop = r1->top;
1056 }
1057 else
1058 {
1059 ytop = r1->top;
1060 }
1061
1062 /*
1063 * If any rectangles got added to the region, try and coalesce them
1064 * with rectangles from the previous band. Note we could just do
1065 * this test in miCoalesce, but some machines incur a not
1066 * inconsiderable cost for function calls, so...
1067 */
1068 if (newReg->rdh.nCount != curBand)
1069 {
1070 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1071 }
1072
1073 /*
1074 * Now see if we've hit an intersecting band. The two bands only
1075 * intersect if ybot > ytop
1076 */
1077 ybot = min(r1->bottom, r2->bottom);
1078 curBand = newReg->rdh.nCount;
1079 if (ybot > ytop)
1080 {
1081 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1082 }
1083
1084 if (newReg->rdh.nCount != curBand)
1085 {
1086 prevBand = REGION_Coalesce (newReg, prevBand, curBand);
1087 }
1088
1089 /*
1090 * If we've finished with a band (bottom == ybot) we skip forward
1091 * in the region to the next band.
1092 */
1093 if (r1->bottom == ybot)
1094 {
1095 r1 = r1BandEnd;
1096 }
1097 if (r2->bottom == ybot)
1098 {
1099 r2 = r2BandEnd;
1100 }
1101 }
1102 while ((r1 != r1End) && (r2 != r2End));
1103
1104 /*
1105 * Deal with whichever region still has rectangles left.
1106 */
1107 curBand = newReg->rdh.nCount;
1108 if (r1 != r1End)
1109 {
1110 if (nonOverlap1Func != NULL)
1111 {
1112 do
1113 {
1114 r1BandEnd = r1;
1115 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top))
1116 {
1117 r1BandEnd++;
1118 }
1119 (* nonOverlap1Func) (newReg, r1, r1BandEnd,
1120 max(r1->top,ybot), r1->bottom);
1121 r1 = r1BandEnd;
1122 }
1123 while (r1 != r1End);
1124 }
1125 }
1126 else if ((r2 != r2End) && (nonOverlap2Func != NULL))
1127 {
1128 do
1129 {
1130 r2BandEnd = r2;
1131 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top))
1132 {
1133 r2BandEnd++;
1134 }
1135 (* nonOverlap2Func) (newReg, r2, r2BandEnd,
1136 max(r2->top,ybot), r2->bottom);
1137 r2 = r2BandEnd;
1138 }
1139 while (r2 != r2End);
1140 }
1141
1142 if (newReg->rdh.nCount != curBand)
1143 {
1144 (void) REGION_Coalesce (newReg, prevBand, curBand);
1145 }
1146
1147 /*
1148 * A bit of cleanup. To keep regions from growing without bound,
1149 * we shrink the array of rectangles to match the new number of
1150 * rectangles in the region. This never goes to 0, however...
1151 *
1152 * Only do this stuff if the number of rectangles allocated is more than
1153 * twice the number of rectangles in the region (a simple optimization...).
1154 */
1155 if ((2 * newReg->rdh.nCount*sizeof(RECT) < newReg->rdh.nRgnSize && (newReg->rdh.nCount > 2)))
1156 {
1157 if (REGION_NOT_EMPTY(newReg))
1158 {
1159 RECTL *prev_rects = newReg->Buffer;
1160 newReg->Buffer = ExAllocatePoolWithTag(PagedPool, newReg->rdh.nCount*sizeof(RECT), TAG_REGION);
1161
1162 if (! newReg->Buffer)
1163 newReg->Buffer = prev_rects;
1164 else
1165 {
1166 newReg->rdh.nRgnSize = newReg->rdh.nCount*sizeof(RECT);
1167 COPY_RECTS(newReg->Buffer, prev_rects, newReg->rdh.nCount);
1168 if (prev_rects != &newReg->rdh.rcBound)
1169 ExFreePoolWithTag(prev_rects, TAG_REGION);
1170 }
1171 }
1172 else
1173 {
1174 /*
1175 * No point in doing the extra work involved in an Xrealloc if
1176 * the region is empty
1177 */
1178 newReg->rdh.nRgnSize = sizeof(RECT);
1179 if (newReg->Buffer != &newReg->rdh.rcBound)
1180 ExFreePoolWithTag(newReg->Buffer, TAG_REGION);
1181 newReg->Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(RECT), TAG_REGION);
1182 ASSERT(newReg->Buffer);
1183 }
1184 }
1185 newReg->rdh.iType = RDH_RECTANGLES;
1186
1187 if (oldRects != &newReg->rdh.rcBound)
1188 ExFreePoolWithTag(oldRects, TAG_REGION);
1189 return;
1190 }
1191
1192 /***********************************************************************
1193 * Region Intersection
1194 ***********************************************************************/
1195
1196
1197 /*!
1198 * Handle an overlapping band for REGION_Intersect.
1199 *
1200 * Results:
1201 * None.
1202 *
1203 * \note Side Effects:
1204 * Rectangles may be added to the region.
1205 *
1206 */
1207 static void FASTCALL
1208 REGION_IntersectO(
1209 PROSRGNDATA pReg,
1210 PRECTL r1,
1211 PRECTL r1End,
1212 PRECTL r2,
1213 PRECTL r2End,
1214 INT top,
1215 INT bottom
1216 )
1217 {
1218 INT left, right;
1219 RECTL *pNextRect;
1220
1221 pNextRect = pReg->Buffer + pReg->rdh.nCount;
1222
1223 while ((r1 != r1End) && (r2 != r2End))
1224 {
1225 left = max(r1->left, r2->left);
1226 right = min(r1->right, r2->right);
1227
1228 /*
1229 * If there's any overlap between the two rectangles, add that
1230 * overlap to the new region.
1231 * There's no need to check for subsumption because the only way
1232 * such a need could arise is if some region has two rectangles
1233 * right next to each other. Since that should never happen...
1234 */
1235 if (left < right)
1236 {
1237 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1238 pNextRect->left = left;
1239 pNextRect->top = top;
1240 pNextRect->right = right;
1241 pNextRect->bottom = bottom;
1242 pReg->rdh.nCount += 1;
1243 pNextRect++;
1244 }
1245
1246 /*
1247 * Need to advance the pointers. Shift the one that extends
1248 * to the right the least, since the other still has a chance to
1249 * overlap with that region's next rectangle, if you see what I mean.
1250 */
1251 if (r1->right < r2->right)
1252 {
1253 r1++;
1254 }
1255 else if (r2->right < r1->right)
1256 {
1257 r2++;
1258 }
1259 else
1260 {
1261 r1++;
1262 r2++;
1263 }
1264 }
1265 return;
1266 }
1267
1268 /***********************************************************************
1269 * REGION_IntersectRegion
1270 */
1271 static void FASTCALL
1272 REGION_IntersectRegion(
1273 ROSRGNDATA *newReg,
1274 ROSRGNDATA *reg1,
1275 ROSRGNDATA *reg2
1276 )
1277 {
1278 /* Check for trivial reject */
1279 if ( (!(reg1->rdh.nCount)) || (!(reg2->rdh.nCount)) ||
1280 (!EXTENTCHECK(&reg1->rdh.rcBound, &reg2->rdh.rcBound)) )
1281 newReg->rdh.nCount = 0;
1282 else
1283 REGION_RegionOp (newReg, reg1, reg2,
1284 REGION_IntersectO, NULL, NULL);
1285
1286 /*
1287 * Can't alter newReg's extents before we call miRegionOp because
1288 * it might be one of the source regions and miRegionOp depends
1289 * on the extents of those regions being the same. Besides, this
1290 * way there's no checking against rectangles that will be nuked
1291 * due to coalescing, so we have to examine fewer rectangles.
1292 */
1293
1294 REGION_SetExtents(newReg);
1295 }
1296
1297 /***********************************************************************
1298 * Region Union
1299 ***********************************************************************/
1300
1301 /*!
1302 * Handle a non-overlapping band for the union operation. Just
1303 * Adds the rectangles into the region. Doesn't have to check for
1304 * subsumption or anything.
1305 *
1306 * Results:
1307 * None.
1308 *
1309 * \note Side Effects:
1310 * pReg->numRects is incremented and the final rectangles overwritten
1311 * with the rectangles we're passed.
1312 *
1313 */
1314 static void FASTCALL
1315 REGION_UnionNonO (
1316 PROSRGNDATA pReg,
1317 PRECTL r,
1318 PRECTL rEnd,
1319 INT top,
1320 INT bottom
1321 )
1322 {
1323 RECTL *pNextRect;
1324
1325 pNextRect = pReg->Buffer + pReg->rdh.nCount;
1326
1327 while (r != rEnd)
1328 {
1329 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1330 pNextRect->left = r->left;
1331 pNextRect->top = top;
1332 pNextRect->right = r->right;
1333 pNextRect->bottom = bottom;
1334 pReg->rdh.nCount += 1;
1335 pNextRect++;
1336 r++;
1337 }
1338 return;
1339 }
1340
1341 /*!
1342 * Handle an overlapping band for the union operation. Picks the
1343 * left-most rectangle each time and merges it into the region.
1344 *
1345 * Results:
1346 * None.
1347 *
1348 * \note Side Effects:
1349 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1350 * be changed.
1351 *
1352 */
1353 static void FASTCALL
1354 REGION_UnionO (
1355 PROSRGNDATA pReg,
1356 PRECTL r1,
1357 PRECTL r1End,
1358 PRECTL r2,
1359 PRECTL r2End,
1360 INT top,
1361 INT bottom
1362 )
1363 {
1364 RECTL *pNextRect;
1365
1366 pNextRect = pReg->Buffer + pReg->rdh.nCount;
1367
1368 #define MERGERECT(r) \
1369 if ((pReg->rdh.nCount != 0) && \
1370 ((pNextRect-1)->top == top) && \
1371 ((pNextRect-1)->bottom == bottom) && \
1372 ((pNextRect-1)->right >= r->left)) \
1373 { \
1374 if ((pNextRect-1)->right < r->right) \
1375 { \
1376 (pNextRect-1)->right = r->right; \
1377 } \
1378 } \
1379 else \
1380 { \
1381 MEMCHECK(pReg, pNextRect, pReg->Buffer); \
1382 pNextRect->top = top; \
1383 pNextRect->bottom = bottom; \
1384 pNextRect->left = r->left; \
1385 pNextRect->right = r->right; \
1386 pReg->rdh.nCount += 1; \
1387 pNextRect += 1; \
1388 } \
1389 r++;
1390
1391 while ((r1 != r1End) && (r2 != r2End))
1392 {
1393 if (r1->left < r2->left)
1394 {
1395 MERGERECT(r1);
1396 }
1397 else
1398 {
1399 MERGERECT(r2);
1400 }
1401 }
1402
1403 if (r1 != r1End)
1404 {
1405 do
1406 {
1407 MERGERECT(r1);
1408 }
1409 while (r1 != r1End);
1410 }
1411 else while (r2 != r2End)
1412 {
1413 MERGERECT(r2);
1414 }
1415 return;
1416 }
1417
1418 /***********************************************************************
1419 * REGION_UnionRegion
1420 */
1421 static void FASTCALL
1422 REGION_UnionRegion(
1423 ROSRGNDATA *newReg,
1424 ROSRGNDATA *reg1,
1425 ROSRGNDATA *reg2
1426 )
1427 {
1428 /* Checks all the simple cases */
1429
1430 /*
1431 * Region 1 and 2 are the same or region 1 is empty
1432 */
1433 if (reg1 == reg2 || 0 == reg1->rdh.nCount ||
1434 reg1->rdh.rcBound.right <= reg1->rdh.rcBound.left ||
1435 reg1->rdh.rcBound.bottom <= reg1->rdh.rcBound.top)
1436 {
1437 if (newReg != reg2)
1438 {
1439 REGION_CopyRegion(newReg, reg2);
1440 }
1441 return;
1442 }
1443
1444 /*
1445 * If nothing to union (region 2 empty)
1446 */
1447 if (0 == reg2->rdh.nCount ||
1448 reg2->rdh.rcBound.right <= reg2->rdh.rcBound.left ||
1449 reg2->rdh.rcBound.bottom <= reg2->rdh.rcBound.top)
1450 {
1451 if (newReg != reg1)
1452 {
1453 REGION_CopyRegion(newReg, reg1);
1454 }
1455 return;
1456 }
1457
1458 /*
1459 * Region 1 completely subsumes region 2
1460 */
1461 if (1 == reg1->rdh.nCount &&
1462 reg1->rdh.rcBound.left <= reg2->rdh.rcBound.left &&
1463 reg1->rdh.rcBound.top <= reg2->rdh.rcBound.top &&
1464 reg2->rdh.rcBound.right <= reg1->rdh.rcBound.right &&
1465 reg2->rdh.rcBound.bottom <= reg1->rdh.rcBound.bottom)
1466 {
1467 if (newReg != reg1)
1468 {
1469 REGION_CopyRegion(newReg, reg1);
1470 }
1471 return;
1472 }
1473
1474 /*
1475 * Region 2 completely subsumes region 1
1476 */
1477 if (1 == reg2->rdh.nCount &&
1478 reg2->rdh.rcBound.left <= reg1->rdh.rcBound.left &&
1479 reg2->rdh.rcBound.top <= reg1->rdh.rcBound.top &&
1480 reg1->rdh.rcBound.right <= reg2->rdh.rcBound.right &&
1481 reg1->rdh.rcBound.bottom <= reg2->rdh.rcBound.bottom)
1482 {
1483 if (newReg != reg2)
1484 {
1485 REGION_CopyRegion(newReg, reg2);
1486 }
1487 return;
1488 }
1489
1490 REGION_RegionOp (newReg, reg1, reg2, REGION_UnionO,
1491 REGION_UnionNonO, REGION_UnionNonO);
1492 newReg->rdh.rcBound.left = min(reg1->rdh.rcBound.left, reg2->rdh.rcBound.left);
1493 newReg->rdh.rcBound.top = min(reg1->rdh.rcBound.top, reg2->rdh.rcBound.top);
1494 newReg->rdh.rcBound.right = max(reg1->rdh.rcBound.right, reg2->rdh.rcBound.right);
1495 newReg->rdh.rcBound.bottom = max(reg1->rdh.rcBound.bottom, reg2->rdh.rcBound.bottom);
1496 }
1497
1498 /***********************************************************************
1499 * Region Subtraction
1500 ***********************************************************************/
1501
1502 /*!
1503 * Deal with non-overlapping band for subtraction. Any parts from
1504 * region 2 we discard. Anything from region 1 we add to the region.
1505 *
1506 * Results:
1507 * None.
1508 *
1509 * \note Side Effects:
1510 * pReg may be affected.
1511 *
1512 */
1513 static void FASTCALL
1514 REGION_SubtractNonO1(
1515 PROSRGNDATA pReg,
1516 PRECTL r,
1517 PRECTL rEnd,
1518 INT top,
1519 INT bottom
1520 )
1521 {
1522 RECTL *pNextRect;
1523
1524 pNextRect = pReg->Buffer + pReg->rdh.nCount;
1525
1526 while (r != rEnd)
1527 {
1528 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1529 pNextRect->left = r->left;
1530 pNextRect->top = top;
1531 pNextRect->right = r->right;
1532 pNextRect->bottom = bottom;
1533 pReg->rdh.nCount += 1;
1534 pNextRect++;
1535 r++;
1536 }
1537 return;
1538 }
1539
1540
1541 /*!
1542 * Overlapping band subtraction. x1 is the left-most point not yet
1543 * checked.
1544 *
1545 * Results:
1546 * None.
1547 *
1548 * \note Side Effects:
1549 * pReg may have rectangles added to it.
1550 *
1551 */
1552 static void FASTCALL
1553 REGION_SubtractO(
1554 PROSRGNDATA pReg,
1555 PRECTL r1,
1556 PRECTL r1End,
1557 PRECTL r2,
1558 PRECTL r2End,
1559 INT top,
1560 INT bottom
1561 )
1562 {
1563 RECTL *pNextRect;
1564 INT left;
1565
1566 left = r1->left;
1567 pNextRect = pReg->Buffer + pReg->rdh.nCount;
1568
1569 while ((r1 != r1End) && (r2 != r2End))
1570 {
1571 if (r2->right <= left)
1572 {
1573 /*
1574 * Subtrahend missed the boat: go to next subtrahend.
1575 */
1576 r2++;
1577 }
1578 else if (r2->left <= left)
1579 {
1580 /*
1581 * Subtrahend preceeds minuend: nuke left edge of minuend.
1582 */
1583 left = r2->right;
1584 if (left >= r1->right)
1585 {
1586 /*
1587 * Minuend completely covered: advance to next minuend and
1588 * reset left fence to edge of new minuend.
1589 */
1590 r1++;
1591 if (r1 != r1End)
1592 left = r1->left;
1593 }
1594 else
1595 {
1596 /*
1597 * Subtrahend now used up since it doesn't extend beyond
1598 * minuend
1599 */
1600 r2++;
1601 }
1602 }
1603 else if (r2->left < r1->right)
1604 {
1605 /*
1606 * Left part of subtrahend covers part of minuend: add uncovered
1607 * part of minuend to region and skip to next subtrahend.
1608 */
1609 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1610 pNextRect->left = left;
1611 pNextRect->top = top;
1612 pNextRect->right = r2->left;
1613 pNextRect->bottom = bottom;
1614 pReg->rdh.nCount += 1;
1615 pNextRect++;
1616 left = r2->right;
1617 if (left >= r1->right)
1618 {
1619 /*
1620 * Minuend used up: advance to new...
1621 */
1622 r1++;
1623 if (r1 != r1End)
1624 left = r1->left;
1625 }
1626 else
1627 {
1628 /*
1629 * Subtrahend used up
1630 */
1631 r2++;
1632 }
1633 }
1634 else
1635 {
1636 /*
1637 * Minuend used up: add any remaining piece before advancing.
1638 */
1639 if (r1->right > left)
1640 {
1641 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1642 pNextRect->left = left;
1643 pNextRect->top = top;
1644 pNextRect->right = r1->right;
1645 pNextRect->bottom = bottom;
1646 pReg->rdh.nCount += 1;
1647 pNextRect++;
1648 }
1649 r1++;
1650 if (r1 != r1End)
1651 left = r1->left;
1652 }
1653 }
1654
1655 /*
1656 * Add remaining minuend rectangles to region.
1657 */
1658 while (r1 != r1End)
1659 {
1660 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1661 pNextRect->left = left;
1662 pNextRect->top = top;
1663 pNextRect->right = r1->right;
1664 pNextRect->bottom = bottom;
1665 pReg->rdh.nCount += 1;
1666 pNextRect++;
1667 r1++;
1668 if (r1 != r1End)
1669 {
1670 left = r1->left;
1671 }
1672 }
1673 return;
1674 }
1675
1676 /*!
1677 * Subtract regS from regM and leave the result in regD.
1678 * S stands for subtrahend, M for minuend and D for difference.
1679 *
1680 * Results:
1681 * TRUE.
1682 *
1683 * \note Side Effects:
1684 * regD is overwritten.
1685 *
1686 */
1687 static void FASTCALL
1688 REGION_SubtractRegion(
1689 ROSRGNDATA *regD,
1690 ROSRGNDATA *regM,
1691 ROSRGNDATA *regS
1692 )
1693 {
1694 /* Check for trivial reject */
1695 if ( (!(regM->rdh.nCount)) || (!(regS->rdh.nCount)) ||
1696 (!EXTENTCHECK(&regM->rdh.rcBound, &regS->rdh.rcBound)) )
1697 {
1698 REGION_CopyRegion(regD, regM);
1699 return;
1700 }
1701
1702 REGION_RegionOp (regD, regM, regS, REGION_SubtractO,
1703 REGION_SubtractNonO1, NULL);
1704
1705 /*
1706 * Can't alter newReg's extents before we call miRegionOp because
1707 * it might be one of the source regions and miRegionOp depends
1708 * on the extents of those regions being the unaltered. Besides, this
1709 * way there's no checking against rectangles that will be nuked
1710 * due to coalescing, so we have to examine fewer rectangles.
1711 */
1712 REGION_SetExtents (regD);
1713 }
1714
1715 /***********************************************************************
1716 * REGION_XorRegion
1717 */
1718 static void FASTCALL
1719 REGION_XorRegion(
1720 ROSRGNDATA *dr,
1721 ROSRGNDATA *sra,
1722 ROSRGNDATA *srb
1723 )
1724 {
1725 HRGN htra, htrb;
1726 ROSRGNDATA *tra, *trb;
1727
1728 // FIXME: Don't use a handle
1729 tra = REGION_AllocRgnWithHandle(sra->rdh.nCount + 1);
1730 if (!tra )
1731 {
1732 return;
1733 }
1734 htra = tra->BaseObject.hHmgr;
1735
1736 // FIXME: Don't use a handle
1737 trb = REGION_AllocRgnWithHandle(srb->rdh.nCount + 1);
1738 if (!trb)
1739 {
1740 RGNOBJAPI_Unlock(tra);
1741 GreDeleteObject(htra);
1742 return;
1743 }
1744 htrb = trb->BaseObject.hHmgr;
1745
1746 REGION_SubtractRegion(tra, sra, srb);
1747 REGION_SubtractRegion(trb, srb, sra);
1748 REGION_UnionRegion(dr, tra, trb);
1749 RGNOBJAPI_Unlock(tra);
1750 RGNOBJAPI_Unlock(trb);
1751
1752 GreDeleteObject(htra);
1753 GreDeleteObject(htrb);
1754 return;
1755 }
1756
1757
1758 /*!
1759 * Adds a rectangle to a REGION
1760 */
1761 VOID FASTCALL
1762 REGION_UnionRectWithRgn(
1763 ROSRGNDATA *rgn,
1764 const RECTL *rect
1765 )
1766 {
1767 ROSRGNDATA region;
1768
1769 region.Buffer = &region.rdh.rcBound;
1770 region.rdh.nCount = 1;
1771 region.rdh.nRgnSize = sizeof(RECT);
1772 region.rdh.rcBound = *rect;
1773 REGION_UnionRegion(rgn, rgn, &region);
1774 }
1775
1776 BOOL FASTCALL
1777 REGION_CreateSimpleFrameRgn(
1778 PROSRGNDATA rgn,
1779 INT x,
1780 INT y
1781 )
1782 {
1783 RECTL rc[4];
1784 PRECTL prc;
1785
1786 if ((x != 0) || (y != 0))
1787 {
1788 prc = rc;
1789
1790 if (rgn->rdh.rcBound.bottom - rgn->rdh.rcBound.top > y * 2 &&
1791 rgn->rdh.rcBound.right - rgn->rdh.rcBound.left > x * 2)
1792 {
1793 if (y != 0)
1794 {
1795 /* Top rectangle */
1796 prc->left = rgn->rdh.rcBound.left;
1797 prc->top = rgn->rdh.rcBound.top;
1798 prc->right = rgn->rdh.rcBound.right;
1799 prc->bottom = prc->top + y;
1800 prc++;
1801 }
1802
1803 if (x != 0)
1804 {
1805 /* Left rectangle */
1806 prc->left = rgn->rdh.rcBound.left;
1807 prc->top = rgn->rdh.rcBound.top + y;
1808 prc->right = prc->left + x;
1809 prc->bottom = rgn->rdh.rcBound.bottom - y;
1810 prc++;
1811
1812 /* Right rectangle */
1813 prc->left = rgn->rdh.rcBound.right - x;
1814 prc->top = rgn->rdh.rcBound.top + y;
1815 prc->right = rgn->rdh.rcBound.right;
1816 prc->bottom = rgn->rdh.rcBound.bottom - y;
1817 prc++;
1818 }
1819
1820 if (y != 0)
1821 {
1822 /* Bottom rectangle */
1823 prc->left = rgn->rdh.rcBound.left;
1824 prc->top = rgn->rdh.rcBound.bottom - y;
1825 prc->right = rgn->rdh.rcBound.right;
1826 prc->bottom = rgn->rdh.rcBound.bottom;
1827 prc++;
1828 }
1829 }
1830
1831 if (prc != rc)
1832 {
1833 /* The frame results in a complex region. rcBounds remains
1834 the same, though. */
1835 rgn->rdh.nCount = (DWORD)(prc - rc);
1836 ASSERT(rgn->rdh.nCount > 1);
1837 rgn->rdh.nRgnSize = rgn->rdh.nCount * sizeof(RECT);
1838 rgn->Buffer = ExAllocatePoolWithTag(PagedPool, rgn->rdh.nRgnSize, TAG_REGION);
1839 if (!rgn->Buffer)
1840 {
1841 rgn->rdh.nRgnSize = 0;
1842 return FALSE;
1843 }
1844
1845 _PRAGMA_WARNING_SUPPRESS(__WARNING_MAYBE_UNINIT_VAR) // rc is initialized
1846 COPY_RECTS(rgn->Buffer, rc, rgn->rdh.nCount);
1847 }
1848 }
1849
1850 return TRUE;
1851 }
1852
1853 BOOL FASTCALL
1854 REGION_CreateFrameRgn(
1855 HRGN hDest,
1856 HRGN hSrc,
1857 INT x,
1858 INT y
1859 )
1860 {
1861 PROSRGNDATA srcObj, destObj;
1862 PRECTL rc;
1863 ULONG i;
1864
1865 if (!(srcObj = RGNOBJAPI_Lock(hSrc, NULL)))
1866 {
1867 return FALSE;
1868 }
1869 if (!REGION_NOT_EMPTY(srcObj))
1870 {
1871 RGNOBJAPI_Unlock(srcObj);
1872 return FALSE;
1873 }
1874 if (!(destObj = RGNOBJAPI_Lock(hDest, NULL)))
1875 {
1876 RGNOBJAPI_Unlock(srcObj);
1877 return FALSE;
1878 }
1879
1880 EMPTY_REGION(destObj);
1881 if (!REGION_CopyRegion(destObj, srcObj))
1882 {
1883 RGNOBJAPI_Unlock(destObj);
1884 RGNOBJAPI_Unlock(srcObj);
1885 return FALSE;
1886 }
1887
1888 if (REGION_Complexity(srcObj) == SIMPLEREGION)
1889 {
1890 if (!REGION_CreateSimpleFrameRgn(destObj, x, y))
1891 {
1892 EMPTY_REGION(destObj);
1893 RGNOBJAPI_Unlock(destObj);
1894 RGNOBJAPI_Unlock(srcObj);
1895 return FALSE;
1896 }
1897 }
1898 else
1899 {
1900 /* Original region moved to right */
1901 rc = srcObj->Buffer;
1902 for (i = 0; i < srcObj->rdh.nCount; i++)
1903 {
1904 rc->left += x;
1905 rc->right += x;
1906 rc++;
1907 }
1908 REGION_IntersectRegion(destObj, destObj, srcObj);
1909
1910 /* Original region moved to left */
1911 rc = srcObj->Buffer;
1912 for (i = 0; i < srcObj->rdh.nCount; i++)
1913 {
1914 rc->left -= 2 * x;
1915 rc->right -= 2 * x;
1916 rc++;
1917 }
1918 REGION_IntersectRegion(destObj, destObj, srcObj);
1919
1920 /* Original region moved down */
1921 rc = srcObj->Buffer;
1922 for (i = 0; i < srcObj->rdh.nCount; i++)
1923 {
1924 rc->left += x;
1925 rc->right += x;
1926 rc->top += y;
1927 rc->bottom += y;
1928 rc++;
1929 }
1930 REGION_IntersectRegion(destObj, destObj, srcObj);
1931
1932 /* Original region moved up */
1933 rc = srcObj->Buffer;
1934 for (i = 0; i < srcObj->rdh.nCount; i++)
1935 {
1936 rc->top -= 2 * y;
1937 rc->bottom -= 2 * y;
1938 rc++;
1939 }
1940 REGION_IntersectRegion(destObj, destObj, srcObj);
1941
1942 /* Restore the original region */
1943 rc = srcObj->Buffer;
1944 for (i = 0; i < srcObj->rdh.nCount; i++)
1945 {
1946 rc->top += y;
1947 rc->bottom += y;
1948 rc++;
1949 }
1950 REGION_SubtractRegion(destObj, srcObj, destObj);
1951 }
1952
1953 RGNOBJAPI_Unlock(destObj);
1954 RGNOBJAPI_Unlock(srcObj);
1955 return TRUE;
1956 }
1957
1958
1959 BOOL FASTCALL
1960 REGION_LPTODP(
1961 PDC dc,
1962 HRGN hDest,
1963 HRGN hSrc)
1964 {
1965 RECTL *pCurRect, *pEndRect;
1966 PROSRGNDATA srcObj = NULL;
1967 PROSRGNDATA destObj = NULL;
1968
1969 RECTL tmpRect;
1970 BOOL ret = FALSE;
1971 PDC_ATTR pdcattr;
1972
1973 if (!dc)
1974 return ret;
1975 pdcattr = dc->pdcattr;
1976
1977 if (pdcattr->iMapMode == MM_TEXT) // Requires only a translation
1978 {
1979 if (NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
1980 goto done;
1981
1982 NtGdiOffsetRgn(hDest, pdcattr->ptlViewportOrg.x - pdcattr->ptlWindowOrg.x,
1983 pdcattr->ptlViewportOrg.y - pdcattr->ptlWindowOrg.y);
1984 ret = TRUE;
1985 goto done;
1986 }
1987
1988 if ( !(srcObj = RGNOBJAPI_Lock(hSrc, NULL)) )
1989 goto done;
1990 if ( !(destObj = RGNOBJAPI_Lock(hDest, NULL)) )
1991 {
1992 RGNOBJAPI_Unlock(srcObj);
1993 goto done;
1994 }
1995 EMPTY_REGION(destObj);
1996
1997 pEndRect = srcObj->Buffer + srcObj->rdh.nCount;
1998 for (pCurRect = srcObj->Buffer; pCurRect < pEndRect; pCurRect++)
1999 {
2000 tmpRect = *pCurRect;
2001 tmpRect.left = XLPTODP(pdcattr, tmpRect.left);
2002 tmpRect.top = YLPTODP(pdcattr, tmpRect.top);
2003 tmpRect.right = XLPTODP(pdcattr, tmpRect.right);
2004 tmpRect.bottom = YLPTODP(pdcattr, tmpRect.bottom);
2005
2006 if (tmpRect.left > tmpRect.right)
2007 {
2008 INT tmp = tmpRect.left;
2009 tmpRect.left = tmpRect.right;
2010 tmpRect.right = tmp;
2011 }
2012 if (tmpRect.top > tmpRect.bottom)
2013 {
2014 INT tmp = tmpRect.top;
2015 tmpRect.top = tmpRect.bottom;
2016 tmpRect.bottom = tmp;
2017 }
2018
2019 REGION_UnionRectWithRgn(destObj, &tmpRect);
2020 }
2021 ret = TRUE;
2022
2023 RGNOBJAPI_Unlock(srcObj);
2024 RGNOBJAPI_Unlock(destObj);
2025
2026 done:
2027 return ret;
2028 }
2029
2030 PROSRGNDATA
2031 FASTCALL
2032 REGION_AllocRgnWithHandle(INT nReg)
2033 {
2034 //HRGN hReg;
2035 PROSRGNDATA pReg;
2036
2037 pReg = (PROSRGNDATA)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE,
2038 sizeof(REGION),
2039 BASEFLAG_LOOKASIDE);
2040 if (!pReg)
2041 {
2042 DPRINT1("Could not allocate a palette.\n");
2043 return NULL;
2044 }
2045
2046 if (!GDIOBJ_hInsertObject(&pReg->BaseObject, GDI_OBJ_HMGR_POWNED))
2047 {
2048 DPRINT1("Could not insert palette into handle table.\n");
2049 GDIOBJ_vFreeObject(&pReg->BaseObject);
2050 return NULL;
2051 }
2052
2053 //hReg = pReg->BaseObject.hHmgr;
2054
2055 if (nReg == 0 || nReg == 1)
2056 {
2057 /* Testing shows that > 95% of all regions have only 1 rect.
2058 Including that here saves us from having to do another allocation */
2059 pReg->Buffer = &pReg->rdh.rcBound;
2060 }
2061 else
2062 {
2063 pReg->Buffer = ExAllocatePoolWithTag(PagedPool, nReg * sizeof(RECT), TAG_REGION);
2064 if (!pReg->Buffer)
2065 {
2066 DPRINT1("Could not allocate region buffer\n");
2067 GDIOBJ_vDeleteObject(&pReg->BaseObject);
2068 return NULL;
2069 }
2070 }
2071
2072 EMPTY_REGION(pReg);
2073 pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
2074 pReg->rdh.nCount = nReg;
2075 pReg->rdh.nRgnSize = nReg * sizeof(RECT);
2076 pReg->prgnattr = &pReg->rgnattr;
2077
2078 return pReg;
2079 }
2080
2081 BOOL
2082 NTAPI
2083 REGION_bAllocRgnAttr(PREGION prgn)
2084 {
2085 PPROCESSINFO ppi;
2086 PRGN_ATTR prgnattr;
2087
2088 ppi = PsGetCurrentProcessWin32Process();
2089 ASSERT(ppi);
2090
2091 prgnattr = GdiPoolAllocate(ppi->pPoolRgnAttr);
2092 if (!prgnattr)
2093 {
2094 DPRINT1("Could not allocate RGN attr\n");
2095 return FALSE;
2096 }
2097
2098 /* Set the object attribute in the handle table */
2099 prgn->prgnattr = prgnattr;
2100 GDIOBJ_vSetObjectAttr(&prgn->BaseObject, prgnattr);
2101
2102 return TRUE;
2103 }
2104
2105
2106 //
2107 // Allocate User Space Region Handle.
2108 //
2109 PROSRGNDATA
2110 FASTCALL
2111 REGION_AllocUserRgnWithHandle(INT nRgn)
2112 {
2113 PREGION prgn;
2114
2115 prgn = REGION_AllocRgnWithHandle(nRgn);
2116 if (!prgn)
2117 {
2118 return NULL;
2119 }
2120
2121 if (!REGION_bAllocRgnAttr(prgn))
2122 {
2123 ASSERT(FALSE);
2124 }
2125
2126 return prgn;
2127 }
2128
2129 VOID
2130 NTAPI
2131 REGION_vSyncRegion(PREGION pRgn)
2132 {
2133 PRGN_ATTR pRgn_Attr = NULL;
2134
2135 if (pRgn && pRgn->prgnattr != &pRgn->rgnattr)
2136 {
2137 pRgn_Attr = GDIOBJ_pvGetObjectAttr(&pRgn->BaseObject);
2138
2139 if ( pRgn_Attr )
2140 {
2141 _SEH2_TRY
2142 {
2143 if ( !(pRgn_Attr->AttrFlags & ATTR_CACHED) )
2144 {
2145 if ( pRgn_Attr->AttrFlags & (ATTR_RGN_VALID|ATTR_RGN_DIRTY) )
2146 {
2147 switch (pRgn_Attr->Flags)
2148 {
2149 case NULLREGION:
2150 EMPTY_REGION( pRgn );
2151 break;
2152
2153 case SIMPLEREGION:
2154 REGION_SetRectRgn( pRgn,
2155 pRgn_Attr->Rect.left,
2156 pRgn_Attr->Rect.top,
2157 pRgn_Attr->Rect.right,
2158 pRgn_Attr->Rect.bottom );
2159 break;
2160 }
2161 pRgn_Attr->AttrFlags &= ~ATTR_RGN_DIRTY;
2162 }
2163 }
2164 }
2165 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2166 {
2167 (void)0;
2168 }
2169 _SEH2_END;
2170 }
2171 }
2172
2173 }
2174
2175 PROSRGNDATA
2176 FASTCALL
2177 RGNOBJAPI_Lock(HRGN hRgn, PRGN_ATTR *ppRgn_Attr)
2178 {
2179 PROSRGNDATA pRgn = NULL;
2180
2181 pRgn = REGION_LockRgn(hRgn);
2182
2183 REGION_vSyncRegion(pRgn);
2184
2185 if (ppRgn_Attr)
2186 *ppRgn_Attr = pRgn->prgnattr;
2187
2188 return pRgn;
2189 }
2190
2191 VOID
2192 FASTCALL
2193 RGNOBJAPI_Unlock(PROSRGNDATA pRgn)
2194 {
2195 PRGN_ATTR pRgn_Attr;
2196
2197 if (pRgn && GreGetObjectOwner(pRgn->BaseObject.hHmgr) == GDI_OBJ_HMGR_POWNED)
2198 {
2199 pRgn_Attr = GDIOBJ_pvGetObjectAttr(&pRgn->BaseObject);
2200
2201 if ( pRgn_Attr )
2202 {
2203 _SEH2_TRY
2204 {
2205 if ( pRgn_Attr->AttrFlags & ATTR_RGN_VALID )
2206 {
2207 pRgn_Attr->Flags = REGION_Complexity( pRgn );
2208 pRgn_Attr->Rect.left = pRgn->rdh.rcBound.left;
2209 pRgn_Attr->Rect.top = pRgn->rdh.rcBound.top;
2210 pRgn_Attr->Rect.right = pRgn->rdh.rcBound.right;
2211 pRgn_Attr->Rect.bottom = pRgn->rdh.rcBound.bottom;
2212 }
2213 }
2214 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2215 {
2216 (void)0;
2217 }
2218 _SEH2_END;
2219 }
2220 }
2221 REGION_UnlockRgn(pRgn);
2222 }
2223
2224 /*
2225 System Regions:
2226 These regions do not use attribute sections and when allocated, use gdiobj
2227 level functions.
2228 */
2229 //
2230 // System Region Functions
2231 //
2232 PROSRGNDATA
2233 FASTCALL
2234 IntSysCreateRectpRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
2235 {
2236 PREGION prgn;
2237
2238 /* Allocate a region, witout a handle */
2239 prgn = (PREGION)GDIOBJ_AllocateObject(GDIObjType_RGN_TYPE, sizeof(REGION), 0);
2240 if (!prgn)
2241 {
2242 return NULL;
2243 }
2244
2245 /* Initialize it */
2246 prgn->Buffer = &prgn->rdh.rcBound;
2247 prgn->prgnattr = &prgn->rgnattr;
2248 REGION_SetRectRgn(prgn, LeftRect, TopRect, RightRect, BottomRect);
2249
2250 return prgn;
2251 }
2252
2253 HRGN
2254 FASTCALL
2255 IntSysCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
2256 {
2257 PREGION prgn;
2258 HRGN hrgn;
2259
2260 /* Allocate a region, witout a handle */
2261 prgn = (PREGION)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_REGION, sizeof(REGION));
2262 if (!prgn)
2263 {
2264 return NULL;
2265 }
2266
2267 /* Initialize it */
2268 prgn->Buffer = &prgn->rdh.rcBound;
2269 REGION_SetRectRgn(prgn, LeftRect, TopRect, RightRect, BottomRect);
2270 hrgn = prgn->BaseObject.hHmgr;
2271 prgn->prgnattr = &prgn->rgnattr;
2272
2273 REGION_UnlockRgn(prgn);
2274
2275 return hrgn;
2276 }
2277
2278 VOID NTAPI
2279 REGION_vCleanup(PVOID ObjectBody)
2280 {
2281 PROSRGNDATA pRgn = (PROSRGNDATA)ObjectBody;
2282 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
2283 ASSERT(ppi);
2284
2285 ASSERT(pRgn->prgnattr);
2286 if (pRgn->prgnattr != &pRgn->rgnattr)
2287 GdiPoolFree(ppi->pPoolRgnAttr, pRgn->prgnattr);
2288
2289 if (pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
2290 ExFreePoolWithTag(pRgn->Buffer, TAG_REGION);
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 UINT i;
3602 RECT* rects;
3603 NTSTATUS Status = STATUS_SUCCESS;
3604 MATRIX matrix;
3605 XFORMOBJ xo;
3606
3607 DPRINT("NtGdiExtCreateRegion\n");
3608 _SEH2_TRY
3609 {
3610 ProbeForRead(RgnData, Count, 1);
3611 nCount = RgnData->rdh.nCount;
3612 iType = RgnData->rdh.iType;
3613 dwSize = RgnData->rdh.dwSize;
3614 rects = (RECT*)RgnData->Buffer;
3615 }
3616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3617 {
3618 Status = _SEH2_GetExceptionCode();
3619 }
3620 _SEH2_END;
3621 if (!NT_SUCCESS(Status))
3622 {
3623 SetLastNtError(Status);
3624 return NULL;
3625 }
3626
3627 /* Check parameters, but don't set last error here */
3628 if (Count < sizeof(RGNDATAHEADER) + nCount * sizeof(RECT) ||
3629 iType != RDH_RECTANGLES ||
3630 dwSize != sizeof(RGNDATAHEADER))
3631 {
3632 return NULL;
3633 }
3634
3635 Region = REGION_AllocUserRgnWithHandle(nCount);
3636
3637 if (Region == NULL)
3638 {
3639 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3640 return FALSE;
3641 }
3642 hRgn = Region->BaseObject.hHmgr;
3643
3644 _SEH2_TRY
3645 {
3646 /* Insert the rectangles one by one */
3647 for(i=0; i<nCount; i++)
3648 {
3649 REGION_UnionRectWithRgn(Region, &rects[i]);
3650 }
3651 if (Xform)
3652 {
3653 ULONG ret;
3654
3655 /* Init the XFORMOBJ from the Xform struct */
3656 Status = STATUS_INVALID_PARAMETER;
3657 XFORMOBJ_vInit(&xo, &matrix);
3658 ret = XFORMOBJ_iSetXform(&xo, (XFORML*)Xform);
3659
3660 /* Check for error, also no scale and shear allowed */
3661 if (ret != DDI_ERROR && ret != GX_GENERAL)
3662 {
3663 /* Apply the coordinate transformation on the rects */
3664 if (XFORMOBJ_bApplyXform(&xo,
3665 XF_LTOL,
3666 Region->rdh.nCount * 2,
3667 Region->Buffer,
3668 Region->Buffer))
3669 {
3670 Status = STATUS_SUCCESS;
3671 }
3672 }
3673 }
3674 }
3675 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3676 {
3677 Status = _SEH2_GetExceptionCode();
3678 }
3679 _SEH2_END;
3680 if (!NT_SUCCESS(Status))
3681 {
3682 EngSetLastError(ERROR_INVALID_PARAMETER);
3683 RGNOBJAPI_Unlock(Region);
3684 GreDeleteObject(hRgn);
3685 return NULL;
3686 }
3687
3688 RGNOBJAPI_Unlock(Region);
3689
3690 return hRgn;
3691 }
3692
3693 BOOL
3694 APIENTRY
3695 NtGdiFillRgn(
3696 HDC hDC,
3697 HRGN hRgn,
3698 HBRUSH hBrush
3699 )
3700 {
3701 HBRUSH oldhBrush;
3702 PROSRGNDATA rgn;
3703 PRECTL r;
3704
3705 if (NULL == (rgn = RGNOBJAPI_Lock(hRgn, NULL)))
3706 {
3707 return FALSE;
3708 }
3709
3710 if (NULL == (oldhBrush = NtGdiSelectBrush(hDC, hBrush)))
3711 {
3712 RGNOBJAPI_Unlock(rgn);
3713 return FALSE;
3714 }
3715
3716 for (r = rgn->Buffer; r < rgn->Buffer + rgn->rdh.nCount; r++)
3717 {
3718 NtGdiPatBlt(hDC, r->left, r->top, r->right - r->left, r->bottom - r->top, PATCOPY);
3719 }
3720
3721 RGNOBJAPI_Unlock(rgn);
3722 NtGdiSelectBrush(hDC, oldhBrush);
3723
3724 return TRUE;
3725 }
3726
3727 BOOL
3728 APIENTRY
3729 NtGdiFrameRgn(
3730 HDC hDC,
3731 HRGN hRgn,
3732 HBRUSH hBrush,
3733 INT Width,
3734 INT Height
3735 )
3736 {
3737 HRGN FrameRgn;
3738 BOOL Ret;
3739
3740 if (!(FrameRgn = IntSysCreateRectRgn(0, 0, 0, 0)))
3741 {
3742 return FALSE;
3743 }
3744 if (!REGION_CreateFrameRgn(FrameRgn, hRgn, Width, Height))
3745 {
3746 GreDeleteObject(FrameRgn);
3747 return FALSE;
3748 }
3749
3750 Ret = NtGdiFillRgn(hDC, FrameRgn, hBrush);
3751
3752 GreDeleteObject(FrameRgn);
3753 return Ret;
3754 }
3755
3756
3757 INT APIENTRY
3758 NtGdiGetRgnBox(
3759 HRGN hRgn,
3760 PRECTL pRect
3761 )
3762 {
3763 PROSRGNDATA Rgn;
3764 RECTL SafeRect;
3765 DWORD ret;
3766 NTSTATUS Status = STATUS_SUCCESS;
3767
3768 if (!(Rgn = RGNOBJAPI_Lock(hRgn, NULL)))
3769 {
3770 return ERROR;
3771 }
3772
3773 ret = REGION_GetRgnBox(Rgn, &SafeRect);
3774 RGNOBJAPI_Unlock(Rgn);
3775 if (ERROR == ret)
3776 {
3777 return ret;
3778 }
3779
3780 _SEH2_TRY
3781 {
3782 ProbeForWrite(pRect, sizeof(RECT), 1);
3783 *pRect = SafeRect;
3784 }
3785 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3786 {
3787 Status = _SEH2_GetExceptionCode();
3788 }
3789 _SEH2_END;
3790 if (!NT_SUCCESS(Status))
3791 {
3792 return ERROR;
3793 }
3794
3795 return ret;
3796 }
3797
3798 BOOL
3799 APIENTRY
3800 NtGdiInvertRgn(
3801 HDC hDC,
3802 HRGN hRgn
3803 )
3804 {
3805 PROSRGNDATA RgnData;
3806 ULONG i;
3807 PRECTL rc;
3808
3809 if (!(RgnData = RGNOBJAPI_Lock(hRgn, NULL)))
3810 {
3811 EngSetLastError(ERROR_INVALID_HANDLE);
3812 return FALSE;
3813 }
3814
3815 rc = RgnData->Buffer;
3816 for (i = 0; i < RgnData->rdh.nCount; i++)
3817 {
3818
3819 if (!NtGdiPatBlt(hDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, DSTINVERT))
3820 {
3821 RGNOBJAPI_Unlock(RgnData);
3822 return FALSE;
3823 }
3824 rc++;
3825 }
3826
3827 RGNOBJAPI_Unlock(RgnData);
3828 return TRUE;
3829 }
3830
3831 INT
3832 APIENTRY
3833 NtGdiOffsetRgn(
3834 HRGN hRgn,
3835 INT XOffset,
3836 INT YOffset
3837 )
3838 {
3839 PROSRGNDATA rgn = RGNOBJAPI_Lock(hRgn, NULL);
3840 INT ret;
3841
3842 DPRINT("NtGdiOffsetRgn: hRgn %p Xoffs %d Yoffs %d rgn %p\n", hRgn, XOffset, YOffset, rgn );
3843
3844 if (!rgn)
3845 {
3846 DPRINT("NtGdiOffsetRgn: hRgn error\n");
3847 return ERROR;
3848 }
3849
3850 ret = IntGdiOffsetRgn(rgn, XOffset, YOffset);
3851
3852 RGNOBJAPI_Unlock(rgn);
3853 return ret;
3854 }
3855
3856 BOOL
3857 APIENTRY
3858 NtGdiPtInRegion(
3859 HRGN hRgn,
3860 INT X,
3861 INT Y
3862 )
3863 {
3864 PROSRGNDATA rgn;
3865 ULONG i;
3866 PRECTL r;
3867
3868 if (!(rgn = RGNOBJAPI_Lock(hRgn, NULL) ) )
3869 return FALSE;
3870
3871 if (rgn->rdh.nCount > 0 && INRECT(rgn->rdh.rcBound, X, Y))
3872 {
3873 r = rgn->Buffer;
3874 for (i = 0; i < rgn->rdh.nCount; i++)
3875 {
3876 if (INRECT(*r, X, Y))
3877 {
3878 RGNOBJAPI_Unlock(rgn);
3879 return TRUE;
3880 }
3881 r++;
3882 }
3883 }
3884 RGNOBJAPI_Unlock(rgn);
3885 return FALSE;
3886 }
3887
3888 BOOL
3889 APIENTRY
3890 NtGdiRectInRegion(
3891 HRGN hRgn,
3892 LPRECTL unsaferc
3893 )
3894 {
3895 RECTL rc = { 0 };
3896 NTSTATUS Status = STATUS_SUCCESS;
3897
3898 _SEH2_TRY
3899 {
3900 ProbeForRead(unsaferc, sizeof(RECT), 1);
3901 rc = *unsaferc;
3902 }
3903 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3904 {
3905 Status = _SEH2_GetExceptionCode();
3906 }
3907 _SEH2_END;
3908
3909 if (!NT_SUCCESS(Status))
3910 {
3911 SetLastNtError(Status);
3912 DPRINT1("NtGdiRectInRegion: Bogus rc\n");
3913 return ERROR;
3914 }
3915
3916 return IntRectInRegion(hRgn, &rc);
3917 }
3918
3919 BOOL
3920 APIENTRY
3921 NtGdiSetRectRgn(
3922 HRGN hRgn,
3923 INT LeftRect,
3924 INT TopRect,
3925 INT RightRect,
3926 INT BottomRect
3927 )
3928 {
3929 PROSRGNDATA rgn;
3930
3931 if ( !(rgn = RGNOBJAPI_Lock(hRgn, NULL)) )
3932 {
3933 return 0; // Per documentation
3934 }
3935
3936 REGION_SetRectRgn(rgn, LeftRect, TopRect, RightRect, BottomRect);
3937
3938 RGNOBJAPI_Unlock(rgn);
3939 return TRUE;
3940 }
3941
3942 HRGN APIENTRY
3943 NtGdiUnionRectWithRgn(
3944 HRGN hDest,
3945 const RECTL *UnsafeRect
3946 )
3947 {
3948 RECTL SafeRect = { 0 };
3949 PROSRGNDATA Rgn;
3950 NTSTATUS Status = STATUS_SUCCESS;
3951
3952 if (!(Rgn = RGNOBJAPI_Lock(hDest, NULL)))
3953 {
3954 EngSetLastError(ERROR_INVALID_HANDLE);
3955 return NULL;
3956 }
3957
3958 _SEH2_TRY
3959 {
3960 ProbeForRead(UnsafeRect, sizeof(RECT), 1);
3961 SafeRect = *UnsafeRect;
3962 }
3963 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3964 {
3965 Status = _SEH2_GetExceptionCode();
3966 }
3967 _SEH2_END;
3968
3969 if (! NT_SUCCESS(Status))
3970 {
3971 RGNOBJAPI_Unlock(Rgn);
3972 SetLastNtError(Status);
3973 return NULL;
3974 }
3975
3976 REGION_UnionRectWithRgn(Rgn, &SafeRect);
3977 RGNOBJAPI_Unlock(Rgn);
3978 return hDest;
3979 }
3980
3981 /*!
3982 * MSDN: GetRegionData, Return Values:
3983 *
3984 * "If the function succeeds and dwCount specifies an adequate number of bytes,
3985 * the return value is always dwCount. If dwCount is too small or the function
3986 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
3987 * required number of bytes.
3988 *
3989 * If the function fails, the return value is zero."
3990 */
3991 _Success_(return!=0)
3992 ULONG
3993 APIENTRY
3994 NtGdiGetRegionData(
3995 _In_ HRGN hrgn,
3996 _In_ ULONG cjBuffer,
3997 _Out_opt_bytecap_(cjBuffer) LPRGNDATA lpRgnData)
3998 {
3999 ULONG cjRects, cjSize;
4000 PREGION prgn;
4001
4002 /* Lock the region */
4003 prgn = RGNOBJAPI_Lock(hrgn, NULL);
4004 if (!prgn)
4005 {
4006 EngSetLastError(ERROR_INVALID_HANDLE);
4007 return 0;
4008 }
4009
4010 /* Calculate the region sizes */
4011 cjRects = prgn->rdh.nCount * sizeof(RECT);
4012 cjSize = cjRects + sizeof(RGNDATAHEADER);
4013
4014 /* Check if region data is requested */
4015 if (lpRgnData)
4016 {
4017 /* Check if the buffer is large enough */
4018 if (cjBuffer >= cjSize)
4019 {
4020 /* Probe the buffer and copy the data */
4021 _SEH2_TRY
4022 {
4023 ProbeForWrite(lpRgnData, cjSize, sizeof(ULONG));
4024 RtlCopyMemory(lpRgnData, &prgn->rdh, sizeof(RGNDATAHEADER));
4025 RtlCopyMemory(lpRgnData->Buffer, prgn->Buffer, cjRects);
4026 }
4027 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4028 {
4029 EngSetLastError(ERROR_INVALID_PARAMETER);
4030 cjSize = 0;
4031 }
4032 _SEH2_END;
4033 }
4034 else
4035 {
4036 /* Buffer is too small */
4037 EngSetLastError(ERROR_INVALID_PARAMETER);
4038 cjSize = 0;
4039 }
4040 }
4041
4042 /* Unlock the region and return the size */
4043 RGNOBJAPI_Unlock(prgn);
4044 return cjSize;
4045 }
4046
4047 /* EOF */