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