gdiplus: Implement GdipCreateRegionRgnData. Take 2.
Dmitry Timoshkov
dmitry at baikal.ru
Mon Nov 18 01:42:27 CST 2013
This version of the patch follows Windows behaviour by refusing to create
an empty path in a region, and marks the test as broken when some Windows
versions fail to properly clear an aligned DWORD.
---
dlls/gdiplus/region.c | 219 ++++++++++++++++++++++++++++++++++++++++----
dlls/gdiplus/tests/region.c | 65 +++++++++++++
2 files changed, 266 insertions(+), 18 deletions(-)
diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c
index 832ce69..bc625f5 100644
--- a/dlls/gdiplus/region.c
+++ b/dlls/gdiplus/region.c
@@ -76,6 +76,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
*/
@@ -522,12 +544,185 @@ GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect,
return GdipCreateRegionRect(&rectf, region);
}
+static inline void init_memory_buffer(struct _memory_buffer *mbuf, const BYTE *buffer, INT size)
+{
+ mbuf->buffer = buffer;
+ mbuf->size = size;
+ mbuf->pos = 0;
+}
+
+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(struct _memory_buffer *mbuf, GpRegion *region, region_element *node, INT *count)
+{
+ GpStatus status;
+ const DWORD *type;
+
+ type = buffer_read(mbuf, sizeof(DWORD));
+ if (!type) return Ok;
+
+ 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;
+ }
+
+ case RegionDataRect:
+ {
+ const GpRectF *rc;
+
+ rc = buffer_read(mbuf, sizeof(GpRectF));
+ if (!rc) 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(struct _path_header));
+ if (!path_header || path_header->magic != VERSION_MAGIC)
+ return InvalidParameter;
+
+ /* Windows always fails to create an empty path in a region */
+ if (!path_header->count) 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;
+
+ switch (path_header->flags & FLAGS_INTPATH)
+ {
+ case FLAGS_NOFLAGS:
+ {
+ const GpPointF *ptf;
+
+ ptf = buffer_read(mbuf, sizeof(GpPointF) * path_header->count);
+ if (!ptf) return InvalidParameter;
+ memcpy(path->pathdata.Points, ptf, sizeof(GpPointF) * path_header->count);
+ break;
+ }
+ case FLAGS_INTPATH:
+ {
+ const packed_point *pt;
+ DWORD i;
+
+ pt = buffer_read(mbuf, sizeof(packed_point) * path_header->count);
+ if (!pt) 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;
+ }
+ break;
+ }
+ }
+
+ types = buffer_read(mbuf, path_header->count);
+ if (!types) return InvalidParameter;
+ memcpy(path->pathdata.Types, types, path_header->count);
+
+ *count += 1;
+ return Ok;
+ }
+
+ case RegionDataEmptyRect:
+ case RegionDataInfiniteRect:
+ *count += 1;
+ return Ok;
+
+ default:
+ FIXME("element type %#x is not supported\n", *type);
+ break;
+ }
+
+ return InvalidParameter;
+}
+
GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region)
{
- FIXME("(%p, %d, %p): stub\n", data, size, region);
+ GpStatus status;
+ struct _memory_buffer mbuf;
+ const struct _region_header *region_header;
+ INT count;
+
+ if (!data || !size) return InvalidParameter;
+
+ TRACE("%p, %d, %p\n", data, size, region);
- *region = NULL;
- return NotImplemented;
+ init_memory_buffer(&mbuf, data, size);
+
+ region_header = buffer_read(&mbuf, sizeof(struct _region_header));
+ if (!region_header || region_header->magic != VERSION_MAGIC)
+ return InvalidParameter;
+
+ status = GdipCreateRegion(region);
+ if (status != Ok) return status;
+
+ count = 0;
+ status = read_element(&mbuf, *region, &(*region)->node, &count);
+ if (status == Ok && !count)
+ status = InvalidParameter;
+
+ if (status != Ok)
+ GdipDeleteRegion(*region);
+
+ return status;
}
@@ -738,15 +933,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 */
@@ -823,13 +1012,7 @@ static void write_element(const region_element* element, DWORD *buffer,
GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size,
UINT *needed)
{
- struct _region_header
- {
- DWORD size;
- DWORD checksum;
- DWORD magic;
- DWORD num_children;
- } *region_header;
+ struct _region_header *region_header;
INT filled = 0;
UINT required;
GpStatus status;
diff --git a/dlls/gdiplus/tests/region.c b/dlls/gdiplus/tests/region.c
index 66d950a..33cd851 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, ®ion);
+ /* 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);
--
1.8.4.2
More information about the wine-patches
mailing list