2 * Copyright (C) 2008 Google (Lei Zhang)
3 * Copyright (C) 2013 Dmitry Timoshkov
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.
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.
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
30 #include "gdiplus_private.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus
);
35 /**********************************************************
37 * Data returned by GdipGetRegionData looks something like this:
39 * struct region_data_header
41 * DWORD size; size in bytes of the data - 8.
42 * DWORD magic1; probably a checksum.
43 * DWORD magic2; always seems to be 0xdbc01001 - version?
44 * DWORD num_ops; number of combining ops * 2
47 * Then follows a sequence of combining ops and region elements.
49 * A region element is either a RECTF or some path data.
51 * Combining ops are just stored as their CombineMode value.
53 * Each RECTF is preceded by the DWORD 0x10000000. An empty rect is
54 * stored as 0x10000002 (with no following RECTF) and an infinite rect
55 * is stored as 0x10000003 (again with no following RECTF).
57 * Path data is preceded by the DWORD 0x10000001. Then follows a
58 * DWORD size and then size bytes of data.
60 * The combining ops are stored in the reverse order to the region
61 * elements and in the reverse order to which the region was
64 * When two or more complex regions (ie those with more than one
65 * element) are combined, the combining op for the two regions comes
66 * first, then the combining ops for the region elements in region 1,
67 * followed by the region elements for region 1, then follows the
68 * combining ops for region 2 and finally region 2's region elements.
69 * Presumably you're supposed to use the 0x1000000x header to find the
70 * end of the op list (the count of the elements in each region is not
73 * When a simple region (1 element) is combined, it's treated as if a
74 * single rect/path is being combined.
78 #define FLAGS_INTPATH 0x4000
86 struct region_data_header
90 struct region_header header
;
101 typedef struct packed_point
107 static void get_region_bounding_box(struct region_element
*element
,
108 REAL
*min_x
, REAL
*min_y
, REAL
*max_x
, REAL
*max_y
, BOOL
*empty
, BOOL
*infinite
);
110 static inline INT
get_element_size(const region_element
* element
)
112 INT needed
= sizeof(DWORD
); /* DWORD for the type */
113 switch(element
->type
)
116 return needed
+ sizeof(GpRect
);
119 needed
+= write_path_data(element
->elementdata
.path
, NULL
);
120 needed
+= sizeof(DWORD
); /* Extra DWORD for path size */
123 case RegionDataEmptyRect
:
124 case RegionDataInfiniteRect
:
127 needed
+= get_element_size(element
->elementdata
.combine
.left
);
128 needed
+= get_element_size(element
->elementdata
.combine
.right
);
135 /* Does not check parameters, caller must do that */
136 static inline GpStatus
init_region(GpRegion
* region
, const RegionType type
)
138 region
->node
.type
= type
;
139 region
->num_children
= 0;
144 static inline GpStatus
clone_element(const region_element
* element
,
145 region_element
** element2
)
149 /* root node is allocated with GpRegion */
151 *element2
= calloc(1, sizeof(region_element
));
156 (*element2
)->type
= element
->type
;
158 switch (element
->type
)
161 (*element2
)->elementdata
.rect
= element
->elementdata
.rect
;
163 case RegionDataEmptyRect
:
164 case RegionDataInfiniteRect
:
167 stat
= GdipClonePath(element
->elementdata
.path
, &(*element2
)->elementdata
.path
);
168 if (stat
== Ok
) return Ok
;
171 (*element2
)->elementdata
.combine
.left
= NULL
;
172 (*element2
)->elementdata
.combine
.right
= NULL
;
174 stat
= clone_element(element
->elementdata
.combine
.left
,
175 &(*element2
)->elementdata
.combine
.left
);
178 stat
= clone_element(element
->elementdata
.combine
.right
,
179 &(*element2
)->elementdata
.combine
.right
);
180 if (stat
== Ok
) return Ok
;
185 delete_element(*element2
);
190 /* Common code for CombineRegion*
191 * All the caller has to do is get its format into an element
193 static inline void fuse_region(GpRegion
* region
, region_element
* left
,
194 region_element
* right
, const CombineMode mode
)
196 region
->node
.type
= mode
;
197 region
->node
.elementdata
.combine
.left
= left
;
198 region
->node
.elementdata
.combine
.right
= right
;
199 region
->num_children
+= 2;
202 /*****************************************************************************
203 * GdipCloneRegion [GDIPLUS.@]
205 * Creates a deep copy of the region
208 * region [I] source region
209 * clone [O] resulting clone
213 * FAILURE: InvalidParameter or OutOfMemory
215 GpStatus WINGDIPAPI
GdipCloneRegion(GpRegion
*region
, GpRegion
**clone
)
217 region_element
*element
;
219 TRACE("%p %p\n", region
, clone
);
221 if (!(region
&& clone
))
222 return InvalidParameter
;
224 *clone
= calloc(1, sizeof(GpRegion
));
227 element
= &(*clone
)->node
;
229 (*clone
)->num_children
= region
->num_children
;
230 return clone_element(®ion
->node
, &element
);
233 /*****************************************************************************
234 * GdipCombineRegionPath [GDIPLUS.@]
236 GpStatus WINGDIPAPI
GdipCombineRegionPath(GpRegion
*region
, GpPath
*path
, CombineMode mode
)
238 GpRegion
*path_region
;
239 region_element
*left
, *right
= NULL
;
242 TRACE("%p %p %d\n", region
, path
, mode
);
244 if (!(region
&& path
))
245 return InvalidParameter
;
247 stat
= GdipCreateRegionPath(path
, &path_region
);
251 /* simply replace region data */
252 if(mode
== CombineModeReplace
){
253 delete_element(®ion
->node
);
254 memcpy(region
, path_region
, sizeof(GpRegion
));
259 left
= malloc(sizeof(region_element
));
262 *left
= region
->node
;
263 stat
= clone_element(&path_region
->node
, &right
);
266 fuse_region(region
, left
, right
, mode
);
267 GdipDeleteRegion(path_region
);
275 GdipDeleteRegion(path_region
);
279 /*****************************************************************************
280 * GdipCombineRegionRect [GDIPLUS.@]
282 GpStatus WINGDIPAPI
GdipCombineRegionRect(GpRegion
*region
,
283 GDIPCONST GpRectF
*rect
, CombineMode mode
)
285 GpRegion
*rect_region
;
286 region_element
*left
, *right
= NULL
;
289 TRACE("%p %s %d\n", region
, debugstr_rectf(rect
), mode
);
291 if (!(region
&& rect
))
292 return InvalidParameter
;
294 stat
= GdipCreateRegionRect(rect
, &rect_region
);
298 /* simply replace region data */
299 if(mode
== CombineModeReplace
){
300 delete_element(®ion
->node
);
301 memcpy(region
, rect_region
, sizeof(GpRegion
));
306 left
= malloc(sizeof(region_element
));
309 memcpy(left
, ®ion
->node
, sizeof(region_element
));
310 stat
= clone_element(&rect_region
->node
, &right
);
313 fuse_region(region
, left
, right
, mode
);
314 GdipDeleteRegion(rect_region
);
322 GdipDeleteRegion(rect_region
);
326 /*****************************************************************************
327 * GdipCombineRegionRectI [GDIPLUS.@]
329 GpStatus WINGDIPAPI
GdipCombineRegionRectI(GpRegion
*region
,
330 GDIPCONST GpRect
*rect
, CombineMode mode
)
334 TRACE("%p %p %d\n", region
, rect
, mode
);
337 return InvalidParameter
;
339 set_rect(&rectf
, rect
->X
, rect
->Y
, rect
->Width
, rect
->Height
);
340 return GdipCombineRegionRect(region
, &rectf
, mode
);
343 /*****************************************************************************
344 * GdipCombineRegionRegion [GDIPLUS.@]
346 GpStatus WINGDIPAPI
GdipCombineRegionRegion(GpRegion
*region1
,
347 GpRegion
*region2
, CombineMode mode
)
349 region_element
*left
, *right
= NULL
;
353 TRACE("%p %p %d\n", region1
, region2
, mode
);
355 if(!(region1
&& region2
))
356 return InvalidParameter
;
358 /* simply replace region data */
359 if(mode
== CombineModeReplace
){
360 stat
= GdipCloneRegion(region2
, ®2copy
);
361 if(stat
!= Ok
) return stat
;
363 delete_element(®ion1
->node
);
364 memcpy(region1
, reg2copy
, sizeof(GpRegion
));
369 left
= malloc(sizeof(region_element
));
373 *left
= region1
->node
;
374 stat
= clone_element(®ion2
->node
, &right
);
381 fuse_region(region1
, left
, right
, mode
);
382 region1
->num_children
+= region2
->num_children
;
387 /*****************************************************************************
388 * GdipCreateRegion [GDIPLUS.@]
390 GpStatus WINGDIPAPI
GdipCreateRegion(GpRegion
**region
)
392 TRACE("%p\n", region
);
395 return InvalidParameter
;
397 *region
= calloc(1, sizeof(GpRegion
));
401 TRACE("=> %p\n", *region
);
403 return init_region(*region
, RegionDataInfiniteRect
);
406 /*****************************************************************************
407 * GdipCreateRegionPath [GDIPLUS.@]
409 * Creates a GpRegion from a GpPath
412 * path [I] path to base the region on
413 * region [O] pointer to the newly allocated region
417 * FAILURE: InvalidParameter
420 * If a path has no floating point points, its points will be stored as shorts
423 * If a path is empty, it is considered to be an INTPATH
425 GpStatus WINGDIPAPI
GdipCreateRegionPath(GpPath
*path
, GpRegion
**region
)
427 region_element
* element
;
430 TRACE("%p, %p\n", path
, region
);
432 if (!(path
&& region
))
433 return InvalidParameter
;
435 *region
= calloc(1, sizeof(GpRegion
));
438 stat
= init_region(*region
, RegionDataPath
);
441 GdipDeleteRegion(*region
);
444 element
= &(*region
)->node
;
446 stat
= GdipClonePath(path
, &element
->elementdata
.path
);
449 GdipDeleteRegion(*region
);
456 /*****************************************************************************
457 * GdipCreateRegionRect [GDIPLUS.@]
459 GpStatus WINGDIPAPI
GdipCreateRegionRect(GDIPCONST GpRectF
*rect
,
464 TRACE("%s, %p\n", debugstr_rectf(rect
), region
);
466 if (!(rect
&& region
))
467 return InvalidParameter
;
469 *region
= calloc(1, sizeof(GpRegion
));
470 stat
= init_region(*region
, RegionDataRect
);
473 GdipDeleteRegion(*region
);
477 (*region
)->node
.elementdata
.rect
.X
= rect
->X
;
478 (*region
)->node
.elementdata
.rect
.Y
= rect
->Y
;
479 (*region
)->node
.elementdata
.rect
.Width
= rect
->Width
;
480 (*region
)->node
.elementdata
.rect
.Height
= rect
->Height
;
485 /*****************************************************************************
486 * GdipCreateRegionRectI [GDIPLUS.@]
488 GpStatus WINGDIPAPI
GdipCreateRegionRectI(GDIPCONST GpRect
*rect
,
493 TRACE("%p, %p\n", rect
, region
);
495 set_rect(&rectf
, rect
->X
, rect
->Y
, rect
->Width
, rect
->Height
);
496 return GdipCreateRegionRect(&rectf
, region
);
499 /******************************************************************************
500 * GdipCreateRegionHrgn [GDIPLUS.@]
502 GpStatus WINGDIPAPI
GdipCreateRegionHrgn(HRGN hrgn
, GpRegion
**region
)
512 TRACE("(%p, %p)\n", hrgn
, region
);
514 if(!region
|| !(size
= GetRegionData(hrgn
, 0, NULL
)))
515 return InvalidParameter
;
521 if(!GetRegionData(hrgn
, size
, buf
)){
526 if(buf
->rdh
.nCount
== 0){
527 if((stat
= GdipCreateRegion(&local
)) != Ok
){
531 if((stat
= GdipSetEmpty(local
)) != Ok
){
533 GdipDeleteRegion(local
);
541 if((stat
= GdipCreatePath(FillModeAlternate
, &path
)) != Ok
){
546 rect
= (LPRECT
)buf
->Buffer
;
547 for(i
= 0; i
< buf
->rdh
.nCount
; i
++){
548 if((stat
= GdipAddPathRectangle(path
, (REAL
)rect
->left
, (REAL
)rect
->top
,
549 (REAL
)(rect
->right
- rect
->left
), (REAL
)(rect
->bottom
- rect
->top
))) != Ok
){
551 GdipDeletePath(path
);
557 stat
= GdipCreateRegionPath(path
, region
);
560 GdipDeletePath(path
);
564 /*****************************************************************************
565 * GdipDeleteRegion [GDIPLUS.@]
567 GpStatus WINGDIPAPI
GdipDeleteRegion(GpRegion
*region
)
569 TRACE("%p\n", region
);
572 return InvalidParameter
;
574 delete_element(®ion
->node
);
580 /*****************************************************************************
581 * GdipGetRegionBounds [GDIPLUS.@]
583 GpStatus WINGDIPAPI
GdipGetRegionBounds(GpRegion
*region
, GpGraphics
*graphics
, GpRectF
*rect
)
585 REAL min_x
, min_y
, max_x
, max_y
;
586 BOOL empty
, infinite
;
588 TRACE("(%p, %p, %p)\n", region
, graphics
, rect
);
590 if(!region
|| !graphics
|| !rect
)
591 return InvalidParameter
;
593 /* Contrary to MSDN, native ignores the graphics transform. */
594 get_region_bounding_box(®ion
->node
, &min_x
, &min_y
, &max_x
, &max_y
, &empty
, &infinite
);
598 rect
->X
= rect
->Y
= -(REAL
)(1 << 22);
599 rect
->Width
= rect
->Height
= (REAL
)(1 << 23);
600 TRACE("%p => infinite\n", region
);
605 rect
->X
= rect
->Y
= rect
->Width
= rect
->Height
= 0.0;
606 TRACE("%p => empty\n", region
);
612 rect
->Width
= max_x
- min_x
;
613 rect
->Height
= max_y
- min_y
;
614 TRACE("%p => %s\n", region
, debugstr_rectf(rect
));
619 /*****************************************************************************
620 * GdipGetRegionBoundsI [GDIPLUS.@]
622 GpStatus WINGDIPAPI
GdipGetRegionBoundsI(GpRegion
*region
, GpGraphics
*graphics
, GpRect
*rect
)
627 TRACE("(%p, %p, %p)\n", region
, graphics
, rect
);
630 return InvalidParameter
;
632 status
= GdipGetRegionBounds(region
, graphics
, &rectf
);
634 rect
->X
= gdip_round(rectf
.X
);
635 rect
->Y
= gdip_round(rectf
.Y
);
636 rect
->Width
= gdip_round(rectf
.Width
);
637 rect
->Height
= gdip_round(rectf
.Height
);
643 static inline void write_dword(DWORD
* location
, INT
* offset
, const DWORD write
)
645 location
[*offset
] = write
;
649 static inline void write_float(DWORD
* location
, INT
* offset
, const FLOAT write
)
651 ((FLOAT
*)location
)[*offset
] = write
;
655 static void write_element(const region_element
* element
, DWORD
*buffer
,
658 write_dword(buffer
, filled
, element
->type
);
659 switch (element
->type
)
661 case CombineModeReplace
:
662 case CombineModeIntersect
:
663 case CombineModeUnion
:
665 case CombineModeExclude
:
666 case CombineModeComplement
:
667 write_element(element
->elementdata
.combine
.left
, buffer
, filled
);
668 write_element(element
->elementdata
.combine
.right
, buffer
, filled
);
671 write_float(buffer
, filled
, element
->elementdata
.rect
.X
);
672 write_float(buffer
, filled
, element
->elementdata
.rect
.Y
);
673 write_float(buffer
, filled
, element
->elementdata
.rect
.Width
);
674 write_float(buffer
, filled
, element
->elementdata
.rect
.Height
);
678 DWORD size
= write_path_data(element
->elementdata
.path
, buffer
+ *filled
+ 1);
679 write_dword(buffer
, filled
, size
);
680 *filled
+= size
/ sizeof(DWORD
);
683 case RegionDataEmptyRect
:
684 case RegionDataInfiniteRect
:
689 DWORD
write_region_data(const GpRegion
*region
, void *data
)
691 struct region_header
*header
= data
;
695 size
= sizeof(struct region_header
) + get_element_size(®ion
->node
);
696 if (!data
) return size
;
698 header
->magic
= VERSION_MAGIC2
;
699 header
->num_children
= region
->num_children
;
701 /* With few exceptions, everything written is DWORD aligned,
702 * so use that as our base */
703 write_element(®ion
->node
, (DWORD
*)data
, &filled
);
707 /*****************************************************************************
708 * GdipGetRegionData [GDIPLUS.@]
710 * Returns the header, followed by combining ops and region elements.
713 * region [I] region to retrieve from
714 * buffer [O] buffer to hold the resulting data
715 * size [I] size of the buffer
716 * needed [O] (optional) how much data was written
720 * FAILURE: InvalidParameter
723 * The header contains the size, a checksum, a version string, and the number
724 * of children. The size does not count itself or the checksum.
725 * Version is always something like 0xdbc01001 or 0xdbc01002
727 * An element is a RECT, or PATH; Combining ops are stored as their
728 * CombineMode value. Special regions (infinite, empty) emit just their
729 * op-code; GpRectFs emit their code followed by their points; GpPaths emit
730 * their code followed by a second header for the path followed by the actual
731 * path data. Followed by the flags for each point. The pathheader contains
732 * the size of the data to follow, a version number again, followed by a count
733 * of how many points, and any special flags which may apply. 0x4000 means it's
734 * a path of shorts instead of FLOAT.
736 * Combining Ops are stored in reverse order from when they were constructed;
737 * the output is a tree where the left side combining area is always taken
740 GpStatus WINGDIPAPI
GdipGetRegionData(GpRegion
*region
, BYTE
*buffer
, UINT size
,
743 struct region_data_header
*region_data_header
;
746 TRACE("%p, %p, %d, %p\n", region
, buffer
, size
, needed
);
748 if (!region
|| !buffer
|| !size
)
749 return InvalidParameter
;
751 required
= FIELD_OFFSET(struct region_data_header
, header
) + write_region_data(region
, NULL
);
754 if (needed
) *needed
= size
;
755 return InsufficientBuffer
;
758 region_data_header
= (struct region_data_header
*)buffer
;
759 region_data_header
->size
= write_region_data(region
, ®ion_data_header
->header
);
760 region_data_header
->checksum
= 0;
768 static GpStatus
read_element(struct memory_buffer
*mbuf
, GpRegion
*region
, region_element
*node
, INT
*count
)
773 type
= buffer_read(mbuf
, sizeof(*type
));
774 if (!type
) return Ok
;
776 TRACE("type %#lx\n", *type
);
782 case CombineModeReplace
:
783 case CombineModeIntersect
:
784 case CombineModeUnion
:
786 case CombineModeExclude
:
787 case CombineModeComplement
:
789 region_element
*left
, *right
;
791 left
= calloc(1, sizeof(region_element
));
792 if (!left
) return OutOfMemory
;
793 right
= calloc(1, sizeof(region_element
));
800 status
= read_element(mbuf
, region
, left
, count
);
803 status
= read_element(mbuf
, region
, right
, count
);
806 node
->elementdata
.combine
.left
= left
;
807 node
->elementdata
.combine
.right
= right
;
808 region
->num_children
+= 2;
822 rc
= buffer_read(mbuf
, sizeof(*rc
));
825 ERR("failed to read rect data\n");
826 return InvalidParameter
;
829 node
->elementdata
.rect
= *rc
;
837 const struct path_header
*path_header
;
840 path_header
= buffer_read(mbuf
, sizeof(*path_header
));
843 ERR("failed to read path header\n");
844 return InvalidParameter
;
846 if (!VALID_MAGIC(path_header
->magic
))
848 ERR("invalid path header magic %#lx\n", path_header
->magic
);
849 return InvalidParameter
;
852 /* Windows always fails to create an empty path in a region */
853 if (!path_header
->count
)
855 TRACE("refusing to create an empty path in a region\n");
859 status
= GdipCreatePath(FillModeAlternate
, &path
);
860 if (status
) return status
;
862 node
->elementdata
.path
= path
;
864 if (!lengthen_path(path
, path_header
->count
))
867 path
->pathdata
.Count
= path_header
->count
;
869 if (path_header
->flags
& ~FLAGS_INTPATH
)
870 FIXME("unhandled path flags %#lx\n", path_header
->flags
);
872 if (path_header
->flags
& FLAGS_INTPATH
)
874 const packed_point
*pt
;
877 pt
= buffer_read(mbuf
, sizeof(*pt
) * path_header
->count
);
880 ERR("failed to read packed %lu path points\n", path_header
->count
);
881 return InvalidParameter
;
884 for (i
= 0; i
< path_header
->count
; i
++)
886 path
->pathdata
.Points
[i
].X
= (REAL
)pt
[i
].X
;
887 path
->pathdata
.Points
[i
].Y
= (REAL
)pt
[i
].Y
;
894 ptf
= buffer_read(mbuf
, sizeof(*ptf
) * path_header
->count
);
897 ERR("failed to read %lu path points\n", path_header
->count
);
898 return InvalidParameter
;
900 memcpy(path
->pathdata
.Points
, ptf
, sizeof(*ptf
) * path_header
->count
);
903 types
= buffer_read(mbuf
, path_header
->count
);
906 ERR("failed to read %lu path types\n", path_header
->count
);
907 return InvalidParameter
;
909 memcpy(path
->pathdata
.Types
, types
, path_header
->count
);
910 if (path_header
->count
& 3)
912 if (!buffer_read(mbuf
, 4 - (path_header
->count
& 3)))
914 ERR("failed to read rounding %lu bytes\n", 4 - (path_header
->count
& 3));
915 return InvalidParameter
;
923 case RegionDataEmptyRect
:
924 case RegionDataInfiniteRect
:
929 FIXME("element type %#lx is not supported\n", *type
);
933 return InvalidParameter
;
936 /*****************************************************************************
937 * GdipCreateRegionRgnData [GDIPLUS.@]
939 GpStatus WINGDIPAPI
GdipCreateRegionRgnData(GDIPCONST BYTE
*data
, INT size
, GpRegion
**region
)
941 const struct region_data_header
*region_data_header
;
942 struct memory_buffer mbuf
;
946 TRACE("(%p, %d, %p)\n", data
, size
, region
);
949 return InvalidParameter
;
951 init_memory_buffer(&mbuf
, data
, size
);
953 region_data_header
= buffer_read(&mbuf
, sizeof(*region_data_header
));
954 if (!region_data_header
|| !VALID_MAGIC(region_data_header
->header
.magic
))
955 return InvalidParameter
;
957 status
= GdipCreateRegion(region
);
962 status
= read_element(&mbuf
, *region
, &(*region
)->node
, &count
);
963 if (status
== Ok
&& !count
)
964 status
= InvalidParameter
;
968 GdipDeleteRegion(*region
);
975 /*****************************************************************************
976 * GdipGetRegionDataSize [GDIPLUS.@]
978 GpStatus WINGDIPAPI
GdipGetRegionDataSize(GpRegion
*region
, UINT
*needed
)
980 TRACE("%p, %p\n", region
, needed
);
982 if (!(region
&& needed
))
983 return InvalidParameter
;
985 /* header.size doesn't count header.size and header.checksum */
986 *needed
= FIELD_OFFSET(struct region_data_header
, header
) + write_region_data(region
, NULL
);
991 static GpStatus
get_path_hrgn(GpPath
*path
, GpGraphics
*graphics
, HRGN
*hrgn
)
993 HDC new_hdc
=NULL
, hdc
;
994 GpGraphics
*new_graphics
=NULL
;
998 if (!path
->pathdata
.Count
) /* PathToRegion doesn't support empty paths */
1000 *hrgn
= CreateRectRgn( 0, 0, 0, 0 );
1001 return *hrgn
? Ok
: OutOfMemory
;
1006 hdc
= new_hdc
= CreateCompatibleDC(0);
1010 stat
= GdipCreateFromHDC(new_hdc
, &new_graphics
);
1011 graphics
= new_graphics
;
1018 else if (has_gdi_dc(graphics
))
1020 stat
= gdi_dc_acquire(graphics
, &hdc
);
1026 graphics
->hdc
= hdc
= new_hdc
= CreateCompatibleDC(0);
1031 save_state
= SaveDC(hdc
);
1034 SetPolyFillMode(hdc
, (path
->fill
== FillModeAlternate
? ALTERNATE
: WINDING
));
1036 gdi_transform_acquire(graphics
);
1038 stat
= trace_path(graphics
, path
);
1041 *hrgn
= PathToRegion(hdc
);
1042 stat
= *hrgn
? Ok
: OutOfMemory
;
1045 gdi_transform_release(graphics
);
1047 RestoreDC(hdc
, save_state
);
1052 GdipDeleteGraphics(new_graphics
);
1054 graphics
->hdc
= NULL
;
1057 gdi_dc_release(graphics
, hdc
);
1062 static GpStatus
get_region_hrgn(struct region_element
*element
, GpGraphics
*graphics
, HRGN
*hrgn
)
1064 switch (element
->type
)
1066 case RegionDataInfiniteRect
:
1069 case RegionDataEmptyRect
:
1070 *hrgn
= CreateRectRgn(0, 0, 0, 0);
1071 return *hrgn
? Ok
: OutOfMemory
;
1072 case RegionDataPath
:
1073 return get_path_hrgn(element
->elementdata
.path
, graphics
, hrgn
);
1074 case RegionDataRect
:
1078 GpRectF
* rc
= &element
->elementdata
.rect
;
1080 stat
= GdipCreatePath(FillModeAlternate
, &path
);
1083 stat
= GdipAddPathRectangle(path
, rc
->X
, rc
->Y
, rc
->Width
, rc
->Height
);
1086 stat
= get_path_hrgn(path
, graphics
, hrgn
);
1088 GdipDeletePath(path
);
1092 case CombineModeIntersect
:
1093 case CombineModeUnion
:
1094 case CombineModeXor
:
1095 case CombineModeExclude
:
1096 case CombineModeComplement
:
1102 stat
= get_region_hrgn(element
->elementdata
.combine
.left
, graphics
, &left
);
1111 /* existing region is infinite */
1112 switch (element
->type
)
1114 case CombineModeIntersect
:
1115 return get_region_hrgn(element
->elementdata
.combine
.right
, graphics
, hrgn
);
1116 case CombineModeXor
: case CombineModeExclude
:
1117 left
= CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22);
1119 case CombineModeComplement
:
1120 *hrgn
= CreateRectRgn(0, 0, 0, 0);
1121 return *hrgn
? Ok
: OutOfMemory
;
1122 case CombineModeUnion
:
1128 stat
= get_region_hrgn(element
->elementdata
.combine
.right
, graphics
, &right
);
1138 /* new region is infinite */
1139 switch (element
->type
)
1141 case CombineModeIntersect
:
1144 case CombineModeXor
: case CombineModeComplement
:
1145 right
= CreateRectRgn(-(1 << 22), -(1 << 22), 1 << 22, 1 << 22);
1147 case CombineModeExclude
:
1149 *hrgn
= CreateRectRgn(0, 0, 0, 0);
1150 return *hrgn
? Ok
: OutOfMemory
;
1151 case CombineModeUnion
:
1158 switch (element
->type
)
1160 case CombineModeIntersect
:
1161 ret
= CombineRgn(left
, left
, right
, RGN_AND
);
1163 case CombineModeUnion
:
1164 ret
= CombineRgn(left
, left
, right
, RGN_OR
);
1166 case CombineModeXor
:
1167 ret
= CombineRgn(left
, left
, right
, RGN_XOR
);
1169 case CombineModeExclude
:
1170 ret
= CombineRgn(left
, left
, right
, RGN_DIFF
);
1172 case CombineModeComplement
:
1173 ret
= CombineRgn(left
, right
, left
, RGN_DIFF
);
1179 DeleteObject(right
);
1185 return GenericError
;
1192 FIXME("GdipGetRegionHRgn unimplemented for region type=%lx\n", element
->type
);
1194 return NotImplemented
;
1198 /*****************************************************************************
1199 * GdipGetRegionHRgn [GDIPLUS.@]
1201 GpStatus WINGDIPAPI
GdipGetRegionHRgn(GpRegion
*region
, GpGraphics
*graphics
, HRGN
*hrgn
)
1203 TRACE("(%p, %p, %p)\n", region
, graphics
, hrgn
);
1205 if (!region
|| !hrgn
)
1206 return InvalidParameter
;
1208 return get_region_hrgn(®ion
->node
, graphics
, hrgn
);
1211 GpStatus WINGDIPAPI
GdipIsEmptyRegion(GpRegion
*region
, GpGraphics
*graphics
, BOOL
*res
)
1216 TRACE("(%p, %p, %p)\n", region
, graphics
, res
);
1218 if(!region
|| !graphics
|| !res
)
1219 return InvalidParameter
;
1221 status
= GdipGetRegionBounds(region
, graphics
, &rect
);
1222 if (status
!= Ok
) return status
;
1224 *res
= rect
.Width
== 0.0 && rect
.Height
== 0.0;
1225 TRACE("=> %d\n", *res
);
1230 /*****************************************************************************
1231 * GdipIsEqualRegion [GDIPLUS.@]
1233 GpStatus WINGDIPAPI
GdipIsEqualRegion(GpRegion
*region
, GpRegion
*region2
, GpGraphics
*graphics
,
1239 TRACE("(%p, %p, %p, %p)\n", region
, region2
, graphics
, res
);
1241 if(!region
|| !region2
|| !graphics
|| !res
)
1242 return InvalidParameter
;
1244 stat
= GdipGetRegionHRgn(region
, graphics
, &hrgn1
);
1247 stat
= GdipGetRegionHRgn(region2
, graphics
, &hrgn2
);
1249 DeleteObject(hrgn1
);
1253 *res
= EqualRgn(hrgn1
, hrgn2
);
1255 /* one of GpRegions is infinite */
1257 *res
= (!hrgn1
&& !hrgn2
);
1259 DeleteObject(hrgn1
);
1260 DeleteObject(hrgn2
);
1265 /*****************************************************************************
1266 * GdipIsInfiniteRegion [GDIPLUS.@]
1268 GpStatus WINGDIPAPI
GdipIsInfiniteRegion(GpRegion
*region
, GpGraphics
*graphics
, BOOL
*res
)
1270 /* I think graphics is ignored here */
1271 TRACE("(%p, %p, %p)\n", region
, graphics
, res
);
1273 if(!region
|| !graphics
|| !res
)
1274 return InvalidParameter
;
1276 *res
= (region
->node
.type
== RegionDataInfiniteRect
);
1281 /*****************************************************************************
1282 * GdipIsVisibleRegionRect [GDIPLUS.@]
1284 GpStatus WINGDIPAPI
GdipIsVisibleRegionRect(GpRegion
* region
, REAL x
, REAL y
, REAL w
, REAL h
, GpGraphics
*graphics
, BOOL
*res
)
1290 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %p, %p)\n", region
, x
, y
, w
, h
, graphics
, res
);
1293 return InvalidParameter
;
1295 if((stat
= GdipGetRegionHRgn(region
, NULL
, &hrgn
)) != Ok
)
1304 SetRect(&rect
, ceilr(x
), ceilr(y
), ceilr(x
+ w
), ceilr(y
+ h
));
1305 *res
= RectInRegion(hrgn
, &rect
);
1312 /*****************************************************************************
1313 * GdipIsVisibleRegionRectI [GDIPLUS.@]
1315 GpStatus WINGDIPAPI
GdipIsVisibleRegionRectI(GpRegion
* region
, INT x
, INT y
, INT w
, INT h
, GpGraphics
*graphics
, BOOL
*res
)
1317 TRACE("(%p, %d, %d, %d, %d, %p, %p)\n", region
, x
, y
, w
, h
, graphics
, res
);
1319 return InvalidParameter
;
1321 return GdipIsVisibleRegionRect(region
, (REAL
)x
, (REAL
)y
, (REAL
)w
, (REAL
)h
, graphics
, res
);
1324 /* get_region_bounding_box
1326 * Returns a box guaranteed to enclose the entire region, but not guaranteed to be minimal.
1327 * Sets "empty" if bounding box is empty.
1328 * Sets "infinite" if everything outside bounding box is inside the region.
1329 * In the infinite case, the bounding box encloses all points not in the region. */
1330 static void get_region_bounding_box(struct region_element
*element
,
1331 REAL
*min_x
, REAL
*min_y
, REAL
*max_x
, REAL
*max_y
, BOOL
*empty
, BOOL
*infinite
)
1333 REAL left_min_x
, left_min_y
, left_max_x
, left_max_y
;
1334 BOOL left_empty
, left_infinite
;
1335 REAL right_min_x
, right_min_y
, right_max_x
, right_max_y
;
1336 BOOL right_empty
, right_infinite
;
1337 /* For combine modes, we convert the mode to flags as follows to simplify the logic:
1338 * 0x8 = point in combined region if it's in both
1339 * 0x4 = point in combined region if it's in left and not right
1340 * 0x2 = point in combined region if it's not in left and is in right
1341 * 0x1 = point in combined region if it's in neither region */
1343 const int combine_mode_flags
[] = {
1344 0xa, /* CombineModeReplace - shouldn't be used */
1345 0x8, /* CombineModeIntersect */
1346 0xe, /* CombineModeUnion */
1347 0x6, /* CombineModeXor */
1348 0x4, /* CombineModeExclude */
1349 0x2, /* CombineModeComplement */
1352 /* handle unit elements first */
1353 switch (element
->type
)
1355 case RegionDataInfiniteRect
:
1356 *min_x
= *min_y
= *max_x
= *max_y
= 0.0;
1360 case RegionDataEmptyRect
:
1361 *min_x
= *min_y
= *max_x
= *max_y
= 0.0;
1365 case RegionDataPath
:
1367 GpPath
*path
= element
->elementdata
.path
;
1370 if (path
->pathdata
.Count
<= 1) {
1371 *min_x
= *min_y
= *max_x
= *max_y
= 0.0;
1377 *min_x
= *max_x
= path
->pathdata
.Points
[0].X
;
1378 *min_y
= *max_y
= path
->pathdata
.Points
[0].Y
;
1382 for (i
=1; i
< path
->pathdata
.Count
; i
++)
1384 if (path
->pathdata
.Points
[i
].X
< *min_x
)
1385 *min_x
= path
->pathdata
.Points
[i
].X
;
1386 else if (path
->pathdata
.Points
[i
].X
> *max_x
)
1387 *max_x
= path
->pathdata
.Points
[i
].X
;
1388 if (path
->pathdata
.Points
[i
].Y
< *min_y
)
1389 *min_y
= path
->pathdata
.Points
[i
].Y
;
1390 else if (path
->pathdata
.Points
[i
].Y
> *max_y
)
1391 *max_y
= path
->pathdata
.Points
[i
].Y
;
1396 case RegionDataRect
:
1397 *min_x
= element
->elementdata
.rect
.X
;
1398 *min_y
= element
->elementdata
.rect
.Y
;
1399 *max_x
= element
->elementdata
.rect
.X
+ element
->elementdata
.rect
.Width
;
1400 *max_y
= element
->elementdata
.rect
.Y
+ element
->elementdata
.rect
.Height
;
1406 /* Should be only combine modes left */
1407 assert(element
->type
< ARRAY_SIZE(combine_mode_flags
));
1409 flags
= combine_mode_flags
[element
->type
];
1411 get_region_bounding_box(element
->elementdata
.combine
.left
,
1412 &left_min_x
, &left_min_y
, &left_max_x
, &left_max_y
, &left_empty
, &left_infinite
);
1416 /* change our function so we can ignore the infinity */
1417 flags
= ((flags
& 0x3) << 2) | ((flags
& 0xc) >> 2);
1420 if (left_empty
&& (flags
& 0x3) == 0) {
1421 /* no points in region regardless of right region, return empty */
1427 if (left_empty
&& (flags
& 0x3) == 0x3) {
1428 /* all points in region regardless of right region, return infinite */
1434 get_region_bounding_box(element
->elementdata
.combine
.right
,
1435 &right_min_x
, &right_min_y
, &right_max_x
, &right_max_y
, &right_empty
, &right_infinite
);
1439 /* change our function so we can ignore the infinity */
1440 flags
= ((flags
& 0x5) << 1) | ((flags
& 0xa) >> 1);
1443 /* result is infinite if points in neither region are in the result */
1444 *infinite
= (flags
& 0x1);
1448 /* Again, we modify our function to ignore the infinity.
1449 * The points we care about are the ones that are different from the outside of our box,
1450 * not the points inside the region, so we invert the whole thing.
1451 * From here we can assume 0x1 is not set. */
1457 /* We already took care of the cases where the right region doesn't matter,
1458 * so we can just use the right bounding box. */
1459 *min_x
= right_min_x
;
1460 *min_y
= right_min_y
;
1461 *max_x
= right_max_x
;
1462 *max_y
= right_max_y
;
1463 *empty
= right_empty
;
1469 /* With no points in right region, and infinities eliminated, we only care
1470 * about flag 0x4, the case where a point is in left region and not right. */
1473 /* We have a copy of the left region. */
1474 *min_x
= left_min_x
;
1475 *min_y
= left_min_y
;
1476 *max_x
= left_max_x
;
1477 *max_y
= left_max_y
;
1478 *empty
= left_empty
;
1481 /* otherwise, it's an empty (or infinite) region */
1486 /* From here we know 0x1 isn't set, and we know at least one flag is set.
1487 * We can ignore flag 0x8 because we must assume that any point within the
1488 * intersection of the bounding boxes might be within the region. */
1489 switch (flags
& 0x6)
1493 *min_x
= fmaxf(left_min_x
, right_min_x
);
1494 *min_y
= fmaxf(left_min_y
, right_min_y
);
1495 *max_x
= fminf(left_max_x
, right_max_x
);
1496 *max_y
= fminf(left_max_y
, right_max_y
);
1497 *empty
= *min_x
> *max_x
|| *min_y
> *max_y
;
1500 /* right (or complement) */
1501 *min_x
= right_min_x
;
1502 *min_y
= right_min_y
;
1503 *max_x
= right_max_x
;
1504 *max_y
= right_max_y
;
1505 *empty
= right_empty
;
1508 /* left (or exclude) */
1509 *min_x
= left_min_x
;
1510 *min_y
= left_min_y
;
1511 *max_x
= left_max_x
;
1512 *max_y
= left_max_y
;
1513 *empty
= left_empty
;
1516 /* union (or xor) */
1517 *min_x
= fminf(left_min_x
, right_min_x
);
1518 *min_y
= fminf(left_min_y
, right_min_y
);
1519 *max_x
= fmaxf(left_max_x
, right_max_x
);
1520 *max_y
= fmaxf(left_max_y
, right_max_y
);
1526 /*****************************************************************************
1527 * GdipIsVisibleRegionPoint [GDIPLUS.@]
1529 GpStatus WINGDIPAPI
GdipIsVisibleRegionPoint(GpRegion
* region
, REAL x
, REAL y
, GpGraphics
*graphics
, BOOL
*res
)
1533 REAL min_x
, min_y
, max_x
, max_y
;
1534 BOOL empty
, infinite
;
1536 TRACE("(%p, %.2f, %.2f, %p, %p)\n", region
, x
, y
, graphics
, res
);
1539 return InvalidParameter
;
1544 /* Check for cases where we can skip quantization. */
1545 get_region_bounding_box(®ion
->node
, &min_x
, &min_y
, &max_x
, &max_y
, &empty
, &infinite
);
1546 if (empty
|| x
< min_x
|| y
< min_y
|| x
> max_x
|| y
> max_y
)
1552 if((stat
= GdipGetRegionHRgn(region
, NULL
, &hrgn
)) != Ok
)
1561 *res
= PtInRegion(hrgn
, x
, y
);
1568 /*****************************************************************************
1569 * GdipIsVisibleRegionPointI [GDIPLUS.@]
1571 GpStatus WINGDIPAPI
GdipIsVisibleRegionPointI(GpRegion
* region
, INT x
, INT y
, GpGraphics
*graphics
, BOOL
*res
)
1573 TRACE("(%p, %d, %d, %p, %p)\n", region
, x
, y
, graphics
, res
);
1575 return GdipIsVisibleRegionPoint(region
, (REAL
)x
, (REAL
)y
, graphics
, res
);
1578 /*****************************************************************************
1579 * GdipSetEmpty [GDIPLUS.@]
1581 GpStatus WINGDIPAPI
GdipSetEmpty(GpRegion
*region
)
1585 TRACE("%p\n", region
);
1588 return InvalidParameter
;
1590 delete_element(®ion
->node
);
1591 stat
= init_region(region
, RegionDataEmptyRect
);
1596 GpStatus WINGDIPAPI
GdipSetInfinite(GpRegion
*region
)
1600 TRACE("%p\n", region
);
1603 return InvalidParameter
;
1605 delete_element(®ion
->node
);
1606 stat
= init_region(region
, RegionDataInfiniteRect
);
1611 /* Transforms GpRegion elements with given matrix */
1612 static GpStatus
transform_region_element(region_element
* element
, GpMatrix
*matrix
)
1616 switch(element
->type
)
1618 case RegionDataEmptyRect
:
1619 case RegionDataInfiniteRect
:
1621 case RegionDataRect
:
1623 GpRegion
*new_region
;
1626 if (matrix
->matrix
[1] == 0.0 && matrix
->matrix
[2] == 0.0)
1630 points
[0].X
= element
->elementdata
.rect
.X
;
1631 points
[0].Y
= element
->elementdata
.rect
.Y
;
1632 points
[1].X
= element
->elementdata
.rect
.X
+ element
->elementdata
.rect
.Width
;
1633 points
[1].Y
= element
->elementdata
.rect
.Y
+ element
->elementdata
.rect
.Height
;
1635 stat
= GdipTransformMatrixPoints(matrix
, points
, 2);
1639 if (points
[0].X
> points
[1].X
)
1643 points
[0].X
= points
[1].X
;
1647 if (points
[0].Y
> points
[1].Y
)
1651 points
[0].Y
= points
[1].Y
;
1655 element
->elementdata
.rect
.X
= points
[0].X
;
1656 element
->elementdata
.rect
.Y
= points
[0].Y
;
1657 element
->elementdata
.rect
.Width
= points
[1].X
- points
[0].X
;
1658 element
->elementdata
.rect
.Height
= points
[1].Y
- points
[0].Y
;
1662 /* We can't rotate/shear a rectangle, so convert it to a path. */
1663 stat
= GdipCreatePath(FillModeAlternate
, &path
);
1666 stat
= GdipAddPathRectangle(path
,
1667 element
->elementdata
.rect
.X
, element
->elementdata
.rect
.Y
,
1668 element
->elementdata
.rect
.Width
, element
->elementdata
.rect
.Height
);
1671 stat
= GdipCreateRegionPath(path
, &new_region
);
1673 GdipDeletePath(path
);
1678 /* Steal the element from the created region. */
1679 memcpy(element
, &new_region
->node
, sizeof(region_element
));
1685 /* Fall-through to do the actual conversion. */
1686 case RegionDataPath
:
1687 if (!element
->elementdata
.path
->pathdata
.Count
)
1690 stat
= GdipTransformMatrixPoints(matrix
,
1691 element
->elementdata
.path
->pathdata
.Points
,
1692 element
->elementdata
.path
->pathdata
.Count
);
1695 stat
= transform_region_element(element
->elementdata
.combine
.left
, matrix
);
1697 stat
= transform_region_element(element
->elementdata
.combine
.right
, matrix
);
1702 GpStatus WINGDIPAPI
GdipTransformRegion(GpRegion
*region
, GpMatrix
*matrix
)
1704 TRACE("(%p, %s)\n", region
, debugstr_matrix(matrix
));
1706 if (!region
|| !matrix
)
1707 return InvalidParameter
;
1709 return transform_region_element(®ion
->node
, matrix
);
1712 /* Translates GpRegion elements with specified offsets */
1713 static void translate_region_element(region_element
* element
, REAL dx
, REAL dy
)
1717 switch(element
->type
)
1719 case RegionDataEmptyRect
:
1720 case RegionDataInfiniteRect
:
1722 case RegionDataRect
:
1723 element
->elementdata
.rect
.X
+= dx
;
1724 element
->elementdata
.rect
.Y
+= dy
;
1726 case RegionDataPath
:
1727 for(i
= 0; i
< element
->elementdata
.path
->pathdata
.Count
; i
++){
1728 element
->elementdata
.path
->pathdata
.Points
[i
].X
+= dx
;
1729 element
->elementdata
.path
->pathdata
.Points
[i
].Y
+= dy
;
1733 translate_region_element(element
->elementdata
.combine
.left
, dx
, dy
);
1734 translate_region_element(element
->elementdata
.combine
.right
, dx
, dy
);
1739 /*****************************************************************************
1740 * GdipTranslateRegion [GDIPLUS.@]
1742 GpStatus WINGDIPAPI
GdipTranslateRegion(GpRegion
*region
, REAL dx
, REAL dy
)
1744 TRACE("(%p, %f, %f)\n", region
, dx
, dy
);
1747 return InvalidParameter
;
1749 translate_region_element(®ion
->node
, dx
, dy
);
1754 /*****************************************************************************
1755 * GdipTranslateRegionI [GDIPLUS.@]
1757 GpStatus WINGDIPAPI
GdipTranslateRegionI(GpRegion
*region
, INT dx
, INT dy
)
1759 TRACE("(%p, %d, %d)\n", region
, dx
, dy
);
1761 return GdipTranslateRegion(region
, (REAL
)dx
, (REAL
)dy
);
1764 static GpStatus
get_region_scans_data(GpRegion
*region
, GpMatrix
*matrix
, LPRGNDATA
*data
)
1766 GpRegion
*region_copy
;
1771 stat
= GdipCloneRegion(region
, ®ion_copy
);
1775 stat
= GdipTransformRegion(region_copy
, matrix
);
1778 stat
= GdipGetRegionHRgn(region_copy
, NULL
, &hrgn
);
1784 data_size
= GetRegionData(hrgn
, 0, NULL
);
1786 *data
= malloc(data_size
);
1789 GetRegionData(hrgn
, data_size
, *data
);
1797 data_size
= sizeof(RGNDATAHEADER
) + sizeof(RECT
);
1799 *data
= calloc(1, data_size
);
1803 (*data
)->rdh
.dwSize
= sizeof(RGNDATAHEADER
);
1804 (*data
)->rdh
.iType
= RDH_RECTANGLES
;
1805 (*data
)->rdh
.nCount
= 1;
1806 (*data
)->rdh
.nRgnSize
= sizeof(RECT
);
1807 (*data
)->rdh
.rcBound
.left
= (*data
)->rdh
.rcBound
.top
= -0x400000;
1808 (*data
)->rdh
.rcBound
.right
= (*data
)->rdh
.rcBound
.bottom
= 0x400000;
1810 memcpy((*data
)->Buffer
, &(*data
)->rdh
.rcBound
, sizeof(RECT
));
1817 GdipDeleteRegion(region_copy
);
1823 GpStatus WINGDIPAPI
GdipGetRegionScansCount(GpRegion
*region
, UINT
*count
, GpMatrix
*matrix
)
1828 TRACE("(%p, %p, %s)\n", region
, count
, debugstr_matrix(matrix
));
1830 if (!region
|| !count
|| !matrix
)
1831 return InvalidParameter
;
1833 stat
= get_region_scans_data(region
, matrix
, &data
);
1837 *count
= data
->rdh
.nCount
;
1844 GpStatus WINGDIPAPI
GdipGetRegionScansI(GpRegion
*region
, GpRect
*scans
, INT
*count
, GpMatrix
*matrix
)
1851 if (!region
|| !count
|| !matrix
)
1852 return InvalidParameter
;
1854 stat
= get_region_scans_data(region
, matrix
, &data
);
1858 *count
= data
->rdh
.nCount
;
1859 rects
= (RECT
*)data
->Buffer
;
1863 for (i
=0; i
<data
->rdh
.nCount
; i
++)
1865 scans
[i
].X
= rects
[i
].left
;
1866 scans
[i
].Y
= rects
[i
].top
;
1867 scans
[i
].Width
= rects
[i
].right
- rects
[i
].left
;
1868 scans
[i
].Height
= rects
[i
].bottom
- rects
[i
].top
;
1878 GpStatus WINGDIPAPI
GdipGetRegionScans(GpRegion
*region
, GpRectF
*scans
, INT
*count
, GpMatrix
*matrix
)
1885 if (!region
|| !count
|| !matrix
)
1886 return InvalidParameter
;
1888 stat
= get_region_scans_data(region
, matrix
, &data
);
1892 *count
= data
->rdh
.nCount
;
1893 rects
= (RECT
*)data
->Buffer
;
1897 for (i
=0; i
<data
->rdh
.nCount
; i
++)
1899 scans
[i
].X
= rects
[i
].left
;
1900 scans
[i
].Y
= rects
[i
].top
;
1901 scans
[i
].Width
= rects
[i
].right
- rects
[i
].left
;
1902 scans
[i
].Height
= rects
[i
].bottom
- rects
[i
].top
;