/**********************************************************
*
- * Data returned by GdipGetRegionData (for rectangle based regions)
- * looks something like this:
+ * Data returned by GdipGetRegionData looks something like this:
*
* struct region_data_header
* {
* DWORD num_ops; number of combining ops * 2
* };
*
- * Then follows a sequence of combining ops and RECTFs.
+ * Then follows a sequence of combining ops and region elements.
+ *
+ * A region element is either a RECTF or some path data.
*
* Combining ops are just stored as their CombineMode value.
*
* stored as 0x10000002 (with no following RECTF) and an infinite rect
* is stored as 0x10000003 (again with no following RECTF).
*
- * The combining ops are stored in the reverse order to the RECTFs and in the
- * reverse order to which the region was constructed.
+ * Path data is preceded by the DWORD 0x10000001. Then follows a
+ * DWORD size and then size bytes of data.
+ *
+ * The combining ops are stored in the reverse order to the region
+ * elements and in the reverse order to which the region was
+ * constructed.
*
- * When two or more complex regions (ie those with more than one rect)
- * are combined, the combining op for the two regions comes first,
- * then the combining ops for the rects in region 1, followed by the
- * rects for region 1, then follows the combining ops for region 2 and
- * finally region 2's rects. Presumably you're supposed to use the
- * 0x10000000 rect header to find the end of the op list (the count of
- * the rects in each region is not stored).
+ * When two or more complex regions (ie those with more than one
+ * element) are combined, the combining op for the two regions comes
+ * first, then the combining ops for the region elements in region 1,
+ * followed by the region elements for region 1, then follows the
+ * combining ops for region 2 and finally region 2's region elements.
+ * Presumably you're supposed to use the 0x1000000x header to find the
+ * end of the op list (the count of the elements in each region is not
+ * stored).
*
- * When a simple region (1 rect) is combined, it's treated as if a single rect
- * is being combined.
+ * When a simple region (1 element) is combined, it's treated as if a
+ * single rect/path is being combined.
*
*/
+typedef enum RegionType
+{
+ RegionDataRect = 0x10000000,
+ RegionDataPath = 0x10000001,
+ RegionDataEmptyRect = 0x10000002,
+ RegionDataInfiniteRect = 0x10000003,
+} RegionType;
+
+/* Header size as far as header->size is concerned. This doesn't include
+ * header->size or header->checksum
+ */
+static const INT sizeheader_size = sizeof(DWORD) * 2;
+
+static inline INT get_element_size(const region_element* element)
+{
+ INT needed = sizeof(DWORD); /* DWORD for the type */
+ switch(element->type)
+ {
+ case RegionDataRect:
+ return needed + sizeof(GpRect);
+ case RegionDataPath:
+ needed += element->elementdata.pathdata.pathheader.size;
+ needed += sizeof(DWORD); /* Extra DWORD for pathheader.size */
+ return needed;
+ case RegionDataEmptyRect:
+ case RegionDataInfiniteRect:
+ return needed;
+ default:
+ needed += get_element_size(element->elementdata.combine.left);
+ needed += get_element_size(element->elementdata.combine.right);
+ return needed;
+ }
+
+ return 0;
+}
+
+/* Does not check parameters, caller must do that */
+static inline GpStatus init_region(GpRegion* region, const RegionType type)
+{
+ region->node.type = type;
+ region->header.checksum = 0xdeadbeef;
+ region->header.magic = VERSION_MAGIC;
+ region->header.num_children = 0;
+ region->header.size = sizeheader_size + get_element_size(®ion->node);
+
+ return Ok;
+}
+
+static inline void delete_element(region_element* element)
+{
+ switch(element->type)
+ {
+ case RegionDataRect:
+ break;
+ case RegionDataPath:
+ GdipDeletePath(element->elementdata.pathdata.path);
+ break;
+ case RegionDataEmptyRect:
+ case RegionDataInfiniteRect:
+ break;
+ default:
+ delete_element(element->elementdata.combine.left);
+ delete_element(element->elementdata.combine.right);
+ GdipFree(element->elementdata.combine.left);
+ GdipFree(element->elementdata.combine.right);
+ break;
+ }
+}
+
+/*****************************************************************************
+ * GdipCloneRegion [GDIPLUS.@]
+ */
GpStatus WINGDIPAPI GdipCloneRegion(GpRegion *region, GpRegion **clone)
{
FIXME("(%p %p): stub\n", region, clone);
GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region)
{
- FIXME("(%p): stub\n", region);
+ if(!region)
+ return InvalidParameter;
- *region = NULL;
- return NotImplemented;
+ TRACE("%p\n", region);
+
+ *region = GdipAlloc(sizeof(GpRegion));
+ if(!*region)
+ return OutOfMemory;
+
+ return init_region(*region, RegionDataInfiniteRect);
}
GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region)
GpStatus WINGDIPAPI GdipDeleteRegion(GpRegion *region)
{
- FIXME("(%p): stub\n", region);
- return NotImplemented;
+ TRACE("%p\n", region);
+
+ if (!region)
+ return InvalidParameter;
+
+ delete_element(®ion->node);
+ GdipFree(region);
+
+ return Ok;
}
GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect)
GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed)
{
- FIXME("(%p, %p): stub\n", region, needed);
+ if (!(region && needed))
+ return InvalidParameter;
- return NotImplemented;
+ TRACE("%p, %p\n", region, needed);
+
+ /* header.size doesn't count header.size and header.checksum */
+ *needed = region->header.size + sizeof(DWORD) * 2;
+
+ return Ok;
}
+/*****************************************************************************
+ * GdipGetRegionHRgn [GDIPLUS.@]
+ */
GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HRGN *hrgn)
{
FIXME("(%p, %p, %p): stub\n", region, graphics, hrgn);
GpStatus WINGDIPAPI GdipSetEmpty(GpRegion *region)
{
- static int calls;
+ GpStatus stat;
- if(!(calls++))
- FIXME("not implemented\n");
+ TRACE("%p\n", region);
- return NotImplemented;
+ if (!region)
+ return InvalidParameter;
+
+ delete_element(®ion->node);
+ stat = init_region(region, RegionDataEmptyRect);
+
+ return stat;
}
GpStatus WINGDIPAPI GdipSetInfinite(GpRegion *region)
{
- static int calls;
+ GpStatus stat;
- if(!(calls++))
- FIXME("not implemented\n");
+ if (!region)
+ return InvalidParameter;
- return NotImplemented;
+ TRACE("%p\n", region);
+
+ delete_element(®ion->node);
+ stat = init_region(region, RegionDataInfiniteRect);
+
+ return stat;
}
GpStatus WINGDIPAPI GdipTransformRegion(GpRegion *region, GpMatrix *matrix)