- delayimp.h: Use FORCEINLINE instead of static __inline__
[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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 = REGION_LockRgn(hRgn);
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 REGION_UnlockRgn(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 left = r1->left;
1654 }
1655 }
1656
1657 /*
1658 * Add remaining minuend rectangles to region.
1659 */
1660 while (r1 != r1End)
1661 {
1662 MEMCHECK(pReg, pNextRect, pReg->Buffer);
1663 pNextRect->left = left;
1664 pNextRect->top = top;
1665 pNextRect->right = r1->right;
1666 pNextRect->bottom = bottom;
1667 pReg->rdh.nCount += 1;
1668 pNextRect++;
1669 r1++;
1670 if (r1 != r1End)
1671 {
1672 left = r1->left;
1673 }
1674 }
1675 return;
1676 }
1677
1678 /*!
1679 * Subtract regS from regM and leave the result in regD.
1680 * S stands for subtrahend, M for minuend and D for difference.
1681 *
1682 * Results:
1683 * TRUE.
1684 *
1685 * \note Side Effects:
1686 * regD is overwritten.
1687 *
1688 */
1689 static void FASTCALL
1690 REGION_SubtractRegion(
1691 ROSRGNDATA *regD,
1692 ROSRGNDATA *regM,
1693 ROSRGNDATA *regS
1694 )
1695 {
1696 /* check for trivial reject */
1697 if ( (!(regM->rdh.nCount)) || (!(regS->rdh.nCount)) ||
1698 (!EXTENTCHECK(&regM->rdh.rcBound, &regS->rdh.rcBound)) )
1699 {
1700 REGION_CopyRegion(regD, regM);
1701 return;
1702 }
1703
1704 REGION_RegionOp (regD, regM, regS, REGION_SubtractO,
1705 REGION_SubtractNonO1, NULL);
1706
1707 /*
1708 * Can't alter newReg's extents before we call miRegionOp because
1709 * it might be one of the source regions and miRegionOp depends
1710 * on the extents of those regions being the unaltered. Besides, this
1711 * way there's no checking against rectangles that will be nuked
1712 * due to coalescing, so we have to examine fewer rectangles.
1713 */
1714 REGION_SetExtents (regD);
1715 }
1716
1717 /***********************************************************************
1718 * REGION_XorRegion
1719 */
1720 static void FASTCALL
1721 REGION_XorRegion(
1722 ROSRGNDATA *dr,
1723 ROSRGNDATA *sra,
1724 ROSRGNDATA *srb
1725 )
1726 {
1727 HRGN htra, htrb;
1728 ROSRGNDATA *tra, *trb;
1729
1730 // FIXME: don't use a handle
1731 tra = REGION_AllocRgnWithHandle(sra->rdh.nCount + 1);
1732 if (!tra )
1733 {
1734 return;
1735 }
1736 htra = tra->BaseObject.hHmgr;
1737
1738 // FIXME: don't use a handle
1739 trb = REGION_AllocRgnWithHandle(srb->rdh.nCount + 1);
1740 if (!trb)
1741 {
1742 REGION_UnlockRgn(tra);
1743 GreDeleteObject(htra);
1744 return;
1745 }
1746 htrb = trb->BaseObject.hHmgr;
1747
1748 REGION_SubtractRegion(tra, sra, srb);
1749 REGION_SubtractRegion(trb, srb, sra);
1750 REGION_UnionRegion(dr, tra, trb);
1751 REGION_UnlockRgn(tra);
1752 REGION_UnlockRgn(trb);
1753
1754 GreDeleteObject(htra);
1755 GreDeleteObject(htrb);
1756 return;
1757 }
1758
1759
1760 /*!
1761 * Adds a rectangle to a REGION
1762 */
1763 VOID FASTCALL
1764 REGION_UnionRectWithRgn(
1765 ROSRGNDATA *rgn,
1766 const RECTL *rect
1767 )
1768 {
1769 ROSRGNDATA region;
1770
1771 region.Buffer = &region.rdh.rcBound;
1772 region.rdh.nCount = 1;
1773 region.rdh.nRgnSize = sizeof(RECT);
1774 region.rdh.rcBound = *rect;
1775 REGION_UnionRegion(rgn, rgn, &region);
1776 }
1777
1778 BOOL FASTCALL
1779 REGION_CreateSimpleFrameRgn(
1780 PROSRGNDATA rgn,
1781 INT x,
1782 INT y
1783 )
1784 {
1785 RECTL rc[4];
1786 PRECTL prc;
1787
1788 if (x != 0 || y != 0)
1789 {
1790 prc = rc;
1791
1792 if (rgn->rdh.rcBound.bottom - rgn->rdh.rcBound.top > y * 2 &&
1793 rgn->rdh.rcBound.right - rgn->rdh.rcBound.left > x * 2)
1794 {
1795 if (y != 0)
1796 {
1797 /* top rectangle */
1798 prc->left = rgn->rdh.rcBound.left;
1799 prc->top = rgn->rdh.rcBound.top;
1800 prc->right = rgn->rdh.rcBound.right;
1801 prc->bottom = prc->top + y;
1802 prc++;
1803 }
1804
1805 if (x != 0)
1806 {
1807 /* left rectangle */
1808 prc->left = rgn->rdh.rcBound.left;
1809 prc->top = rgn->rdh.rcBound.top + y;
1810 prc->right = prc->left + x;
1811 prc->bottom = rgn->rdh.rcBound.bottom - y;
1812 prc++;
1813
1814 /* right rectangle */
1815 prc->left = rgn->rdh.rcBound.right - x;
1816 prc->top = rgn->rdh.rcBound.top + y;
1817 prc->right = rgn->rdh.rcBound.right;
1818 prc->bottom = rgn->rdh.rcBound.bottom - y;
1819 prc++;
1820 }
1821
1822 if (y != 0)
1823 {
1824 /* bottom rectangle */
1825 prc->left = rgn->rdh.rcBound.left;
1826 prc->top = rgn->rdh.rcBound.bottom - y;
1827 prc->right = rgn->rdh.rcBound.right;
1828 prc->bottom = rgn->rdh.rcBound.bottom;
1829 prc++;
1830 }
1831 }
1832
1833 if (prc != rc)
1834 {
1835 /* The frame results in a complex region. rcBounds remains
1836 the same, though. */
1837 rgn->rdh.nCount = (DWORD)(prc - rc);
1838 ASSERT(rgn->rdh.nCount > 1);
1839 rgn->rdh.nRgnSize = rgn->rdh.nCount * sizeof(RECT);
1840 rgn->Buffer = ExAllocatePoolWithTag(PagedPool, rgn->rdh.nRgnSize, TAG_REGION);
1841 if (!rgn->Buffer)
1842 {
1843 rgn->rdh.nRgnSize = 0;
1844 return FALSE;
1845 }
1846
1847 COPY_RECTS(rgn->Buffer, rc, rgn->rdh.nCount);
1848 }
1849 }
1850
1851 return TRUE;
1852 }
1853
1854 BOOL FASTCALL
1855 REGION_CreateFrameRgn(
1856 HRGN hDest,
1857 HRGN hSrc,
1858 INT x,
1859 INT y
1860 )
1861 {
1862 PROSRGNDATA srcObj, destObj;
1863 PRECTL rc;
1864 ULONG i;
1865
1866 if (!(srcObj = REGION_LockRgn(hSrc)))
1867 {
1868 return FALSE;
1869 }
1870 if (!REGION_NOT_EMPTY(srcObj))
1871 {
1872 REGION_UnlockRgn(srcObj);
1873 return FALSE;
1874 }
1875 if (!(destObj = REGION_LockRgn(hDest)))
1876 {
1877 REGION_UnlockRgn(srcObj);
1878 return FALSE;
1879 }
1880
1881 EMPTY_REGION(destObj);
1882 if (!REGION_CopyRegion(destObj, srcObj))
1883 {
1884 REGION_UnlockRgn(destObj);
1885 REGION_UnlockRgn(srcObj);
1886 return FALSE;
1887 }
1888
1889 if (REGION_Complexity(srcObj) == SIMPLEREGION)
1890 {
1891 if (!REGION_CreateSimpleFrameRgn(destObj, x, y))
1892 {
1893 EMPTY_REGION(destObj);
1894 REGION_UnlockRgn(destObj);
1895 REGION_UnlockRgn(srcObj);
1896 return FALSE;
1897 }
1898 }
1899 else
1900 {
1901 /* Original region moved to right */
1902 rc = srcObj->Buffer;
1903 for (i = 0; i < srcObj->rdh.nCount; i++)
1904 {
1905 rc->left += x;
1906 rc->right += x;
1907 rc++;
1908 }
1909 REGION_IntersectRegion(destObj, destObj, srcObj);
1910
1911 /* Original region moved to left */
1912 rc = srcObj->Buffer;
1913 for (i = 0; i < srcObj->rdh.nCount; i++)
1914 {
1915 rc->left -= 2 * x;
1916 rc->right -= 2 * x;
1917 rc++;
1918 }
1919 REGION_IntersectRegion(destObj, destObj, srcObj);
1920
1921 /* Original region moved down */
1922 rc = srcObj->Buffer;
1923 for (i = 0; i < srcObj->rdh.nCount; i++)
1924 {
1925 rc->left += x;
1926 rc->right += x;
1927 rc->top += y;
1928 rc->bottom += y;
1929 rc++;
1930 }
1931 REGION_IntersectRegion(destObj, destObj, srcObj);
1932
1933 /* Original region moved up */
1934 rc = srcObj->Buffer;
1935 for (i = 0; i < srcObj->rdh.nCount; i++)
1936 {
1937 rc->top -= 2 * y;
1938 rc->bottom -= 2 * y;
1939 rc++;
1940 }
1941 REGION_IntersectRegion(destObj, destObj, srcObj);
1942
1943 /* Restore the original region */
1944 rc = srcObj->Buffer;
1945 for (i = 0; i < srcObj->rdh.nCount; i++)
1946 {
1947 rc->top += y;
1948 rc->bottom += y;
1949 rc++;
1950 }
1951 REGION_SubtractRegion(destObj, srcObj, destObj);
1952 }
1953
1954 REGION_UnlockRgn(destObj);
1955 REGION_UnlockRgn(srcObj);
1956 return TRUE;
1957 }
1958
1959
1960 BOOL FASTCALL
1961 REGION_LPTODP(
1962 PDC dc,
1963 HRGN hDest,
1964 HRGN hSrc)
1965 {
1966 RECTL *pCurRect, *pEndRect;
1967 PROSRGNDATA srcObj = NULL;
1968 PROSRGNDATA destObj = NULL;
1969
1970 RECTL tmpRect;
1971 BOOL ret = FALSE;
1972 PDC_ATTR pdcattr;
1973
1974 if (!dc)
1975 return ret;
1976 pdcattr = dc->pdcattr;
1977
1978 if (pdcattr->iMapMode == MM_TEXT) // Requires only a translation
1979 {
1980 if (NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
1981 goto done;
1982
1983 NtGdiOffsetRgn(hDest, pdcattr->ptlViewportOrg.x - pdcattr->ptlWindowOrg.x,
1984 pdcattr->ptlViewportOrg.y - pdcattr->ptlWindowOrg.y);
1985 ret = TRUE;
1986 goto done;
1987 }
1988
1989 if ( !(srcObj = REGION_LockRgn(hSrc)) )
1990 goto done;
1991 if ( !(destObj = REGION_LockRgn(hDest)) )
1992 {
1993 REGION_UnlockRgn(srcObj);
1994 goto done;
1995 }
1996 EMPTY_REGION(destObj);
1997
1998 pEndRect = srcObj->Buffer + srcObj->rdh.nCount;
1999 for (pCurRect = srcObj->Buffer; pCurRect < pEndRect; pCurRect++)
2000 {
2001 tmpRect = *pCurRect;
2002 tmpRect.left = XLPTODP(pdcattr, tmpRect.left);
2003 tmpRect.top = YLPTODP(pdcattr, tmpRect.top);
2004 tmpRect.right = XLPTODP(pdcattr, tmpRect.right);
2005 tmpRect.bottom = YLPTODP(pdcattr, tmpRect.bottom);
2006
2007 if (tmpRect.left > tmpRect.right)
2008 {
2009 INT tmp = tmpRect.left;
2010 tmpRect.left = tmpRect.right;
2011 tmpRect.right = tmp;
2012 }
2013 if (tmpRect.top > tmpRect.bottom)
2014 {
2015 INT tmp = tmpRect.top;
2016 tmpRect.top = tmpRect.bottom;
2017 tmpRect.bottom = tmp;
2018 }
2019
2020 REGION_UnionRectWithRgn(destObj, &tmpRect);
2021 }
2022 ret = TRUE;
2023
2024 REGION_UnlockRgn(srcObj);
2025 REGION_UnlockRgn(destObj);
2026
2027 done:
2028 return ret;
2029 }
2030
2031 PROSRGNDATA
2032 FASTCALL
2033 REGION_AllocRgnWithHandle(INT nReg)
2034 {
2035 HRGN hReg;
2036 PROSRGNDATA pReg;
2037
2038 pReg = (PROSRGNDATA)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_REGION);
2039 if(!pReg)
2040 {
2041 return NULL;
2042 }
2043
2044 hReg = pReg->BaseObject.hHmgr;
2045
2046 if (nReg == 0 || nReg == 1)
2047 {
2048 /* Testing shows that > 95% of all regions have only 1 rect.
2049 Including that here saves us from having to do another allocation */
2050 pReg->Buffer = &pReg->rdh.rcBound;
2051 }
2052 else
2053 {
2054 pReg->Buffer = ExAllocatePoolWithTag(PagedPool, nReg * sizeof(RECT), TAG_REGION);
2055 if (!pReg->Buffer)
2056 {
2057 REGION_UnlockRgn(pReg);
2058 GDIOBJ_FreeObjByHandle(hReg, GDI_OBJECT_TYPE_REGION);
2059 return NULL;
2060 }
2061 }
2062
2063 EMPTY_REGION(pReg);
2064 pReg->rdh.dwSize = sizeof(RGNDATAHEADER);
2065 pReg->rdh.nCount = nReg;
2066 pReg->rdh.nRgnSize = nReg * sizeof(RECT);
2067
2068 return pReg;
2069 }
2070
2071 BOOL INTERNAL_CALL
2072 REGION_Cleanup(PVOID ObjectBody)
2073 {
2074 PROSRGNDATA pRgn = (PROSRGNDATA)ObjectBody;
2075 if (pRgn->Buffer && pRgn->Buffer != &pRgn->rdh.rcBound)
2076 ExFreePool(pRgn->Buffer);
2077 return TRUE;
2078 }
2079
2080 VOID FASTCALL
2081 REGION_Delete(PROSRGNDATA pRgn)
2082 {
2083 if ( pRgn == prgnDefault) return;
2084 REGION_FreeRgn(pRgn);
2085 }
2086
2087
2088 VOID FASTCALL
2089 IntGdiReleaseRaoRgn(PDC pDC)
2090 {
2091 INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
2092 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
2093 pDC->fs |= DC_FLAG_DIRTY_RAO;
2094 Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
2095 RECTL_vSetEmptyRect(&pDC->erclClip);
2096 }
2097
2098
2099 VOID FASTCALL
2100 IntGdiReleaseVisRgn(PDC pDC)
2101 {
2102 INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
2103 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
2104 pDC->fs |= DC_FLAG_DIRTY_RAO;
2105 Entry->Flags |= GDI_ENTRY_VALIDATE_VIS;
2106 RECTL_vSetEmptyRect(&pDC->erclClip);
2107 REGION_Delete(pDC->prgnVis);
2108 pDC->prgnVis = prgnDefault;
2109 }
2110
2111 VOID FASTCALL
2112 IntUpdateVisRectRgn(PDC pDC, PROSRGNDATA pRgn)
2113 {
2114 INT Index = GDI_HANDLE_GET_INDEX(pDC->BaseObject.hHmgr);
2115 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
2116 PDC_ATTR pdcattr;
2117 RECTL rcl;
2118
2119 if (Entry->Flags & GDI_ENTRY_VALIDATE_VIS)
2120 {
2121 pdcattr = pDC->pdcattr;
2122
2123 pdcattr->VisRectRegion.Flags = REGION_Complexity(pRgn);
2124
2125 if (pRgn && pdcattr->VisRectRegion.Flags != NULLREGION)
2126 {
2127 rcl.left = pRgn->rdh.rcBound.left;
2128 rcl.top = pRgn->rdh.rcBound.top;
2129 rcl.right = pRgn->rdh.rcBound.right;
2130 rcl.bottom = pRgn->rdh.rcBound.bottom;
2131
2132 rcl.left -= pDC->erclWindow.left;
2133 rcl.top -= pDC->erclWindow.top;
2134 rcl.right -= pDC->erclWindow.left;
2135 rcl.bottom -= pDC->erclWindow.top;
2136 }
2137 else
2138 RECTL_vSetEmptyRect(&rcl);
2139
2140 pdcattr->VisRectRegion.Rect = rcl;
2141
2142 Entry->Flags &= ~GDI_ENTRY_VALIDATE_VIS;
2143 }
2144 }
2145
2146 INT
2147 FASTCALL
2148 IntGdiCombineRgn(PROSRGNDATA destRgn,
2149 PROSRGNDATA src1Rgn,
2150 PROSRGNDATA src2Rgn,
2151 INT CombineMode)
2152 {
2153 INT result = ERROR;
2154
2155 if (destRgn)
2156 {
2157 if (src1Rgn)
2158 {
2159 if (CombineMode == RGN_COPY)
2160 {
2161 if ( !REGION_CopyRegion(destRgn, src1Rgn) )
2162 return ERROR;
2163 result = REGION_Complexity(destRgn);
2164 }
2165 else
2166 {
2167 if (src2Rgn)
2168 {
2169 switch (CombineMode)
2170 {
2171 case RGN_AND:
2172 REGION_IntersectRegion(destRgn, src1Rgn, src2Rgn);
2173 break;
2174 case RGN_OR:
2175 REGION_UnionRegion(destRgn, src1Rgn, src2Rgn);
2176 break;
2177 case RGN_XOR:
2178 REGION_XorRegion(destRgn, src1Rgn, src2Rgn);
2179 break;
2180 case RGN_DIFF:
2181 REGION_SubtractRegion(destRgn, src1Rgn, src2Rgn);
2182 break;
2183 }
2184 result = REGION_Complexity(destRgn);
2185 }
2186 else if (src2Rgn == NULL)
2187 {
2188 DPRINT1("IntGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", CombineMode);
2189 }
2190 }
2191 }
2192 }
2193 else
2194 {
2195 DPRINT("IntGdiCombineRgn: hDest unavailable\n");
2196 result = ERROR;
2197 }
2198 return result;
2199 }
2200
2201
2202 // NtGdi Exported Functions
2203 INT
2204 APIENTRY
2205 NtGdiCombineRgn(HRGN hDest,
2206 HRGN hSrc1,
2207 HRGN hSrc2,
2208 INT CombineMode)
2209 {
2210 INT result = ERROR;
2211 PROSRGNDATA destRgn, src1Rgn, src2Rgn;
2212
2213 destRgn = REGION_LockRgn(hDest);
2214 if (destRgn)
2215 {
2216 src1Rgn = REGION_LockRgn(hSrc1);
2217 if (src1Rgn)
2218 {
2219 if (CombineMode == RGN_COPY)
2220 {
2221 if ( !REGION_CopyRegion(destRgn, src1Rgn) )
2222 return ERROR;
2223 result = REGION_Complexity(destRgn);
2224 }
2225 else
2226 {
2227 src2Rgn = REGION_LockRgn(hSrc2);
2228 if (src2Rgn)
2229 {
2230 switch (CombineMode)
2231 {
2232 case RGN_AND:
2233 REGION_IntersectRegion(destRgn, src1Rgn, src2Rgn);
2234 break;
2235 case RGN_OR:
2236 REGION_UnionRegion(destRgn, src1Rgn, src2Rgn);
2237 break;
2238 case RGN_XOR:
2239 REGION_XorRegion(destRgn, src1Rgn, src2Rgn);
2240 break;
2241 case RGN_DIFF:
2242 REGION_SubtractRegion(destRgn, src1Rgn, src2Rgn);
2243 break;
2244 }
2245 REGION_UnlockRgn(src2Rgn);
2246 result = REGION_Complexity(destRgn);
2247 }
2248 else if (hSrc2 == NULL)
2249 {
2250 DPRINT1("NtGdiCombineRgn requires hSrc2 != NULL for combine mode %d!\n", CombineMode);
2251 }
2252 }
2253
2254 REGION_UnlockRgn(src1Rgn);
2255 }
2256
2257 REGION_UnlockRgn(destRgn);
2258 }
2259 else
2260 {
2261 DPRINT("NtGdiCombineRgn: hDest unavailable\n");
2262 result = ERROR;
2263 }
2264
2265 return result;
2266 }
2267
2268 HRGN
2269 APIENTRY
2270 NtGdiCreateEllipticRgn(
2271 INT Left,
2272 INT Top,
2273 INT Right,
2274 INT Bottom
2275 )
2276 {
2277 return NtGdiCreateRoundRectRgn(Left, Top, Right, Bottom,
2278 Right - Left, Bottom - Top);
2279 }
2280
2281 PROSRGNDATA
2282 FASTCALL
2283 IntGdiCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
2284 {
2285 PROSRGNDATA pRgn;
2286
2287 if (!(pRgn = REGION_AllocRgnWithHandle(1))) return NULL;
2288
2289 REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
2290 REGION_UnlockRgn(pRgn);
2291 // Return pointer with Share locks.
2292 pRgn = GDIOBJ_ShareLockObj(pRgn->BaseObject.hHmgr, GDI_OBJECT_TYPE_REGION);
2293
2294 return pRgn;
2295 }
2296
2297
2298 HRGN APIENTRY
2299 NtGdiCreateRectRgn(INT LeftRect, INT TopRect, INT RightRect, INT BottomRect)
2300 {
2301 PROSRGNDATA pRgn;
2302 HRGN hRgn;
2303
2304 /* Allocate region data structure with space for 1 RECTL */
2305 if (!(pRgn = REGION_AllocRgnWithHandle(1)))
2306 {
2307 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2308 return NULL;
2309 }
2310 hRgn = pRgn->BaseObject.hHmgr;
2311
2312 REGION_SetRectRgn(pRgn, LeftRect, TopRect, RightRect, BottomRect);
2313 REGION_UnlockRgn(pRgn);
2314
2315 return hRgn;
2316 }
2317
2318
2319 HRGN
2320 APIENTRY
2321 NtGdiCreateRoundRectRgn(
2322 INT left,
2323 INT top,
2324 INT right,
2325 INT bottom,
2326 INT ellipse_width,
2327 INT ellipse_height
2328 )
2329 {
2330 PROSRGNDATA obj;
2331 HRGN hrgn;
2332 int asq, bsq, d, xd, yd;
2333 RECTL rect;
2334
2335 /* Make the dimensions sensible */
2336
2337 if (left > right)
2338 {
2339 INT tmp = left;
2340 left = right;
2341 right = tmp;
2342 }
2343 if (top > bottom)
2344 {
2345 INT tmp = top;
2346 top = bottom;
2347 bottom = tmp;
2348 }
2349
2350 ellipse_width = abs(ellipse_width);
2351 ellipse_height = abs(ellipse_height);
2352
2353 /* Check parameters */
2354
2355 if (ellipse_width > right-left) ellipse_width = right-left;
2356 if (ellipse_height > bottom-top) ellipse_height = bottom-top;
2357
2358 /* Check if we can do a normal rectangle instead */
2359
2360 if ((ellipse_width < 2) || (ellipse_height < 2))
2361 return NtGdiCreateRectRgn(left, top, right, bottom);
2362
2363 /* Create region */
2364
2365 d = (ellipse_height < 128) ? ((3 * ellipse_height) >> 2) : 64;
2366 if (!(obj = REGION_AllocRgnWithHandle(d))) return 0;
2367 hrgn = obj->BaseObject.hHmgr;
2368
2369 /* Ellipse algorithm, based on an article by K. Porter */
2370 /* in DDJ Graphics Programming Column, 8/89 */
2371
2372 asq = ellipse_width * ellipse_width / 4; /* a^2 */
2373 bsq = ellipse_height * ellipse_height / 4; /* b^2 */
2374 d = bsq - asq * ellipse_height / 2 + asq / 4; /* b^2 - a^2b + a^2/4 */
2375 xd = 0;
2376 yd = asq * ellipse_height; /* 2a^2b */
2377
2378 rect.left = left + ellipse_width / 2;
2379 rect.right = right - ellipse_width / 2;
2380
2381 /* Loop to draw first half of quadrant */
2382
2383 while (xd < yd)
2384 {
2385 if (d > 0) /* if nearest pixel is toward the center */
2386 {
2387 /* move toward center */
2388 rect.top = top++;
2389 rect.bottom = rect.top + 1;
2390 REGION_UnionRectWithRgn(obj, &rect);
2391 rect.top = --bottom;
2392 rect.bottom = rect.top + 1;
2393 REGION_UnionRectWithRgn(obj, &rect);
2394 yd -= 2*asq;
2395 d -= yd;
2396 }
2397 rect.left--; /* next horiz point */
2398 rect.right++;
2399 xd += 2*bsq;
2400 d += bsq + xd;
2401 }
2402 /* Loop to draw second half of quadrant */
2403
2404 d += (3 * (asq-bsq) / 2 - (xd+yd)) / 2;
2405 while (yd >= 0)
2406 {
2407 /* next vertical point */
2408 rect.top = top++;
2409 rect.bottom = rect.top + 1;
2410 REGION_UnionRectWithRgn(obj, &rect);
2411 rect.top = --bottom;
2412 rect.bottom = rect.top + 1;
2413 REGION_UnionRectWithRgn(obj, &rect);
2414 if (d < 0) /* if nearest pixel is outside ellipse */
2415 {
2416 rect.left--; /* move away from center */
2417 rect.right++;
2418 xd += 2*bsq;
2419 d += xd;
2420 }
2421 yd -= 2*asq;
2422 d += asq - yd;
2423 }
2424 /* Add the inside rectangle */
2425
2426 if (top <= bottom)
2427 {
2428 rect.top = top;
2429 rect.bottom = bottom;
2430 REGION_UnionRectWithRgn(obj, &rect);
2431 }
2432
2433 REGION_UnlockRgn(obj);
2434 return hrgn;
2435 }
2436
2437 BOOL
2438 APIENTRY
2439 NtGdiEqualRgn(
2440 HRGN hSrcRgn1,
2441 HRGN hSrcRgn2
2442 )
2443 {
2444 PROSRGNDATA rgn1, rgn2;
2445 PRECTL tRect1, tRect2;
2446 ULONG i;
2447 BOOL bRet = FALSE;
2448
2449 if ( !(rgn1 = REGION_LockRgn(hSrcRgn1)) )
2450 return ERROR;
2451
2452 if ( !(rgn2 = REGION_LockRgn(hSrcRgn2)) )
2453 {
2454 REGION_UnlockRgn(rgn1);
2455 return ERROR;
2456 }
2457
2458 if (rgn1->rdh.nCount != rgn2->rdh.nCount ||
2459 rgn1->rdh.nCount == 0 ||
2460 rgn1->rdh.rcBound.left != rgn2->rdh.rcBound.left ||
2461 rgn1->rdh.rcBound.right != rgn2->rdh.rcBound.right ||
2462 rgn1->rdh.rcBound.top != rgn2->rdh.rcBound.top ||
2463 rgn1->rdh.rcBound.bottom != rgn2->rdh.rcBound.bottom)
2464 goto exit;
2465
2466 tRect1 = rgn1->Buffer;
2467 tRect2 = rgn2->Buffer;
2468
2469 if (!tRect1 || !tRect2)
2470 goto exit;
2471
2472 for (i=0; i < rgn1->rdh.nCount; i++)
2473 {
2474 if (tRect1[i].left != tRect2[i].left ||
2475 tRect1[i].right != tRect2[i].right ||
2476 tRect1[i].top != tRect2[i].top ||
2477 tRect1[i].bottom != tRect2[i].bottom)
2478 goto exit;
2479 }
2480 bRet = TRUE;
2481
2482 exit:
2483 REGION_UnlockRgn(rgn1);
2484 REGION_UnlockRgn(rgn2);
2485 return bRet;
2486 }
2487
2488 HRGN
2489 APIENTRY
2490 NtGdiExtCreateRegion(
2491 OPTIONAL LPXFORM Xform,
2492 DWORD Count,
2493 LPRGNDATA RgnData
2494 )
2495 {
2496 HRGN hRgn;
2497 PROSRGNDATA Region;
2498 DWORD nCount = 0;
2499 DWORD iType = 0;
2500 DWORD dwSize = 0;
2501 NTSTATUS Status = STATUS_SUCCESS;
2502 MATRIX matrix;
2503
2504 DPRINT("NtGdiExtCreateRegion\n");
2505 _SEH2_TRY
2506 {
2507 ProbeForRead(RgnData, Count, 1);
2508 nCount = RgnData->rdh.nCount;
2509 iType = RgnData->rdh.iType;
2510 dwSize = RgnData->rdh.dwSize;
2511 }
2512 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2513 {
2514 Status = _SEH2_GetExceptionCode();
2515 }
2516 _SEH2_END;
2517 if (!NT_SUCCESS(Status))
2518 {
2519 SetLastNtError(Status);
2520 return NULL;
2521 }
2522
2523 /* Check parameters, but don't set last error here */
2524 if (Count < sizeof(RGNDATAHEADER) + nCount * sizeof(RECT) ||
2525 iType != RDH_RECTANGLES ||
2526 dwSize != sizeof(RGNDATAHEADER))
2527 {
2528 return NULL;
2529 }
2530
2531 Region = REGION_AllocRgnWithHandle(nCount);
2532
2533 if (Region == NULL)
2534 {
2535 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2536 return FALSE;
2537 }
2538 hRgn = Region->BaseObject.hHmgr;
2539
2540 _SEH2_TRY
2541 {
2542 if (Xform)
2543 {
2544 ULONG ret;
2545
2546 /* Init the XFORMOBJ from the Xform struct */
2547 Status = STATUS_INVALID_PARAMETER;
2548 ret = XFORMOBJ_iSetXform((XFORMOBJ*)&matrix, (XFORML*)Xform);
2549
2550 /* Check for error, also no scale and shear allowed */
2551 if (ret != DDI_ERROR && ret != GX_GENERAL)
2552 {
2553 /* Apply the coordinate transformation on the rects */
2554 if (XFORMOBJ_bApplyXform((XFORMOBJ*)&matrix,
2555 XF_LTOL,
2556 nCount * 2,
2557 RgnData->Buffer,
2558 Region->Buffer))
2559 {
2560 Status = STATUS_SUCCESS;
2561 }
2562 }
2563 }
2564 else
2565 {
2566 /* Copy rect coordinates */
2567 RtlCopyMemory(Region->Buffer,
2568 RgnData->Buffer,
2569 nCount * sizeof(RECT));
2570 }
2571 }
2572 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2573 {
2574 Status = _SEH2_GetExceptionCode();
2575 }
2576 _SEH2_END;
2577 if (!NT_SUCCESS(Status))
2578 {
2579 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2580 REGION_UnlockRgn(Region);
2581 GreDeleteObject(hRgn);
2582 return NULL;
2583 }
2584
2585 REGION_UnlockRgn(Region);
2586
2587 return hRgn;
2588 }
2589
2590 BOOL
2591 APIENTRY
2592 NtGdiFillRgn(
2593 HDC hDC,
2594 HRGN hRgn,
2595 HBRUSH hBrush
2596 )
2597 {
2598 HBRUSH oldhBrush;
2599 PROSRGNDATA rgn;
2600 PRECTL r;
2601
2602 if (NULL == (rgn = REGION_LockRgn(hRgn)))
2603 {
2604 return FALSE;
2605 }
2606
2607 if (NULL == (oldhBrush = NtGdiSelectBrush(hDC, hBrush)))
2608 {
2609 REGION_UnlockRgn(rgn);
2610 return FALSE;
2611 }
2612
2613 for (r = rgn->Buffer; r < rgn->Buffer + rgn->rdh.nCount; r++)
2614 {
2615 NtGdiPatBlt(hDC, r->left, r->top, r->right - r->left, r->bottom - r->top, PATCOPY);
2616 }
2617
2618 REGION_UnlockRgn(rgn);
2619 NtGdiSelectBrush(hDC, oldhBrush);
2620
2621 return TRUE;
2622 }
2623
2624 BOOL
2625 APIENTRY
2626 NtGdiFrameRgn(
2627 HDC hDC,
2628 HRGN hRgn,
2629 HBRUSH hBrush,
2630 INT Width,
2631 INT Height
2632 )
2633 {
2634 HRGN FrameRgn;
2635 BOOL Ret;
2636
2637 if (!(FrameRgn = NtGdiCreateRectRgn(0, 0, 0, 0)))
2638 {
2639 return FALSE;
2640 }
2641 if (!REGION_CreateFrameRgn(FrameRgn, hRgn, Width, Height))
2642 {
2643 GreDeleteObject(FrameRgn);
2644 return FALSE;
2645 }
2646
2647 Ret = NtGdiFillRgn(hDC, FrameRgn, hBrush);
2648
2649 GreDeleteObject(FrameRgn);
2650 return Ret;
2651 }
2652
2653 INT FASTCALL
2654 REGION_GetRgnBox(
2655 PROSRGNDATA Rgn,
2656 PRECTL pRect
2657 )
2658 {
2659 DWORD ret;
2660
2661 if (Rgn)
2662 {
2663 *pRect = Rgn->rdh.rcBound;
2664 ret = REGION_Complexity(Rgn);
2665
2666 return ret;
2667 }
2668 return 0; //if invalid region return zero
2669 }
2670
2671
2672 /* See wine, msdn, osr and Feng Yuan - Windows Graphics Programming Win32 Gdi And Directdraw
2673
2674 1st: http://www.codeproject.com/gdi/cliprgnguide.asp is wrong!
2675
2676 The intersection of the clip with the meta region is not Rao it's API!
2677 Go back and read 7.2 Clipping pages 418-19:
2678 Rao = API & Vis:
2679 1) The Rao region is the intersection of the API region and the system region,
2680 named after the Microsoft engineer who initially proposed it.
2681 2) The Rao region can be calculated from the API region and the system region.
2682
2683 API:
2684 API region is the intersection of the meta region and the clipping region,
2685 clearly named after the fact that it is controlled by GDI API calls.
2686 */
2687 INT APIENTRY
2688 NtGdiGetRandomRgn(
2689 HDC hDC,
2690 HRGN hDest,
2691 INT iCode
2692 )
2693 {
2694 INT ret = 0;
2695 PDC pDC;
2696 HRGN hSrc = NULL;
2697 POINT org;
2698
2699 pDC = DC_LockDc(hDC);
2700 if (pDC == NULL)
2701 {
2702 SetLastWin32Error(ERROR_INVALID_HANDLE);
2703 return -1;
2704 }
2705
2706 switch (iCode)
2707 {
2708 case CLIPRGN:
2709 hSrc = pDC->rosdc.hClipRgn;
2710 // if (pDC->dclevel.prgnClip) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnClip)->BaseObject.hHmgr;
2711 break;
2712 case METARGN:
2713 if (pDC->dclevel.prgnMeta) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnMeta)->BaseObject.hHmgr;
2714 break;
2715 case APIRGN:
2716 hSrc = pDC->rosdc.hClipRgn;
2717 // if (pDC->prgnAPI) hSrc = ((PROSRGNDATA)pDC->prgnAPI)->BaseObject.hHmgr;
2718 // else if (pDC->dclevel.prgnClip) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnClip)->BaseObject.hHmgr;
2719 // else if (pDC->dclevel.prgnMeta) hSrc = ((PROSRGNDATA)pDC->dclevel.prgnMeta)->BaseObject.hHmgr;
2720 break;
2721 case SYSRGN:
2722 hSrc = pDC->rosdc.hVisRgn;
2723 // if (pDC->prgnVis) hSrc = ((PROSRGNDATA)pDC->prgnVis)->BaseObject.hHmgr;
2724 break;
2725 default:
2726 hSrc = 0;
2727 }
2728 if (hSrc)
2729 {
2730 if (NtGdiCombineRgn(hDest, hSrc, 0, RGN_COPY) == ERROR)
2731 {
2732 ret = -1;
2733 }
2734 else
2735 {
2736 ret = 1;
2737 }
2738 }
2739 if (iCode == SYSRGN)
2740 {
2741 IntGdiGetDCOrg(pDC, &org);
2742 NtGdiOffsetRgn(hDest, org.x, org.y );
2743 }
2744
2745 DC_UnlockDc(pDC);
2746
2747 return ret;
2748 }
2749
2750 INT APIENTRY
2751 IntGdiGetRgnBox(
2752 HRGN hRgn,
2753 PRECTL pRect
2754 )
2755 {
2756 PROSRGNDATA Rgn;
2757 DWORD ret;
2758
2759 if (!(Rgn = REGION_LockRgn(hRgn)))
2760 {
2761 return ERROR;
2762 }
2763
2764 ret = REGION_GetRgnBox(Rgn, pRect);
2765 REGION_UnlockRgn(Rgn);
2766
2767 return ret;
2768 }
2769
2770
2771 INT APIENTRY
2772 NtGdiGetRgnBox(
2773 HRGN hRgn,
2774 PRECTL pRect
2775 )
2776 {
2777 PROSRGNDATA Rgn;
2778 RECTL SafeRect;
2779 DWORD ret;
2780 NTSTATUS Status = STATUS_SUCCESS;
2781
2782 if (!(Rgn = REGION_LockRgn(hRgn)))
2783 {
2784 return ERROR;
2785 }
2786
2787 ret = REGION_GetRgnBox(Rgn, &SafeRect);
2788 REGION_UnlockRgn(Rgn);
2789 if (ERROR == ret)
2790 {
2791 return ret;
2792 }
2793
2794 _SEH2_TRY
2795 {
2796 ProbeForWrite(pRect, sizeof(RECT), 1);
2797 *pRect = SafeRect;
2798 }
2799 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2800 {
2801 Status = _SEH2_GetExceptionCode();
2802 }
2803 _SEH2_END;
2804 if (!NT_SUCCESS(Status))
2805 {
2806 return ERROR;
2807 }
2808
2809 return ret;
2810 }
2811
2812 BOOL
2813 APIENTRY
2814 NtGdiInvertRgn(
2815 HDC hDC,
2816 HRGN hRgn
2817 )
2818 {
2819 PROSRGNDATA RgnData;
2820 ULONG i;
2821 PRECTL rc;
2822
2823 if (!(RgnData = REGION_LockRgn(hRgn)))
2824 {
2825 SetLastWin32Error(ERROR_INVALID_HANDLE);
2826 return FALSE;
2827 }
2828
2829 rc = RgnData->Buffer;
2830 for (i = 0; i < RgnData->rdh.nCount; i++)
2831 {
2832
2833 if (!NtGdiPatBlt(hDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, DSTINVERT))
2834 {
2835 REGION_UnlockRgn(RgnData);
2836 return FALSE;
2837 }
2838 rc++;
2839 }
2840
2841 REGION_UnlockRgn(RgnData);
2842 return TRUE;
2843 }
2844
2845 INT
2846 APIENTRY
2847 NtGdiOffsetRgn(
2848 HRGN hRgn,
2849 INT XOffset,
2850 INT YOffset
2851 )
2852 {
2853 PROSRGNDATA rgn = REGION_LockRgn(hRgn);
2854 INT ret;
2855
2856 DPRINT("NtGdiOffsetRgn: hRgn %d Xoffs %d Yoffs %d rgn %x\n", hRgn, XOffset, YOffset, rgn );
2857
2858 if (!rgn)
2859 {
2860 DPRINT("NtGdiOffsetRgn: hRgn error\n");
2861 return ERROR;
2862 }
2863
2864 if (XOffset || YOffset)
2865 {
2866 int nbox = rgn->rdh.nCount;
2867 PRECTL pbox = rgn->Buffer;
2868
2869 if (nbox && pbox)
2870 {
2871 while (nbox--)
2872 {
2873 pbox->left += XOffset;
2874 pbox->right += XOffset;
2875 pbox->top += YOffset;
2876 pbox->bottom += YOffset;
2877 pbox++;
2878 }
2879 if (rgn->Buffer != &rgn->rdh.rcBound)
2880 {
2881 rgn->rdh.rcBound.left += XOffset;
2882 rgn->rdh.rcBound.right += XOffset;
2883 rgn->rdh.rcBound.top += YOffset;
2884 rgn->rdh.rcBound.bottom += YOffset;
2885 }
2886 }
2887 }
2888 ret = REGION_Complexity(rgn);
2889 REGION_UnlockRgn(rgn);
2890 return ret;
2891 }
2892
2893 BOOL
2894 FASTCALL
2895 IntGdiPaintRgn(
2896 PDC dc,
2897 HRGN hRgn
2898 )
2899 {
2900 HRGN tmpVisRgn;
2901 PROSRGNDATA visrgn;
2902 CLIPOBJ* ClipRegion;
2903 BOOL bRet = FALSE;
2904 POINTL BrushOrigin;
2905 SURFACE *psurf;
2906 PDC_ATTR pdcattr;
2907
2908 if (!dc) return FALSE;
2909 pdcattr = dc->pdcattr;
2910
2911 ASSERT(!(pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY)));
2912
2913 if (!(tmpVisRgn = NtGdiCreateRectRgn(0, 0, 0, 0))) return FALSE;
2914
2915 // Transform region into device co-ords
2916 if (!REGION_LPTODP(dc, tmpVisRgn, hRgn) ||
2917 NtGdiOffsetRgn(tmpVisRgn, dc->ptlDCOrig.x, dc->ptlDCOrig.y) == ERROR)
2918 {
2919 GreDeleteObject(tmpVisRgn);
2920 return FALSE;
2921 }
2922
2923 NtGdiCombineRgn(tmpVisRgn, tmpVisRgn, dc->rosdc.hGCClipRgn, RGN_AND);
2924
2925 visrgn = REGION_LockRgn(tmpVisRgn);
2926 if (visrgn == NULL)
2927 {
2928 GreDeleteObject(tmpVisRgn);
2929 return FALSE;
2930 }
2931
2932 ClipRegion = IntEngCreateClipRegion(visrgn->rdh.nCount,
2933 visrgn->Buffer,
2934 &visrgn->rdh.rcBound );
2935 ASSERT(ClipRegion);
2936
2937 BrushOrigin.x = pdcattr->ptlBrushOrigin.x;
2938 BrushOrigin.y = pdcattr->ptlBrushOrigin.y;
2939 psurf = dc->dclevel.pSurface;
2940 /* FIXME - Handle psurf == NULL !!!! */
2941
2942 bRet = IntEngPaint(&psurf->SurfObj,
2943 ClipRegion,
2944 &dc->eboFill.BrushObject,
2945 &BrushOrigin,
2946 0xFFFF);//FIXME:don't know what to put here
2947
2948 REGION_UnlockRgn(visrgn);
2949 GreDeleteObject(tmpVisRgn);
2950
2951 // Fill the region
2952 return TRUE;
2953 }
2954
2955 BOOL
2956 APIENTRY
2957 NtGdiPtInRegion(
2958 HRGN hRgn,
2959 INT X,
2960 INT Y
2961 )
2962 {
2963 PROSRGNDATA rgn;
2964 ULONG i;
2965 PRECTL r;
2966
2967 if (!(rgn = REGION_LockRgn(hRgn) ) )
2968 return FALSE;
2969
2970 if (rgn->rdh.nCount > 0 && INRECT(rgn->rdh.rcBound, X, Y))
2971 {
2972 r = rgn->Buffer;
2973 for (i = 0; i < rgn->rdh.nCount; i++)
2974 {
2975 if (INRECT(*r, X, Y))
2976 {
2977 REGION_UnlockRgn(rgn);
2978 return TRUE;
2979 }
2980 r++;
2981 }
2982 }
2983 REGION_UnlockRgn(rgn);
2984 return FALSE;
2985 }
2986
2987 BOOL
2988 FASTCALL
2989 REGION_RectInRegion(
2990 PROSRGNDATA Rgn,
2991 const RECTL *rc
2992 )
2993 {
2994 PRECTL pCurRect, pRectEnd;
2995
2996 // this is (just) a useful optimization
2997 if ((Rgn->rdh.nCount > 0) && EXTENTCHECK(&Rgn->rdh.rcBound, rc))
2998 {
2999 for (pCurRect = Rgn->Buffer, pRectEnd = pCurRect + Rgn->rdh.nCount; pCurRect < pRectEnd; pCurRect++)
3000 {
3001 if (pCurRect->bottom <= rc->top) continue; // not far enough down yet
3002 if (pCurRect->top >= rc->bottom) break; // too far down
3003 if (pCurRect->right <= rc->left) continue; // not far enough over yet
3004 if (pCurRect->left >= rc->right) continue;
3005
3006 return TRUE;
3007 }
3008 }
3009 return FALSE;
3010 }
3011
3012 BOOL
3013 APIENTRY
3014 NtGdiRectInRegion(
3015 HRGN hRgn,
3016 LPRECTL unsaferc
3017 )
3018 {
3019 PROSRGNDATA Rgn;
3020 RECTL rc = {0};
3021 BOOL Ret;
3022 NTSTATUS Status = STATUS_SUCCESS;
3023
3024 if (!(Rgn = REGION_LockRgn(hRgn)))
3025 {
3026 return ERROR;
3027 }
3028
3029 _SEH2_TRY
3030 {
3031 ProbeForRead(unsaferc, sizeof(RECT), 1);
3032 rc = *unsaferc;
3033 }
3034 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3035 {
3036 Status = _SEH2_GetExceptionCode();
3037 }
3038 _SEH2_END;
3039
3040 if (!NT_SUCCESS(Status))
3041 {
3042 REGION_UnlockRgn(Rgn);
3043 SetLastNtError(Status);
3044 DPRINT1("NtGdiRectInRegion: bogus rc\n");
3045 return ERROR;
3046 }
3047
3048 Ret = REGION_RectInRegion(Rgn, &rc);
3049 REGION_UnlockRgn(Rgn);
3050 return Ret;
3051 }
3052
3053 VOID
3054 FASTCALL
3055 REGION_SetRectRgn(
3056 PROSRGNDATA rgn,
3057 INT LeftRect,
3058 INT TopRect,
3059 INT RightRect,
3060 INT BottomRect
3061 )
3062 {
3063 PRECTL firstRect;
3064
3065 if (LeftRect > RightRect)
3066 {
3067 INT tmp = LeftRect;
3068 LeftRect = RightRect;
3069 RightRect = tmp;
3070 }
3071 if (TopRect > BottomRect)
3072 {
3073 INT tmp = TopRect;
3074 TopRect = BottomRect;
3075 BottomRect = tmp;
3076 }
3077
3078 if ((LeftRect != RightRect) && (TopRect != BottomRect))
3079 {
3080 firstRect = rgn->Buffer;
3081 ASSERT(firstRect);
3082 firstRect->left = rgn->rdh.rcBound.left = LeftRect;
3083 firstRect->top = rgn->rdh.rcBound.top = TopRect;
3084 firstRect->right = rgn->rdh.rcBound.right = RightRect;
3085 firstRect->bottom = rgn->rdh.rcBound.bottom = BottomRect;
3086 rgn->rdh.nCount = 1;
3087 rgn->rdh.iType = RDH_RECTANGLES;
3088 }
3089 else
3090 EMPTY_REGION(rgn);
3091 }
3092
3093 BOOL
3094 APIENTRY
3095 NtGdiSetRectRgn(
3096 HRGN hRgn,
3097 INT LeftRect,
3098 INT TopRect,
3099 INT RightRect,
3100 INT BottomRect
3101 )
3102 {
3103 PROSRGNDATA rgn;
3104
3105 if ( !(rgn = REGION_LockRgn(hRgn)) )
3106 {
3107 return 0; //per documentation
3108 }
3109
3110 REGION_SetRectRgn(rgn, LeftRect, TopRect, RightRect, BottomRect);
3111
3112 REGION_UnlockRgn(rgn);
3113 return TRUE;
3114 }
3115
3116 HRGN APIENTRY
3117 NtGdiUnionRectWithRgn(
3118 HRGN hDest,
3119 const RECTL *UnsafeRect
3120 )
3121 {
3122 RECTL SafeRect = {0};
3123 PROSRGNDATA Rgn;
3124 NTSTATUS Status = STATUS_SUCCESS;
3125
3126 if (!(Rgn = REGION_LockRgn(hDest)))
3127 {
3128 SetLastWin32Error(ERROR_INVALID_HANDLE);
3129 return NULL;
3130 }
3131
3132 _SEH2_TRY
3133 {
3134 ProbeForRead(UnsafeRect, sizeof(RECT), 1);
3135 SafeRect = *UnsafeRect;
3136 }
3137 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3138 {
3139 Status = _SEH2_GetExceptionCode();
3140 }
3141 _SEH2_END;
3142
3143 if (! NT_SUCCESS(Status))
3144 {
3145 REGION_UnlockRgn(Rgn);
3146 SetLastNtError(Status);
3147 return NULL;
3148 }
3149
3150 REGION_UnionRectWithRgn(Rgn, &SafeRect);
3151 REGION_UnlockRgn(Rgn);
3152 return hDest;
3153 }
3154
3155 /*!
3156 * MSDN: GetRegionData, Return Values:
3157 *
3158 * "If the function succeeds and dwCount specifies an adequate number of bytes,
3159 * the return value is always dwCount. If dwCount is too small or the function
3160 * fails, the return value is 0. If lpRgnData is NULL, the return value is the
3161 * required number of bytes.
3162 *
3163 * If the function fails, the return value is zero."
3164 */
3165 DWORD APIENTRY
3166 NtGdiGetRegionData(
3167 HRGN hrgn,
3168 DWORD count,
3169 LPRGNDATA rgndata
3170 )
3171 {
3172 DWORD size;
3173 PROSRGNDATA obj = REGION_LockRgn(hrgn);
3174 NTSTATUS Status = STATUS_SUCCESS;
3175
3176 if (!obj)
3177 return 0;
3178
3179 size = obj->rdh.nCount * sizeof(RECT);
3180 if (count < (size + sizeof(RGNDATAHEADER)) || rgndata == NULL)
3181 {
3182 REGION_UnlockRgn(obj);
3183 if (rgndata) /* buffer is too small, signal it by return 0 */
3184 return 0;
3185 else /* user requested buffer size with rgndata NULL */
3186 return size + sizeof(RGNDATAHEADER);
3187 }
3188
3189 _SEH2_TRY
3190 {
3191 ProbeForWrite(rgndata, count, 1);
3192 RtlCopyMemory(rgndata, &obj->rdh, sizeof(RGNDATAHEADER));
3193 RtlCopyMemory(rgndata->Buffer, obj->Buffer, size);
3194 }
3195 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3196 {
3197 Status = _SEH2_GetExceptionCode();
3198 }
3199 _SEH2_END;
3200
3201 if (!NT_SUCCESS(Status))
3202 {
3203 SetLastNtError(Status);
3204 REGION_UnlockRgn(obj);
3205 return 0;
3206 }
3207
3208 REGION_UnlockRgn(obj);
3209 return size + sizeof(RGNDATAHEADER);
3210 }
3211
3212
3213 /***********************************************************************
3214 * REGION_InsertEdgeInET
3215 *
3216 * Insert the given edge into the edge table.
3217 * First we must find the correct bucket in the
3218 * Edge table, then find the right slot in the
3219 * bucket. Finally, we can insert it.
3220 *
3221 */
3222 static void FASTCALL
3223 REGION_InsertEdgeInET(
3224 EdgeTable *ET,
3225 EdgeTableEntry *ETE,
3226 INT scanline,
3227 ScanLineListBlock **SLLBlock,
3228 INT *iSLLBlock
3229 )
3230 {
3231 EdgeTableEntry *start, *prev;
3232 ScanLineList *pSLL, *pPrevSLL;
3233 ScanLineListBlock *tmpSLLBlock;
3234
3235 /*
3236 * find the right bucket to put the edge into
3237 */
3238 pPrevSLL = &ET->scanlines;
3239 pSLL = pPrevSLL->next;
3240 while (pSLL && (pSLL->scanline < scanline))
3241 {
3242 pPrevSLL = pSLL;
3243 pSLL = pSLL->next;
3244 }
3245
3246 /*
3247 * reassign pSLL (pointer to ScanLineList) if necessary
3248 */
3249 if ((!pSLL) || (pSLL->scanline > scanline))
3250 {
3251 if (*iSLLBlock > SLLSPERBLOCK-1)
3252 {
3253 tmpSLLBlock = ExAllocatePoolWithTag(PagedPool, sizeof(ScanLineListBlock), TAG_REGION);
3254 if (!tmpSLLBlock)
3255 {
3256 DPRINT1("REGION_InsertEdgeInETL(): Can't alloc SLLB\n");
3257 /* FIXME - free resources? */
3258 return;
3259 }
3260 (*SLLBlock)->next = tmpSLLBlock;
3261 tmpSLLBlock->next = (ScanLineListBlock *)NULL;
3262 *SLLBlock = tmpSLLBlock;
3263 *iSLLBlock = 0;
3264 }
3265 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
3266
3267 pSLL->next = pPrevSLL->next;
3268 pSLL->edgelist = (EdgeTableEntry *)NULL;
3269 pPrevSLL->next = pSLL;
3270 }
3271 pSLL->scanline = scanline;
3272
3273 /*
3274 * now insert the edge in the right bucket
3275 */
3276 prev = (EdgeTableEntry *)NULL;
3277 start = pSLL->edgelist;
3278 while (start && (start->bres.minor_axis < ETE->bres.minor_axis))
3279 {
3280 prev = start;
3281 start = start->next;
3282 }
3283 ETE->next = start;
3284
3285 if (prev)
3286 prev->next = ETE;
3287 else
3288 pSLL->edgelist = ETE;
3289 }
3290
3291 /***********************************************************************
3292 * REGION_loadAET
3293 *
3294 * This routine moves EdgeTableEntries from the
3295 * EdgeTable into the Active Edge Table,
3296 * leaving them sorted by smaller x coordinate.
3297 *
3298 */
3299 static void FASTCALL
3300 REGION_loadAET(
3301 EdgeTableEntry *AET,
3302 EdgeTableEntry *ETEs
3303 )
3304 {
3305 EdgeTableEntry *pPrevAET;
3306 EdgeTableEntry *tmp;
3307
3308 pPrevAET = AET;
3309 AET = AET->next;
3310 while (ETEs)
3311 {
3312 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis))
3313 {
3314 pPrevAET = AET;
3315 AET = AET->next;
3316 }
3317 tmp = ETEs->next;
3318 ETEs->next = AET;
3319 if (AET)
3320 AET->back = ETEs;
3321 ETEs->back = pPrevAET;
3322 pPrevAET->next = ETEs;
3323 pPrevAET = ETEs;
3324
3325 ETEs = tmp;
3326 }
3327 }
3328
3329 /***********************************************************************
3330 * REGION_computeWAET
3331 *
3332 * This routine links the AET by the
3333 * nextWETE (winding EdgeTableEntry) link for
3334 * use by the winding number rule. The final
3335 * Active Edge Table (AET) might look something
3336 * like:
3337 *
3338 * AET
3339 * ---------- --------- ---------
3340 * |ymax | |ymax | |ymax |
3341 * | ... | |... | |... |
3342 * |next |->|next |->|next |->...
3343 * |nextWETE| |nextWETE| |nextWETE|
3344 * --------- --------- ^--------
3345 * | | |
3346 * V-------------------> V---> ...
3347 *
3348 */
3349 static void FASTCALL
3350 REGION_computeWAET(EdgeTableEntry *AET)
3351 {
3352 register EdgeTableEntry *pWETE;
3353 register int inside = 1;
3354 register int isInside = 0;
3355
3356 AET->nextWETE = (EdgeTableEntry *)NULL;
3357 pWETE = AET;
3358 AET = AET->next;
3359 while (AET)
3360 {
3361 if (AET->ClockWise)
3362 isInside++;
3363 else
3364 isInside--;
3365
3366 if ( (!inside && !isInside) ||
3367 ( inside && isInside) )
3368 {
3369 pWETE->nextWETE = AET;
3370 pWETE = AET;
3371 inside = !inside;
3372 }
3373 AET = AET->next;
3374 }
3375 pWETE->nextWETE = (EdgeTableEntry *)NULL;
3376 }
3377
3378 /***********************************************************************
3379 * REGION_InsertionSort
3380 *
3381 * Just a simple insertion sort using
3382 * pointers and back pointers to sort the Active
3383 * Edge Table.
3384 *
3385 */
3386 static BOOL FASTCALL
3387 REGION_InsertionSort(EdgeTableEntry *AET)
3388 {
3389 EdgeTableEntry *pETEchase;
3390 EdgeTableEntry *pETEinsert;
3391 EdgeTableEntry *pETEchaseBackTMP;
3392 BOOL changed = FALSE;
3393
3394 AET = AET->next;
3395 while (AET)
3396 {
3397 pETEinsert = AET;
3398 pETEchase = AET;
3399 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
3400 pETEchase = pETEchase->back;
3401
3402 AET = AET->next;
3403 if (pETEchase != pETEinsert)
3404 {
3405 pETEchaseBackTMP = pETEchase->back;
3406 pETEinsert->back->next = AET;
3407 if (AET)
3408 AET->back = pETEinsert->back;
3409 pETEinsert->next = pETEchase;
3410 pETEchase->back->next = pETEinsert;
3411 pETEchase->back = pETEinsert;
3412 pETEinsert->back = pETEchaseBackTMP;
3413 changed = TRUE;
3414 }
3415 }
3416 return changed;
3417 }
3418
3419 /***********************************************************************
3420 * REGION_FreeStorage
3421 *
3422 * Clean up our act.
3423 */
3424 static void FASTCALL
3425 REGION_FreeStorage(ScanLineListBlock *pSLLBlock)
3426 {
3427 ScanLineListBlock *tmpSLLBlock;
3428
3429 while (pSLLBlock)
3430 {
3431 tmpSLLBlock = pSLLBlock->next;
3432 ExFreePool(pSLLBlock);
3433 pSLLBlock = tmpSLLBlock;
3434 }
3435 }
3436
3437
3438 /***********************************************************************
3439 * REGION_PtsToRegion
3440 *
3441 * Create an array of rectangles from a list of points.
3442 */
3443 static int FASTCALL
3444 REGION_PtsToRegion(
3445 int numFullPtBlocks,
3446 int iCurPtBlock,
3447 POINTBLOCK *FirstPtBlock,
3448 ROSRGNDATA *reg)
3449 {
3450 RECTL *rects;
3451 POINT *pts;
3452 POINTBLOCK *CurPtBlock;
3453 int i;
3454 RECTL *extents, *temp;
3455 INT numRects;
3456
3457 extents = &reg->rdh.rcBound;
3458
3459 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
3460
3461 if (!(temp = ExAllocatePoolWithTag(PagedPool, numRects * sizeof(RECT), TAG_REGION)))
3462 {
3463 return 0;
3464 }
3465 if (reg->Buffer != NULL)
3466 {
3467 COPY_RECTS(temp, reg->Buffer, reg->rdh.nCount);
3468 if (reg->Buffer != &reg->rdh.rcBound)
3469 ExFreePoolWithTag(reg->Buffer, TAG_REGION);
3470 }
3471 reg->Buffer = temp;
3472
3473 reg->rdh.nCount = numRects;
3474 CurPtBlock = FirstPtBlock;
3475 rects = reg->Buffer - 1;
3476 numRects = 0;
3477 extents->left = LARGE_COORDINATE, extents->right = SMALL_COORDINATE;
3478
3479 for ( ; numFullPtBlocks >= 0; numFullPtBlocks--)
3480 {
3481 /* the loop uses 2 points per iteration */
3482 i = NUMPTSTOBUFFER >> 1;
3483 if (!numFullPtBlocks)
3484 i = iCurPtBlock >> 1;
3485 for (pts = CurPtBlock->pts; i--; pts += 2)
3486 {
3487 if (pts->x == pts[1].x)
3488 continue;
3489 if (numRects && pts->x == rects->left && pts->y == rects->bottom &&
3490 pts[1].x == rects->right &&
3491 (numRects == 1 || rects[-1].top != rects->top) &&
3492 (i && pts[2].y > pts[1].y))
3493 {
3494 rects->bottom = pts[1].y + 1;
3495 continue;
3496 }
3497 numRects++;
3498 rects++;
3499 rects->left = pts->x;
3500 rects->top = pts->y;
3501 rects->right = pts[1].x;
3502 rects->bottom = pts[1].y + 1;
3503 if (rects->left < extents->left)
3504 extents->left = rects->left;
3505 if (rects->right > extents->right)
3506 extents->right = rects->right;
3507 }
3508 CurPtBlock = CurPtBlock->next;
3509 }
3510
3511 if (numRects)
3512 {
3513 extents->top = reg->Buffer->top;
3514 extents->bottom = rects->bottom;
3515 }
3516 else
3517 {
3518 extents->left = 0;
3519 extents->top = 0;
3520 extents->right = 0;
3521 extents->bottom = 0;
3522 }
3523 reg->rdh.nCount = numRects;
3524
3525 return(TRUE);
3526 }
3527
3528 /***********************************************************************
3529 * REGION_CreateEdgeTable
3530 *
3531 * This routine creates the edge table for
3532 * scan converting polygons.
3533 * The Edge Table (ET) looks like:
3534 *
3535 * EdgeTable
3536 * --------
3537 * | ymax | ScanLineLists
3538 * |scanline|-->------------>-------------->...
3539 * -------- |scanline| |scanline|
3540 * |edgelist| |edgelist|
3541 * --------- ---------
3542 * | |
3543 * | |
3544 * V V
3545 * list of ETEs list of ETEs
3546 *
3547 * where ETE is an EdgeTableEntry data structure,
3548 * and there is one ScanLineList per scanline at
3549 * which an edge is initially entered.
3550 *
3551 */
3552 static void FASTCALL
3553 REGION_CreateETandAET(
3554 const ULONG *Count,
3555 INT nbpolygons,
3556 const POINT *pts,
3557 EdgeTable *ET,
3558 EdgeTableEntry *AET,
3559 EdgeTableEntry *pETEs,
3560 ScanLineListBlock *pSLLBlock
3561 )
3562 {
3563 const POINT *top, *bottom;
3564 const POINT *PrevPt, *CurrPt, *EndPt;
3565 INT poly, count;
3566 int iSLLBlock = 0;
3567 int dy;
3568
3569
3570 /*
3571 * initialize the Active Edge Table
3572 */
3573 AET->next = (EdgeTableEntry *)NULL;
3574 AET->back = (EdgeTableEntry *)NULL;
3575 AET->nextWETE = (EdgeTableEntry *)NULL;
3576 AET->bres.minor_axis = SMALL_COORDINATE;
3577
3578 /*
3579 * initialize the Edge Table.
3580 */
3581 ET->scanlines.next = (ScanLineList *)NULL;
3582 ET->ymax = SMALL_COORDINATE;
3583 ET->ymin = LARGE_COORDINATE;
3584 pSLLBlock->next = (ScanLineListBlock *)NULL;
3585
3586 EndPt = pts - 1;
3587 for (poly = 0; poly < nbpolygons; poly++)
3588 {
3589 count = Count[poly];
3590 EndPt += count;
3591 if (count < 2)
3592 continue;
3593
3594 PrevPt = EndPt;
3595
3596 /*
3597 * for each vertex in the array of points.
3598 * In this loop we are dealing with two vertices at
3599 * a time -- these make up one edge of the polygon.
3600 */
3601 while (count--)
3602 {
3603 CurrPt = pts++;
3604
3605 /*
3606 * find out which point is above and which is below.
3607 */
3608 if (PrevPt->y > CurrPt->y)
3609 {
3610 bottom = PrevPt, top = CurrPt;
3611 pETEs->ClockWise = 0;
3612 }
3613 else
3614 {
3615 bottom = CurrPt, top = PrevPt;
3616 pETEs->ClockWise = 1;
3617 }
3618
3619 /*
3620 * don't add horizontal edges to the Edge table.
3621 */
3622 if (bottom->y != top->y)
3623 {
3624 pETEs->ymax = bottom->y-1;
3625 /* -1 so we don't get last scanline */
3626
3627 /*
3628 * initialize integer edge algorithm
3629 */
3630 dy = bottom->y - top->y;
3631 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
3632
3633 REGION_InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock,
3634 &iSLLBlock);
3635
3636 if (PrevPt->y > ET->ymax)
3637 ET->ymax = PrevPt->y;
3638 if (PrevPt->y < ET->ymin)
3639 ET->ymin = PrevPt->y;
3640 pETEs++;
3641 }
3642
3643 PrevPt = CurrPt;
3644 }
3645 }
3646 }
3647
3648 HRGN FASTCALL
3649 IntCreatePolyPolygonRgn(
3650 POINT *Pts,
3651 PULONG Count,
3652 INT nbpolygons,
3653 INT mode
3654 )
3655 {
3656 HRGN hrgn;
3657 ROSRGNDATA *region;
3658 EdgeTableEntry *pAET; /* Active Edge Table */
3659 INT y; /* current scanline */
3660 int iPts = 0; /* number of pts in buffer */
3661 EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
3662 ScanLineList *pSLL; /* current scanLineList */
3663 POINT *pts; /* output buffer */
3664 EdgeTableEntry *pPrevAET; /* ptr to previous AET */
3665 EdgeTable ET; /* header node for ET */
3666 EdgeTableEntry AET; /* header node for AET */
3667 EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
3668 ScanLineListBlock SLLBlock; /* header for scanlinelist */
3669 int fixWAET = FALSE;
3670 POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
3671 POINTBLOCK *tmpPtBlock;
3672 int numFullPtBlocks = 0;
3673 INT poly, total;
3674
3675 if (mode == 0 || mode > 2) return 0;
3676
3677 if (!(region = REGION_AllocRgnWithHandle(nbpolygons)))
3678 return 0;
3679 hrgn = region->BaseObject.hHmgr;
3680
3681 /* special case a rectangle */
3682
3683 if (((nbpolygons == 1) && ((*Count == 4) ||
3684 ((*Count == 5) && (Pts[4].x == Pts[0].x) && (Pts[4].y == Pts[0].y)))) &&
3685 (((Pts[0].y == Pts[1].y) &&
3686 (Pts[1].x == Pts[2].x) &&
3687 (Pts[2].y == Pts[3].y) &&
3688 (Pts[3].x == Pts[0].x)) ||
3689 ((Pts[0].x == Pts[1].x) &&
3690 (Pts[1].y == Pts[2].y) &&
3691 (Pts[2].x == Pts[3].x) &&
3692 (Pts[3].y == Pts[0].y))))
3693 {
3694 REGION_UnlockRgn(region);
3695 NtGdiSetRectRgn(hrgn, min(Pts[0].x, Pts[2].x), min(Pts[0].y, Pts[2].y),
3696 max(Pts[0].x, Pts[2].x), max(Pts[0].y, Pts[2].y));
3697 return hrgn;
3698 }
3699
3700 for (poly = total = 0; poly < nbpolygons; poly++)
3701 total += Count[poly];
3702 if (! (pETEs = ExAllocatePoolWithTag(PagedPool, sizeof(EdgeTableEntry) * total, TAG_REGION)) )
3703 {
3704 GreDeleteObject(hrgn);
3705 return 0;
3706 }
3707 pts = FirstPtBlock.pts;
3708 REGION_CreateETandAET(Count, nbpolygons, Pts, &ET, &AET, pETEs, &SLLBlock);
3709 pSLL = ET.scanlines.next;
3710 curPtBlock = &FirstPtBlock;
3711
3712 if (mode != WINDING)
3713 {
3714 /*
3715 * for each scanline
3716 */
3717 for (y = ET.ymin; y < ET.ymax; y++)
3718 {
3719 /*
3720 * Add a new edge to the active edge table when we
3721 * get to the next edge.
3722 */
3723 if (pSLL != NULL && y == pSLL->scanline)
3724 {
3725 REGION_loadAET(&AET, pSLL->edgelist);
3726 pSLL = pSLL->next;
3727 }
3728 pPrevAET = &AET;
3729 pAET = AET.next;
3730
3731 /*
3732 * for each active edge
3733 */
3734 while (pAET)
3735 {
3736 pts->x = pAET->bres.minor_axis, pts->y = y;
3737 pts++, iPts++;
3738
3739 /*
3740 * send out the buffer
3741 */
3742 if (iPts == NUMPTSTOBUFFER)
3743 {
3744 tmpPtBlock = ExAllocatePoolWithTag(PagedPool, sizeof(POINTBLOCK), TAG_REGION);
3745 if (!tmpPtBlock)
3746 {
3747 DPRINT1("Can't alloc tPB\n");
3748 ExFreePoolWithTag(pETEs, TAG_REGION);
3749 return 0;
3750 }
3751 curPtBlock->next = tmpPtBlock;
3752 curPtBlock = tmpPtBlock;
3753 pts = curPtBlock->pts;
3754 numFullPtBlocks++;
3755 iPts = 0;
3756 }
3757 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
3758 }
3759 REGION_InsertionSort(&AET);
3760 }
3761 }
3762 else
3763 {
3764 /*
3765 * for each scanline
3766 */
3767 for (y = ET.ymin; y < ET.ymax; y++)
3768 {
3769 /*
3770 * Add a new edge to the active edge table when we
3771 * get to the next edge.
3772 */
3773 if (pSLL != NULL && y == pSLL->scanline)
3774 {
3775 REGION_loadAET(&AET, pSLL->edgelist);
3776 REGION_computeWAET(&AET);
3777 pSLL = pSLL->next;
3778 }
3779 pPrevAET = &AET;
3780 pAET = AET.next;
3781 pWETE = pAET;
3782
3783 /*
3784 * for each active edge
3785 */
3786 while (pAET)
3787 {
3788 /*
3789 * add to the buffer only those edges that
3790 * are in the Winding active edge table.
3791 */
3792 if (pWETE == pAET)
3793 {
3794 pts->x = pAET->bres.minor_axis, pts->y = y;
3795 pts++, iPts++;
3796
3797 /*
3798 * send out the buffer
3799 */
3800 if (iPts == NUMPTSTOBUFFER)
3801 {
3802 tmpPtBlock = ExAllocatePoolWithTag(PagedPool,
3803 sizeof(POINTBLOCK), TAG_REGION);
3804 if (!tmpPtBlock)
3805 {
3806 DPRINT1("Can't alloc tPB\n");
3807 ExFreePoolWithTag(pETEs, TAG_REGION);
3808 GreDeleteObject(hrgn);
3809 return 0;
3810 }
3811 curPtBlock->next = tmpPtBlock;
3812 curPtBlock = tmpPtBlock;
3813 pts = curPtBlock->pts;
3814 numFullPtBlocks++;
3815 iPts = 0;
3816 }
3817 pWETE = pWETE->nextWETE;
3818 }
3819 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
3820 }
3821
3822 /*
3823 * recompute the winding active edge table if
3824 * we just resorted or have exited an edge.
3825 */
3826 if (REGION_InsertionSort(&AET) || fixWAET)
3827 {
3828 REGION_computeWAET(&AET);
3829 fixWAET = FALSE;
3830 }
3831 }
3832 }
3833 REGION_FreeStorage(SLLBlock.next);
3834 REGION_PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
3835
3836 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;)
3837 {
3838 tmpPtBlock = curPtBlock->next;
3839 ExFreePoolWithTag(curPtBlock, TAG_REGION);
3840 curPtBlock = tmpPtBlock;
3841 }
3842 ExFreePoolWithTag(pETEs, TAG_REGION);
3843 REGION_UnlockRgn(region);
3844 return hrgn;
3845 }
3846
3847 /* EOF */