gdiplus/metafile: Implement EmfPlusRegion deserialization.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Vincent Povirk <vincent@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2017-10-20 00:45:40 +03:00 committed by Alexandre Julliard
parent 363d24d458
commit aef2f0799f
4 changed files with 164 additions and 25 deletions

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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 },