diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 1a028a56c25..4ad49caad30 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -96,6 +96,10 @@ extern GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST extern GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order) DECLSPEC_HIDDEN; extern GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order) DECLSPEC_HIDDEN; extern GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex) DECLSPEC_HIDDEN; extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN; extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1, @@ -368,6 +372,7 @@ struct GpMetafile{ GpUnit page_unit; REAL page_scale; GpRegion *base_clip; /* clip region in device space for all metafile output */ + struct list containers; }; struct GpBitmap{ diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index f0cf98c4bdd..370fa88a0b2 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5194,6 +5194,13 @@ static GpStatus begin_container(GpGraphics *graphics, list_add_head(&graphics->containers, &container->entry); *state = graphics->contid = container->contid; + if (graphics->image && graphics->image->type == ImageTypeMetafile) { + if (type == BEGIN_CONTAINER) + METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid); + else + METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid); + } + return Ok; } @@ -5261,6 +5268,13 @@ static GpStatus end_container(GpGraphics *graphics, GraphicsContainerType type, list_remove(&container->entry); delete_container(container); + if (graphics->image && graphics->image->type == ImageTypeMetafile) { + if (type == BEGIN_CONTAINER) + METAFILE_EndContainer((GpMetafile*)graphics->image, state); + else + METAFILE_RestoreGraphics((GpMetafile*)graphics->image, state); + } + return Ok; } diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c index 4d0496a0cab..c3f6a5e73d3 100644 --- a/dlls/gdiplus/image.c +++ b/dlls/gdiplus/image.c @@ -4062,6 +4062,7 @@ static GpStatus decode_image_olepicture_metafile(IStream* stream, GpImage **imag (*image)->frame_count = 1; (*image)->current_frame = 0; (*image)->palette = NULL; + list_init(&(*(GpMetafile**)image)->containers); TRACE("<-- %p\n", *image); diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c index 63d9e225429..a854e550aeb 100644 --- a/dlls/gdiplus/metafile.c +++ b/dlls/gdiplus/metafile.c @@ -116,6 +116,29 @@ typedef struct EmfPlusTranslateWorldTransform REAL dy; } EmfPlusTranslateWorldTransform; +typedef struct EmfPlusContainerRecord +{ + EmfPlusRecordHeader Header; + DWORD StackIndex; +} EmfPlusContainerRecord; + +enum container_type +{ + BEGIN_CONTAINER, + SAVE_GRAPHICS +}; + +typedef struct container +{ + struct list entry; + DWORD id; + enum container_type type; + GraphicsContainer state; + GpMatrix world_transform; + GpUnit page_unit; + REAL page_scale; +} container; + static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result) { DWORD size_needed; @@ -308,6 +331,7 @@ GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF (*metafile)->comment_data_size = 0; (*metafile)->comment_data_length = 0; (*metafile)->hemf = NULL; + list_init(&(*metafile)->containers); if (!frameRect) { @@ -726,6 +750,98 @@ GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile) return Ok; } +GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex) +{ + if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) + { + EmfPlusContainerRecord *record; + GpStatus stat; + + stat = METAFILE_AllocateRecord(metafile, + sizeof(EmfPlusContainerRecord), + (void**)&record); + if (stat != Ok) + return stat; + + record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams; + record->Header.Flags = 0; + record->StackIndex = StackIndex; + + METAFILE_WriteRecords(metafile); + } + + return Ok; +} + +GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex) +{ + if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) + { + EmfPlusContainerRecord *record; + GpStatus stat; + + stat = METAFILE_AllocateRecord(metafile, + sizeof(EmfPlusContainerRecord), + (void**)&record); + if (stat != Ok) + return stat; + + record->Header.Type = EmfPlusRecordTypeEndContainer; + record->Header.Flags = 0; + record->StackIndex = StackIndex; + + METAFILE_WriteRecords(metafile); + } + + return Ok; +} + +GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex) +{ + if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) + { + EmfPlusContainerRecord *record; + GpStatus stat; + + stat = METAFILE_AllocateRecord(metafile, + sizeof(EmfPlusContainerRecord), + (void**)&record); + if (stat != Ok) + return stat; + + record->Header.Type = EmfPlusRecordTypeSave; + record->Header.Flags = 0; + record->StackIndex = StackIndex; + + METAFILE_WriteRecords(metafile); + } + + return Ok; +} + +GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex) +{ + if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) + { + EmfPlusContainerRecord *record; + GpStatus stat; + + stat = METAFILE_AllocateRecord(metafile, + sizeof(EmfPlusContainerRecord), + (void**)&record); + if (stat != Ok) + return stat; + + record->Header.Type = EmfPlusRecordTypeRestore; + record->Header.Flags = 0; + record->StackIndex = StackIndex; + + METAFILE_WriteRecords(metafile); + } + + return Ok; +} + GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc) { if (hdc != metafile->record_dc) @@ -1131,6 +1247,87 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile, return METAFILE_PlaybackUpdateWorldTransform(real_metafile); } + case EmfPlusRecordTypeBeginContainerNoParams: + case EmfPlusRecordTypeSave: + { + EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header; + container* cont; + + cont = heap_alloc_zero(sizeof(*cont)); + if (!cont) + return OutOfMemory; + + if (recordType == EmfPlusRecordTypeBeginContainerNoParams) + stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state); + else + stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state); + + if (stat != Ok) + { + heap_free(cont); + return stat; + } + + cont->id = record->StackIndex; + if (recordType == EmfPlusRecordTypeBeginContainerNoParams) + cont->type = BEGIN_CONTAINER; + else + cont->type = SAVE_GRAPHICS; + cont->world_transform = *metafile->world_transform; + cont->page_unit = metafile->page_unit; + cont->page_scale = metafile->page_scale; + list_add_head(&real_metafile->containers, &cont->entry); + + break; + } + case EmfPlusRecordTypeEndContainer: + case EmfPlusRecordTypeRestore: + { + EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header; + container* cont; + enum container_type type; + BOOL found=FALSE; + + if (recordType == EmfPlusRecordTypeEndContainer) + type = BEGIN_CONTAINER; + else + type = SAVE_GRAPHICS; + + LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry) + { + if (cont->id == record->StackIndex && cont->type == type) + { + found = TRUE; + break; + } + } + + if (found) + { + container* cont2; + + /* pop any newer items on the stack */ + while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont) + { + list_remove(&cont2->entry); + heap_free(cont2); + } + + if (type == BEGIN_CONTAINER) + GdipEndContainer(real_metafile->playback_graphics, cont->state); + else + GdipRestoreGraphics(real_metafile->playback_graphics, cont->state); + + *real_metafile->world_transform = cont->world_transform; + real_metafile->page_unit = cont->page_unit; + real_metafile->page_scale = cont->page_scale; + + list_remove(&cont->entry); + heap_free(cont); + } + + break; + } default: FIXME("Not implemented for record type %x\n", recordType); return NotImplemented; @@ -1309,6 +1506,13 @@ GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, GdipDeleteRegion(real_metafile->base_clip); real_metafile->base_clip = NULL; + while (list_head(&real_metafile->containers)) + { + container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry); + list_remove(&cont->entry); + heap_free(cont); + } + GdipEndContainer(graphics, state); } @@ -1553,6 +1757,7 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete, (*metafile)->metafile_type = header.Type; (*metafile)->hemf = hemf; (*metafile)->preserve_hemf = !delete; + list_init(&(*metafile)->containers); TRACE("<-- %p\n", *metafile);