[PATCH 3/4] gdiplus/metafile: Implement EmfPlusRegion deserialization

Nikolay Sivov nsivov at codeweavers.com
Thu Oct 19 01:33:00 CDT 2017


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/gdiplus/gdiplus_private.h |  25 ++++++++
 dlls/gdiplus/metafile.c        | 138 +++++++++++++++++++++++++++++++++++++++++
 dlls/gdiplus/region.c          |  24 -------
 dlls/gdiplus/tests/metafile.c  |   2 +-
 4 files changed, 164 insertions(+), 25 deletions(-)

diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index f39838e72d..ca2c022d93 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 bb2654aff7..9d1743dca5 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 8a2ca6b3f3..8eb3ff3535 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 0e47607eac..f23221055c 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 },
-- 
2.14.2




More information about the wine-patches mailing list