[try7][Gdiplus 01/10] Implement GdipGetRegionData

Adam Petaccia adam at tpetaccia.com
Mon Aug 4 12:56:25 CDT 2008


Changelog:
(try7):
    Don't write past the end of the buffer.

(try6):
    Use DWORD as the base type in write_element to avoid some casts
    Use another helper function to write pathtypes, and avoid an overzealous ZeroMemory
    Don't hardcode 4 when we mean sizeof(DWORD) or even something else.
    Add documentation, as MSDN isn't helpful on this at all.

(try4):
    region->node is no longer a pointer
    Apply const qualifier to helper function
(try2):
    We might not write a whole DWORD for pathflags, so make sure its not
    garbage; revealed by the added test at the end of this series.
---
 dlls/gdiplus/region.c       |  163 ++++++++++++++++++++++++++++++++++++++++++-
 dlls/gdiplus/tests/region.c |   39 ++++++-----
 2 files changed, 182 insertions(+), 20 deletions(-)

diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c
index c4007b3..fd4c7ba 100644
--- a/dlls/gdiplus/region.c
+++ b/dlls/gdiplus/region.c
@@ -81,11 +81,31 @@ typedef enum RegionType
     RegionDataInfiniteRect  = 0x10000003,
 } RegionType;
 
+#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
+{
+    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);
+}
+
 static inline INT get_element_size(const region_element* element)
 {
     INT needed = sizeof(DWORD); /* DWORD for the type */
@@ -261,11 +281,148 @@ GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics,
     return NotImplemented;
 }
 
-GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, UINT *needed)
+static inline void write_dword(DWORD* location, INT* offset, const DWORD write)
 {
-    FIXME("(%p, %p, %d, %p): stub\n", region, buffer, size, needed);
+    location[*offset] = write;
+    (*offset)++;
+}
 
-    return NotImplemented;
+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 = (short)write->X;
+    point.Y = (short)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: InvalidParamter
+ *
+ * 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;
+
+    if (!(region && buffer && size))
+        return InvalidParameter;
+
+    TRACE("%p, %p, %d, %p\n", region, buffer, size, needed);
+    memcpy(buffer, &region->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(&region->node, (DWORD*)buffer, &filled);
+
+    if (needed)
+        *needed = filled * sizeof(DWORD);
+
+    return Ok;
 }
 
 GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed)
diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c
index 8c3bada..01991a2 100644
--- a/dlls/gdiplus/tests/region.c
+++ b/dlls/gdiplus/tests/region.c
@@ -71,13 +71,9 @@ static void test_getregiondata(void)
     ok(status == Ok, "status %08x\n", status);
     expect(20, needed);
 todo_wine
-{
     status = GdipGetRegionData(region, (BYTE*)buf, sizeof(buf), &needed);
     ok(status == Ok, "status %08x\n", status);
-}
     expect(20, needed);
-todo_wine
-{
     expect_dword(buf, 12);
     trace("buf[1] = %08x\n", buf[1]);
     expect_magic((DWORD*)(buf + 2));
@@ -85,26 +81,18 @@ todo_wine
     expect_dword(buf + 4, RGNDATA_INFINITE_RECT);
 
     status = GdipSetEmpty(region);
-}
     ok(status == Ok, "status %08x\n", status);
-todo_wine
-{
     status = GdipGetRegionDataSize(region, &needed);
-}
     ok(status == Ok, "status %08x\n", status);
     expect(20, needed);
     status = GdipGetRegionData(region, (BYTE*)buf, sizeof(buf), &needed);
-todo_wine
     ok(status == Ok, "status %08x\n", status);
     expect(20, needed);
-todo_wine
-{
     expect_dword(buf, 12);
     trace("buf[1] = %08x\n", buf[1]);
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3, 0);
     expect_dword(buf + 4, RGNDATA_EMPTY_RECT);
-}
 
     status = GdipSetInfinite(region);
     ok(status == Ok, "status %08x\n", status);
@@ -112,17 +100,13 @@ todo_wine
     ok(status == Ok, "status %08x\n", status);
     expect(20, needed);
     status = GdipGetRegionData(region, (BYTE*)buf, sizeof(buf), &needed);
-todo_wine
     ok(status == Ok, "status %08x\n", status);
     expect(20, needed);
-todo_wine
-{
     expect_dword(buf, 12);
     trace("buf[1] = %08x\n", buf[1]);
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3, 0);
     expect_dword(buf + 4, RGNDATA_INFINITE_RECT);
-}
 
     status = GdipDeleteRegion(region);
     ok(status == Ok, "status %08x\n", status);
@@ -143,8 +127,11 @@ todo_wine
     expect(36, needed);
     expect_dword(buf, 28);
     trace("buf[1] = %08x\n", buf[1]);
+}
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3, 0);
+todo_wine
+{
     expect_dword(buf + 4, RGNDATA_RECT);
     expect_float(buf + 5, 10.0);
     expect_float(buf + 6, 20.0);
@@ -195,7 +182,10 @@ todo_wine
     expect(156, needed);
     expect_dword(buf, 148);
     trace("buf[1] = %08x\n", buf[1]);
+}
     expect_magic((DWORD*)(buf + 2));
+todo_wine
+{
     expect_dword(buf + 3, 10);
     expect_dword(buf + 4, CombineModeExclude);
     expect_dword(buf + 5, CombineModeComplement);
@@ -257,8 +247,11 @@ todo_wine
     expect(72, needed);
     expect_dword(buf, 64);
     trace("buf[1] = %08x\n", buf[1]);
+}
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3, 0);
+todo_wine
+{
     expect_dword(buf + 4, RGNDATA_PATH);
     expect_dword(buf + 5, 0x00000030);
     expect_magic((DWORD*)(buf + 6));
@@ -289,7 +282,10 @@ todo_wine
     expect(96, needed);
     expect_dword(buf, 88);
     trace("buf[1] = %08x\n", buf[1]);
+}
     expect_magic((DWORD*)(buf + 2));
+todo_wine
+{
     expect_dword(buf + 3, 2);
     expect_dword(buf + 4, CombineModeIntersect);
     expect_dword(buf + 5, RGNDATA_PATH);
@@ -333,8 +329,11 @@ todo_wine
     expect(36, needed);
     expect_dword(buf, 28);
     trace("buf[1] = %08x\n", buf[1]);
+}
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3, 0);
+todo_wine
+{
     expect_dword(buf + 4, RGNDATA_PATH);
 
     /* Second signature for pathdata */
@@ -365,9 +364,12 @@ todo_wine
     expect(Ok, status);
     expect(56, needed);
     expect_dword(buf, 48);
+}
     trace("buf[1] = %08x\n", buf[1]);
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3 , 0);
+todo_wine
+{
     expect_dword(buf + 4 , RGNDATA_PATH);
 
     expect_dword(buf + 5, 32);
@@ -399,9 +401,9 @@ todo_wine
     expect(Ok, status);
     status = GdipAddPathLine(path, 8.1, 1.6, 5.6, 6.2);
     expect(Ok, status);
+    status = GdipCreateRegionPath(path, &region);
 todo_wine
 {
-    status = GdipCreateRegionPath(path, &region);
     expect(Ok, status);
     status = GdipGetRegionDataSize(region, &needed);
     expect(Ok, status);
@@ -411,8 +413,11 @@ todo_wine
     expect(72, needed);
     expect_dword(buf, 64);
     trace("buf[1] = %08x\n", buf[1]);
+}
     expect_magic((DWORD*)(buf + 2));
     expect_dword(buf + 3, 0);
+todo_wine
+{
     expect_dword(buf + 4, RGNDATA_PATH);
 
     expect_dword(buf + 5, 48);
-- 
1.5.4.3




More information about the wine-patches mailing list