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