gdiplus: Implement GdipCreateRegionRgnData.

Dmitry Timoshkov dmitry at baikal.ru
Sat Jun 13 02:53:58 CDT 2015


---
 dlls/gdiplus/region.c       | 249 ++++++++++++++++++++++++++++++++++++--------
 dlls/gdiplus/tests/region.c |  65 ++++++++++++
 2 files changed, 273 insertions(+), 41 deletions(-)

diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c
index 572df62..8988d70 100644
--- a/dlls/gdiplus/region.c
+++ b/dlls/gdiplus/region.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 Google (Lei Zhang)
+ * Copyright (C) 2013 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -76,6 +77,28 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
 #define FLAGS_NOFLAGS   0x0
 #define FLAGS_INTPATH   0x4000
 
+struct memory_buffer
+{
+    const BYTE *buffer;
+    INT size, pos;
+};
+
+struct region_header
+{
+    DWORD size;
+    DWORD checksum;
+    DWORD magic;
+    DWORD num_children;
+};
+
+struct path_header
+{
+    DWORD size;
+    DWORD magic;
+    DWORD count;
+    DWORD flags;
+};
+
 /* Header size as far as header->size is concerned. This doesn't include
  * header->size or header->checksum
  */
@@ -729,15 +752,9 @@ static void write_element(const region_element* element, DWORD *buffer,
         {
             INT i;
             const GpPath* path = element->elementdata.path;
-            struct _pathheader
-            {
-                DWORD size;
-                DWORD magic;
-                DWORD count;
-                DWORD flags;
-            } *pathheader;
+            struct path_header *pathheader;
 
-            pathheader = (struct _pathheader *)(buffer + *filled);
+            pathheader = (struct path_header *)(buffer + *filled);
 
             pathheader->flags = is_integer_path(path) ? FLAGS_INTPATH : FLAGS_NOFLAGS;
             /* 3 for headers, once again size doesn't count itself */
@@ -778,14 +795,6 @@ static void write_element(const region_element* element, DWORD *buffer,
     }
 }
 
-struct region_header
-{
-    DWORD size;
-    DWORD checksum;
-    DWORD magic;
-    DWORD num_children;
-};
-
 /*****************************************************************************
  * GdipGetRegionData [GDIPLUS.@]
  *
@@ -856,36 +865,190 @@ GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
     return Ok;
 }
 
-static inline GpStatus read_dword(DWORD **buffer, INT *size, DWORD *ret)
+static inline void init_memory_buffer(struct memory_buffer *mbuf, const BYTE *buffer, INT size)
 {
-    if (*size < sizeof(DWORD))
-        return GenericError;
+    mbuf->buffer = buffer;
+    mbuf->size = size;
+    mbuf->pos = 0;
+}
 
-    *ret = **buffer;
-    (*buffer)++;
-    (*size) -= sizeof(DWORD);
-    return Ok;
+static inline const void *buffer_read(struct memory_buffer *mbuf, INT size)
+{
+    if (mbuf->size - mbuf->pos >= size)
+    {
+        const void *data = mbuf->buffer + mbuf->pos;
+        mbuf->pos += size;
+        return data;
+    }
+    return NULL;
 }
 
-static GpStatus read_element(GpRegion *region, region_element *element, DWORD **buffer, INT *size)
+static GpStatus read_element(struct memory_buffer *mbuf, GpRegion *region, region_element *node, INT *count)
 {
     GpStatus status;
+    const DWORD *type;
 
-    status = read_dword(buffer, size, &element->type);
-    if (status != Ok)
+    type = buffer_read(mbuf, sizeof(*type));
+    if (!type) return Ok;
+
+    TRACE("type %#x\n", *type);
+
+    node->type = *type;
+
+    switch (node->type)
+    {
+    case CombineModeReplace:
+    case CombineModeIntersect:
+    case CombineModeUnion:
+    case CombineModeXor:
+    case CombineModeExclude:
+    case CombineModeComplement:
+    {
+        region_element *left, *right;
+
+        left = GdipAlloc(sizeof(region_element));
+        if (!left) return OutOfMemory;
+        right = GdipAlloc(sizeof(region_element));
+        if (!right)
+        {
+            GdipFree(left);
+            return OutOfMemory;
+        }
+
+        status = read_element(mbuf, region, left, count);
+        if (status == Ok)
+        {
+            status = read_element(mbuf, region, right, count);
+            if (status == Ok)
+            {
+                node->elementdata.combine.left = left;
+                node->elementdata.combine.right = right;
+                region->num_children += 2;
+                return Ok;
+            }
+        }
+
+        GdipFree(left);
+        GdipFree(right);
         return status;
+    }
 
-    switch (element->type)
+    case RegionDataRect:
     {
-    case RegionDataInfiniteRect:
+        const GpRectF *rc;
+
+        rc = buffer_read(mbuf, sizeof(*rc));
+        if (!rc)
+        {
+            ERR("failed to read rect data\n");
+            return InvalidParameter;
+        }
+
+        node->elementdata.rect = *rc;
+        *count += 1;
+        return Ok;
+    }
+
+    case RegionDataPath:
+    {
+        GpPath *path;
+        const struct path_header *path_header;
+        const BYTE *types;
+
+        path_header = buffer_read(mbuf, sizeof(*path_header));
+        if (!path_header)
+        {
+            ERR("failed to read path header\n");
+            return InvalidParameter;
+        }
+        if (path_header->magic != VERSION_MAGIC)
+        {
+            ERR("invalid path header magic %#x\n", path_header->magic);
+            return InvalidParameter;
+        }
+
+        /* Windows always fails to create an empty path in a region */
+        if (!path_header->count)
+        {
+            TRACE("refusing to create an empty path in a region\n");
+            return GenericError;
+        }
+
+        status = GdipCreatePath(FillModeAlternate, &path);
+        if (status) return status;
+
+        node->elementdata.path = path;
+
+        if (!lengthen_path(path, path_header->count))
+            return OutOfMemory;
+
+        path->pathdata.Count = path_header->count;
+
+        if (path_header->flags & ~FLAGS_INTPATH)
+            FIXME("unhandled path flags %#x\n", path_header->flags);
+
+        if (path_header->flags & FLAGS_INTPATH)
+        {
+            const packed_point *pt;
+            DWORD i;
+
+            pt = buffer_read(mbuf, sizeof(*pt) * path_header->count);
+            if (!pt)
+            {
+                ERR("failed to read packed %u path points\n", path_header->count);
+                return InvalidParameter;
+            }
+
+            for (i = 0; i < path_header->count; i++)
+            {
+                path->pathdata.Points[i].X = (REAL)pt[i].X;
+                path->pathdata.Points[i].Y = (REAL)pt[i].Y;
+            }
+        }
+        else
+        {
+            const GpPointF *ptf;
+
+            ptf = buffer_read(mbuf, sizeof(*ptf) * path_header->count);
+            if (!ptf)
+            {
+                ERR("failed to read %u path points\n", path_header->count);
+                return InvalidParameter;
+            }
+            memcpy(path->pathdata.Points, ptf, sizeof(*ptf) * path_header->count);
+        }
+
+        types = buffer_read(mbuf, path_header->count);
+        if (!types)
+        {
+            ERR("failed to read %u path types\n", path_header->count);
+            return InvalidParameter;
+        }
+        memcpy(path->pathdata.Types, types, path_header->count);
+        if (path_header->count & 3)
+        {
+            if (!buffer_read(mbuf, 4 - (path_header->count & 3)))
+            {
+                ERR("failed to read rounding %u bytes\n", 4 - (path_header->count & 3));
+                return InvalidParameter;
+            }
+        }
+
+        *count += 1;
+        return Ok;
+    }
+
     case RegionDataEmptyRect:
-        break;
+    case RegionDataInfiniteRect:
+        *count += 1;
+        return Ok;
+
     default:
-        FIXME("region element type 0x%08x not supported\n", element->type);
-        return NotImplemented;
+        FIXME("element type %#x is not supported\n", *type);
+        break;
     }
 
-    return Ok;
+    return InvalidParameter;
 }
 
 /*****************************************************************************
@@ -893,28 +1056,32 @@ static GpStatus read_element(GpRegion *region, region_element *element, DWORD **
  */
 GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region)
 {
-    struct region_header *region_header;
-    DWORD *buffer = (DWORD*)data;
+    const struct region_header *region_header;
+    struct memory_buffer mbuf;
     GpStatus status;
+    INT count;
 
     TRACE("(%p, %d, %p)\n", data, size, region);
 
-    if (!data || size < sizeof(*region_header) || !region)
+    if (!data || !size)
         return InvalidParameter;
 
-    region_header = (struct region_header *)buffer;
-    if (region_header->magic != VERSION_MAGIC && region_header->magic != VERSION_MAGIC2)
+    init_memory_buffer(&mbuf, data, size);
+
+    region_header = buffer_read(&mbuf, sizeof(*region_header));
+    if (!region_header || (region_header->magic != VERSION_MAGIC &&
+                           region_header->magic != VERSION_MAGIC2))
         return InvalidParameter;
 
     status = GdipCreateRegion(region);
     if (status != Ok)
         return status;
 
-    /* skip header */
-    buffer += 4;
-    size -= sizeof(*region_header);
+    count = 0;
+    status = read_element(&mbuf, *region, &(*region)->node, &count);
+    if (status == Ok && !count)
+        status = InvalidParameter;
 
-    status = read_element(*region, &(*region)->node, &buffer, &size);
     if (status != Ok)
     {
         GdipDeleteRegion(*region);
diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c
index 92569c7..38d05cd 100644
--- a/dlls/gdiplus/tests/region.c
+++ b/dlls/gdiplus/tests/region.c
@@ -102,6 +102,56 @@ static void verify_region(HRGN hrgn, const RECT *rc)
        rgn.data.rdh.rcBound.left, rgn.data.rdh.rcBound.top, rgn.data.rdh.rcBound.right, rgn.data.rdh.rcBound.bottom);
 }
 
+static void test_region_data(DWORD *data, UINT size, INT line)
+{
+    GpStatus status;
+    GpRegion *region;
+    DWORD buf[256];
+    UINT needed, i;
+
+    status = GdipCreateRegionRgnData((BYTE *)data, size, &region);
+    /* Windows always fails to create an empty path in a region */
+    if (data[4] == RGNDATA_PATH)
+    {
+        struct path_header
+        {
+            DWORD size;
+            DWORD magic;
+            DWORD count;
+            DWORD flags;
+        } *path_header = (struct path_header *)(data + 5);
+        if (!path_header->count)
+        {
+            ok_(__FILE__, line)(status == GenericError, "expected GenericError, got %d\n", status);
+            return;
+        }
+    }
+
+    ok_(__FILE__, line)(status == Ok, "GdipCreateRegionRgnData error %d\n", status);
+    if (status != Ok) return;
+
+    needed = 0;
+    status = GdipGetRegionDataSize(region, &needed);
+    ok_(__FILE__, line)(status == Ok, "status %d\n", status);
+    ok_(__FILE__, line)(needed == size, "data size mismatch: %u != %u\n", needed, size);
+
+    memset(buf, 0xee, sizeof(buf));
+    needed = 0;
+    status = GdipGetRegionData(region, (BYTE *)buf, sizeof(buf), &needed);
+    ok_(__FILE__, line)(status == Ok, "status %08x\n", status);
+    ok_(__FILE__, line)(needed == size, "data size mismatch: %u != %u\n", needed, size);
+
+    size /= sizeof(DWORD);
+    for (i = 0; i < size - 1; i++)
+    {
+        if (i == 1) continue; /* data[1] never matches */
+        ok_(__FILE__, line)(data[i] == buf[i], "off %u: %#x != %#x\n", i, data[i], buf[i]);
+    }
+    /* some Windows versions fail to properly clear the aligned DWORD */
+    ok_(__FILE__, line)(data[size - 1] == buf[size - 1] || broken(data[size - 1] != buf[size - 1]),
+        "off %u: %#x != %#x\n", size - 1, data[size - 1], buf[size - 1]);
+}
+
 static void test_getregiondata(void)
 {
     GpStatus status;
@@ -143,6 +193,7 @@ static void test_getregiondata(void)
     expect_dword(buf + 3, 0);
     expect_dword(buf + 4, RGNDATA_INFINITE_RECT);
     expect_dword(buf + 6, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipSetEmpty(region);
     ok(status == Ok, "status %08x\n", status);
@@ -160,6 +211,7 @@ static void test_getregiondata(void)
     expect_dword(buf + 3, 0);
     expect_dword(buf + 4, RGNDATA_EMPTY_RECT);
     expect_dword(buf + 6, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipSetInfinite(region);
     ok(status == Ok, "status %08x\n", status);
@@ -177,6 +229,7 @@ static void test_getregiondata(void)
     expect_dword(buf + 3, 0);
     expect_dword(buf + 4, RGNDATA_INFINITE_RECT);
     expect_dword(buf + 6, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeleteRegion(region);
     ok(status == Ok, "status %08x\n", status);
@@ -205,6 +258,7 @@ static void test_getregiondata(void)
     expect_float(buf + 7, 100.0);
     expect_float(buf + 8, 200.0);
     expect_dword(buf + 10, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     rect.X = 50;
     rect.Y = 30;
@@ -290,6 +344,7 @@ static void test_getregiondata(void)
     expect_float(buf + 37, 22.0);
     expect_float(buf + 38, 55.0);
     expect_dword(buf + 39, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeleteRegion(region2);
     ok(status == Ok, "status %08x\n", status);
@@ -331,6 +386,7 @@ static void test_getregiondata(void)
     expect_float(buf + 16, 28.0);
     expect_dword(buf + 17, 0x81010100);
     expect_dword(buf + 18, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     rect.X = 50;
     rect.Y = 30;
@@ -371,6 +427,7 @@ static void test_getregiondata(void)
     expect_float(buf + 22, 10.0);
     expect_float(buf + 23, 20.0);
     expect_dword(buf + 24, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeleteRegion(region);
     ok(status == Ok, "status %08x\n", status);
@@ -403,6 +460,7 @@ static void test_getregiondata(void)
     ok(*(buf + 8) == 0x4000 /* before win7 */ || *(buf + 8) == 0,
        "expected 0x4000 or 0, got %08x\n", *(buf + 8));
     expect_dword(buf + 10, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     /* Transform an empty region */
     status = GdipCreateMatrix(&matrix);
@@ -453,6 +511,7 @@ static void test_getregiondata(void)
     expect(6, point[3].Y);
     expect_dword(buf + 13, 0x81010100); /* 0x01010100 if we don't close the path */
     expect_dword(buf + 14, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipTranslateRegion(region, 0.6, 0.8);
     expect(Ok, status);
@@ -480,6 +539,7 @@ static void test_getregiondata(void)
     expect_float(buf + 16, 6.8);
     expect_dword(buf + 17, 0x81010100); /* 0x01010100 if we don't close the path */
     expect_dword(buf + 18, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeletePath(path);
     expect(Ok, status);
@@ -522,6 +582,7 @@ static void test_getregiondata(void)
     expect_float(buf + 16, 6.2);
     expect_dword(buf + 17, 0x01010100);
     expect_dword(buf + 18, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeletePath(path);
     expect(Ok, status);
@@ -584,6 +645,7 @@ static void test_getregiondata(void)
     ok(*(buf + 28) == 0x00000101 || *(buf + 28) == 0x43050101 /* Win 7 */,
        "expected 00000101 or 43050101 got %08x\n", *(buf + 28));
     expect_dword(buf + 29, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeletePath(path);
     expect(Ok, status);
@@ -627,6 +689,7 @@ static void test_getregiondata(void)
     expect(23, point[3].Y);
     expect_dword(buf + 13, 0x81010100); /* 0x01010100 if we don't close the path */
     expect_dword(buf + 14, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeletePath(path);
     expect(Ok, status);
@@ -669,6 +732,7 @@ static void test_getregiondata(void)
     expect_float(buf + 16, 2300.0);
     expect_dword(buf + 17, 0x81010100); /* 0x01010100 if we don't close the path */
     expect_dword(buf + 18, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeletePath(path);
     expect(Ok, status);
@@ -732,6 +796,7 @@ static void test_getregiondata(void)
        *(buf + 33) == 0x43030303 /* 32-bit win7 */ || *(buf + 33) == 0x4c030303 /* 64-bit win7 */,
        "expected 0x00030303 or 0x43030303 or 0x4c030303 got %08x\n", *(buf + 33));
     expect_dword(buf + 34, 0xeeeeeeee);
+    test_region_data(buf, needed, __LINE__);
 
     status = GdipDeletePath(path);
     expect(Ok, status);
-- 
2.4.3




More information about the wine-patches mailing list