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