xref: /reactos/dll/win32/gdiplus/region.c (revision 8e659192)
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(&region->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(&region->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(&region->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, &region->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, &reg2copy);
349         if(stat != Ok)  return stat;
350 
351         delete_element(&region1->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(&region2->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(&region->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(&region->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(&region->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, &region_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(&region->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(&region->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(&region->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(&region->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(&region->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, &region_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