xref: /reactos/dll/win32/gdiplus/region.c (revision 84ccccab)
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(&region->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(&region->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(&region->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, &region->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, &reg2copy);
355         if(stat != Ok)  return stat;
356 
357         delete_element(&region1->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(&region2->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(&region->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(&region->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(&region->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, &region_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(&region->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(&region->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(&region->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(&region->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(&region->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, &region_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