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