/* * Copyright (C) 2011 Vincent Povirk for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include <math.h> #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "wine/unicode.h" #define COBJMACROS #include "objbase.h" #include "ocidl.h" #include "olectl.h" #include "ole2.h" #include "winreg.h" #include "shlwapi.h" #include "gdiplus.h" #include "gdiplus_private.h" #include "wine/debug.h" #include "wine/list.h" WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); typedef struct EmfPlusRecordHeader { WORD Type; WORD Flags; DWORD Size; DWORD DataSize; } EmfPlusRecordHeader; typedef struct EmfPlusHeader { EmfPlusRecordHeader Header; DWORD Version; DWORD EmfPlusFlags; DWORD LogicalDpiX; DWORD LogicalDpiY; } EmfPlusHeader; typedef struct EmfPlusFillRects { EmfPlusRecordHeader Header; DWORD BrushID; DWORD Count; } EmfPlusFillRects; typedef struct EmfPlusSetPageTransform { EmfPlusRecordHeader Header; REAL PageScale; } EmfPlusSetPageTransform; typedef struct EmfPlusRect { SHORT X; SHORT Y; SHORT Width; SHORT Height; } EmfPlusRect; static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result) { DWORD size_needed; EmfPlusRecordHeader *record; if (!metafile->comment_data_size) { DWORD data_size = max(256, size * 2 + 4); metafile->comment_data = GdipAlloc(data_size); if (!metafile->comment_data) return OutOfMemory; memcpy(metafile->comment_data, "EMF+", 4); metafile->comment_data_size = data_size; metafile->comment_data_length = 4; } size_needed = size + metafile->comment_data_length; if (size_needed > metafile->comment_data_size) { DWORD data_size = size_needed * 2; BYTE *new_data = GdipAlloc(data_size); if (!new_data) return OutOfMemory; memcpy(new_data, metafile->comment_data, metafile->comment_data_length); metafile->comment_data_size = data_size; GdipFree(metafile->comment_data); metafile->comment_data = new_data; } *result = metafile->comment_data + metafile->comment_data_length; metafile->comment_data_length += size; record = (EmfPlusRecordHeader*)*result; record->Size = size; record->DataSize = size - sizeof(EmfPlusRecordHeader); return Ok; } static void METAFILE_WriteRecords(GpMetafile *metafile) { if (metafile->comment_data_length > 4) { GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data); metafile->comment_data_length = 4; } } static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc) { GpStatus stat; if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) { EmfPlusHeader *header; stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header); if (stat != Ok) return stat; header->Header.Type = EmfPlusRecordTypeHeader; if (metafile->metafile_type == MetafileTypeEmfPlusDual) header->Header.Flags = 1; else header->Header.Flags = 0; header->Version = 0xDBC01002; if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY) header->EmfPlusFlags = 1; else header->EmfPlusFlags = 0; header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX); header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY); METAFILE_WriteRecords(metafile); } return Ok; } static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile) { GpStatus stat; if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) { EmfPlusRecordHeader *record; stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record); if (stat != Ok) return stat; record->Type = EmfPlusRecordTypeEndOfFile; record->Flags = 0; METAFILE_WriteRecords(metafile); } return Ok; } GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect, MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) { HDC record_dc; REAL framerect_factor_x, framerect_factor_y; RECT rc; GpStatus stat; TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile); if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile) return InvalidParameter; if (!frameRect) { FIXME("not implemented for NULL rect\n"); return NotImplemented; } switch (frameUnit) { case MetafileFrameUnitPixel: framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX); framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY); break; case MetafileFrameUnitPoint: framerect_factor_x = framerect_factor_y = 2540.0 / 72.0; break; case MetafileFrameUnitInch: framerect_factor_x = framerect_factor_y = 2540.0; break; case MetafileFrameUnitDocument: framerect_factor_x = framerect_factor_y = 2540.0 / 300.0; break; case MetafileFrameUnitMillimeter: framerect_factor_x = framerect_factor_y = 100.0; break; case MetafileFrameUnitGdi: framerect_factor_x = framerect_factor_y = 1.0; break; default: return InvalidParameter; } rc.left = framerect_factor_x * frameRect->X; rc.top = framerect_factor_y * frameRect->Y; rc.right = rc.left + framerect_factor_x * frameRect->Width; rc.bottom = rc.top + framerect_factor_y * frameRect->Height; record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc); if (!record_dc) return GenericError; *metafile = GdipAlloc(sizeof(GpMetafile)); if(!*metafile) { DeleteEnhMetaFile(CloseEnhMetaFile(record_dc)); return OutOfMemory; } (*metafile)->image.type = ImageTypeMetafile; (*metafile)->image.picture = NULL; (*metafile)->image.flags = ImageFlagsNone; (*metafile)->image.palette = NULL; (*metafile)->image.xres = 72.0; (*metafile)->image.yres = 72.0; (*metafile)->bounds = *frameRect; (*metafile)->unit = frameUnit; (*metafile)->metafile_type = type; (*metafile)->record_dc = record_dc; (*metafile)->comment_data = NULL; (*metafile)->comment_data_size = 0; (*metafile)->comment_data_length = 0; (*metafile)->hemf = NULL; stat = METAFILE_WriteHeader(*metafile, hdc); if (stat != Ok) { DeleteEnhMetaFile(CloseEnhMetaFile(record_dc)); GdipFree(*metafile); *metafile = NULL; return OutOfMemory; } return stat; } /***************************************************************************** * GdipRecordMetafileI [GDIPLUS.@] */ GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect, MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) { GpRectF frameRectF, *pFrameRectF; TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile); if (frameRect) { frameRectF.X = frameRect->X; frameRectF.Y = frameRect->Y; frameRectF.Width = frameRect->Width; frameRectF.Height = frameRect->Height; pFrameRectF = &frameRectF; } else pFrameRectF = NULL; return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile); } GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result) { GpStatus stat; if (!metafile->record_dc || metafile->record_graphics) return InvalidParameter; stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics); if (stat == Ok) { *result = metafile->record_graphics; metafile->record_graphics->xres = 96.0; metafile->record_graphics->yres = 96.0; } return stat; } GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc) { if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) { EmfPlusRecordHeader *record; GpStatus stat; stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record); if (stat != Ok) return stat; record->Type = EmfPlusRecordTypeGetDC; record->Flags = 0; METAFILE_WriteRecords(metafile); } *hdc = metafile->record_dc; return Ok; } static BOOL is_integer_rect(const GpRectF *rect) { SHORT x, y, width, height; x = rect->X; y = rect->Y; width = rect->Width; height = rect->Height; if (rect->X != (REAL)x || rect->Y != (REAL)y || rect->Width != (REAL)width || rect->Height != (REAL)height) return FALSE; return TRUE; } GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush, GDIPCONST GpRectF* rects, INT count) { if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) { EmfPlusFillRects *record; GpStatus stat; BOOL integer_rects = TRUE; int i; DWORD brushid; int flags = 0; if (brush->bt == BrushTypeSolidColor) { flags |= 0x8000; brushid = ((GpSolidFill*)brush)->color; } else { FIXME("brush serialization not implemented\n"); return NotImplemented; } for (i=0; i<count; i++) { if (!is_integer_rect(&rects[i])) { integer_rects = FALSE; break; } } if (integer_rects) flags |= 0x4000; stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)), (void**)&record); if (stat != Ok) return stat; record->Header.Type = EmfPlusRecordTypeFillRects; record->Header.Flags = flags; record->BrushID = brushid; record->Count = count; if (integer_rects) { EmfPlusRect *record_rects = (EmfPlusRect*)(record+1); for (i=0; i<count; i++) { record_rects[i].X = (SHORT)rects[i].X; record_rects[i].Y = (SHORT)rects[i].Y; record_rects[i].Width = (SHORT)rects[i].Width; record_rects[i].Height = (SHORT)rects[i].Height; } } else memcpy(record+1, rects, sizeof(GpRectF) * count); METAFILE_WriteRecords(metafile); } return Ok; } GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale) { if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual) { EmfPlusSetPageTransform *record; GpStatus stat; stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusSetPageTransform), (void**)&record); if (stat != Ok) return stat; record->Header.Type = EmfPlusRecordTypeSetPageTransform; record->Header.Flags = unit; record->PageScale = scale; METAFILE_WriteRecords(metafile); } return Ok; } GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc) { if (hdc != metafile->record_dc) return InvalidParameter; return Ok; } GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) { GpStatus stat; stat = METAFILE_WriteEndOfFile(metafile); metafile->record_graphics = NULL; metafile->hemf = CloseEnhMetaFile(metafile->record_dc); metafile->record_dc = NULL; GdipFree(metafile->comment_data); metafile->comment_data = NULL; metafile->comment_data_size = 0; return stat; } GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf) { TRACE("(%p,%p)\n", metafile, hEmf); if (!metafile || !hEmf || !metafile->hemf) return InvalidParameter; *hEmf = metafile->hemf; metafile->hemf = NULL; return Ok; } static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile) { GpStatus stat = Ok; stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc); if (stat == Ok) { /* The result of GdipGetDC always expects device co-ordinates, but the * device co-ordinates of the source metafile do not correspond to * device co-ordinates of the destination. Therefore, we set up the DC * so that the metafile's bounds map to the destination points where we * are drawing this metafile. */ SetMapMode(metafile->playback_dc, MM_ANISOTROPIC); SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL); SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL); SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL); SetViewportExtEx(metafile->playback_dc, metafile->playback_points[1].X - metafile->playback_points[0].X, metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL); } return stat; } static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile) { if (metafile->playback_dc) { GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc); metafile->playback_dc = NULL; } } static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile) { GpMatrix *real_transform; GpStatus stat; stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform); if (stat == Ok) { REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0); if (metafile->page_unit != UnitDisplay) scale *= metafile->page_scale; stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend); if (stat == Ok) stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend); if (stat == Ok) stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform); GdipDeleteMatrix(real_transform); } return stat; } GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile, EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data) { GpStatus stat; GpMetafile *real_metafile = (GpMetafile*)metafile; TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data); if (!metafile || (dataSize && !data) || !metafile->playback_graphics) return InvalidParameter; if (recordType >= 1 && recordType <= 0x7a) { /* regular EMF record */ if (metafile->playback_dc) { ENHMETARECORD *record; record = GdipAlloc(dataSize + 8); if (record) { record->iType = recordType; record->nSize = dataSize + 8; memcpy(record->dParm, data, dataSize); PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table, record, metafile->handle_count); GdipFree(record); } else return OutOfMemory; } } else { EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1; METAFILE_PlaybackReleaseDC((GpMetafile*)metafile); switch(recordType) { case EmfPlusRecordTypeHeader: case EmfPlusRecordTypeEndOfFile: break; case EmfPlusRecordTypeGetDC: METAFILE_PlaybackGetDC((GpMetafile*)metafile); break; case EmfPlusRecordTypeFillRects: { EmfPlusFillRects *record = (EmfPlusFillRects*)header; GpBrush *brush, *temp_brush=NULL; GpRectF *rects, *temp_rects=NULL; if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects)) return InvalidParameter; if (flags & 0x4000) { if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count) return InvalidParameter; } else { if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count) return InvalidParameter; } if (flags & 0x8000) { stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush); brush = temp_brush; } else { FIXME("brush deserialization not implemented\n"); return NotImplemented; } if (stat == Ok) { if (flags & 0x4000) { EmfPlusRect *int_rects = (EmfPlusRect*)(record+1); int i; rects = temp_rects = GdipAlloc(sizeof(GpRectF) * record->Count); if (rects) { for (i=0; i<record->Count; i++) { rects[i].X = int_rects[i].X; rects[i].Y = int_rects[i].Y; rects[i].Width = int_rects[i].Width; rects[i].Height = int_rects[i].Height; } } else stat = OutOfMemory; } else rects = (GpRectF*)(record+1); } if (stat == Ok) { stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count); } GdipDeleteBrush(temp_brush); GdipFree(temp_rects); return stat; } case EmfPlusRecordTypeSetPageTransform: { EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header; GpUnit unit = (GpUnit)flags; if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform)) return InvalidParameter; real_metafile->page_unit = unit; real_metafile->page_scale = record->PageScale; return METAFILE_PlaybackUpdateWorldTransform(real_metafile); } default: FIXME("Not implemented for record type %x\n", recordType); return NotImplemented; } } return Ok; } struct enum_metafile_data { EnumerateMetafileProc callback; void *callback_data; GpMetafile *metafile; }; static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, int nObj, LPARAM lpData) { BOOL ret; struct enum_metafile_data *data = (struct enum_metafile_data*)lpData; const BYTE* pStr; data->metafile->handle_table = lpHTable; data->metafile->handle_count = nObj; /* First check for an EMF+ record. */ if (lpEMFR->iType == EMR_GDICOMMENT) { const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR; if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0) { int offset = 4; while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData) { const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset]; if (record->DataSize) pStr = (const BYTE*)(record+1); else pStr = NULL; ret = data->callback(record->Type, record->Flags, record->DataSize, pStr, data->callback_data); if (!ret) return 0; offset += record->Size; } return 1; } } if (lpEMFR->nSize != 8) pStr = (const BYTE*)lpEMFR->dParm; else pStr = NULL; return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8, pStr, data->callback_data); } GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics, GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count, GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback, VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes) { struct enum_metafile_data data; GpStatus stat; GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */ GraphicsContainer state; TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile, destPoints, count, srcRect, srcUnit, callback, callbackData, imageAttributes); if (!graphics || !metafile || !destPoints || count != 3 || !srcRect) return InvalidParameter; if (!metafile->hemf) return InvalidParameter; if (metafile->playback_graphics) return ObjectBusy; TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit, debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]), debugstr_pointf(&destPoints[2])); data.callback = callback; data.callback_data = callbackData; data.metafile = real_metafile; real_metafile->playback_graphics = graphics; real_metafile->playback_dc = NULL; real_metafile->src_rect = *srcRect; memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3); stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3); if (stat == Ok) stat = GdipBeginContainer2(graphics, &state); if (stat == Ok) { stat = GdipSetPageScale(graphics, 1.0); if (stat == Ok) stat = GdipSetPageUnit(graphics, UnitPixel); if (stat == Ok) stat = GdipCreateMatrix(&real_metafile->world_transform); if (stat == Ok) { real_metafile->page_unit = UnitDisplay; real_metafile->page_scale = 1.0; stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile); } if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf || metafile->metafile_type == MetafileTypeWmfPlaceable || metafile->metafile_type == MetafileTypeWmf)) stat = METAFILE_PlaybackGetDC(real_metafile); if (stat == Ok) EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL); METAFILE_PlaybackReleaseDC(real_metafile); GdipDeleteMatrix(real_metafile->world_transform); real_metafile->world_transform = NULL; GdipEndContainer(graphics, state); } real_metafile->playback_graphics = NULL; return stat; } GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics, GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest, EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) { GpPointF points[3]; if (!graphics || !metafile || !dest) return InvalidParameter; points[0].X = points[2].X = dest->X; points[0].Y = points[1].Y = dest->Y; points[1].X = dest->X + dest->Width; points[2].Y = dest->Y + dest->Height; return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3, &metafile->bounds, metafile->unit, callback, cb_data, attrs); } GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics, GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest, EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) { GpRectF destf; if (!graphics || !metafile || !dest) return InvalidParameter; destf.X = dest->X; destf.Y = dest->Y; destf.Width = dest->Width; destf.Height = dest->Height; return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs); } GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics, GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest, EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) { GpRectF destf; if (!graphics || !metafile || !dest) return InvalidParameter; destf.X = dest->X; destf.Y = dest->Y; destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres); destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres); return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs); } GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics, GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest, EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs) { GpPointF ptf; if (!graphics || !metafile || !dest) return InvalidParameter; ptf.X = dest->X; ptf.Y = dest->Y; return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs); } static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, int nObj, LPARAM lpData) { MetafileType *result = (MetafileType*)lpData; if (lpEMFR->iType == EMR_GDICOMMENT) { const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR; if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0) { const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4]; if (4 + sizeof(EmfPlusRecordHeader) <= comment->cbData && header->Type == EmfPlusRecordTypeHeader) { if ((header->Flags & 1) == 1) *result = MetafileTypeEmfPlusDual; else *result = MetafileTypeEmfPlusOnly; } } else *result = MetafileTypeEmf; } else *result = MetafileTypeEmf; return FALSE; } static MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf) { MetafileType result = MetafileTypeInvalid; EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL); return result; } GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile, MetafileHeader * header) { static int calls; TRACE("(%p, %p)\n", metafile, header); if(!metafile || !header) return InvalidParameter; if(!(calls++)) FIXME("not implemented\n"); memset(header, 0, sizeof(MetafileHeader)); return Ok; } GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hEmf, MetafileHeader *header) { static int calls; if(!hEmf || !header) return InvalidParameter; if(!(calls++)) FIXME("not implemented\n"); memset(header, 0, sizeof(MetafileHeader)); return Ok; } GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename, MetafileHeader *header) { static int calls; TRACE("(%s,%p)\n", debugstr_w(filename), header); if(!filename || !header) return InvalidParameter; if(!(calls++)) FIXME("not implemented\n"); memset(header, 0, sizeof(MetafileHeader)); return Ok; } GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream, MetafileHeader *header) { static int calls; TRACE("(%p,%p)\n", stream, header); if(!stream || !header) return InvalidParameter; if(!(calls++)) FIXME("not implemented\n"); memset(header, 0, sizeof(MetafileHeader)); return Ok; } GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete, GpMetafile **metafile) { ENHMETAHEADER header; MetafileType metafile_type; TRACE("(%p,%i,%p)\n", hemf, delete, metafile); if(!hemf || !metafile) return InvalidParameter; if (GetEnhMetaFileHeader(hemf, sizeof(header), &header) == 0) return GenericError; metafile_type = METAFILE_GetEmfType(hemf); if (metafile_type == MetafileTypeInvalid) return GenericError; *metafile = GdipAlloc(sizeof(GpMetafile)); if (!*metafile) return OutOfMemory; (*metafile)->image.type = ImageTypeMetafile; (*metafile)->image.format = ImageFormatEMF; (*metafile)->image.frame_count = 1; (*metafile)->image.xres = (REAL)header.szlDevice.cx; (*metafile)->image.yres = (REAL)header.szlDevice.cy; (*metafile)->bounds.X = (REAL)header.rclBounds.left; (*metafile)->bounds.Y = (REAL)header.rclBounds.top; (*metafile)->bounds.Width = (REAL)(header.rclBounds.right - header.rclBounds.left); (*metafile)->bounds.Height = (REAL)(header.rclBounds.bottom - header.rclBounds.top); (*metafile)->unit = UnitPixel; (*metafile)->metafile_type = metafile_type; (*metafile)->hemf = hemf; (*metafile)->preserve_hemf = !delete; TRACE("<-- %p\n", *metafile); return Ok; } GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete, GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile) { UINT read; BYTE *copy; HENHMETAFILE hemf; GpStatus retval = Ok; TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile); if(!hwmf || !metafile || !placeable) return InvalidParameter; *metafile = NULL; read = GetMetaFileBitsEx(hwmf, 0, NULL); if(!read) return GenericError; copy = GdipAlloc(read); GetMetaFileBitsEx(hwmf, read, copy); hemf = SetWinMetaFileBits(read, copy, NULL, NULL); GdipFree(copy); /* FIXME: We should store and use hwmf instead of converting to hemf */ retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile); if (retval == Ok) { (*metafile)->image.xres = (REAL)placeable->Inch; (*metafile)->image.yres = (REAL)placeable->Inch; (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch); (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch); (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right - placeable->BoundingBox.Left); (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom - placeable->BoundingBox.Top); (*metafile)->metafile_type = MetafileTypeWmfPlaceable; (*metafile)->image.format = ImageFormatWMF; if (delete) DeleteMetaFile(hwmf); } else DeleteEnhMetaFile(hemf); return retval; } GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file, GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile) { HMETAFILE hmf = GetMetaFileW(file); TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile); if(!hmf) return InvalidParameter; return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile); } GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file, GpMetafile **metafile) { FIXME("(%p, %p): stub\n", file, metafile); return NotImplemented; } GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream, GpMetafile **metafile) { FIXME("(%p, %p): stub\n", stream, metafile); return NotImplemented; } GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile, UINT limitDpi) { static int calls; TRACE("(%p,%u)\n", metafile, limitDpi); if(!(calls++)) FIXME("not implemented\n"); return NotImplemented; } GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref, GpMetafile* metafile, BOOL* succ, EmfType emfType, const WCHAR* description, GpMetafile** out_metafile) { static int calls; TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType, debugstr_w(description), out_metafile); if(!ref || !metafile || !out_metafile) return InvalidParameter; *succ = FALSE; *out_metafile = NULL; if(!(calls++)) FIXME("not implemented\n"); return NotImplemented; } GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16, LPBYTE pData16, INT iMapMode, INT eFlags) { FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags); return NotImplemented; } GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect, MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) { FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect, frameUnit, debugstr_w(desc), metafile); return NotImplemented; } GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type, GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile) { FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect, frameUnit, debugstr_w(desc), metafile); return NotImplemented; } /***************************************************************************** * GdipConvertToEmfPlusToFile [GDIPLUS.@] */ GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics, GpMetafile* metafile, BOOL* conversionSuccess, const WCHAR* filename, EmfType emfType, const WCHAR* description, GpMetafile** out_metafile) { FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile); return NotImplemented; }