diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index c4007b3ff38..69d31f26192 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 = write->X; + point.Y = 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 8c3badac93a..01991a260c0 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);