2 * Copyright (C) 2011 Vincent Povirk for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/unicode.h"
37 #include "gdiplus_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43 typedef struct EmfPlusRecordHeader
49 } EmfPlusRecordHeader;
51 typedef struct EmfPlusHeader
53 EmfPlusRecordHeader Header;
60 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
63 EmfPlusRecordHeader *record;
65 if (!metafile->comment_data_size)
67 DWORD data_size = max(256, size * 2 + 4);
68 metafile->comment_data = GdipAlloc(data_size);
70 if (!metafile->comment_data)
73 memcpy(metafile->comment_data, "EMF+", 4);
75 metafile->comment_data_size = data_size;
76 metafile->comment_data_length = 4;
79 size_needed = size + metafile->comment_data_length;
81 if (size_needed > metafile->comment_data_size)
83 DWORD data_size = size_needed * 2;
84 BYTE *new_data = GdipAlloc(data_size);
89 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
91 metafile->comment_data_size = data_size;
92 GdipFree(metafile->comment_data);
93 metafile->comment_data = new_data;
96 *result = metafile->comment_data + metafile->comment_data_length;
97 metafile->comment_data_length += size;
99 record = (EmfPlusRecordHeader*)*result;
101 record->DataSize = size - sizeof(EmfPlusRecordHeader);
106 static void METAFILE_WriteRecords(GpMetafile *metafile)
108 if (metafile->comment_data_length > 4)
110 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
111 metafile->comment_data_length = 4;
115 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
119 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
121 EmfPlusHeader *header;
123 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
127 header->Header.Type = EmfPlusRecordTypeHeader;
129 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
130 header->Header.Flags = 1;
132 header->Header.Flags = 0;
134 header->Version = 0xDBC01002;
136 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
137 header->EmfPlusFlags = 1;
139 header->EmfPlusFlags = 0;
141 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
142 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
144 METAFILE_WriteRecords(metafile);
150 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
154 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
156 EmfPlusRecordHeader *record;
158 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
162 record->Type = EmfPlusRecordTypeEndOfFile;
165 METAFILE_WriteRecords(metafile);
171 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
172 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
175 REAL framerect_factor_x, framerect_factor_y;
179 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
181 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
182 return InvalidParameter;
186 FIXME("not implemented for NULL rect\n");
187 return NotImplemented;
192 case MetafileFrameUnitPixel:
193 framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX);
194 framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY);
196 case MetafileFrameUnitPoint:
197 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
199 case MetafileFrameUnitInch:
200 framerect_factor_x = framerect_factor_y = 2540.0;
202 case MetafileFrameUnitDocument:
203 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
205 case MetafileFrameUnitMillimeter:
206 framerect_factor_x = framerect_factor_y = 100.0;
208 case MetafileFrameUnitGdi:
209 framerect_factor_x = framerect_factor_y = 1.0;
212 return InvalidParameter;
215 rc.left = framerect_factor_x * frameRect->X;
216 rc.top = framerect_factor_y * frameRect->Y;
217 rc.right = rc.left + framerect_factor_x * frameRect->Width;
218 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
220 record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
225 *metafile = GdipAlloc(sizeof(GpMetafile));
228 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
232 (*metafile)->image.type = ImageTypeMetafile;
233 (*metafile)->image.picture = NULL;
234 (*metafile)->image.flags = ImageFlagsNone;
235 (*metafile)->image.palette = NULL;
236 (*metafile)->bounds = *frameRect;
237 (*metafile)->unit = frameUnit;
238 (*metafile)->metafile_type = type;
239 (*metafile)->record_dc = record_dc;
240 (*metafile)->comment_data = NULL;
241 (*metafile)->comment_data_size = 0;
242 (*metafile)->comment_data_length = 0;
243 (*metafile)->hemf = NULL;
245 stat = METAFILE_WriteHeader(*metafile, hdc);
249 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
258 /*****************************************************************************
259 * GdipRecordMetafileI [GDIPLUS.@]
261 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
262 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
264 GpRectF frameRectF, *pFrameRectF;
266 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
270 frameRectF.X = frameRect->X;
271 frameRectF.Y = frameRect->Y;
272 frameRectF.Width = frameRect->Width;
273 frameRectF.Height = frameRect->Height;
274 pFrameRectF = &frameRectF;
279 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
282 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
286 if (!metafile->record_dc || metafile->record_graphics)
287 return InvalidParameter;
289 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
292 *result = metafile->record_graphics;
297 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
299 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
301 EmfPlusRecordHeader *record;
304 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
308 record->Type = EmfPlusRecordTypeGetDC;
311 METAFILE_WriteRecords(metafile);
314 *hdc = metafile->record_dc;
319 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
321 if (hdc != metafile->record_dc)
322 return InvalidParameter;
327 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
331 stat = METAFILE_WriteEndOfFile(metafile);
332 metafile->record_graphics = NULL;
334 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
335 metafile->record_dc = NULL;
337 GdipFree(metafile->comment_data);
338 metafile->comment_data = NULL;
339 metafile->comment_data_size = 0;
344 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
346 TRACE("(%p,%p)\n", metafile, hEmf);
348 if (!metafile || !hEmf || !metafile->hemf)
349 return InvalidParameter;
351 *hEmf = metafile->hemf;
352 metafile->hemf = NULL;
357 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
361 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
365 /* The result of GdipGetDC always expects device co-ordinates, but the
366 * device co-ordinates of the source metafile do not correspond to
367 * device co-ordinates of the destination. Therefore, we set up the DC
368 * so that the metafile's bounds map to the destination points where we
369 * are drawing this metafile. */
370 SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
372 SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
373 SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
375 SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
376 SetViewportExtEx(metafile->playback_dc,
377 metafile->playback_points[1].X - metafile->playback_points[0].X,
378 metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
384 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
386 if (metafile->playback_dc)
388 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
389 metafile->playback_dc = NULL;
393 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
394 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
396 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
398 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
399 return InvalidParameter;
401 if (recordType >= 1 && recordType <= 0x7a)
403 /* regular EMF record */
404 if (metafile->playback_dc)
406 ENHMETARECORD *record;
408 record = GdipAlloc(dataSize + 8);
412 record->iType = recordType;
413 record->nSize = dataSize;
414 memcpy(record->dParm, data, dataSize);
416 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
417 record, metafile->handle_count);
427 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
431 case EmfPlusRecordTypeHeader:
432 case EmfPlusRecordTypeEndOfFile:
434 case EmfPlusRecordTypeGetDC:
435 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
438 FIXME("Not implemented for record type %x\n", recordType);
439 return NotImplemented;
446 struct enum_metafile_data
448 EnumerateMetafileProc callback;
450 GpMetafile *metafile;
453 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
454 int nObj, LPARAM lpData)
457 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
460 data->metafile->handle_table = lpHTable;
461 data->metafile->handle_count = nObj;
463 /* First check for an EMF+ record. */
464 if (lpEMFR->iType == EMR_GDICOMMENT)
466 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
468 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
472 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
474 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
476 if (record->DataSize)
477 pStr = (const BYTE*)(record+1);
481 ret = data->callback(record->Type, record->Flags, record->DataSize,
482 pStr, data->callback_data);
487 offset += record->Size;
494 if (lpEMFR->nSize != 8)
495 pStr = (const BYTE*)lpEMFR->dParm;
499 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
500 pStr, data->callback_data);
503 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
504 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
505 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
506 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
508 struct enum_metafile_data data;
510 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
512 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
513 destPoints, count, srcRect, srcUnit, callback, callbackData,
516 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
517 return InvalidParameter;
520 return InvalidParameter;
522 if (metafile->playback_graphics)
525 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
526 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
527 debugstr_pointf(&destPoints[2]));
529 data.callback = callback;
530 data.callback_data = callbackData;
531 data.metafile = real_metafile;
533 real_metafile->playback_graphics = graphics;
534 real_metafile->playback_dc = NULL;
536 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
537 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
539 if (stat == Ok && metafile->metafile_type == MetafileTypeEmf)
540 stat = METAFILE_PlaybackGetDC((GpMetafile*)metafile);
543 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
545 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
547 real_metafile->playback_graphics = NULL;
552 static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
553 int nObj, LPARAM lpData)
555 MetafileType *result = (MetafileType*)lpData;
557 if (lpEMFR->iType == EMR_GDICOMMENT)
559 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
561 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
563 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
565 if (4 + sizeof(EmfPlusRecordHeader) <= comment->cbData &&
566 header->Type == EmfPlusRecordTypeHeader)
568 if ((header->Flags & 1) == 1)
569 *result = MetafileTypeEmfPlusDual;
571 *result = MetafileTypeEmfPlusOnly;
575 *result = MetafileTypeEmf;
578 *result = MetafileTypeEmf;
583 MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf)
585 MetafileType result = MetafileTypeInvalid;
586 EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL);