/**********************************************************
*
- * 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.
*
*/
-GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region, GDIPCONST GpRectF *rect,
- CombineMode mode)
+#define FLAGS_NOFLAGS 0x0
+#define FLAGS_INTPATH 0x4000
+
+/* 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;
+
+typedef struct packed_point
{
- FIXME("(%p %p %d): stub\n", region, rect, mode);
- return NotImplemented;
+ short X;
+ short Y;
+} packed_point;
+
+/* Everything is measured in DWORDS; round up if there's a remainder */
+static inline INT get_pathtypes_size(const GpPath* path)
+{
+ INT needed = path->pathdata.Count / sizeof(DWORD);
+
+ if (path->pathdata.Count % sizeof(DWORD) > 0)
+ needed++;
+
+ return needed * sizeof(DWORD);
}
-GpStatus WINGDIPAPI GdipCombineRegionRectI(GpRegion *region, GDIPCONST GpRect *rect,
- CombineMode mode)
+static inline INT get_element_size(const region_element* element)
{
- FIXME("(%p %p %d): stub\n", region, rect, mode);
- return NotImplemented;
+ 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;
}
-GpStatus WINGDIPAPI GdipCombineRegionRegion(GpRegion *region1, GpRegion *region2,
- CombineMode mode)
+/* Does not check parameters, caller must do that */
+static inline GpStatus init_region(GpRegion* region, const RegionType type)
{
- FIXME("(%p %p %d): stub\n", region1, region2, mode);
- return NotImplemented;
+ 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 GpStatus clone_element(const region_element* element,
+ region_element** element2)
+{
+ GpStatus stat;
+
+ /* root node is allocated with GpRegion */
+ if(!*element2){
+ *element2 = GdipAlloc(sizeof(region_element));
+ if (!*element2)
+ return OutOfMemory;
+ }
+
+ (*element2)->type = element->type;
+
+ switch (element->type)
+ {
+ case RegionDataRect:
+ (*element2)->elementdata.rect = element->elementdata.rect;
+ break;
+ case RegionDataEmptyRect:
+ case RegionDataInfiniteRect:
+ break;
+ case RegionDataPath:
+ (*element2)->elementdata.pathdata.pathheader = element->elementdata.pathdata.pathheader;
+ stat = GdipClonePath(element->elementdata.pathdata.path,
+ &(*element2)->elementdata.pathdata.path);
+ if (stat != Ok) goto clone_out;
+ break;
+ default:
+ (*element2)->elementdata.combine.left = NULL;
+ (*element2)->elementdata.combine.right = NULL;
+
+ stat = clone_element(element->elementdata.combine.left,
+ &(*element2)->elementdata.combine.left);
+ if (stat != Ok) goto clone_out;
+ stat = clone_element(element->elementdata.combine.right,
+ &(*element2)->elementdata.combine.right);
+ if (stat != Ok) goto clone_out;
+ break;
+ }
+
+ return Ok;
+
+clone_out:
+ delete_element(*element2);
+ *element2 = NULL;
+ return stat;
+}
+
+/* Common code for CombineRegion*
+ * All the caller has to do is get its format into an element
+ */
+static inline void fuse_region(GpRegion* region, region_element* left,
+ region_element* right, const CombineMode mode)
+{
+ region->node.type = mode;
+ region->node.elementdata.combine.left = left;
+ region->node.elementdata.combine.right = right;
+
+ region->header.size = sizeheader_size + get_element_size(®ion->node);
+ region->header.num_children += 2;
+}
+
+/*****************************************************************************
+ * GdipCloneRegion [GDIPLUS.@]
+ *
+ * Creates a deep copy of the region
+ *
+ * PARAMS
+ * region [I] source region
+ * clone [O] resulting clone
+ *
+ * RETURNS
+ * SUCCESS: Ok
+ * FAILURE: InvalidParameter or OutOfMemory
+ */
+GpStatus WINGDIPAPI GdipCloneRegion(GpRegion *region, GpRegion **clone)
+{
+ region_element *element;
+
+ TRACE("%p %p\n", region, clone);
+
+ if (!(region && clone))
+ return InvalidParameter;
+
+ *clone = GdipAlloc(sizeof(GpRegion));
+ if (!*clone)
+ return OutOfMemory;
+ element = &(*clone)->node;
+
+ (*clone)->header = region->header;
+ return clone_element(®ion->node, &element);
}
+/*****************************************************************************
+ * GdipCombineRegionPath [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCombineRegionPath(GpRegion *region, GpPath *path, CombineMode mode)
+{
+ GpRegion *path_region;
+ region_element *left, *right = NULL;
+ GpStatus stat;
+
+ TRACE("%p %p %d\n", region, path, mode);
+
+ if (!(region && path))
+ return InvalidParameter;
+
+ stat = GdipCreateRegionPath(path, &path_region);
+ if (stat != Ok)
+ return stat;
+
+ /* simply replace region data */
+ if(mode == CombineModeReplace){
+ delete_element(®ion->node);
+ memcpy(region, path_region, sizeof(GpRegion));
+ GdipFree(path_region);
+ return Ok;
+ }
+
+ left = GdipAlloc(sizeof(region_element));
+ if (!left)
+ goto out;
+ *left = region->node;
+
+ stat = clone_element(&path_region->node, &right);
+ if (stat != Ok)
+ goto out;
+
+ fuse_region(region, left, right, mode);
+
+ GdipDeleteRegion(path_region);
+ return Ok;
+
+out:
+ GdipFree(left);
+ GdipDeleteRegion(path_region);
+ return stat;
+}
+
+/*****************************************************************************
+ * GdipCombineRegionRect [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region,
+ GDIPCONST GpRectF *rect, CombineMode mode)
+{
+ GpRegion *rect_region;
+ region_element *left, *right = NULL;
+ GpStatus stat;
+
+ TRACE("%p %p %d\n", region, rect, mode);
+
+ if (!(region && rect))
+ return InvalidParameter;
+
+ stat = GdipCreateRegionRect(rect, &rect_region);
+ if (stat != Ok)
+ return stat;
+
+ /* simply replace region data */
+ if(mode == CombineModeReplace){
+ delete_element(®ion->node);
+ memcpy(region, rect_region, sizeof(GpRegion));
+ GdipFree(rect_region);
+ return Ok;
+ }
+
+ left = GdipAlloc(sizeof(region_element));
+ if (!left)
+ goto out;
+ memcpy(left, ®ion->node, sizeof(region_element));
+
+ stat = clone_element(&rect_region->node, &right);
+ if (stat != Ok)
+ goto out;
+
+ fuse_region(region, left, right, mode);
+
+ GdipDeleteRegion(rect_region);
+ return Ok;
+
+out:
+ GdipFree(left);
+ GdipDeleteRegion(rect_region);
+ return stat;
+}
+
+/*****************************************************************************
+ * GdipCombineRegionRectI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCombineRegionRectI(GpRegion *region,
+ GDIPCONST GpRect *rect, CombineMode mode)
+{
+ GpRectF rectf;
+
+ TRACE("%p %p %d\n", region, rect, mode);
+
+ if (!rect)
+ return InvalidParameter;
+
+ rectf.X = (REAL)rect->X;
+ rectf.Y = (REAL)rect->Y;
+ rectf.Height = (REAL)rect->Height;
+ rectf.Width = (REAL)rect->Width;
+
+ return GdipCombineRegionRect(region, &rectf, mode);
+}
+
+/*****************************************************************************
+ * GdipCombineRegionRegion [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCombineRegionRegion(GpRegion *region1,
+ GpRegion *region2, CombineMode mode)
+{
+ region_element *left, *right = NULL;
+ GpStatus stat;
+ GpRegion *reg2copy;
+
+ TRACE("%p %p %d\n", region1, region2, mode);
+
+ if(!(region1 && region2))
+ return InvalidParameter;
+
+ /* simply replace region data */
+ if(mode == CombineModeReplace){
+ stat = GdipCloneRegion(region2, ®2copy);
+ if(stat != Ok) return stat;
+
+ delete_element(®ion1->node);
+ memcpy(region1, reg2copy, sizeof(GpRegion));
+ GdipFree(reg2copy);
+ return Ok;
+ }
+
+ left = GdipAlloc(sizeof(region_element));
+ if (!left)
+ return OutOfMemory;
+
+ *left = region1->node;
+ stat = clone_element(®ion2->node, &right);
+ if (stat != Ok)
+ {
+ GdipFree(left);
+ return OutOfMemory;
+ }
+
+ fuse_region(region1, left, right, mode);
+ region1->header.num_children += region2->header.num_children;
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipCreateRegion [GDIPLUS.@]
+ */
GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region)
{
- FIXME("(%p): stub\n", region);
+ TRACE("%p\n", region);
- *region = NULL;
- return NotImplemented;
+ if(!region)
+ return InvalidParameter;
+
+ *region = GdipAlloc(sizeof(GpRegion));
+ if(!*region)
+ return OutOfMemory;
+
+ return init_region(*region, RegionDataInfiniteRect);
}
+/*****************************************************************************
+ * GdipCreateRegionPath [GDIPLUS.@]
+ *
+ * Creates a GpRegion from a GpPath
+ *
+ * PARAMS
+ * path [I] path to base the region on
+ * region [O] pointer to the newly allocated region
+ *
+ * RETURNS
+ * SUCCESS: Ok
+ * FAILURE: InvalidParameter
+ *
+ * NOTES
+ * If a path has no floating point points, its points will be stored as shorts
+ * (INTPATH)
+ *
+ * If a path is empty, it is considered to be an INTPATH
+ */
GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region)
{
- FIXME("(%p, %p): stub\n", path, region);
+ region_element* element;
+ GpPoint *pointsi;
+ GpPointF *pointsf;
- *region = NULL;
- return NotImplemented;
+ GpStatus stat;
+ DWORD flags = FLAGS_INTPATH;
+ INT count, i;
+
+ TRACE("%p, %p\n", path, region);
+
+ if (!(path && region))
+ return InvalidParameter;
+
+ *region = GdipAlloc(sizeof(GpRegion));
+ if(!*region)
+ return OutOfMemory;
+ stat = init_region(*region, RegionDataPath);
+ if (stat != Ok)
+ {
+ GdipDeleteRegion(*region);
+ return stat;
+ }
+ element = &(*region)->node;
+ count = path->pathdata.Count;
+
+ /* Test to see if the path is an Integer path */
+ if (count)
+ {
+ pointsi = GdipAlloc(sizeof(GpPoint) * count);
+ pointsf = GdipAlloc(sizeof(GpPointF) * count);
+ if (!(pointsi && pointsf))
+ {
+ GdipFree(pointsi);
+ GdipFree(pointsf);
+ GdipDeleteRegion(*region);
+ return OutOfMemory;
+ }
+
+ stat = GdipGetPathPointsI(path, pointsi, count);
+ if (stat != Ok)
+ {
+ GdipDeleteRegion(*region);
+ return stat;
+ }
+ stat = GdipGetPathPoints(path, pointsf, count);
+ if (stat != Ok)
+ {
+ GdipDeleteRegion(*region);
+ return stat;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ if (!(pointsi[i].X == pointsf[i].X &&
+ pointsi[i].Y == pointsf[i].Y ))
+ {
+ flags = FLAGS_NOFLAGS;
+ break;
+ }
+ }
+ GdipFree(pointsi);
+ GdipFree(pointsf);
+ }
+
+ stat = GdipClonePath(path, &element->elementdata.pathdata.path);
+ if (stat != Ok)
+ {
+ GdipDeleteRegion(*region);
+ return stat;
+ }
+
+ /* 3 for headers, once again size doesn't count itself */
+ element->elementdata.pathdata.pathheader.size = ((sizeof(DWORD) * 3));
+ switch(flags)
+ {
+ /* Floats, sent out as floats */
+ case FLAGS_NOFLAGS:
+ element->elementdata.pathdata.pathheader.size +=
+ (sizeof(DWORD) * count * 2);
+ break;
+ /* INTs, sent out as packed shorts */
+ case FLAGS_INTPATH:
+ element->elementdata.pathdata.pathheader.size +=
+ (sizeof(DWORD) * count);
+ break;
+ default:
+ FIXME("Unhandled flags (%08x). Expect wrong results.\n", flags);
+ }
+ element->elementdata.pathdata.pathheader.size += get_pathtypes_size(path);
+ element->elementdata.pathdata.pathheader.magic = VERSION_MAGIC;
+ element->elementdata.pathdata.pathheader.count = count;
+ element->elementdata.pathdata.pathheader.flags = flags;
+ (*region)->header.size = sizeheader_size + get_element_size(element);
+
+ return Ok;
}
-GpStatus WINGDIPAPI GdipCreateRegionRect(GDIPCONST GpRectF *rect, GpRegion **region)
+/*****************************************************************************
+ * GdipCreateRegionRect [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCreateRegionRect(GDIPCONST GpRectF *rect,
+ GpRegion **region)
{
- FIXME("(%p, %p): stub\n", rect, region);
+ GpStatus stat;
- *region = NULL;
- return NotImplemented;
+ TRACE("%p, %p\n", rect, region);
+
+ if (!(rect && region))
+ return InvalidParameter;
+
+ *region = GdipAlloc(sizeof(GpRegion));
+ stat = init_region(*region, RegionDataRect);
+ if(stat != Ok)
+ {
+ GdipDeleteRegion(*region);
+ return stat;
+ }
+
+ (*region)->node.elementdata.rect.X = rect->X;
+ (*region)->node.elementdata.rect.Y = rect->Y;
+ (*region)->node.elementdata.rect.Width = rect->Width;
+ (*region)->node.elementdata.rect.Height = rect->Height;
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipCreateRegionRectI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect,
+ GpRegion **region)
+{
+ GpRectF rectf;
+
+ TRACE("%p, %p\n", rect, region);
+
+ rectf.X = (REAL)rect->X;
+ rectf.Y = (REAL)rect->Y;
+ rectf.Width = (REAL)rect->Width;
+ rectf.Height = (REAL)rect->Height;
+
+ return GdipCreateRegionRect(&rectf, region);
}
-GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect, GpRegion **region)
+GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region)
{
- FIXME("(%p, %p): stub\n", rect, region);
+ FIXME("(%p, %d, %p): stub\n", data, size, region);
*region = NULL;
return NotImplemented;
}
+
+/******************************************************************************
+ * GdipCreateRegionHrgn [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipCreateRegionHrgn(HRGN hrgn, GpRegion **region)
+{
+ DWORD size;
+ LPRGNDATA buf;
+ LPRECT rect;
+ GpStatus stat;
+ GpPath* path;
+ GpRegion* local;
+ int i;
+
+ TRACE("(%p, %p)\n", hrgn, region);
+
+ if(!region || !(size = GetRegionData(hrgn, 0, NULL)))
+ return InvalidParameter;
+
+ buf = GdipAlloc(size);
+ if(!buf)
+ return OutOfMemory;
+
+ if(!GetRegionData(hrgn, size, buf)){
+ GdipFree(buf);
+ return GenericError;
+ }
+
+ if(buf->rdh.nCount == 0){
+ if((stat = GdipCreateRegion(&local)) != Ok){
+ GdipFree(buf);
+ return stat;
+ }
+ if((stat = GdipSetEmpty(local)) != Ok){
+ GdipFree(buf);
+ GdipDeleteRegion(local);
+ return stat;
+ }
+ *region = local;
+ GdipFree(buf);
+ return Ok;
+ }
+
+ if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok){
+ GdipFree(buf);
+ return stat;
+ }
+
+ rect = (LPRECT)buf->Buffer;
+ for(i = 0; i < buf->rdh.nCount; i++){
+ if((stat = GdipAddPathRectangle(path, (REAL)rect->left, (REAL)rect->top,
+ (REAL)(rect->right - rect->left), (REAL)(rect->bottom - rect->top))) != Ok){
+ GdipFree(buf);
+ GdipDeletePath(path);
+ return stat;
+ }
+ rect++;
+ }
+
+ stat = GdipCreateRegionPath(path, region);
+
+ GdipFree(buf);
+ GdipDeletePath(path);
+ return stat;
+}
+
+/*****************************************************************************
+ * GdipDeleteRegion [GDIPLUS.@]
+ */
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 GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, UINT *needed)
+/*****************************************************************************
+ * GdipGetRegionBounds [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect)
{
- FIXME("(%p, %p, %d, %p): stub\n", region, buffer, size, needed);
+ HRGN hrgn;
+ RECT r;
+ GpStatus status;
- return NotImplemented;
+ TRACE("(%p, %p, %p)\n", region, graphics, rect);
+
+ if(!region || !graphics || !rect)
+ return InvalidParameter;
+
+ /* Contrary to MSDN, native ignores the graphics transform. */
+ status = GdipGetRegionHRgn(region, NULL, &hrgn);
+ if(status != Ok)
+ return status;
+
+ /* infinite */
+ if(!hrgn){
+ rect->X = rect->Y = -(REAL)(1 << 22);
+ rect->Width = rect->Height = (REAL)(1 << 23);
+ return Ok;
+ }
+
+ if(!GetRgnBox(hrgn, &r)){
+ DeleteObject(hrgn);
+ return GenericError;
+ }
+
+ rect->X = r.left;
+ rect->Y = r.top;
+ rect->Width = r.right - r.left;
+ rect->Height = r.bottom - r.top;
+
+ return Ok;
}
+/*****************************************************************************
+ * GdipGetRegionBoundsI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics, GpRect *rect)
+{
+ GpRectF rectf;
+ GpStatus status;
+
+ TRACE("(%p, %p, %p)\n", region, graphics, rect);
+
+ if(!rect)
+ return InvalidParameter;
+
+ status = GdipGetRegionBounds(region, graphics, &rectf);
+ if(status == Ok){
+ rect->X = roundr(rectf.X);
+ rect->Y = roundr(rectf.X);
+ rect->Width = roundr(rectf.Width);
+ rect->Height = roundr(rectf.Height);
+ }
+
+ return status;
+}
+
+static inline void write_dword(DWORD* location, INT* offset, const DWORD write)
+{
+ location[*offset] = write;
+ (*offset)++;
+}
+
+static inline void write_float(DWORD* location, INT* offset, const FLOAT write)
+{
+ ((FLOAT*)location)[*offset] = write;
+ (*offset)++;
+}
+
+static inline void write_packed_point(DWORD* location, INT* offset,
+ const GpPointF* write)
+{
+ packed_point point;
+
+ point.X = write->X;
+ point.Y = write->Y;
+ memcpy(location + *offset, &point, sizeof(packed_point));
+ (*offset)++;
+}
+
+static inline void write_path_types(DWORD* location, INT* offset,
+ const GpPath* path)
+{
+ memcpy(location + *offset, path->pathdata.Types, path->pathdata.Count);
+
+ /* The unwritten parts of the DWORD (if any) must be cleared */
+ if (path->pathdata.Count % sizeof(DWORD))
+ ZeroMemory(((BYTE*)location) + (*offset * sizeof(DWORD)) +
+ path->pathdata.Count,
+ sizeof(DWORD) - path->pathdata.Count % sizeof(DWORD));
+ *offset += (get_pathtypes_size(path) / sizeof(DWORD));
+}
+
+static void write_element(const region_element* element, DWORD *buffer,
+ INT* filled)
+{
+ write_dword(buffer, filled, element->type);
+ switch (element->type)
+ {
+ case CombineModeReplace:
+ case CombineModeIntersect:
+ case CombineModeUnion:
+ case CombineModeXor:
+ case CombineModeExclude:
+ case CombineModeComplement:
+ write_element(element->elementdata.combine.left, buffer, filled);
+ write_element(element->elementdata.combine.right, buffer, filled);
+ break;
+ case RegionDataRect:
+ write_float(buffer, filled, element->elementdata.rect.X);
+ write_float(buffer, filled, element->elementdata.rect.Y);
+ write_float(buffer, filled, element->elementdata.rect.Width);
+ write_float(buffer, filled, element->elementdata.rect.Height);
+ break;
+ case RegionDataPath:
+ {
+ INT i;
+ const GpPath* path = element->elementdata.pathdata.path;
+
+ memcpy(buffer + *filled, &element->elementdata.pathdata.pathheader,
+ sizeof(element->elementdata.pathdata.pathheader));
+ *filled += sizeof(element->elementdata.pathdata.pathheader) / sizeof(DWORD);
+ switch (element->elementdata.pathdata.pathheader.flags)
+ {
+ case FLAGS_NOFLAGS:
+ for (i = 0; i < path->pathdata.Count; i++)
+ {
+ write_float(buffer, filled, path->pathdata.Points[i].X);
+ write_float(buffer, filled, path->pathdata.Points[i].Y);
+ }
+ break;
+ case FLAGS_INTPATH:
+ for (i = 0; i < path->pathdata.Count; i++)
+ {
+ write_packed_point(buffer, filled,
+ &path->pathdata.Points[i]);
+ }
+ }
+ write_path_types(buffer, filled, path);
+ break;
+ }
+ case RegionDataEmptyRect:
+ case RegionDataInfiniteRect:
+ break;
+ }
+}
+
+/*****************************************************************************
+ * GdipGetRegionData [GDIPLUS.@]
+ *
+ * Returns the header, followed by combining ops and region elements.
+ *
+ * PARAMS
+ * region [I] region to retrieve from
+ * buffer [O] buffer to hold the resulting data
+ * size [I] size of the buffer
+ * needed [O] (optional) how much data was written
+ *
+ * RETURNS
+ * SUCCESS: Ok
+ * FAILURE: InvalidParameter
+ *
+ * NOTES
+ * The header contains the size, a checksum, a version string, and the number
+ * of children. The size does not count itself or the checksum.
+ * Version is always something like 0xdbc01001 or 0xdbc01002
+ *
+ * An element is a RECT, or PATH; Combining ops are stored as their
+ * CombineMode value. Special regions (infinite, empty) emit just their
+ * op-code; GpRectFs emit their code followed by their points; GpPaths emit
+ * their code followed by a second header for the path followed by the actual
+ * path data. Followed by the flags for each point. The pathheader contains
+ * the size of the data to follow, a version number again, followed by a count
+ * of how many points, and any special flags which may apply. 0x4000 means its
+ * a path of shorts instead of FLOAT.
+ *
+ * Combining Ops are stored in reverse order from when they were constructed;
+ * the output is a tree where the left side combining area is always taken
+ * first.
+ */
+GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
+ UINT *needed)
+{
+ INT filled = 0;
+
+ TRACE("%p, %p, %d, %p\n", region, buffer, size, needed);
+
+ if (!(region && buffer && size))
+ return InvalidParameter;
+
+ memcpy(buffer, ®ion->header, sizeof(region->header));
+ filled += sizeof(region->header) / sizeof(DWORD);
+ /* With few exceptions, everything written is DWORD aligned,
+ * so use that as our base */
+ write_element(®ion->node, (DWORD*)buffer, &filled);
+
+ if (needed)
+ *needed = filled * sizeof(DWORD);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipGetRegionDataSize [GDIPLUS.@]
+ */
GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed)
{
- FIXME("(%p, %p): stub\n", region, needed);
+ TRACE("%p, %p\n", region, needed);
- return NotImplemented;
+ if (!(region && needed))
+ return InvalidParameter;
+
+ /* header.size doesn't count header.size and header.checksum */
+ *needed = region->header.size + sizeof(DWORD) * 2;
+
+ return Ok;
+}
+
+static GpStatus get_path_hrgn(GpPath *path, GpGraphics *graphics, HRGN *hrgn)
+{
+ HDC new_hdc=NULL;
+ GpStatus stat;
+ INT save_state;
+
+ if (!graphics)
+ {
+ new_hdc = GetDC(0);
+ if (!new_hdc)
+ return OutOfMemory;
+
+ stat = GdipCreateFromHDC(new_hdc, &graphics);
+ if (stat != Ok)
+ {
+ ReleaseDC(0, new_hdc);
+ return stat;
+ }
+ }
+
+ save_state = SaveDC(graphics->hdc);
+ EndPath(graphics->hdc);
+
+ SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
+ : WINDING));
+
+ stat = trace_path(graphics, path);
+ if (stat == Ok)
+ {
+ *hrgn = PathToRegion(graphics->hdc);
+ stat = *hrgn ? Ok : OutOfMemory;
+ }
+
+ RestoreDC(graphics->hdc, save_state);
+ if (new_hdc)
+ {
+ ReleaseDC(0, new_hdc);
+ GdipDeleteGraphics(graphics);
+ }
+
+ return stat;
}
+static GpStatus get_region_hrgn(struct region_element *element, GpGraphics *graphics, HRGN *hrgn)
+{
+ switch (element->type)
+ {
+ case RegionDataInfiniteRect:
+ *hrgn = NULL;
+ return Ok;
+ case RegionDataEmptyRect:
+ *hrgn = CreateRectRgn(0, 0, 0, 0);
+ return *hrgn ? Ok : OutOfMemory;
+ case RegionDataPath:
+ return get_path_hrgn(element->elementdata.pathdata.path, graphics, hrgn);
+ case RegionDataRect:
+ {
+ GpPath* path;
+ GpStatus stat;
+ GpRectF* rc = &element->elementdata.rect;
+
+ stat = GdipCreatePath(FillModeAlternate, &path);
+ if (stat != Ok)
+ return stat;
+ stat = GdipAddPathRectangle(path, rc->X, rc->Y, rc->Width, rc->Height);
+
+ if (stat == Ok)
+ stat = get_path_hrgn(path, graphics, hrgn);
+
+ GdipDeletePath(path);
+
+ return stat;
+ }
+ case CombineModeIntersect:
+ case CombineModeUnion:
+ case CombineModeXor:
+ case CombineModeExclude:
+ case CombineModeComplement:
+ {
+ HRGN left, right;
+ GpStatus stat;
+ int ret;
+
+ stat = get_region_hrgn(element->elementdata.combine.left, graphics, &left);
+ if (stat != Ok)
+ {
+ *hrgn = NULL;
+ return stat;
+ }
+
+ if (left == NULL)
+ {
+ /* existing region is infinite */
+ switch (element->type)
+ {
+ case CombineModeIntersect:
+ return get_region_hrgn(element->elementdata.combine.right, graphics, hrgn);
+ case CombineModeXor: case CombineModeExclude:
+ FIXME("cannot exclude from an infinite region\n");
+ /* fall-through */
+ case CombineModeUnion: case CombineModeComplement:
+ *hrgn = NULL;
+ return Ok;
+ }
+ }
+
+ stat = get_region_hrgn(element->elementdata.combine.right, graphics, &right);
+ if (stat != Ok)
+ {
+ DeleteObject(left);
+ *hrgn = NULL;
+ return stat;
+ }
+
+ if (right == NULL)
+ {
+ /* new region is infinite */
+ switch (element->type)
+ {
+ case CombineModeIntersect:
+ *hrgn = left;
+ return Ok;
+ case CombineModeXor: case CombineModeComplement:
+ FIXME("cannot exclude from an infinite region\n");
+ /* fall-through */
+ case CombineModeUnion: case CombineModeExclude:
+ DeleteObject(left);
+ *hrgn = NULL;
+ return Ok;
+ }
+ }
+
+ switch (element->type)
+ {
+ case CombineModeIntersect:
+ ret = CombineRgn(left, left, right, RGN_AND);
+ break;
+ case CombineModeUnion:
+ ret = CombineRgn(left, left, right, RGN_OR);
+ break;
+ case CombineModeXor:
+ ret = CombineRgn(left, left, right, RGN_XOR);
+ break;
+ case CombineModeExclude:
+ ret = CombineRgn(left, left, right, RGN_DIFF);
+ break;
+ case CombineModeComplement:
+ ret = CombineRgn(left, right, left, RGN_DIFF);
+ break;
+ default:
+ ret = ERROR;
+ }
+
+ DeleteObject(right);
+
+ if (ret == ERROR)
+ {
+ DeleteObject(left);
+ *hrgn = NULL;
+ return GenericError;
+ }
+
+ *hrgn = left;
+ return Ok;
+ }
+ default:
+ FIXME("GdipGetRegionHRgn unimplemented for region type=%x\n", element->type);
+ *hrgn = NULL;
+ return NotImplemented;
+ }
+}
+
+/*****************************************************************************
+ * GdipGetRegionHRgn [GDIPLUS.@]
+ */
GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HRGN *hrgn)
{
- FIXME("(%p, %p, %p): stub\n", region, graphics, hrgn);
+ TRACE("(%p, %p, %p)\n", region, graphics, hrgn);
- *hrgn = NULL;
- return NotImplemented;
+ if (!region || !hrgn)
+ return InvalidParameter;
+
+ return get_region_hrgn(®ion->node, graphics, hrgn);
+}
+
+GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
+{
+ TRACE("(%p, %p, %p)\n", region, graphics, res);
+
+ if(!region || !graphics || !res)
+ return InvalidParameter;
+
+ *res = (region->node.type == RegionDataEmptyRect);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipIsEqualRegion [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipIsEqualRegion(GpRegion *region, GpRegion *region2, GpGraphics *graphics,
+ BOOL *res)
+{
+ HRGN hrgn1, hrgn2;
+ GpStatus stat;
+
+ TRACE("(%p, %p, %p, %p)\n", region, region2, graphics, res);
+
+ if(!region || !region2 || !graphics || !res)
+ return InvalidParameter;
+
+ stat = GdipGetRegionHRgn(region, graphics, &hrgn1);
+ if(stat != Ok)
+ return stat;
+ stat = GdipGetRegionHRgn(region2, graphics, &hrgn2);
+ if(stat != Ok){
+ DeleteObject(hrgn1);
+ return stat;
+ }
+
+ *res = EqualRgn(hrgn1, hrgn2);
+
+ /* one of GpRegions is infinite */
+ if(*res == ERROR)
+ *res = (!hrgn1 && !hrgn2);
+
+ DeleteObject(hrgn1);
+ DeleteObject(hrgn2);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipIsInfiniteRegion [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipIsInfiniteRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
+{
+ /* I think graphics is ignored here */
+ TRACE("(%p, %p, %p)\n", region, graphics, res);
+
+ if(!region || !graphics || !res)
+ return InvalidParameter;
+
+ *res = (region->node.type == RegionDataInfiniteRect);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipIsVisibleRegionRect [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipIsVisibleRegionRect(GpRegion* region, REAL x, REAL y, REAL w, REAL h, GpGraphics *graphics, BOOL *res)
+{
+ HRGN hrgn;
+ GpStatus stat;
+ RECT rect;
+
+ TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %p, %p)\n", region, x, y, w, h, graphics, res);
+
+ if(!region || !res)
+ return InvalidParameter;
+
+ if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok)
+ return stat;
+
+ /* infinite */
+ if(!hrgn){
+ *res = TRUE;
+ return Ok;
+ }
+
+ rect.left = ceilr(x);
+ rect.top = ceilr(y);
+ rect.right = ceilr(x + w);
+ rect.bottom = ceilr(y + h);
+
+ *res = RectInRegion(hrgn, &rect);
+
+ DeleteObject(hrgn);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipIsVisibleRegionRectI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipIsVisibleRegionRectI(GpRegion* region, INT x, INT y, INT w, INT h, GpGraphics *graphics, BOOL *res)
+{
+ TRACE("(%p, %d, %d, %d, %d, %p, %p)\n", region, x, y, w, h, graphics, res);
+ if(!region || !res)
+ return InvalidParameter;
+
+ return GdipIsVisibleRegionRect(region, (REAL)x, (REAL)y, (REAL)w, (REAL)h, graphics, res);
+}
+
+/*****************************************************************************
+ * GdipIsVisibleRegionPoint [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipIsVisibleRegionPoint(GpRegion* region, REAL x, REAL y, GpGraphics *graphics, BOOL *res)
+{
+ HRGN hrgn;
+ GpStatus stat;
+
+ TRACE("(%p, %.2f, %.2f, %p, %p)\n", region, x, y, graphics, res);
+
+ if(!region || !res)
+ return InvalidParameter;
+
+ if((stat = GdipGetRegionHRgn(region, NULL, &hrgn)) != Ok)
+ return stat;
+
+ /* infinite */
+ if(!hrgn){
+ *res = TRUE;
+ return Ok;
+ }
+
+ *res = PtInRegion(hrgn, roundr(x), roundr(y));
+
+ DeleteObject(hrgn);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipIsVisibleRegionPointI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipIsVisibleRegionPointI(GpRegion* region, INT x, INT y, GpGraphics *graphics, BOOL *res)
+{
+ TRACE("(%p, %d, %d, %p, %p)\n", region, x, y, graphics, res);
+
+ return GdipIsVisibleRegionPoint(region, (REAL)x, (REAL)y, graphics, res);
}
+/*****************************************************************************
+ * GdipSetEmpty [GDIPLUS.@]
+ */
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)
+{
+ GpStatus stat;
+
+ TRACE("%p\n", region);
+
+ if (!region)
+ return InvalidParameter;
+
+ delete_element(®ion->node);
+ stat = init_region(region, RegionDataInfiniteRect);
+
+ return stat;
+}
+
+GpStatus WINGDIPAPI GdipTransformRegion(GpRegion *region, GpMatrix *matrix)
+{
+ FIXME("(%p, %p): stub\n", region, matrix);
+
+ return NotImplemented;
+}
+
+/* Translates GpRegion elements with specified offsets */
+static void translate_region_element(region_element* element, REAL dx, REAL dy)
+{
+ INT i;
+
+ switch(element->type)
+ {
+ case RegionDataEmptyRect:
+ case RegionDataInfiniteRect:
+ return;
+ case RegionDataRect:
+ element->elementdata.rect.X += dx;
+ element->elementdata.rect.Y += dy;
+ return;
+ case RegionDataPath:
+ for(i = 0; i < element->elementdata.pathdata.path->pathdata.Count; i++){
+ element->elementdata.pathdata.path->pathdata.Points[i].X += dx;
+ element->elementdata.pathdata.path->pathdata.Points[i].Y += dy;
+ }
+ return;
+ default:
+ translate_region_element(element->elementdata.combine.left, dx, dy);
+ translate_region_element(element->elementdata.combine.right, dx, dy);
+ return;
+ }
+}
+
+/*****************************************************************************
+ * GdipTranslateRegion [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipTranslateRegion(GpRegion *region, REAL dx, REAL dy)
+{
+ TRACE("(%p, %f, %f)\n", region, dx, dy);
+
+ if(!region)
+ return InvalidParameter;
+
+ translate_region_element(®ion->node, dx, dy);
+
+ return Ok;
+}
+
+/*****************************************************************************
+ * GdipTranslateRegionI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipTranslateRegionI(GpRegion *region, INT dx, INT dy)
+{
+ TRACE("(%p, %d, %d)\n", region, dx, dy);
+
+ return GdipTranslateRegion(region, (REAL)dx, (REAL)dy);
+}
+
+GpStatus WINGDIPAPI GdipGetRegionScansCount(GpRegion *region, UINT *count, GpMatrix *matrix)
{
static int calls;
- if(!(calls++))
+ TRACE("(%p, %p, %p)\n", region, count, matrix);
+
+ if (!(calls++))
FIXME("not implemented\n");
return NotImplemented;