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