[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, ®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;
}
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, ®ion);
todo_wine
{
- status = GdipCreateRegionPath(path, ®ion);
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