1 /* 2 * Copyright (C) 2008 Google (Lei Zhang) 3 * Copyright (C) 2013 Dmitry Timoshkov 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library 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 GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #include "gdiplus_private.h" 21 22 /********************************************************** 23 * 24 * Data returned by GdipGetRegionData looks something like this: 25 * 26 * struct region_data_header 27 * { 28 * DWORD size; size in bytes of the data - 8. 29 * DWORD magic1; probably a checksum. 30 * DWORD magic2; always seems to be 0xdbc01001 - version? 31 * DWORD num_ops; number of combining ops * 2 32 * }; 33 * 34 * Then follows a sequence of combining ops and region elements. 35 * 36 * A region element is either a RECTF or some path data. 37 * 38 * Combining ops are just stored as their CombineMode value. 39 * 40 * Each RECTF is preceded by the DWORD 0x10000000. An empty rect is 41 * stored as 0x10000002 (with no following RECTF) and an infinite rect 42 * is stored as 0x10000003 (again with no following RECTF). 43 * 44 * Path data is preceded by the DWORD 0x10000001. Then follows a 45 * DWORD size and then size bytes of data. 46 * 47 * The combining ops are stored in the reverse order to the region 48 * elements and in the reverse order to which the region was 49 * constructed. 50 * 51 * When two or more complex regions (ie those with more than one 52 * element) are combined, the combining op for the two regions comes 53 * first, then the combining ops for the region elements in region 1, 54 * followed by the region elements for region 1, then follows the 55 * combining ops for region 2 and finally region 2's region elements. 56 * Presumably you're supposed to use the 0x1000000x header to find the 57 * end of the op list (the count of the elements in each region is not 58 * stored). 59 * 60 * When a simple region (1 element) is combined, it's treated as if a 61 * single rect/path is being combined. 62 * 63 */ 64 65 #define FLAGS_INTPATH 0x4000 66 67 struct region_header 68 { 69 DWORD magic; 70 DWORD num_children; 71 }; 72 73 struct region_data_header 74 { 75 DWORD size; 76 DWORD checksum; 77 struct region_header header; 78 }; 79 80 struct path_header 81 { 82 DWORD size; 83 DWORD magic; 84 DWORD count; 85 DWORD flags; 86 }; 87 88 typedef struct packed_point 89 { 90 short X; 91 short Y; 92 } packed_point; 93 94 static inline INT get_element_size(const region_element* element) 95 { 96 INT needed = sizeof(DWORD); /* DWORD for the type */ 97 switch(element->type) 98 { 99 case RegionDataRect: 100 return needed + sizeof(GpRect); 101 case RegionDataPath: 102 { 103 needed += write_path_data(element->elementdata.path, NULL); 104 needed += sizeof(DWORD); /* Extra DWORD for path size */ 105 return needed; 106 } 107 case RegionDataEmptyRect: 108 case RegionDataInfiniteRect: 109 return needed; 110 default: 111 needed += get_element_size(element->elementdata.combine.left); 112 needed += get_element_size(element->elementdata.combine.right); 113 return needed; 114 } 115 116 return 0; 117 } 118 119 /* Does not check parameters, caller must do that */ 120 static inline GpStatus init_region(GpRegion* region, const RegionType type) 121 { 122 region->node.type = type; 123 region->num_children = 0; 124 125 return Ok; 126 } 127 128 static inline GpStatus clone_element(const region_element* element, 129 region_element** element2) 130 { 131 GpStatus stat; 132 133 /* root node is allocated with GpRegion */ 134 if(!*element2){ 135 *element2 = heap_alloc_zero(sizeof(region_element)); 136 if (!*element2) 137 return OutOfMemory; 138 } 139 140 (*element2)->type = element->type; 141 142 switch (element->type) 143 { 144 case RegionDataRect: 145 (*element2)->elementdata.rect = element->elementdata.rect; 146 return Ok; 147 case RegionDataEmptyRect: 148 case RegionDataInfiniteRect: 149 return Ok; 150 case RegionDataPath: 151 stat = GdipClonePath(element->elementdata.path, &(*element2)->elementdata.path); 152 if (stat == Ok) return Ok; 153 break; 154 default: 155 (*element2)->elementdata.combine.left = NULL; 156 (*element2)->elementdata.combine.right = NULL; 157 158 stat = clone_element(element->elementdata.combine.left, 159 &(*element2)->elementdata.combine.left); 160 if (stat == Ok) 161 { 162 stat = clone_element(element->elementdata.combine.right, 163 &(*element2)->elementdata.combine.right); 164 if (stat == Ok) return Ok; 165 } 166 break; 167 } 168 169 delete_element(*element2); 170 *element2 = NULL; 171 return stat; 172 } 173 174 /* Common code for CombineRegion* 175 * All the caller has to do is get its format into an element 176 */ 177 static inline void fuse_region(GpRegion* region, region_element* left, 178 region_element* right, const CombineMode mode) 179 { 180 region->node.type = mode; 181 region->node.elementdata.combine.left = left; 182 region->node.elementdata.combine.right = right; 183 region->num_children += 2; 184 } 185 186 /***************************************************************************** 187 * GdipCloneRegion [GDIPLUS.@] 188 * 189 * Creates a deep copy of the region 190 * 191 * PARAMS 192 * region [I] source region 193 * clone [O] resulting clone 194 * 195 * RETURNS 196 * SUCCESS: Ok 197 * FAILURE: InvalidParameter or OutOfMemory 198 */ 199 GpStatus WINGDIPAPI GdipCloneRegion(GpRegion *region, GpRegion **clone) 200 { 201 region_element *element; 202 203 TRACE("%p %p\n", region, clone); 204 205 if (!(region && clone)) 206 return InvalidParameter; 207 208 *clone = heap_alloc_zero(sizeof(GpRegion)); 209 if (!*clone) 210 return OutOfMemory; 211 element = &(*clone)->node; 212 213 (*clone)->num_children = region->num_children; 214 return clone_element(®ion->node, &element); 215 } 216 217 /***************************************************************************** 218 * GdipCombineRegionPath [GDIPLUS.@] 219 */ 220 GpStatus WINGDIPAPI GdipCombineRegionPath(GpRegion *region, GpPath *path, CombineMode mode) 221 { 222 GpRegion *path_region; 223 region_element *left, *right = NULL; 224 GpStatus stat; 225 226 TRACE("%p %p %d\n", region, path, mode); 227 228 if (!(region && path)) 229 return InvalidParameter; 230 231 stat = GdipCreateRegionPath(path, &path_region); 232 if (stat != Ok) 233 return stat; 234 235 /* simply replace region data */ 236 if(mode == CombineModeReplace){ 237 delete_element(®ion->node); 238 memcpy(region, path_region, sizeof(GpRegion)); 239 heap_free(path_region); 240 return Ok; 241 } 242 243 left = heap_alloc_zero(sizeof(region_element)); 244 if (left) 245 { 246 *left = region->node; 247 stat = clone_element(&path_region->node, &right); 248 if (stat == Ok) 249 { 250 fuse_region(region, left, right, mode); 251 GdipDeleteRegion(path_region); 252 return Ok; 253 } 254 } 255 else 256 stat = OutOfMemory; 257 258 heap_free(left); 259 GdipDeleteRegion(path_region); 260 return stat; 261 } 262 263 /***************************************************************************** 264 * GdipCombineRegionRect [GDIPLUS.@] 265 */ 266 GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region, 267 GDIPCONST GpRectF *rect, CombineMode mode) 268 { 269 GpRegion *rect_region; 270 region_element *left, *right = NULL; 271 GpStatus stat; 272 273 TRACE("%p %s %d\n", region, debugstr_rectf(rect), mode); 274 275 if (!(region && rect)) 276 return InvalidParameter; 277 278 stat = GdipCreateRegionRect(rect, &rect_region); 279 if (stat != Ok) 280 return stat; 281 282 /* simply replace region data */ 283 if(mode == CombineModeReplace){ 284 delete_element(®ion->node); 285 memcpy(region, rect_region, sizeof(GpRegion)); 286 heap_free(rect_region); 287 return Ok; 288 } 289 290 left = heap_alloc_zero(sizeof(region_element)); 291 if (left) 292 { 293 memcpy(left, ®ion->node, sizeof(region_element)); 294 stat = clone_element(&rect_region->node, &right); 295 if (stat == Ok) 296 { 297 fuse_region(region, left, right, mode); 298 GdipDeleteRegion(rect_region); 299 return Ok; 300 } 301 } 302 else 303 stat = OutOfMemory; 304 305 heap_free(left); 306 GdipDeleteRegion(rect_region); 307 return stat; 308 } 309 310 /***************************************************************************** 311 * GdipCombineRegionRectI [GDIPLUS.@] 312 */ 313 GpStatus WINGDIPAPI GdipCombineRegionRectI(GpRegion *region, 314 GDIPCONST GpRect *rect, CombineMode mode) 315 { 316 GpRectF rectf; 317 318 TRACE("%p %p %d\n", region, rect, mode); 319 320 if (!rect) 321 return InvalidParameter; 322 323 rectf.X = (REAL)rect->X; 324 rectf.Y = (REAL)rect->Y; 325 rectf.Height = (REAL)rect->Height; 326 rectf.Width = (REAL)rect->Width; 327 328 return GdipCombineRegionRect(region, &rectf, mode); 329 } 330 331 /***************************************************************************** 332 * GdipCombineRegionRegion [GDIPLUS.@] 333 */ 334 GpStatus WINGDIPAPI GdipCombineRegionRegion(GpRegion *region1, 335 GpRegion *region2, CombineMode mode) 336 { 337 region_element *left, *right = NULL; 338 GpStatus stat; 339 GpRegion *reg2copy; 340 341 TRACE("%p %p %d\n", region1, region2, mode); 342 343 if(!(region1 && region2)) 344 return InvalidParameter; 345 346 /* simply replace region data */ 347 if(mode == CombineModeReplace){ 348 stat = GdipCloneRegion(region2, ®2copy); 349 if(stat != Ok) return stat; 350 351 delete_element(®ion1->node); 352 memcpy(region1, reg2copy, sizeof(GpRegion)); 353 heap_free(reg2copy); 354 return Ok; 355 } 356 357 left = heap_alloc_zero(sizeof(region_element)); 358 if (!left) 359 return OutOfMemory; 360 361 *left = region1->node; 362 stat = clone_element(®ion2->node, &right); 363 if (stat != Ok) 364 { 365 heap_free(left); 366 return OutOfMemory; 367 } 368 369 fuse_region(region1, left, right, mode); 370 region1->num_children += region2->num_children; 371 372 return Ok; 373 } 374 375 /***************************************************************************** 376 * GdipCreateRegion [GDIPLUS.@] 377 */ 378 GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region) 379 { 380 TRACE("%p\n", region); 381 382 if(!region) 383 return InvalidParameter; 384 385 *region = heap_alloc_zero(sizeof(GpRegion)); 386 if(!*region) 387 return OutOfMemory; 388 389 TRACE("=> %p\n", *region); 390 391 return init_region(*region, RegionDataInfiniteRect); 392 } 393 394 /***************************************************************************** 395 * GdipCreateRegionPath [GDIPLUS.@] 396 * 397 * Creates a GpRegion from a GpPath 398 * 399 * PARAMS 400 * path [I] path to base the region on 401 * region [O] pointer to the newly allocated region 402 * 403 * RETURNS 404 * SUCCESS: Ok 405 * FAILURE: InvalidParameter 406 * 407 * NOTES 408 * If a path has no floating point points, its points will be stored as shorts 409 * (INTPATH) 410 * 411 * If a path is empty, it is considered to be an INTPATH 412 */ 413 GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region) 414 { 415 region_element* element; 416 GpStatus stat; 417 418 TRACE("%p, %p\n", path, region); 419 420 if (!(path && region)) 421 return InvalidParameter; 422 423 *region = heap_alloc_zero(sizeof(GpRegion)); 424 if(!*region) 425 return OutOfMemory; 426 stat = init_region(*region, RegionDataPath); 427 if (stat != Ok) 428 { 429 GdipDeleteRegion(*region); 430 return stat; 431 } 432 element = &(*region)->node; 433 434 stat = GdipClonePath(path, &element->elementdata.path); 435 if (stat != Ok) 436 { 437 GdipDeleteRegion(*region); 438 return stat; 439 } 440 441 return Ok; 442 } 443 444 /***************************************************************************** 445 * GdipCreateRegionRect [GDIPLUS.@] 446 */ 447 GpStatus WINGDIPAPI GdipCreateRegionRect(GDIPCONST GpRectF *rect, 448 GpRegion **region) 449 { 450 GpStatus stat; 451 452 TRACE("%p, %p\n", rect, region); 453 454 if (!(rect && region)) 455 return InvalidParameter; 456 457 *region = heap_alloc_zero(sizeof(GpRegion)); 458 stat = init_region(*region, RegionDataRect); 459 if(stat != Ok) 460 { 461 GdipDeleteRegion(*region); 462 return stat; 463 } 464 465 (*region)->node.elementdata.rect.X = rect->X; 466 (*region)->node.elementdata.rect.Y = rect->Y; 467 (*region)->node.elementdata.rect.Width = rect->Width; 468 (*region)->node.elementdata.rect.Height = rect->Height; 469 470 return Ok; 471 } 472 473 /***************************************************************************** 474 * GdipCreateRegionRectI [GDIPLUS.@] 475 */ 476 GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect, 477 GpRegion **region) 478 { 479 GpRectF rectf; 480 481 TRACE("%p, %p\n", rect, region); 482 483 rectf.X = (REAL)rect->X; 484 rectf.Y = (REAL)rect->Y; 485 rectf.Width = (REAL)rect->Width; 486 rectf.Height = (REAL)rect->Height; 487 488 return GdipCreateRegionRect(&rectf, region); 489 } 490 491 /****************************************************************************** 492 * GdipCreateRegionHrgn [GDIPLUS.@] 493 */ 494 GpStatus WINGDIPAPI GdipCreateRegionHrgn(HRGN hrgn, GpRegion **region) 495 { 496 DWORD size; 497 LPRGNDATA buf; 498 LPRECT rect; 499 GpStatus stat; 500 GpPath* path; 501 GpRegion* local; 502 DWORD i; 503 504 TRACE("(%p, %p)\n", hrgn, region); 505 506 if(!region || !(size = GetRegionData(hrgn, 0, NULL))) 507 return InvalidParameter; 508 509 buf = heap_alloc_zero(size); 510 if(!buf) 511 return OutOfMemory; 512 513 if(!GetRegionData(hrgn, size, buf)){ 514 heap_free(buf); 515 return GenericError; 516 } 517 518 if(buf->rdh.nCount == 0){ 519 if((stat = GdipCreateRegion(&local)) != Ok){ 520 heap_free(buf); 521 return stat; 522 } 523 if((stat = GdipSetEmpty(local)) != Ok){ 524 heap_free(buf); 525 GdipDeleteRegion(local); 526 return stat; 527 } 528 *region = local; 529 heap_free(buf); 530 return Ok; 531 } 532 533 if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok){ 534 heap_free(buf); 535 return stat; 536 } 537 538 rect = (LPRECT)buf->Buffer; 539 for(i = 0; i < buf->rdh.nCount; i++){ 540 if((stat = GdipAddPathRectangle(path, (REAL)rect->left, (REAL)rect->top, 541 (REAL)(rect->right - rect->left), (REAL)(rect->bottom - rect->top))) != Ok){ 542 heap_free(buf); 543 GdipDeletePath(path); 544 return stat; 545 } 546 rect++; 547 } 548 549 stat = GdipCreateRegionPath(path, region); 550 551 heap_free(buf); 552 GdipDeletePath(path); 553 return stat; 554 } 555 556 /***************************************************************************** 557 * GdipDeleteRegion [GDIPLUS.@] 558 */ 559 GpStatus WINGDIPAPI GdipDeleteRegion(GpRegion *region) 560 { 561 TRACE("%p\n", region); 562 563 if (!region) 564 return InvalidParameter; 565 566 delete_element(®ion->node); 567 heap_free(region); 568 569 return Ok; 570 } 571 572 /***************************************************************************** 573 * GdipGetRegionBounds [GDIPLUS.@] 574 */ 575 GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect) 576 { 577 HRGN hrgn; 578 RECT r; 579 GpStatus status; 580 581 TRACE("(%p, %p, %p)\n", region, graphics, rect); 582 583 if(!region || !graphics || !rect) 584 return InvalidParameter; 585 586 /* Contrary to MSDN, native ignores the graphics transform. */ 587 status = GdipGetRegionHRgn(region, NULL, &hrgn); 588 if(status != Ok) 589 return status; 590 591 /* infinite */ 592 if(!hrgn){ 593 rect->X = rect->Y = -(REAL)(1 << 22); 594 rect->Width = rect->Height = (REAL)(1 << 23); 595 TRACE("%p => infinite\n", region); 596 return Ok; 597 } 598 599 if(GetRgnBox(hrgn, &r)){ 600 rect->X = r.left; 601 rect->Y = r.top; 602 rect->Width = r.right - r.left; 603 rect->Height = r.bottom - r.top; 604 TRACE("%p => %s\n", region, debugstr_rectf(rect)); 605 } 606 else 607 status = GenericError; 608 609 DeleteObject(hrgn); 610 611 return status; 612 } 613 614 /***************************************************************************** 615 * GdipGetRegionBoundsI [GDIPLUS.@] 616 */ 617 GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics, GpRect *rect) 618 { 619 GpRectF rectf; 620 GpStatus status; 621 622 TRACE("(%p, %p, %p)\n", region, graphics, rect); 623 624 if(!rect) 625 return InvalidParameter; 626 627 status = GdipGetRegionBounds(region, graphics, &rectf); 628 if(status == Ok){ 629 rect->X = gdip_round(rectf.X); 630 rect->Y = gdip_round(rectf.Y); 631 rect->Width = gdip_round(rectf.Width); 632 rect->Height = gdip_round(rectf.Height); 633 } 634 635 return status; 636 } 637 638 static inline void write_dword(DWORD* location, INT* offset, const DWORD write) 639 { 640 location[*offset] = write; 641 (*offset)++; 642 } 643 644 static inline void write_float(DWORD* location, INT* offset, const FLOAT write) 645 { 646 ((FLOAT*)location)[*offset] = write; 647 (*offset)++; 648 } 649 650 static void write_element(const region_element* element, DWORD *buffer, 651 INT* filled) 652 { 653 write_dword(buffer, filled, element->type); 654 switch (element->type) 655 { 656 case CombineModeReplace: 657 case CombineModeIntersect: 658 case CombineModeUnion: 659 case CombineModeXor: 660 case CombineModeExclude: 661 case CombineModeComplement: 662 write_element(element->elementdata.combine.left, buffer, filled); 663 write_element(element->elementdata.combine.right, buffer, filled); 664 break; 665 case RegionDataRect: 666 write_float(buffer, filled, element->elementdata.rect.X); 667 write_float(buffer, filled, element->elementdata.rect.Y); 668 write_float(buffer, filled, element->elementdata.rect.Width); 669 write_float(buffer, filled, element->elementdata.rect.Height); 670 break; 671 case RegionDataPath: 672 { 673 DWORD size = write_path_data(element->elementdata.path, buffer + *filled + 1); 674 write_dword(buffer, filled, size); 675 *filled += size / sizeof(DWORD); 676 break; 677 } 678 case RegionDataEmptyRect: 679 case RegionDataInfiniteRect: 680 break; 681 } 682 } 683 684 DWORD write_region_data(const GpRegion *region, void *data) 685 { 686 struct region_header *header = data; 687 INT filled = 0; 688 DWORD size; 689 690 size = sizeof(struct region_header) + get_element_size(®ion->node); 691 if (!data) return size; 692 693 header->magic = VERSION_MAGIC2; 694 header->num_children = region->num_children; 695 filled += 2; 696 /* With few exceptions, everything written is DWORD aligned, 697 * so use that as our base */ 698 write_element(®ion->node, (DWORD*)data, &filled); 699 return size; 700 } 701 702 /***************************************************************************** 703 * GdipGetRegionData [GDIPLUS.@] 704 * 705 * Returns the header, followed by combining ops and region elements. 706 * 707 * PARAMS 708 * region [I] region to retrieve from 709 * buffer [O] buffer to hold the resulting data 710 * size [I] size of the buffer 711 * needed [O] (optional) how much data was written 712 * 713 * RETURNS 714 * SUCCESS: Ok 715 * FAILURE: InvalidParameter 716 * 717 * NOTES 718 * The header contains the size, a checksum, a version string, and the number 719 * of children. The size does not count itself or the checksum. 720 * Version is always something like 0xdbc01001 or 0xdbc01002 721 * 722 * An element is a RECT, or PATH; Combining ops are stored as their 723 * CombineMode value. Special regions (infinite, empty) emit just their 724 * op-code; GpRectFs emit their code followed by their points; GpPaths emit 725 * their code followed by a second header for the path followed by the actual 726 * path data. Followed by the flags for each point. The pathheader contains 727 * the size of the data to follow, a version number again, followed by a count 728 * of how many points, and any special flags which may apply. 0x4000 means it's 729 * a path of shorts instead of FLOAT. 730 * 731 * Combining Ops are stored in reverse order from when they were constructed; 732 * the output is a tree where the left side combining area is always taken 733 * first. 734 */ 735 GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, 736 UINT *needed) 737 { 738 struct region_data_header *region_data_header; 739 UINT required; 740 741 TRACE("%p, %p, %d, %p\n", region, buffer, size, needed); 742 743 if (!region || !buffer || !size) 744 return InvalidParameter; 745 746 required = FIELD_OFFSET(struct region_data_header, header) + write_region_data(region, NULL); 747 if (size < required) 748 { 749 if (needed) *needed = size; 750 return InsufficientBuffer; 751 } 752 753 region_data_header = (struct region_data_header *)buffer; 754 region_data_header->size = write_region_data(region, ®ion_data_header->header); 755 region_data_header->checksum = 0; 756 757 if (needed) 758 *needed = required; 759 760 return Ok; 761 } 762 763 static GpStatus read_element(struct memory_buffer *mbuf, GpRegion *region, region_element *node, INT *count) 764 { 765 GpStatus status; 766 const DWORD *type; 767 768 type = buffer_read(mbuf, sizeof(*type)); 769 if (!type) return Ok; 770 771 TRACE("type %#x\n", *type); 772 773 node->type = *type; 774 775 switch (node->type) 776 { 777 case CombineModeReplace: 778 case CombineModeIntersect: 779 case CombineModeUnion: 780 case CombineModeXor: 781 case CombineModeExclude: 782 case CombineModeComplement: 783 { 784 region_element *left, *right; 785 786 left = heap_alloc_zero(sizeof(region_element)); 787 if (!left) return OutOfMemory; 788 right = heap_alloc_zero(sizeof(region_element)); 789 if (!right) 790 { 791 heap_free(left); 792 return OutOfMemory; 793 } 794 795 status = read_element(mbuf, region, left, count); 796 if (status == Ok) 797 { 798 status = read_element(mbuf, region, right, count); 799 if (status == Ok) 800 { 801 node->elementdata.combine.left = left; 802 node->elementdata.combine.right = right; 803 region->num_children += 2; 804 return Ok; 805 } 806 } 807 808 heap_free(left); 809 heap_free(right); 810 return status; 811 } 812 813 case RegionDataRect: 814 { 815 const GpRectF *rc; 816 817 rc = buffer_read(mbuf, sizeof(*rc)); 818 if (!rc) 819 { 820 ERR("failed to read rect data\n"); 821 return InvalidParameter; 822 } 823 824 node->elementdata.rect = *rc; 825 *count += 1; 826 return Ok; 827 } 828 829 case RegionDataPath: 830 { 831 GpPath *path; 832 const struct path_header *path_header; 833 const BYTE *types; 834 835 path_header = buffer_read(mbuf, sizeof(*path_header)); 836 if (!path_header) 837 { 838 ERR("failed to read path header\n"); 839 return InvalidParameter; 840 } 841 if (!VALID_MAGIC(path_header->magic)) 842 { 843 ERR("invalid path header magic %#x\n", path_header->magic); 844 return InvalidParameter; 845 } 846 847 /* Windows always fails to create an empty path in a region */ 848 if (!path_header->count) 849 { 850 TRACE("refusing to create an empty path in a region\n"); 851 return GenericError; 852 } 853 854 status = GdipCreatePath(FillModeAlternate, &path); 855 if (status) return status; 856 857 node->elementdata.path = path; 858 859 if (!lengthen_path(path, path_header->count)) 860 return OutOfMemory; 861 862 path->pathdata.Count = path_header->count; 863 864 if (path_header->flags & ~FLAGS_INTPATH) 865 FIXME("unhandled path flags %#x\n", path_header->flags); 866 867 if (path_header->flags & FLAGS_INTPATH) 868 { 869 const packed_point *pt; 870 DWORD i; 871 872 pt = buffer_read(mbuf, sizeof(*pt) * path_header->count); 873 if (!pt) 874 { 875 ERR("failed to read packed %u path points\n", path_header->count); 876 return InvalidParameter; 877 } 878 879 for (i = 0; i < path_header->count; i++) 880 { 881 path->pathdata.Points[i].X = (REAL)pt[i].X; 882 path->pathdata.Points[i].Y = (REAL)pt[i].Y; 883 } 884 } 885 else 886 { 887 const GpPointF *ptf; 888 889 ptf = buffer_read(mbuf, sizeof(*ptf) * path_header->count); 890 if (!ptf) 891 { 892 ERR("failed to read %u path points\n", path_header->count); 893 return InvalidParameter; 894 } 895 memcpy(path->pathdata.Points, ptf, sizeof(*ptf) * path_header->count); 896 } 897 898 types = buffer_read(mbuf, path_header->count); 899 if (!types) 900 { 901 ERR("failed to read %u path types\n", path_header->count); 902 return InvalidParameter; 903 } 904 memcpy(path->pathdata.Types, types, path_header->count); 905 if (path_header->count & 3) 906 { 907 if (!buffer_read(mbuf, 4 - (path_header->count & 3))) 908 { 909 ERR("failed to read rounding %u bytes\n", 4 - (path_header->count & 3)); 910 return InvalidParameter; 911 } 912 } 913 914 *count += 1; 915 return Ok; 916 } 917 918 case RegionDataEmptyRect: 919 case RegionDataInfiniteRect: 920 *count += 1; 921 return Ok; 922 923 default: 924 FIXME("element type %#x is not supported\n", *type); 925 break; 926 } 927 928 return InvalidParameter; 929 } 930 931 /***************************************************************************** 932 * GdipCreateRegionRgnData [GDIPLUS.@] 933 */ 934 GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region) 935 { 936 const struct region_data_header *region_data_header; 937 struct memory_buffer mbuf; 938 GpStatus status; 939 INT count; 940 941 TRACE("(%p, %d, %p)\n", data, size, region); 942 943 if (!data || !size) 944 return InvalidParameter; 945 946 init_memory_buffer(&mbuf, data, size); 947 948 region_data_header = buffer_read(&mbuf, sizeof(*region_data_header)); 949 if (!region_data_header || !VALID_MAGIC(region_data_header->header.magic)) 950 return InvalidParameter; 951 952 status = GdipCreateRegion(region); 953 if (status != Ok) 954 return status; 955 956 count = 0; 957 status = read_element(&mbuf, *region, &(*region)->node, &count); 958 if (status == Ok && !count) 959 status = InvalidParameter; 960 961 if (status != Ok) 962 { 963 GdipDeleteRegion(*region); 964 *region = NULL; 965 } 966 967 return status; 968 } 969 970 /***************************************************************************** 971 * GdipGetRegionDataSize [GDIPLUS.@] 972 */ 973 GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed) 974 { 975 TRACE("%p, %p\n", region, needed); 976 977 if (!(region && needed)) 978 return InvalidParameter; 979 980 /* header.size doesn't count header.size and header.checksum */ 981 *needed = FIELD_OFFSET(struct region_data_header, header) + write_region_data(region, NULL); 982 983 return Ok; 984 } 985 986 static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn) 987 { 988 HDC new_hdc=NULL; 989 GpGraphics *new_graphics=NULL; 990 GpStatus stat; 991 INT save_state; 992 993 if (!path->pathdata.Count) /* PathToRegion doesn't support empty paths */ 994 { 995 *hrgn = CreateRectRgn( 0, 0, 0, 0 ); 996 return *hrgn ? Ok : OutOfMemory; 997 } 998 999 if (!graphics) 1000 { 1001 new_hdc = CreateCompatibleDC(0); 1002 if (!new_hdc) 1003 return OutOfMemory; 1004 1005 stat = GdipCreateFromHDC(new_hdc, &new_graphics); 1006 graphics = new_graphics; 1007 if (stat != Ok) 1008 { 1009 DeleteDC(new_hdc); 1010 return stat; 1011 } 1012 } 1013 else if (!graphics->hdc) 1014 { 1015 graphics->hdc = new_hdc = CreateCompatibleDC(0); 1016 if (!new_hdc) 1017 return OutOfMemory; 1018 } 1019 1020 save_state = SaveDC(graphics->hdc); 1021 EndPath(graphics->hdc); 1022 1023 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE 1024 : WINDING)); 1025 1026 gdi_transform_acquire(graphics); 1027 1028 stat = trace_path(graphics, path); 1029 if (stat == Ok) 1030 { 1031 *hrgn = PathToRegion(graphics->hdc); 1032 stat = *hrgn ? Ok : OutOfMemory; 1033 } 1034 1035 gdi_transform_release(graphics); 1036 1037 RestoreDC(graphics->hdc, save_state); 1038 if (new_hdc) 1039 { 1040 DeleteDC(new_hdc); 1041 if (new_graphics) 1042 GdipDeleteGraphics(new_graphics); 1043 else 1044 graphics->hdc = NULL; 1045 } 1046 1047 return stat; 1048 } 1049 1050 static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, HRGN *hrgn) 1051 { 1052 switch (element->type) 1053 { 1054 case RegionDataInfiniteRect: 1055 *hrgn = NULL; 1056 return Ok; 1057 case RegionDataEmptyRect: 1058 *hrgn = CreateRectRgn(0, 0, 0, 0); 1059 return *hrgn ? Ok : OutOfMemory; 1060 case RegionDataPath: 1061 return get_path_hrgn(element->elementdata.path, graphics, hrgn); 1062 case RegionDataRect: 1063 { 1064 GpPath* path; 1065 GpStatus stat; 1066 GpRectF* rc = &element->elementdata.rect; 1067 1068 stat = GdipCreatePath(FillModeAlternate, &path); 1069 if (stat != Ok) 1070 return stat; 1071 stat = GdipAddPathRectangle(path, rc->X, rc->Y, rc->Width, rc->Height); 1072 1073 if (stat == Ok) 1074 stat = get_path_hrgn(path, graphics, hrgn); 1075 1076 GdipDeletePath(path); 1077 1078 return stat; 1079 } 1080 case CombineModeIntersect: 1081 case CombineModeUnion: 1082 case CombineModeXor: 1083 case CombineModeExclude: 1084 case CombineModeComplement: 1085 { 1086 HRGN left, right; 1087 GpStatus stat; 1088 int ret; 1089 1090 stat = get_region_hrgn(element->elementdata.combine.left, graphics, &left); 1091 if (stat != Ok) 1092 { 1093 *hrgn = NULL; 1094 return stat; 1095 } 1096 1097 if (left == NULL) 1098 { 1099 /* existing region is infinite */ 1100 switch (element->type) 1101 { 1102 case CombineModeIntersect: 1103 return get_region_hrgn(element->elementdata.combine.right, graphics, hrgn); 1104 case CombineModeXor: case CombineModeExclude: 1105 left = CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22); 1106 break; 1107 case CombineModeUnion: case CombineModeComplement: 1108 *hrgn = NULL; 1109 return Ok; 1110 } 1111 } 1112 1113 stat = get_region_hrgn(element->elementdata.combine.right, graphics, &right); 1114 if (stat != Ok) 1115 { 1116 DeleteObject(left); 1117 *hrgn = NULL; 1118 return stat; 1119 } 1120 1121 if (right == NULL) 1122 { 1123 /* new region is infinite */ 1124 switch (element->type) 1125 { 1126 case CombineModeIntersect: 1127 *hrgn = left; 1128 return Ok; 1129 case CombineModeXor: case CombineModeComplement: 1130 right = CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22); 1131 break; 1132 case CombineModeUnion: case CombineModeExclude: 1133 DeleteObject(left); 1134 *hrgn = NULL; 1135 return Ok; 1136 } 1137 } 1138 1139 switch (element->type) 1140 { 1141 case CombineModeIntersect: 1142 ret = CombineRgn(left, left, right, RGN_AND); 1143 break; 1144 case CombineModeUnion: 1145 ret = CombineRgn(left, left, right, RGN_OR); 1146 break; 1147 case CombineModeXor: 1148 ret = CombineRgn(left, left, right, RGN_XOR); 1149 break; 1150 case CombineModeExclude: 1151 ret = CombineRgn(left, left, right, RGN_DIFF); 1152 break; 1153 case CombineModeComplement: 1154 ret = CombineRgn(left, right, left, RGN_DIFF); 1155 break; 1156 default: 1157 ret = ERROR; 1158 } 1159 1160 DeleteObject(right); 1161 1162 if (ret == ERROR) 1163 { 1164 DeleteObject(left); 1165 *hrgn = NULL; 1166 return GenericError; 1167 } 1168 1169 *hrgn = left; 1170 return Ok; 1171 } 1172 default: 1173 FIXME("GdipGetRegionHRgn unimplemented for region type=%x\n", element->type); 1174 *hrgn = NULL; 1175 return NotImplemented; 1176 } 1177 } 1178 1179 /***************************************************************************** 1180 * GdipGetRegionHRgn [GDIPLUS.@] 1181 */ 1182 GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HRGN *hrgn) 1183 { 1184 TRACE("(%p, %p, %p)\n", region, graphics, hrgn); 1185 1186 if (!region || !hrgn) 1187 return InvalidParameter; 1188 1189 return get_region_hrgn(®ion->node, graphics, hrgn); 1190 } 1191 1192 GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res) 1193 { 1194 GpStatus status; 1195 GpRectF rect; 1196 1197 TRACE("(%p, %p, %p)\n", region, graphics, res); 1198 1199 if(!region || !graphics || !res) 1200 return InvalidParameter; 1201 1202 status = GdipGetRegionBounds(region, graphics, &rect); 1203 if (status != Ok) return status; 1204 1205 *res = rect.Width == 0.0 && rect.Height == 0.0; 1206 TRACE("=> %d\n", *res); 1207 1208 return Ok; 1209 } 1210 1211 /***************************************************************************** 1212 * GdipIsEqualRegion [GDIPLUS.@] 1213 */ 1214 GpStatus WINGDIPAPI GdipIsEqualRegion(GpRegion *region, GpRegion *region2, GpGraphics *graphics, 1215 BOOL *res) 1216 { 1217 HRGN hrgn1, hrgn2; 1218 GpStatus stat; 1219 1220 TRACE("(%p, %p, %p, %p)\n", region, region2, graphics, res); 1221 1222 if(!region || !region2 || !graphics || !res) 1223 return InvalidParameter; 1224 1225 stat = GdipGetRegionHRgn(region, graphics, &hrgn1); 1226 if(stat != Ok) 1227 return stat; 1228 stat = GdipGetRegionHRgn(region2, graphics, &hrgn2); 1229 if(stat != Ok){ 1230 DeleteObject(hrgn1); 1231 return stat; 1232 } 1233 1234 *res = EqualRgn(hrgn1, hrgn2); 1235 1236 /* one of GpRegions is infinite */ 1237 if(*res == ERROR) 1238 *res = (!hrgn1 && !hrgn2); 1239 1240 DeleteObject(hrgn1); 1241 DeleteObject(hrgn2); 1242 1243 return Ok; 1244 } 1245 1246 /***************************************************************************** 1247 * GdipIsInfiniteRegion [GDIPLUS.@] 1248 */ 1249 GpStatus WINGDIPAPI GdipIsInfiniteRegion(GpRegion *region, GpGraphics *graphics, BOOL *res) 1250 { 1251 /* I think graphics is ignored here */ 1252 TRACE("(%p, %p, %p)\n", region, graphics, res); 1253 1254 if(!region || !graphics || !res) 1255 return InvalidParameter; 1256 1257 *res = (region->node.type == RegionDataInfiniteRect); 1258 1259 return Ok; 1260 } 1261 1262 /***************************************************************************** 1263 * GdipIsVisibleRegionRect [GDIPLUS.@] 1264 */ 1265 GpStatus WINGDIPAPI GdipIsVisibleRegionRect(GpRegion* region, REAL x, REAL y, REAL w, REAL h, GpGraphics *graphics, BOOL *res) 1266 { 1267 HRGN hrgn; 1268 GpStatus stat; 1269 RECT rect; 1270 1271 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %p, %p)\n", region, x, y, w, h, graphics, res); 1272 1273 if(!region || !res) 1274 return InvalidParameter; 1275 1276 if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok) 1277 return stat; 1278 1279 /* infinite */ 1280 if(!hrgn){ 1281 *res = TRUE; 1282 return Ok; 1283 } 1284 1285 SetRect(&rect, ceilr(x), ceilr(y), ceilr(x + w), ceilr(y + h)); 1286 *res = RectInRegion(hrgn, &rect); 1287 1288 DeleteObject(hrgn); 1289 1290 return Ok; 1291 } 1292 1293 /***************************************************************************** 1294 * GdipIsVisibleRegionRectI [GDIPLUS.@] 1295 */ 1296 GpStatus WINGDIPAPI GdipIsVisibleRegionRectI(GpRegion* region, INT x, INT y, INT w, INT h, GpGraphics *graphics, BOOL *res) 1297 { 1298 TRACE("(%p, %d, %d, %d, %d, %p, %p)\n", region, x, y, w, h, graphics, res); 1299 if(!region || !res) 1300 return InvalidParameter; 1301 1302 return GdipIsVisibleRegionRect(region, (REAL)x, (REAL)y, (REAL)w, (REAL)h, graphics, res); 1303 } 1304 1305 /***************************************************************************** 1306 * GdipIsVisibleRegionPoint [GDIPLUS.@] 1307 */ 1308 GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, GpGraphics *graphics, BOOL *res) 1309 { 1310 HRGN hrgn; 1311 GpStatus stat; 1312 1313 TRACE("(%p, %.2f, %.2f, %p, %p)\n", region, x, y, graphics, res); 1314 1315 if(!region || !res) 1316 return InvalidParameter; 1317 1318 if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok) 1319 return stat; 1320 1321 /* infinite */ 1322 if(!hrgn){ 1323 *res = TRUE; 1324 return Ok; 1325 } 1326 1327 *res = PtInRegion(hrgn, gdip_round(x), gdip_round(y)); 1328 1329 DeleteObject(hrgn); 1330 1331 return Ok; 1332 } 1333 1334 /***************************************************************************** 1335 * GdipIsVisibleRegionPointI [GDIPLUS.@] 1336 */ 1337 GpStatus WINGDIPAPI GdipIsVisibleRegionPointI(GpRegion* region, INT x, INT y, GpGraphics *graphics, BOOL *res) 1338 { 1339 TRACE("(%p, %d, %d, %p, %p)\n", region, x, y, graphics, res); 1340 1341 return GdipIsVisibleRegionPoint(region, (REAL)x, (REAL)y, graphics, res); 1342 } 1343 1344 /***************************************************************************** 1345 * GdipSetEmpty [GDIPLUS.@] 1346 */ 1347 GpStatus WINGDIPAPI GdipSetEmpty(GpRegion *region) 1348 { 1349 GpStatus stat; 1350 1351 TRACE("%p\n", region); 1352 1353 if (!region) 1354 return InvalidParameter; 1355 1356 delete_element(®ion->node); 1357 stat = init_region(region, RegionDataEmptyRect); 1358 1359 return stat; 1360 } 1361 1362 GpStatus WINGDIPAPI GdipSetInfinite(GpRegion *region) 1363 { 1364 GpStatus stat; 1365 1366 TRACE("%p\n", region); 1367 1368 if (!region) 1369 return InvalidParameter; 1370 1371 delete_element(®ion->node); 1372 stat = init_region(region, RegionDataInfiniteRect); 1373 1374 return stat; 1375 } 1376 1377 /* Transforms GpRegion elements with given matrix */ 1378 static GpStatus transform_region_element(region_element* element, GpMatrix *matrix) 1379 { 1380 GpStatus stat; 1381 1382 switch(element->type) 1383 { 1384 case RegionDataEmptyRect: 1385 case RegionDataInfiniteRect: 1386 return Ok; 1387 case RegionDataRect: 1388 { 1389 /* We can't transform a rectangle, so convert it to a path. */ 1390 GpRegion *new_region; 1391 GpPath *path; 1392 1393 stat = GdipCreatePath(FillModeAlternate, &path); 1394 if (stat == Ok) 1395 { 1396 stat = GdipAddPathRectangle(path, 1397 element->elementdata.rect.X, element->elementdata.rect.Y, 1398 element->elementdata.rect.Width, element->elementdata.rect.Height); 1399 1400 if (stat == Ok) 1401 stat = GdipCreateRegionPath(path, &new_region); 1402 1403 GdipDeletePath(path); 1404 } 1405 1406 if (stat == Ok) 1407 { 1408 /* Steal the element from the created region. */ 1409 memcpy(element, &new_region->node, sizeof(region_element)); 1410 heap_free(new_region); 1411 } 1412 else 1413 return stat; 1414 } 1415 /* Fall-through to do the actual conversion. */ 1416 case RegionDataPath: 1417 if (!element->elementdata.path->pathdata.Count) 1418 return Ok; 1419 1420 stat = GdipTransformMatrixPoints(matrix, 1421 element->elementdata.path->pathdata.Points, 1422 element->elementdata.path->pathdata.Count); 1423 return stat; 1424 default: 1425 stat = transform_region_element(element->elementdata.combine.left, matrix); 1426 if (stat == Ok) 1427 stat = transform_region_element(element->elementdata.combine.right, matrix); 1428 return stat; 1429 } 1430 } 1431 1432 GpStatus WINGDIPAPI GdipTransformRegion(GpRegion *region, GpMatrix *matrix) 1433 { 1434 TRACE("(%p, %p)\n", region, matrix); 1435 1436 if (!region || !matrix) 1437 return InvalidParameter; 1438 1439 return transform_region_element(®ion->node, matrix); 1440 } 1441 1442 /* Translates GpRegion elements with specified offsets */ 1443 static void translate_region_element(region_element* element, REAL dx, REAL dy) 1444 { 1445 INT i; 1446 1447 switch(element->type) 1448 { 1449 case RegionDataEmptyRect: 1450 case RegionDataInfiniteRect: 1451 return; 1452 case RegionDataRect: 1453 element->elementdata.rect.X += dx; 1454 element->elementdata.rect.Y += dy; 1455 return; 1456 case RegionDataPath: 1457 for(i = 0; i < element->elementdata.path->pathdata.Count; i++){ 1458 element->elementdata.path->pathdata.Points[i].X += dx; 1459 element->elementdata.path->pathdata.Points[i].Y += dy; 1460 } 1461 return; 1462 default: 1463 translate_region_element(element->elementdata.combine.left, dx, dy); 1464 translate_region_element(element->elementdata.combine.right, dx, dy); 1465 return; 1466 } 1467 } 1468 1469 /***************************************************************************** 1470 * GdipTranslateRegion [GDIPLUS.@] 1471 */ 1472 GpStatus WINGDIPAPI GdipTranslateRegion(GpRegion *region, REAL dx, REAL dy) 1473 { 1474 TRACE("(%p, %f, %f)\n", region, dx, dy); 1475 1476 if(!region) 1477 return InvalidParameter; 1478 1479 translate_region_element(®ion->node, dx, dy); 1480 1481 return Ok; 1482 } 1483 1484 /***************************************************************************** 1485 * GdipTranslateRegionI [GDIPLUS.@] 1486 */ 1487 GpStatus WINGDIPAPI GdipTranslateRegionI(GpRegion *region, INT dx, INT dy) 1488 { 1489 TRACE("(%p, %d, %d)\n", region, dx, dy); 1490 1491 return GdipTranslateRegion(region, (REAL)dx, (REAL)dy); 1492 } 1493 1494 static GpStatus get_region_scans_data(GpRegion *region, GpMatrix *matrix, LPRGNDATA *data) 1495 { 1496 GpRegion *region_copy; 1497 GpStatus stat; 1498 HRGN hrgn; 1499 DWORD data_size; 1500 1501 stat = GdipCloneRegion(region, ®ion_copy); 1502 1503 if (stat == Ok) 1504 { 1505 stat = GdipTransformRegion(region_copy, matrix); 1506 1507 if (stat == Ok) 1508 stat = GdipGetRegionHRgn(region_copy, NULL, &hrgn); 1509 1510 if (stat == Ok) 1511 { 1512 if (hrgn) 1513 { 1514 data_size = GetRegionData(hrgn, 0, NULL); 1515 1516 *data = heap_alloc_zero(data_size); 1517 1518 if (*data) 1519 GetRegionData(hrgn, data_size, *data); 1520 else 1521 stat = OutOfMemory; 1522 1523 DeleteObject(hrgn); 1524 } 1525 else 1526 { 1527 data_size = sizeof(RGNDATAHEADER) + sizeof(RECT); 1528 1529 *data = heap_alloc_zero(data_size); 1530 1531 if (*data) 1532 { 1533 (*data)->rdh.dwSize = sizeof(RGNDATAHEADER); 1534 (*data)->rdh.iType = RDH_RECTANGLES; 1535 (*data)->rdh.nCount = 1; 1536 (*data)->rdh.nRgnSize = sizeof(RECT); 1537 (*data)->rdh.rcBound.left = (*data)->rdh.rcBound.top = -0x400000; 1538 (*data)->rdh.rcBound.right = (*data)->rdh.rcBound.bottom = 0x400000; 1539 1540 memcpy((*data)->Buffer, &(*data)->rdh.rcBound, sizeof(RECT)); 1541 } 1542 else 1543 stat = OutOfMemory; 1544 } 1545 } 1546 1547 GdipDeleteRegion(region_copy); 1548 } 1549 1550 return stat; 1551 } 1552 1553 GpStatus WINGDIPAPI GdipGetRegionScansCount(GpRegion *region, UINT *count, GpMatrix *matrix) 1554 { 1555 GpStatus stat; 1556 LPRGNDATA data; 1557 1558 TRACE("(%p, %p, %p)\n", region, count, matrix); 1559 1560 if (!region || !count || !matrix) 1561 return InvalidParameter; 1562 1563 stat = get_region_scans_data(region, matrix, &data); 1564 1565 if (stat == Ok) 1566 { 1567 *count = data->rdh.nCount; 1568 heap_free(data); 1569 } 1570 1571 return stat; 1572 } 1573 1574 GpStatus WINGDIPAPI GdipGetRegionScansI(GpRegion *region, GpRect *scans, INT *count, GpMatrix *matrix) 1575 { 1576 GpStatus stat; 1577 DWORD i; 1578 LPRGNDATA data; 1579 RECT *rects; 1580 1581 if (!region || !count || !matrix) 1582 return InvalidParameter; 1583 1584 stat = get_region_scans_data(region, matrix, &data); 1585 1586 if (stat == Ok) 1587 { 1588 *count = data->rdh.nCount; 1589 rects = (RECT*)data->Buffer; 1590 1591 if (scans) 1592 { 1593 for (i=0; i<data->rdh.nCount; i++) 1594 { 1595 scans[i].X = rects[i].left; 1596 scans[i].Y = rects[i].top; 1597 scans[i].Width = rects[i].right - rects[i].left; 1598 scans[i].Height = rects[i].bottom - rects[i].top; 1599 } 1600 } 1601 1602 heap_free(data); 1603 } 1604 1605 return Ok; 1606 } 1607 1608 GpStatus WINGDIPAPI GdipGetRegionScans(GpRegion *region, GpRectF *scans, INT *count, GpMatrix *matrix) 1609 { 1610 GpStatus stat; 1611 DWORD i; 1612 LPRGNDATA data; 1613 RECT *rects; 1614 1615 if (!region || !count || !matrix) 1616 return InvalidParameter; 1617 1618 stat = get_region_scans_data(region, matrix, &data); 1619 1620 if (stat == Ok) 1621 { 1622 *count = data->rdh.nCount; 1623 rects = (RECT*)data->Buffer; 1624 1625 if (scans) 1626 { 1627 for (i=0; i<data->rdh.nCount; i++) 1628 { 1629 scans[i].X = rects[i].left; 1630 scans[i].Y = rects[i].top; 1631 scans[i].Width = rects[i].right - rects[i].left; 1632 scans[i].Height = rects[i].bottom - rects[i].top; 1633 } 1634 } 1635 1636 heap_free(data); 1637 } 1638 1639 return Ok; 1640 } 1641