From: Rose Hellsing <rose@pinkro.se> Use EnumMetaFile/PlayMetaFileRecord for WMF metafiles instead of relying solely on the SetWinMetaFileBits EMF conversion. --- dlls/gdiplus/gdiplus_private.h | 2 + dlls/gdiplus/metafile.c | 101 +++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index cc5f823214b..a1ed2cf7dbf 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -479,7 +479,9 @@ struct GpMetafile{ GpUnit unit; MetafileType metafile_type; HENHMETAFILE hemf; + HMETAFILE hwmf; int preserve_hemf; /* if true, hemf belongs to the app and should not be deleted */ + int preserve_hwmf; /* if true, hwmf belongs to the app and should not be deleted */ /* recording */ HDC record_dc; diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c index 20adc83b95f..adc15604538 100644 --- a/dlls/gdiplus/metafile.c +++ b/dlls/gdiplus/metafile.c @@ -692,6 +692,8 @@ void METAFILE_Free(GpMetafile *metafile) DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc)); if (!metafile->preserve_hemf) DeleteEnhMetaFile(metafile->hemf); + if (!metafile->preserve_hwmf) + DeleteMetaFile(metafile->hwmf); if (metafile->record_graphics) { WARN("metafile closed while recording\n"); @@ -2800,6 +2802,36 @@ GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile, return OutOfMemory; } } + else if (recordType & GDIP_WMF_RECORD_BASE) + { + /* WMF record */ + if (real_metafile->playback_dc) + { + METARECORD *record; + DWORD record_size = dataSize + 6; + + /* WMF record size is expressed in 16-bit words. */ + if (record_size & 1) + record_size++; + + record = calloc(1, record_size); + if (record) + { + record->rdSize = record_size / 2; + record->rdFunction = recordType & 0xffff; + if (dataSize) + memcpy(record->rdParm, data, dataSize); + + if (!PlayMetaFileRecord(real_metafile->playback_dc, real_metafile->handle_table, + record, real_metafile->handle_count)) + ERR("PlayMetaFileRecord failed\n"); + + free(record); + } + else + return OutOfMemory; + } + } else { EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1; @@ -3826,6 +3858,33 @@ static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENH pStr, data->callback_data); } +static int CALLBACK enum_metafile_wmf_proc(HDC hDC, HANDLETABLE *lpHTable, METARECORD *lpMFR, + int nObj, LPARAM lpData) +{ + struct enum_metafile_data *data = (struct enum_metafile_data*)lpData; + const BYTE *pStr; + UINT data_size; + + data->metafile->handle_table = lpHTable; + data->metafile->handle_count = nObj; + + /* WMF record size is in 16-bit words and includes the 6-byte header + * (DWORD rdSize + WORD rdFunction). */ + if (lpMFR->rdSize * 2 > 6) + { + pStr = (const BYTE*)lpMFR->rdParm; + data_size = lpMFR->rdSize * 2 - 6; + } + else + { + pStr = NULL; + data_size = 0; + } + + return data->callback(GDIP_WMF_RECORD_TO_EMFPLUS(lpMFR->rdFunction), 0, + data_size, pStr, data->callback_data); +} + GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count, GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback, @@ -3845,7 +3904,7 @@ GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, if (!graphics || !metafile || !destPoints || count != 3 || !srcRect) return InvalidParameter; - if (!metafile->hemf) + if (!metafile->hemf && !metafile->hwmf) return InvalidParameter; if (metafile->playback_graphics) @@ -3918,7 +3977,11 @@ GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, { real_metafile->page_unit = UnitDisplay; real_metafile->page_scale = 1.0; - stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile); + if (metafile->hwmf && (metafile->metafile_type == MetafileTypeWmf || + metafile->metafile_type == MetafileTypeWmfPlaceable)) + stat = GdipResetWorldTransform(graphics); + else + stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile); } if (stat == Ok) @@ -3937,8 +4000,30 @@ GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, } if (stat == Ok) - EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc, - &data, &dst_bounds); + { + if (metafile->hwmf && (metafile->metafile_type == MetafileTypeWmf || + metafile->metafile_type == MetafileTypeWmfPlaceable)) + { + /* For native WMF playback, use gdi32's mapping mode to map + * the metafile's logical window directly to the destination + * rectangle. */ + SetMapMode(real_metafile->playback_dc, MM_ANISOTROPIC); + SetWindowOrgEx(real_metafile->playback_dc, srcRect->X, srcRect->Y, NULL); + SetWindowExtEx(real_metafile->playback_dc, srcRect->Width, srcRect->Height, NULL); + SetViewportOrgEx(real_metafile->playback_dc, dst_bounds.left, dst_bounds.top, NULL); + SetViewportExtEx(real_metafile->playback_dc, + dst_bounds.right - dst_bounds.left, + dst_bounds.bottom - dst_bounds.top, NULL); + + EnumMetaFile(real_metafile->playback_dc, metafile->hwmf, + enum_metafile_wmf_proc, (LPARAM)&data); + } + else + { + EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc, + &data, &dst_bounds); + } + } METAFILE_PlaybackReleaseDC(real_metafile); @@ -4315,6 +4400,9 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, if (retval == Ok) { + (*metafile)->hwmf = hwmf; + (*metafile)->preserve_hwmf = !delete; + if (placeable) { (*metafile)->image.xres = (REAL)placeable->Inch; @@ -4330,11 +4418,12 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, else (*metafile)->metafile_type = MetafileTypeWmf; (*metafile)->image.format = ImageFormatWMF; - - if (delete) DeleteMetaFile(hwmf); } else + { DeleteEnhMetaFile(hemf); + } + return retval; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11180