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