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