- Sync gdiplus with Wine head
[reactos.git] / reactos / dll / win32 / gdiplus / region.c
index 3d95723..c4007b3 100644 (file)
@@ -32,8 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
 
 /**********************************************************
  *
- * Data returned by GdipGetRegionData (for rectangle based regions)
- * looks something like this:
+ * Data returned by GdipGetRegionData looks something like this:
  *
  * struct region_data_header
  * {
@@ -43,7 +42,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
  *   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.
  *
@@ -51,22 +52,99 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
  * 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(&region->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);
@@ -104,10 +182,16 @@ GpStatus WINGDIPAPI GdipCombineRegionRegion(GpRegion *region1, GpRegion *region2
 
 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)
@@ -152,8 +236,15 @@ GpStatus WINGDIPAPI GdipCreateRegionHrgn(HRGN hrgn, GpRegion **region)
 
 GpStatus WINGDIPAPI GdipDeleteRegion(GpRegion *region)
 {
-    FIXME("(%p): stub\n", region);
-    return NotImplemented;
+    TRACE("%p\n", region);
+
+    if (!region)
+        return InvalidParameter;
+
+    delete_element(&region->node);
+    GdipFree(region);
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect)
@@ -179,11 +270,20 @@ GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
 
 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);
@@ -216,22 +316,32 @@ GpStatus WINGDIPAPI GdipIsInfiniteRegion(GpRegion *region, GpGraphics *graphics,
 
 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(&region->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(&region->node);
+    stat = init_region(region, RegionDataInfiniteRect);
+
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipTransformRegion(GpRegion *region, GpMatrix *matrix)