wininet/tests: Update test data to match the new certificate on winehq.org.
[wine.git] / dlls / gdiplus / region.c
blob4ba86eb73b87bf7d095d81e94085afe8918440cc
1 /*
2 * Copyright (C) 2008 Google (Lei Zhang)
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wingdi.h"
25 #include "objbase.h"
27 #include "gdiplus.h"
28 #include "gdiplus_private.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
33 /**********************************************************
35 * Data returned by GdipGetRegionData looks something like this:
37 * struct region_data_header
38 * {
39 * DWORD size; size in bytes of the data - 8.
40 * DWORD magic1; probably a checksum.
41 * DWORD magic2; always seems to be 0xdbc01001 - version?
42 * DWORD num_ops; number of combining ops * 2
43 * };
45 * Then follows a sequence of combining ops and region elements.
47 * A region element is either a RECTF or some path data.
49 * Combining ops are just stored as their CombineMode value.
51 * Each RECTF is preceded by the DWORD 0x10000000. An empty rect is
52 * stored as 0x10000002 (with no following RECTF) and an infinite rect
53 * is stored as 0x10000003 (again with no following RECTF).
55 * Path data is preceded by the DWORD 0x10000001. Then follows a
56 * DWORD size and then size bytes of data.
58 * The combining ops are stored in the reverse order to the region
59 * elements and in the reverse order to which the region was
60 * constructed.
62 * When two or more complex regions (ie those with more than one
63 * element) are combined, the combining op for the two regions comes
64 * first, then the combining ops for the region elements in region 1,
65 * followed by the region elements for region 1, then follows the
66 * combining ops for region 2 and finally region 2's region elements.
67 * Presumably you're supposed to use the 0x1000000x header to find the
68 * end of the op list (the count of the elements in each region is not
69 * stored).
71 * When a simple region (1 element) is combined, it's treated as if a
72 * single rect/path is being combined.
76 #define FLAGS_NOFLAGS 0x0
77 #define FLAGS_INTPATH 0x4000
79 /* Header size as far as header->size is concerned. This doesn't include
80 * header->size or header->checksum
82 static const INT sizeheader_size = sizeof(DWORD) * 2;
84 typedef struct packed_point
86 short X;
87 short Y;
88 } packed_point;
90 /* Test to see if the path could be stored as an array of shorts */
91 static BOOL is_integer_path(const GpPath *path)
93 int i;
95 if (!path->pathdata.Count) return FALSE;
97 for (i = 0; i < path->pathdata.Count; i++)
99 short x, y;
100 x = gdip_round(path->pathdata.Points[i].X);
101 y = gdip_round(path->pathdata.Points[i].Y);
102 if (path->pathdata.Points[i].X != (REAL)x || path->pathdata.Points[i].Y != (REAL)y)
103 return FALSE;
105 return TRUE;
108 /* Everything is measured in DWORDS; round up if there's a remainder */
109 static inline INT get_pathtypes_size(const GpPath* path)
111 INT needed = path->pathdata.Count / sizeof(DWORD);
113 if (path->pathdata.Count % sizeof(DWORD) > 0)
114 needed++;
116 return needed * sizeof(DWORD);
119 static inline INT get_element_size(const region_element* element)
121 INT needed = sizeof(DWORD); /* DWORD for the type */
122 switch(element->type)
124 case RegionDataRect:
125 return needed + sizeof(GpRect);
126 case RegionDataPath:
128 const GpPath *path = element->elementdata.path;
129 DWORD flags = is_integer_path(path) ? FLAGS_INTPATH : FLAGS_NOFLAGS;
130 /* 3 for headers, once again size doesn't count itself */
131 needed += sizeof(DWORD) * 3;
132 if (flags & FLAGS_INTPATH)
133 needed += 2 * sizeof(SHORT) * path->pathdata.Count;
134 else
135 needed += 2 * sizeof(FLOAT) * path->pathdata.Count;
137 needed += get_pathtypes_size(path);
138 needed += sizeof(DWORD); /* Extra DWORD for pathheader.size */
139 return needed;
141 case RegionDataEmptyRect:
142 case RegionDataInfiniteRect:
143 return needed;
144 default:
145 needed += get_element_size(element->elementdata.combine.left);
146 needed += get_element_size(element->elementdata.combine.right);
147 return needed;
150 return 0;
153 /* Does not check parameters, caller must do that */
154 static inline GpStatus init_region(GpRegion* region, const RegionType type)
156 region->node.type = type;
157 region->num_children = 0;
159 return Ok;
162 static inline GpStatus clone_element(const region_element* element,
163 region_element** element2)
165 GpStatus stat;
167 /* root node is allocated with GpRegion */
168 if(!*element2){
169 *element2 = GdipAlloc(sizeof(region_element));
170 if (!*element2)
171 return OutOfMemory;
174 (*element2)->type = element->type;
176 switch (element->type)
178 case RegionDataRect:
179 (*element2)->elementdata.rect = element->elementdata.rect;
180 return Ok;
181 case RegionDataEmptyRect:
182 case RegionDataInfiniteRect:
183 return Ok;
184 case RegionDataPath:
185 stat = GdipClonePath(element->elementdata.path, &(*element2)->elementdata.path);
186 if (stat == Ok) return Ok;
187 break;
188 default:
189 (*element2)->elementdata.combine.left = NULL;
190 (*element2)->elementdata.combine.right = NULL;
192 stat = clone_element(element->elementdata.combine.left,
193 &(*element2)->elementdata.combine.left);
194 if (stat == Ok)
196 stat = clone_element(element->elementdata.combine.right,
197 &(*element2)->elementdata.combine.right);
198 if (stat == Ok) return Ok;
200 break;
203 delete_element(*element2);
204 *element2 = NULL;
205 return stat;
208 /* Common code for CombineRegion*
209 * All the caller has to do is get its format into an element
211 static inline void fuse_region(GpRegion* region, region_element* left,
212 region_element* right, const CombineMode mode)
214 region->node.type = mode;
215 region->node.elementdata.combine.left = left;
216 region->node.elementdata.combine.right = right;
217 region->num_children += 2;
220 /*****************************************************************************
221 * GdipCloneRegion [GDIPLUS.@]
223 * Creates a deep copy of the region
225 * PARAMS
226 * region [I] source region
227 * clone [O] resulting clone
229 * RETURNS
230 * SUCCESS: Ok
231 * FAILURE: InvalidParameter or OutOfMemory
233 GpStatus WINGDIPAPI GdipCloneRegion(GpRegion *region, GpRegion **clone)
235 region_element *element;
237 TRACE("%p %p\n", region, clone);
239 if (!(region && clone))
240 return InvalidParameter;
242 *clone = GdipAlloc(sizeof(GpRegion));
243 if (!*clone)
244 return OutOfMemory;
245 element = &(*clone)->node;
247 (*clone)->num_children = region->num_children;
248 return clone_element(&region->node, &element);
251 /*****************************************************************************
252 * GdipCombineRegionPath [GDIPLUS.@]
254 GpStatus WINGDIPAPI GdipCombineRegionPath(GpRegion *region, GpPath *path, CombineMode mode)
256 GpRegion *path_region;
257 region_element *left, *right = NULL;
258 GpStatus stat;
260 TRACE("%p %p %d\n", region, path, mode);
262 if (!(region && path))
263 return InvalidParameter;
265 stat = GdipCreateRegionPath(path, &path_region);
266 if (stat != Ok)
267 return stat;
269 /* simply replace region data */
270 if(mode == CombineModeReplace){
271 delete_element(&region->node);
272 memcpy(region, path_region, sizeof(GpRegion));
273 GdipFree(path_region);
274 return Ok;
277 left = GdipAlloc(sizeof(region_element));
278 if (left)
280 *left = region->node;
281 stat = clone_element(&path_region->node, &right);
282 if (stat == Ok)
284 fuse_region(region, left, right, mode);
285 GdipDeleteRegion(path_region);
286 return Ok;
289 else
290 stat = OutOfMemory;
292 GdipFree(left);
293 GdipDeleteRegion(path_region);
294 return stat;
297 /*****************************************************************************
298 * GdipCombineRegionRect [GDIPLUS.@]
300 GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region,
301 GDIPCONST GpRectF *rect, CombineMode mode)
303 GpRegion *rect_region;
304 region_element *left, *right = NULL;
305 GpStatus stat;
307 TRACE("%p %s %d\n", region, debugstr_rectf(rect), mode);
309 if (!(region && rect))
310 return InvalidParameter;
312 stat = GdipCreateRegionRect(rect, &rect_region);
313 if (stat != Ok)
314 return stat;
316 /* simply replace region data */
317 if(mode == CombineModeReplace){
318 delete_element(&region->node);
319 memcpy(region, rect_region, sizeof(GpRegion));
320 GdipFree(rect_region);
321 return Ok;
324 left = GdipAlloc(sizeof(region_element));
325 if (left)
327 memcpy(left, &region->node, sizeof(region_element));
328 stat = clone_element(&rect_region->node, &right);
329 if (stat == Ok)
331 fuse_region(region, left, right, mode);
332 GdipDeleteRegion(rect_region);
333 return Ok;
336 else
337 stat = OutOfMemory;
339 GdipFree(left);
340 GdipDeleteRegion(rect_region);
341 return stat;
344 /*****************************************************************************
345 * GdipCombineRegionRectI [GDIPLUS.@]
347 GpStatus WINGDIPAPI GdipCombineRegionRectI(GpRegion *region,
348 GDIPCONST GpRect *rect, CombineMode mode)
350 GpRectF rectf;
352 TRACE("%p %p %d\n", region, rect, mode);
354 if (!rect)
355 return InvalidParameter;
357 rectf.X = (REAL)rect->X;
358 rectf.Y = (REAL)rect->Y;
359 rectf.Height = (REAL)rect->Height;
360 rectf.Width = (REAL)rect->Width;
362 return GdipCombineRegionRect(region, &rectf, mode);
365 /*****************************************************************************
366 * GdipCombineRegionRegion [GDIPLUS.@]
368 GpStatus WINGDIPAPI GdipCombineRegionRegion(GpRegion *region1,
369 GpRegion *region2, CombineMode mode)
371 region_element *left, *right = NULL;
372 GpStatus stat;
373 GpRegion *reg2copy;
375 TRACE("%p %p %d\n", region1, region2, mode);
377 if(!(region1 && region2))
378 return InvalidParameter;
380 /* simply replace region data */
381 if(mode == CombineModeReplace){
382 stat = GdipCloneRegion(region2, &reg2copy);
383 if(stat != Ok) return stat;
385 delete_element(&region1->node);
386 memcpy(region1, reg2copy, sizeof(GpRegion));
387 GdipFree(reg2copy);
388 return Ok;
391 left = GdipAlloc(sizeof(region_element));
392 if (!left)
393 return OutOfMemory;
395 *left = region1->node;
396 stat = clone_element(&region2->node, &right);
397 if (stat != Ok)
399 GdipFree(left);
400 return OutOfMemory;
403 fuse_region(region1, left, right, mode);
404 region1->num_children += region2->num_children;
406 return Ok;
409 /*****************************************************************************
410 * GdipCreateRegion [GDIPLUS.@]
412 GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region)
414 TRACE("%p\n", region);
416 if(!region)
417 return InvalidParameter;
419 *region = GdipAlloc(sizeof(GpRegion));
420 if(!*region)
421 return OutOfMemory;
423 TRACE("=> %p\n", *region);
425 return init_region(*region, RegionDataInfiniteRect);
428 /*****************************************************************************
429 * GdipCreateRegionPath [GDIPLUS.@]
431 * Creates a GpRegion from a GpPath
433 * PARAMS
434 * path [I] path to base the region on
435 * region [O] pointer to the newly allocated region
437 * RETURNS
438 * SUCCESS: Ok
439 * FAILURE: InvalidParameter
441 * NOTES
442 * If a path has no floating point points, its points will be stored as shorts
443 * (INTPATH)
445 * If a path is empty, it is considered to be an INTPATH
447 GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region)
449 region_element* element;
450 GpStatus stat;
452 TRACE("%p, %p\n", path, region);
454 if (!(path && region))
455 return InvalidParameter;
457 *region = GdipAlloc(sizeof(GpRegion));
458 if(!*region)
459 return OutOfMemory;
460 stat = init_region(*region, RegionDataPath);
461 if (stat != Ok)
463 GdipDeleteRegion(*region);
464 return stat;
466 element = &(*region)->node;
468 stat = GdipClonePath(path, &element->elementdata.path);
469 if (stat != Ok)
471 GdipDeleteRegion(*region);
472 return stat;
475 return Ok;
478 /*****************************************************************************
479 * GdipCreateRegionRect [GDIPLUS.@]
481 GpStatus WINGDIPAPI GdipCreateRegionRect(GDIPCONST GpRectF *rect,
482 GpRegion **region)
484 GpStatus stat;
486 TRACE("%p, %p\n", rect, region);
488 if (!(rect && region))
489 return InvalidParameter;
491 *region = GdipAlloc(sizeof(GpRegion));
492 stat = init_region(*region, RegionDataRect);
493 if(stat != Ok)
495 GdipDeleteRegion(*region);
496 return stat;
499 (*region)->node.elementdata.rect.X = rect->X;
500 (*region)->node.elementdata.rect.Y = rect->Y;
501 (*region)->node.elementdata.rect.Width = rect->Width;
502 (*region)->node.elementdata.rect.Height = rect->Height;
504 return Ok;
507 /*****************************************************************************
508 * GdipCreateRegionRectI [GDIPLUS.@]
510 GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect,
511 GpRegion **region)
513 GpRectF rectf;
515 TRACE("%p, %p\n", rect, region);
517 rectf.X = (REAL)rect->X;
518 rectf.Y = (REAL)rect->Y;
519 rectf.Width = (REAL)rect->Width;
520 rectf.Height = (REAL)rect->Height;
522 return GdipCreateRegionRect(&rectf, region);
525 GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region)
527 FIXME("(%p, %d, %p): stub\n", data, size, region);
529 *region = NULL;
530 return NotImplemented;
534 /******************************************************************************
535 * GdipCreateRegionHrgn [GDIPLUS.@]
537 GpStatus WINGDIPAPI GdipCreateRegionHrgn(HRGN hrgn, GpRegion **region)
539 DWORD size;
540 LPRGNDATA buf;
541 LPRECT rect;
542 GpStatus stat;
543 GpPath* path;
544 GpRegion* local;
545 DWORD i;
547 TRACE("(%p, %p)\n", hrgn, region);
549 if(!region || !(size = GetRegionData(hrgn, 0, NULL)))
550 return InvalidParameter;
552 buf = GdipAlloc(size);
553 if(!buf)
554 return OutOfMemory;
556 if(!GetRegionData(hrgn, size, buf)){
557 GdipFree(buf);
558 return GenericError;
561 if(buf->rdh.nCount == 0){
562 if((stat = GdipCreateRegion(&local)) != Ok){
563 GdipFree(buf);
564 return stat;
566 if((stat = GdipSetEmpty(local)) != Ok){
567 GdipFree(buf);
568 GdipDeleteRegion(local);
569 return stat;
571 *region = local;
572 GdipFree(buf);
573 return Ok;
576 if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok){
577 GdipFree(buf);
578 return stat;
581 rect = (LPRECT)buf->Buffer;
582 for(i = 0; i < buf->rdh.nCount; i++){
583 if((stat = GdipAddPathRectangle(path, (REAL)rect->left, (REAL)rect->top,
584 (REAL)(rect->right - rect->left), (REAL)(rect->bottom - rect->top))) != Ok){
585 GdipFree(buf);
586 GdipDeletePath(path);
587 return stat;
589 rect++;
592 stat = GdipCreateRegionPath(path, region);
594 GdipFree(buf);
595 GdipDeletePath(path);
596 return stat;
599 /*****************************************************************************
600 * GdipDeleteRegion [GDIPLUS.@]
602 GpStatus WINGDIPAPI GdipDeleteRegion(GpRegion *region)
604 TRACE("%p\n", region);
606 if (!region)
607 return InvalidParameter;
609 delete_element(&region->node);
610 GdipFree(region);
612 return Ok;
615 /*****************************************************************************
616 * GdipGetRegionBounds [GDIPLUS.@]
618 GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect)
620 HRGN hrgn;
621 RECT r;
622 GpStatus status;
624 TRACE("(%p, %p, %p)\n", region, graphics, rect);
626 if(!region || !graphics || !rect)
627 return InvalidParameter;
629 /* Contrary to MSDN, native ignores the graphics transform. */
630 status = GdipGetRegionHRgn(region, NULL, &hrgn);
631 if(status != Ok)
632 return status;
634 /* infinite */
635 if(!hrgn){
636 rect->X = rect->Y = -(REAL)(1 << 22);
637 rect->Width = rect->Height = (REAL)(1 << 23);
638 TRACE("%p => infinite\n", region);
639 return Ok;
642 if(GetRgnBox(hrgn, &r)){
643 rect->X = r.left;
644 rect->Y = r.top;
645 rect->Width = r.right - r.left;
646 rect->Height = r.bottom - r.top;
647 TRACE("%p => %s\n", region, debugstr_rectf(rect));
649 else
650 status = GenericError;
652 DeleteObject(hrgn);
654 return status;
657 /*****************************************************************************
658 * GdipGetRegionBoundsI [GDIPLUS.@]
660 GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics, GpRect *rect)
662 GpRectF rectf;
663 GpStatus status;
665 TRACE("(%p, %p, %p)\n", region, graphics, rect);
667 if(!rect)
668 return InvalidParameter;
670 status = GdipGetRegionBounds(region, graphics, &rectf);
671 if(status == Ok){
672 rect->X = gdip_round(rectf.X);
673 rect->Y = gdip_round(rectf.Y);
674 rect->Width = gdip_round(rectf.Width);
675 rect->Height = gdip_round(rectf.Height);
678 return status;
681 static inline void write_dword(DWORD* location, INT* offset, const DWORD write)
683 location[*offset] = write;
684 (*offset)++;
687 static inline void write_float(DWORD* location, INT* offset, const FLOAT write)
689 ((FLOAT*)location)[*offset] = write;
690 (*offset)++;
693 static inline void write_packed_point(DWORD* location, INT* offset,
694 const GpPointF* write)
696 packed_point *point = (packed_point *)(location + *offset);
697 point->X = gdip_round(write->X);
698 point->Y = gdip_round(write->Y);
699 (*offset)++;
702 static inline void write_path_types(DWORD* location, INT* offset,
703 const GpPath* path)
705 INT rounded_size = get_pathtypes_size(path);
707 memcpy(location + *offset, path->pathdata.Types, path->pathdata.Count);
709 /* The unwritten parts of the DWORD (if any) must be cleared */
710 if (rounded_size - path->pathdata.Count)
711 ZeroMemory(((BYTE*)location) + (*offset * sizeof(DWORD)) +
712 path->pathdata.Count, rounded_size - path->pathdata.Count);
713 *offset += rounded_size / sizeof(DWORD);
716 static void write_element(const region_element* element, DWORD *buffer,
717 INT* filled)
719 write_dword(buffer, filled, element->type);
720 switch (element->type)
722 case CombineModeReplace:
723 case CombineModeIntersect:
724 case CombineModeUnion:
725 case CombineModeXor:
726 case CombineModeExclude:
727 case CombineModeComplement:
728 write_element(element->elementdata.combine.left, buffer, filled);
729 write_element(element->elementdata.combine.right, buffer, filled);
730 break;
731 case RegionDataRect:
732 write_float(buffer, filled, element->elementdata.rect.X);
733 write_float(buffer, filled, element->elementdata.rect.Y);
734 write_float(buffer, filled, element->elementdata.rect.Width);
735 write_float(buffer, filled, element->elementdata.rect.Height);
736 break;
737 case RegionDataPath:
739 INT i;
740 const GpPath* path = element->elementdata.path;
741 struct _pathheader
743 DWORD size;
744 DWORD magic;
745 DWORD count;
746 DWORD flags;
747 } *pathheader;
749 pathheader = (struct _pathheader *)(buffer + *filled);
751 pathheader->flags = is_integer_path(path) ? FLAGS_INTPATH : FLAGS_NOFLAGS;
752 /* 3 for headers, once again size doesn't count itself */
753 pathheader->size = sizeof(DWORD) * 3;
754 if (pathheader->flags & FLAGS_INTPATH)
755 pathheader->size += 2 * sizeof(SHORT) * path->pathdata.Count;
756 else
757 pathheader->size += 2 * sizeof(FLOAT) * path->pathdata.Count;
758 pathheader->size += get_pathtypes_size(path);
759 pathheader->magic = VERSION_MAGIC;
760 pathheader->count = path->pathdata.Count;
762 *filled += 4;
764 switch (pathheader->flags & FLAGS_INTPATH)
766 case FLAGS_NOFLAGS:
767 for (i = 0; i < path->pathdata.Count; i++)
769 write_float(buffer, filled, path->pathdata.Points[i].X);
770 write_float(buffer, filled, path->pathdata.Points[i].Y);
772 break;
773 case FLAGS_INTPATH:
774 for (i = 0; i < path->pathdata.Count; i++)
776 write_packed_point(buffer, filled,
777 &path->pathdata.Points[i]);
779 break;
781 write_path_types(buffer, filled, path);
782 break;
784 case RegionDataEmptyRect:
785 case RegionDataInfiniteRect:
786 break;
790 /*****************************************************************************
791 * GdipGetRegionData [GDIPLUS.@]
793 * Returns the header, followed by combining ops and region elements.
795 * PARAMS
796 * region [I] region to retrieve from
797 * buffer [O] buffer to hold the resulting data
798 * size [I] size of the buffer
799 * needed [O] (optional) how much data was written
801 * RETURNS
802 * SUCCESS: Ok
803 * FAILURE: InvalidParameter
805 * NOTES
806 * The header contains the size, a checksum, a version string, and the number
807 * of children. The size does not count itself or the checksum.
808 * Version is always something like 0xdbc01001 or 0xdbc01002
810 * An element is a RECT, or PATH; Combining ops are stored as their
811 * CombineMode value. Special regions (infinite, empty) emit just their
812 * op-code; GpRectFs emit their code followed by their points; GpPaths emit
813 * their code followed by a second header for the path followed by the actual
814 * path data. Followed by the flags for each point. The pathheader contains
815 * the size of the data to follow, a version number again, followed by a count
816 * of how many points, and any special flags which may apply. 0x4000 means its
817 * a path of shorts instead of FLOAT.
819 * Combining Ops are stored in reverse order from when they were constructed;
820 * the output is a tree where the left side combining area is always taken
821 * first.
823 GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
824 UINT *needed)
826 struct _region_header
828 DWORD size;
829 DWORD checksum;
830 DWORD magic;
831 DWORD num_children;
832 } *region_header;
833 INT filled = 0;
834 UINT required;
835 GpStatus status;
837 TRACE("%p, %p, %d, %p\n", region, buffer, size, needed);
839 if (!region || !buffer || !size)
840 return InvalidParameter;
842 status = GdipGetRegionDataSize(region, &required);
843 if (status != Ok) return status;
844 if (size < required)
846 if (needed) *needed = size;
847 return InsufficientBuffer;
850 region_header = (struct _region_header *)buffer;
851 region_header->size = sizeheader_size + get_element_size(&region->node);
852 region_header->checksum = 0;
853 region_header->magic = VERSION_MAGIC;
854 region_header->num_children = region->num_children;
855 filled += 4;
856 /* With few exceptions, everything written is DWORD aligned,
857 * so use that as our base */
858 write_element(&region->node, (DWORD*)buffer, &filled);
860 if (needed)
861 *needed = filled * sizeof(DWORD);
863 return Ok;
866 /*****************************************************************************
867 * GdipGetRegionDataSize [GDIPLUS.@]
869 GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed)
871 TRACE("%p, %p\n", region, needed);
873 if (!(region && needed))
874 return InvalidParameter;
876 /* header.size doesn't count header.size and header.checksum */
877 *needed = sizeof(DWORD) * 2 + sizeheader_size + get_element_size(&region->node);
879 return Ok;
882 static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn)
884 HDC new_hdc=NULL;
885 GpGraphics *new_graphics=NULL;
886 GpStatus stat;
887 INT save_state;
889 if (!graphics)
891 new_hdc = CreateCompatibleDC(0);
892 if (!new_hdc)
893 return OutOfMemory;
895 stat = GdipCreateFromHDC(new_hdc, &new_graphics);
896 graphics = new_graphics;
897 if (stat != Ok)
899 DeleteDC(new_hdc);
900 return stat;
903 else if (!graphics->hdc)
905 graphics->hdc = new_hdc = CreateCompatibleDC(0);
906 if (!new_hdc)
907 return OutOfMemory;
910 save_state = SaveDC(graphics->hdc);
911 EndPath(graphics->hdc);
913 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
914 : WINDING));
916 stat = trace_path(graphics, path);
917 if (stat == Ok)
919 *hrgn = PathToRegion(graphics->hdc);
920 stat = *hrgn ? Ok : OutOfMemory;
923 RestoreDC(graphics->hdc, save_state);
924 if (new_hdc)
926 DeleteDC(new_hdc);
927 if (new_graphics)
928 GdipDeleteGraphics(new_graphics);
929 else
930 graphics->hdc = NULL;
933 return stat;
936 static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, HRGN *hrgn)
938 switch (element->type)
940 case RegionDataInfiniteRect:
941 *hrgn = NULL;
942 return Ok;
943 case RegionDataEmptyRect:
944 *hrgn = CreateRectRgn(0, 0, 0, 0);
945 return *hrgn ? Ok : OutOfMemory;
946 case RegionDataPath:
947 return get_path_hrgn(element->elementdata.path, graphics, hrgn);
948 case RegionDataRect:
950 GpPath* path;
951 GpStatus stat;
952 GpRectF* rc = &element->elementdata.rect;
954 stat = GdipCreatePath(FillModeAlternate, &path);
955 if (stat != Ok)
956 return stat;
957 stat = GdipAddPathRectangle(path, rc->X, rc->Y, rc->Width, rc->Height);
959 if (stat == Ok)
960 stat = get_path_hrgn(path, graphics, hrgn);
962 GdipDeletePath(path);
964 return stat;
966 case CombineModeIntersect:
967 case CombineModeUnion:
968 case CombineModeXor:
969 case CombineModeExclude:
970 case CombineModeComplement:
972 HRGN left, right;
973 GpStatus stat;
974 int ret;
976 stat = get_region_hrgn(element->elementdata.combine.left, graphics, &left);
977 if (stat != Ok)
979 *hrgn = NULL;
980 return stat;
983 if (left == NULL)
985 /* existing region is infinite */
986 switch (element->type)
988 case CombineModeIntersect:
989 return get_region_hrgn(element->elementdata.combine.right, graphics, hrgn);
990 case CombineModeXor: case CombineModeExclude:
991 left = CreateRectRgn(-4194304, -4194304, 4194304, 4194304);
992 break;
993 case CombineModeUnion: case CombineModeComplement:
994 *hrgn = NULL;
995 return Ok;
999 stat = get_region_hrgn(element->elementdata.combine.right, graphics, &right);
1000 if (stat != Ok)
1002 DeleteObject(left);
1003 *hrgn = NULL;
1004 return stat;
1007 if (right == NULL)
1009 /* new region is infinite */
1010 switch (element->type)
1012 case CombineModeIntersect:
1013 *hrgn = left;
1014 return Ok;
1015 case CombineModeXor: case CombineModeComplement:
1016 right = CreateRectRgn(-4194304, -4194304, 4194304, 4194304);
1017 break;
1018 case CombineModeUnion: case CombineModeExclude:
1019 DeleteObject(left);
1020 *hrgn = NULL;
1021 return Ok;
1025 switch (element->type)
1027 case CombineModeIntersect:
1028 ret = CombineRgn(left, left, right, RGN_AND);
1029 break;
1030 case CombineModeUnion:
1031 ret = CombineRgn(left, left, right, RGN_OR);
1032 break;
1033 case CombineModeXor:
1034 ret = CombineRgn(left, left, right, RGN_XOR);
1035 break;
1036 case CombineModeExclude:
1037 ret = CombineRgn(left, left, right, RGN_DIFF);
1038 break;
1039 case CombineModeComplement:
1040 ret = CombineRgn(left, right, left, RGN_DIFF);
1041 break;
1042 default:
1043 ret = ERROR;
1046 DeleteObject(right);
1048 if (ret == ERROR)
1050 DeleteObject(left);
1051 *hrgn = NULL;
1052 return GenericError;
1055 *hrgn = left;
1056 return Ok;
1058 default:
1059 FIXME("GdipGetRegionHRgn unimplemented for region type=%x\n", element->type);
1060 *hrgn = NULL;
1061 return NotImplemented;
1065 /*****************************************************************************
1066 * GdipGetRegionHRgn [GDIPLUS.@]
1068 GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HRGN *hrgn)
1070 TRACE("(%p, %p, %p)\n", region, graphics, hrgn);
1072 if (!region || !hrgn)
1073 return InvalidParameter;
1075 return get_region_hrgn(&region->node, graphics, hrgn);
1078 GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
1080 GpStatus status;
1081 GpRectF rect;
1083 TRACE("(%p, %p, %p)\n", region, graphics, res);
1085 if(!region || !graphics || !res)
1086 return InvalidParameter;
1088 status = GdipGetRegionBounds(region, graphics, &rect);
1089 if (status != Ok) return status;
1091 *res = rect.Width == 0.0 && rect.Height == 0.0;
1092 TRACE("=> %d\n", *res);
1094 return Ok;
1097 /*****************************************************************************
1098 * GdipIsEqualRegion [GDIPLUS.@]
1100 GpStatus WINGDIPAPI GdipIsEqualRegion(GpRegion *region, GpRegion *region2, GpGraphics *graphics,
1101 BOOL *res)
1103 HRGN hrgn1, hrgn2;
1104 GpStatus stat;
1106 TRACE("(%p, %p, %p, %p)\n", region, region2, graphics, res);
1108 if(!region || !region2 || !graphics || !res)
1109 return InvalidParameter;
1111 stat = GdipGetRegionHRgn(region, graphics, &hrgn1);
1112 if(stat != Ok)
1113 return stat;
1114 stat = GdipGetRegionHRgn(region2, graphics, &hrgn2);
1115 if(stat != Ok){
1116 DeleteObject(hrgn1);
1117 return stat;
1120 *res = EqualRgn(hrgn1, hrgn2);
1122 /* one of GpRegions is infinite */
1123 if(*res == ERROR)
1124 *res = (!hrgn1 && !hrgn2);
1126 DeleteObject(hrgn1);
1127 DeleteObject(hrgn2);
1129 return Ok;
1132 /*****************************************************************************
1133 * GdipIsInfiniteRegion [GDIPLUS.@]
1135 GpStatus WINGDIPAPI GdipIsInfiniteRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
1137 /* I think graphics is ignored here */
1138 TRACE("(%p, %p, %p)\n", region, graphics, res);
1140 if(!region || !graphics || !res)
1141 return InvalidParameter;
1143 *res = (region->node.type == RegionDataInfiniteRect);
1145 return Ok;
1148 /*****************************************************************************
1149 * GdipIsVisibleRegionRect [GDIPLUS.@]
1151 GpStatus WINGDIPAPI GdipIsVisibleRegionRect(GpRegion* region, REAL x, REAL y, REAL w, REAL h, GpGraphics *graphics, BOOL *res)
1153 HRGN hrgn;
1154 GpStatus stat;
1155 RECT rect;
1157 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %p, %p)\n", region, x, y, w, h, graphics, res);
1159 if(!region || !res)
1160 return InvalidParameter;
1162 if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok)
1163 return stat;
1165 /* infinite */
1166 if(!hrgn){
1167 *res = TRUE;
1168 return Ok;
1171 rect.left = ceilr(x);
1172 rect.top = ceilr(y);
1173 rect.right = ceilr(x + w);
1174 rect.bottom = ceilr(y + h);
1176 *res = RectInRegion(hrgn, &rect);
1178 DeleteObject(hrgn);
1180 return Ok;
1183 /*****************************************************************************
1184 * GdipIsVisibleRegionRectI [GDIPLUS.@]
1186 GpStatus WINGDIPAPI GdipIsVisibleRegionRectI(GpRegion* region, INT x, INT y, INT w, INT h, GpGraphics *graphics, BOOL *res)
1188 TRACE("(%p, %d, %d, %d, %d, %p, %p)\n", region, x, y, w, h, graphics, res);
1189 if(!region || !res)
1190 return InvalidParameter;
1192 return GdipIsVisibleRegionRect(region, (REAL)x, (REAL)y, (REAL)w, (REAL)h, graphics, res);
1195 /*****************************************************************************
1196 * GdipIsVisibleRegionPoint [GDIPLUS.@]
1198 GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, GpGraphics *graphics, BOOL *res)
1200 HRGN hrgn;
1201 GpStatus stat;
1203 TRACE("(%p, %.2f, %.2f, %p, %p)\n", region, x, y, graphics, res);
1205 if(!region || !res)
1206 return InvalidParameter;
1208 if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok)
1209 return stat;
1211 /* infinite */
1212 if(!hrgn){
1213 *res = TRUE;
1214 return Ok;
1217 *res = PtInRegion(hrgn, gdip_round(x), gdip_round(y));
1219 DeleteObject(hrgn);
1221 return Ok;
1224 /*****************************************************************************
1225 * GdipIsVisibleRegionPointI [GDIPLUS.@]
1227 GpStatus WINGDIPAPI GdipIsVisibleRegionPointI(GpRegion* region, INT x, INT y, GpGraphics *graphics, BOOL *res)
1229 TRACE("(%p, %d, %d, %p, %p)\n", region, x, y, graphics, res);
1231 return GdipIsVisibleRegionPoint(region, (REAL)x, (REAL)y, graphics, res);
1234 /*****************************************************************************
1235 * GdipSetEmpty [GDIPLUS.@]
1237 GpStatus WINGDIPAPI GdipSetEmpty(GpRegion *region)
1239 GpStatus stat;
1241 TRACE("%p\n", region);
1243 if (!region)
1244 return InvalidParameter;
1246 delete_element(&region->node);
1247 stat = init_region(region, RegionDataEmptyRect);
1249 return stat;
1252 GpStatus WINGDIPAPI GdipSetInfinite(GpRegion *region)
1254 GpStatus stat;
1256 TRACE("%p\n", region);
1258 if (!region)
1259 return InvalidParameter;
1261 delete_element(&region->node);
1262 stat = init_region(region, RegionDataInfiniteRect);
1264 return stat;
1267 /* Transforms GpRegion elements with given matrix */
1268 static GpStatus transform_region_element(region_element* element, GpMatrix *matrix)
1270 GpStatus stat;
1272 switch(element->type)
1274 case RegionDataEmptyRect:
1275 case RegionDataInfiniteRect:
1276 return Ok;
1277 case RegionDataRect:
1279 /* We can't transform a rectangle, so convert it to a path. */
1280 GpRegion *new_region;
1281 GpPath *path;
1283 stat = GdipCreatePath(FillModeAlternate, &path);
1284 if (stat == Ok)
1286 stat = GdipAddPathRectangle(path,
1287 element->elementdata.rect.X, element->elementdata.rect.Y,
1288 element->elementdata.rect.Width, element->elementdata.rect.Height);
1290 if (stat == Ok)
1291 stat = GdipCreateRegionPath(path, &new_region);
1293 GdipDeletePath(path);
1296 if (stat == Ok)
1298 /* Steal the element from the created region. */
1299 memcpy(element, &new_region->node, sizeof(region_element));
1300 GdipFree(new_region);
1302 else
1303 return stat;
1305 /* Fall-through to do the actual conversion. */
1306 case RegionDataPath:
1307 if (!element->elementdata.path->pathdata.Count)
1308 return Ok;
1310 stat = GdipTransformMatrixPoints(matrix,
1311 element->elementdata.path->pathdata.Points,
1312 element->elementdata.path->pathdata.Count);
1313 return stat;
1314 default:
1315 stat = transform_region_element(element->elementdata.combine.left, matrix);
1316 if (stat == Ok)
1317 stat = transform_region_element(element->elementdata.combine.right, matrix);
1318 return stat;
1322 GpStatus WINGDIPAPI GdipTransformRegion(GpRegion *region, GpMatrix *matrix)
1324 TRACE("(%p, %p)\n", region, matrix);
1326 if (!region || !matrix)
1327 return InvalidParameter;
1329 return transform_region_element(&region->node, matrix);
1332 /* Translates GpRegion elements with specified offsets */
1333 static void translate_region_element(region_element* element, REAL dx, REAL dy)
1335 INT i;
1337 switch(element->type)
1339 case RegionDataEmptyRect:
1340 case RegionDataInfiniteRect:
1341 return;
1342 case RegionDataRect:
1343 element->elementdata.rect.X += dx;
1344 element->elementdata.rect.Y += dy;
1345 return;
1346 case RegionDataPath:
1347 for(i = 0; i < element->elementdata.path->pathdata.Count; i++){
1348 element->elementdata.path->pathdata.Points[i].X += dx;
1349 element->elementdata.path->pathdata.Points[i].Y += dy;
1351 return;
1352 default:
1353 translate_region_element(element->elementdata.combine.left, dx, dy);
1354 translate_region_element(element->elementdata.combine.right, dx, dy);
1355 return;
1359 /*****************************************************************************
1360 * GdipTranslateRegion [GDIPLUS.@]
1362 GpStatus WINGDIPAPI GdipTranslateRegion(GpRegion *region, REAL dx, REAL dy)
1364 TRACE("(%p, %f, %f)\n", region, dx, dy);
1366 if(!region)
1367 return InvalidParameter;
1369 translate_region_element(&region->node, dx, dy);
1371 return Ok;
1374 /*****************************************************************************
1375 * GdipTranslateRegionI [GDIPLUS.@]
1377 GpStatus WINGDIPAPI GdipTranslateRegionI(GpRegion *region, INT dx, INT dy)
1379 TRACE("(%p, %d, %d)\n", region, dx, dy);
1381 return GdipTranslateRegion(region, (REAL)dx, (REAL)dy);
1384 static GpStatus get_region_scans_data(GpRegion *region, GpMatrix *matrix, LPRGNDATA *data)
1386 GpRegion *region_copy;
1387 GpStatus stat;
1388 HRGN hrgn;
1389 DWORD data_size;
1391 stat = GdipCloneRegion(region, &region_copy);
1393 if (stat == Ok)
1395 stat = GdipTransformRegion(region_copy, matrix);
1397 if (stat == Ok)
1398 stat = GdipGetRegionHRgn(region_copy, NULL, &hrgn);
1400 if (stat == Ok)
1402 if (hrgn)
1404 data_size = GetRegionData(hrgn, 0, NULL);
1406 *data = GdipAlloc(data_size);
1408 if (*data)
1409 GetRegionData(hrgn, data_size, *data);
1410 else
1411 stat = OutOfMemory;
1413 DeleteObject(hrgn);
1415 else
1417 data_size = sizeof(RGNDATAHEADER) + sizeof(RECT);
1419 *data = GdipAlloc(data_size);
1421 if (*data)
1423 (*data)->rdh.dwSize = sizeof(RGNDATAHEADER);
1424 (*data)->rdh.iType = RDH_RECTANGLES;
1425 (*data)->rdh.nCount = 1;
1426 (*data)->rdh.nRgnSize = sizeof(RECT);
1427 (*data)->rdh.rcBound.left = (*data)->rdh.rcBound.top = -0x400000;
1428 (*data)->rdh.rcBound.right = (*data)->rdh.rcBound.bottom = 0x400000;
1430 memcpy((*data)->Buffer, &(*data)->rdh.rcBound, sizeof(RECT));
1432 else
1433 stat = OutOfMemory;
1437 GdipDeleteRegion(region_copy);
1440 return stat;
1443 GpStatus WINGDIPAPI GdipGetRegionScansCount(GpRegion *region, UINT *count, GpMatrix *matrix)
1445 GpStatus stat;
1446 LPRGNDATA data;
1448 TRACE("(%p, %p, %p)\n", region, count, matrix);
1450 if (!region || !count || !matrix)
1451 return InvalidParameter;
1453 stat = get_region_scans_data(region, matrix, &data);
1455 if (stat == Ok)
1457 *count = data->rdh.nCount;
1458 GdipFree(data);
1461 return stat;
1464 GpStatus WINGDIPAPI GdipGetRegionScansI(GpRegion *region, GpRect *scans, INT *count, GpMatrix *matrix)
1466 GpStatus stat;
1467 DWORD i;
1468 LPRGNDATA data;
1469 RECT *rects;
1471 if (!region || !count || !matrix)
1472 return InvalidParameter;
1474 stat = get_region_scans_data(region, matrix, &data);
1476 if (stat == Ok)
1478 *count = data->rdh.nCount;
1479 rects = (RECT*)data->Buffer;
1481 if (scans)
1483 for (i=0; i<data->rdh.nCount; i++)
1485 scans[i].X = rects[i].left;
1486 scans[i].Y = rects[i].top;
1487 scans[i].Width = rects[i].right - rects[i].left;
1488 scans[i].Height = rects[i].bottom - rects[i].top;
1492 GdipFree(data);
1495 return Ok;
1498 GpStatus WINGDIPAPI GdipGetRegionScans(GpRegion *region, GpRectF *scans, INT *count, GpMatrix *matrix)
1500 GpStatus stat;
1501 DWORD i;
1502 LPRGNDATA data;
1503 RECT *rects;
1505 if (!region || !count || !matrix)
1506 return InvalidParameter;
1508 stat = get_region_scans_data(region, matrix, &data);
1510 if (stat == Ok)
1512 *count = data->rdh.nCount;
1513 rects = (RECT*)data->Buffer;
1515 if (scans)
1517 for (i=0; i<data->rdh.nCount; i++)
1519 scans[i].X = rects[i].left;
1520 scans[i].Y = rects[i].top;
1521 scans[i].Width = rects[i].right - rects[i].left;
1522 scans[i].Height = rects[i].bottom - rects[i].top;
1526 GdipFree(data);
1529 return Ok;