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