diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index f39838e72d7..ca2c022d937 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -396,6 +396,7 @@ struct emfplus_object { GpBrush *brush; GpPen *pen; GpPath *path; + GpRegion *region; GpImage *image; GpFont *font; GpImageAttributes *image_attributes; @@ -561,6 +562,30 @@ struct GpRegion{ region_element node; }; +struct memory_buffer +{ + const BYTE *buffer; + INT size, pos; +}; + +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; +} + typedef GpStatus (*gdip_format_string_callback)(HDC hdc, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c index f18411ba8a2..7289ea56db8 100644 --- a/dlls/gdiplus/metafile.c +++ b/dlls/gdiplus/metafile.c @@ -265,6 +265,12 @@ typedef struct EmfPlusPath BYTE data[1]; } EmfPlusPath; +typedef struct EmfPlusRegionNodePath +{ + DWORD RegionNodePathLength; + EmfPlusPath RegionNodePath; +} EmfPlusRegionNodePath; + typedef struct EmfPlusRegion { DWORD Version; @@ -429,6 +435,9 @@ static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id) case ObjectTypePath: GdipDeletePath(object->u.path); break; + case ObjectTypeRegion: + GdipDeleteRegion(object->u.region); + break; case ObjectTypeImage: GdipDisposeImage(object->u.image); break; @@ -1685,6 +1694,132 @@ static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_siz return Ok; } +static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count) +{ + const DWORD *type; + GpStatus status; + + type = buffer_read(mbuf, sizeof(*type)); + 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 = heap_alloc_zero(sizeof(*left)); + if (!left) + return OutOfMemory; + + right = heap_alloc_zero(sizeof(*right)); + if (!right) + { + heap_free(left); + return OutOfMemory; + } + + status = metafile_read_region_node(mbuf, region, left, count); + if (status == Ok) + { + status = metafile_read_region_node(mbuf, region, right, count); + if (status == Ok) + { + node->elementdata.combine.left = left; + node->elementdata.combine.right = right; + region->num_children += 2; + return Ok; + } + } + + heap_free(left); + heap_free(right); + return status; + } + case RegionDataRect: + { + const EmfPlusRectF *rect; + + rect = buffer_read(mbuf, sizeof(*rect)); + if (!rect) + return InvalidParameter; + + memcpy(&node->elementdata.rect, rect, sizeof(*rect)); + *count += 1; + return Ok; + } + case RegionDataPath: + { + const BYTE *path_data; + const UINT *data_size; + GpPath *path; + + data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath)); + if (!data_size) + return InvalidParameter; + + path_data = buffer_read(mbuf, *data_size); + if (!path_data) + return InvalidParameter; + + status = metafile_deserialize_path(path_data, *data_size, &path); + if (status == Ok) + { + node->elementdata.path = path; + *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; +} + +static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region) +{ + struct memory_buffer mbuf; + GpStatus status; + UINT count; + + *region = NULL; + + init_memory_buffer(&mbuf, record_data, data_size); + + if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode))) + return InvalidParameter; + + status = GdipCreateRegion(region); + if (status != Ok) + return status; + + count = 0; + status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count); + if (status == Ok && !count) + status = InvalidParameter; + + if (status != Ok) + { + GdipDeleteRegion(*region); + *region = NULL; + } + + return status; +} + static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush) { static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData); @@ -1935,6 +2070,9 @@ static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT d case ObjectTypePath: status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object); break; + case ObjectTypeRegion: + status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object); + break; case ObjectTypeImage: status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object); break; diff --git a/dlls/gdiplus/region.c b/dlls/gdiplus/region.c index 8a2ca6b3f3f..8eb3ff3535a 100644 --- a/dlls/gdiplus/region.c +++ b/dlls/gdiplus/region.c @@ -76,12 +76,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); #define FLAGS_INTPATH 0x4000 -struct memory_buffer -{ - const BYTE *buffer; - INT size, pos; -}; - struct region_header { DWORD magic; @@ -778,24 +772,6 @@ GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, return Ok; } -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; diff --git a/dlls/gdiplus/tests/metafile.c b/dlls/gdiplus/tests/metafile.c index 0e47607eac5..f23221055c3 100644 --- a/dlls/gdiplus/tests/metafile.c +++ b/dlls/gdiplus/tests/metafile.c @@ -2170,7 +2170,7 @@ static const emfplus_record clipping_records[] = { { EmfPlusRecordTypeRestore }, { EmfPlusRecordTypeSetClipRect, 0x300 }, { EmfPlusRecordTypeFillRects, 0xc000 }, - { EmfPlusRecordTypeObject, ObjectTypeRegion << 8, 0, 1 }, + { EmfPlusRecordTypeObject, ObjectTypeRegion << 8 }, { EmfPlusRecordTypeSetClipRegion, 0x100, 0, 1 }, { EmfPlusRecordTypeEndOfFile }, { EMR_EOF },