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