gdiplus: Implement writing SetPageTransform records.
[wine.git] / dlls / gdiplus / metafile.c
blob1218800b3d121f534ae2e26ab5d2e81b0b49f04d
1 /*
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
19 #include <stdarg.h>
20 #include <math.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "wine/unicode.h"
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "ocidl.h"
30 #include "olectl.h"
31 #include "ole2.h"
33 #include "winreg.h"
34 #include "shlwapi.h"
36 #include "gdiplus.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
45 WORD Type;
46 WORD Flags;
47 DWORD Size;
48 DWORD DataSize;
49 } EmfPlusRecordHeader;
51 typedef struct EmfPlusHeader
53 EmfPlusRecordHeader Header;
54 DWORD Version;
55 DWORD EmfPlusFlags;
56 DWORD LogicalDpiX;
57 DWORD LogicalDpiY;
58 } EmfPlusHeader;
60 typedef struct EmfPlusFillRects
62 EmfPlusRecordHeader Header;
63 DWORD BrushID;
64 DWORD Count;
65 } EmfPlusFillRects;
67 typedef struct EmfPlusSetPageTransform
69 EmfPlusRecordHeader Header;
70 REAL PageScale;
71 } EmfPlusSetPageTransform;
73 typedef struct EmfPlusRect
75 SHORT X;
76 SHORT Y;
77 SHORT Width;
78 SHORT Height;
79 } EmfPlusRect;
81 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
83 DWORD size_needed;
84 EmfPlusRecordHeader *record;
86 if (!metafile->comment_data_size)
88 DWORD data_size = max(256, size * 2 + 4);
89 metafile->comment_data = GdipAlloc(data_size);
91 if (!metafile->comment_data)
92 return OutOfMemory;
94 memcpy(metafile->comment_data, "EMF+", 4);
96 metafile->comment_data_size = data_size;
97 metafile->comment_data_length = 4;
100 size_needed = size + metafile->comment_data_length;
102 if (size_needed > metafile->comment_data_size)
104 DWORD data_size = size_needed * 2;
105 BYTE *new_data = GdipAlloc(data_size);
107 if (!new_data)
108 return OutOfMemory;
110 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
112 metafile->comment_data_size = data_size;
113 GdipFree(metafile->comment_data);
114 metafile->comment_data = new_data;
117 *result = metafile->comment_data + metafile->comment_data_length;
118 metafile->comment_data_length += size;
120 record = (EmfPlusRecordHeader*)*result;
121 record->Size = size;
122 record->DataSize = size - sizeof(EmfPlusRecordHeader);
124 return Ok;
127 static void METAFILE_WriteRecords(GpMetafile *metafile)
129 if (metafile->comment_data_length > 4)
131 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
132 metafile->comment_data_length = 4;
136 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
138 GpStatus stat;
140 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
142 EmfPlusHeader *header;
144 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
145 if (stat != Ok)
146 return stat;
148 header->Header.Type = EmfPlusRecordTypeHeader;
150 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
151 header->Header.Flags = 1;
152 else
153 header->Header.Flags = 0;
155 header->Version = 0xDBC01002;
157 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
158 header->EmfPlusFlags = 1;
159 else
160 header->EmfPlusFlags = 0;
162 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
163 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
165 METAFILE_WriteRecords(metafile);
168 return Ok;
171 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
173 GpStatus stat;
175 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
177 EmfPlusRecordHeader *record;
179 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
180 if (stat != Ok)
181 return stat;
183 record->Type = EmfPlusRecordTypeEndOfFile;
184 record->Flags = 0;
186 METAFILE_WriteRecords(metafile);
189 return Ok;
192 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
193 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
195 HDC record_dc;
196 REAL framerect_factor_x, framerect_factor_y;
197 RECT rc;
198 GpStatus stat;
200 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
202 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
203 return InvalidParameter;
205 if (!frameRect)
207 FIXME("not implemented for NULL rect\n");
208 return NotImplemented;
211 switch (frameUnit)
213 case MetafileFrameUnitPixel:
214 framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX);
215 framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY);
216 break;
217 case MetafileFrameUnitPoint:
218 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
219 break;
220 case MetafileFrameUnitInch:
221 framerect_factor_x = framerect_factor_y = 2540.0;
222 break;
223 case MetafileFrameUnitDocument:
224 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
225 break;
226 case MetafileFrameUnitMillimeter:
227 framerect_factor_x = framerect_factor_y = 100.0;
228 break;
229 case MetafileFrameUnitGdi:
230 framerect_factor_x = framerect_factor_y = 1.0;
231 break;
232 default:
233 return InvalidParameter;
236 rc.left = framerect_factor_x * frameRect->X;
237 rc.top = framerect_factor_y * frameRect->Y;
238 rc.right = rc.left + framerect_factor_x * frameRect->Width;
239 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
241 record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
243 if (!record_dc)
244 return GenericError;
246 *metafile = GdipAlloc(sizeof(GpMetafile));
247 if(!*metafile)
249 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
250 return OutOfMemory;
253 (*metafile)->image.type = ImageTypeMetafile;
254 (*metafile)->image.picture = NULL;
255 (*metafile)->image.flags = ImageFlagsNone;
256 (*metafile)->image.palette = NULL;
257 (*metafile)->image.xres = 72.0;
258 (*metafile)->image.yres = 72.0;
259 (*metafile)->bounds = *frameRect;
260 (*metafile)->unit = frameUnit;
261 (*metafile)->metafile_type = type;
262 (*metafile)->record_dc = record_dc;
263 (*metafile)->comment_data = NULL;
264 (*metafile)->comment_data_size = 0;
265 (*metafile)->comment_data_length = 0;
266 (*metafile)->hemf = NULL;
268 stat = METAFILE_WriteHeader(*metafile, hdc);
270 if (stat != Ok)
272 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
273 GdipFree(*metafile);
274 *metafile = NULL;
275 return OutOfMemory;
278 return stat;
281 /*****************************************************************************
282 * GdipRecordMetafileI [GDIPLUS.@]
284 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
285 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
287 GpRectF frameRectF, *pFrameRectF;
289 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
291 if (frameRect)
293 frameRectF.X = frameRect->X;
294 frameRectF.Y = frameRect->Y;
295 frameRectF.Width = frameRect->Width;
296 frameRectF.Height = frameRect->Height;
297 pFrameRectF = &frameRectF;
299 else
300 pFrameRectF = NULL;
302 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
305 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
307 GpStatus stat;
309 if (!metafile->record_dc || metafile->record_graphics)
310 return InvalidParameter;
312 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
314 if (stat == Ok)
316 *result = metafile->record_graphics;
317 metafile->record_graphics->xres = 96.0;
318 metafile->record_graphics->yres = 96.0;
321 return stat;
324 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
326 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
328 EmfPlusRecordHeader *record;
329 GpStatus stat;
331 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
332 if (stat != Ok)
333 return stat;
335 record->Type = EmfPlusRecordTypeGetDC;
336 record->Flags = 0;
338 METAFILE_WriteRecords(metafile);
341 *hdc = metafile->record_dc;
343 return Ok;
346 static BOOL is_integer_rect(const GpRectF *rect)
348 SHORT x, y, width, height;
349 x = rect->X;
350 y = rect->Y;
351 width = rect->Width;
352 height = rect->Height;
353 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
354 rect->Width != (REAL)width || rect->Height != (REAL)height)
355 return FALSE;
356 return TRUE;
359 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
360 GDIPCONST GpRectF* rects, INT count)
362 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
364 EmfPlusFillRects *record;
365 GpStatus stat;
366 BOOL integer_rects=1;
367 int i;
368 DWORD brushid;
369 int flags = 0;
371 if (brush->bt == BrushTypeSolidColor)
373 flags |= 0x8000;
374 brushid = ((GpSolidFill*)brush)->color;
376 else
378 FIXME("brush serialization not implemented\n");
379 return NotImplemented;
382 for (i=0; i<count; i++)
384 if (!is_integer_rect(&rects[i]))
386 integer_rects = 0;
387 break;
391 if (integer_rects)
392 flags |= 0x4000;
394 stat = METAFILE_AllocateRecord(metafile,
395 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
396 (void**)&record);
397 if (stat != Ok)
398 return stat;
400 record->Header.Type = EmfPlusRecordTypeFillRects;
401 record->Header.Flags = flags;
402 record->BrushID = brushid;
403 record->Count = count;
405 if (integer_rects)
407 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
408 for (i=0; i<count; i++)
410 record_rects[i].X = (SHORT)rects[i].X;
411 record_rects[i].Y = (SHORT)rects[i].Y;
412 record_rects[i].Width = (SHORT)rects[i].Width;
413 record_rects[i].Height = (SHORT)rects[i].Height;
416 else
417 memcpy(record+1, rects, sizeof(GpRectF) * count);
419 METAFILE_WriteRecords(metafile);
422 return Ok;
425 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
427 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
429 EmfPlusSetPageTransform *record;
430 GpStatus stat;
432 stat = METAFILE_AllocateRecord(metafile,
433 sizeof(EmfPlusSetPageTransform),
434 (void**)&record);
435 if (stat != Ok)
436 return stat;
438 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
439 record->Header.Flags = unit;
440 record->PageScale = scale;
442 METAFILE_WriteRecords(metafile);
445 return Ok;
448 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
450 if (hdc != metafile->record_dc)
451 return InvalidParameter;
453 return Ok;
456 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
458 GpStatus stat;
460 stat = METAFILE_WriteEndOfFile(metafile);
461 metafile->record_graphics = NULL;
463 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
464 metafile->record_dc = NULL;
466 GdipFree(metafile->comment_data);
467 metafile->comment_data = NULL;
468 metafile->comment_data_size = 0;
470 return stat;
473 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
475 TRACE("(%p,%p)\n", metafile, hEmf);
477 if (!metafile || !hEmf || !metafile->hemf)
478 return InvalidParameter;
480 *hEmf = metafile->hemf;
481 metafile->hemf = NULL;
483 return Ok;
486 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
488 GpStatus stat = Ok;
490 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
492 if (stat == Ok)
494 /* The result of GdipGetDC always expects device co-ordinates, but the
495 * device co-ordinates of the source metafile do not correspond to
496 * device co-ordinates of the destination. Therefore, we set up the DC
497 * so that the metafile's bounds map to the destination points where we
498 * are drawing this metafile. */
499 SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
501 SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
502 SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
504 SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
505 SetViewportExtEx(metafile->playback_dc,
506 metafile->playback_points[1].X - metafile->playback_points[0].X,
507 metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
510 return stat;
513 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
515 if (metafile->playback_dc)
517 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
518 metafile->playback_dc = NULL;
522 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
524 GpMatrix *real_transform;
525 GpStatus stat;
527 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
529 if (stat == Ok)
531 /* FIXME: Prepend page transform. */
533 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
535 if (stat == Ok)
536 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
538 GdipDeleteMatrix(real_transform);
541 return stat;
544 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
545 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
547 GpStatus stat;
549 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
551 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
552 return InvalidParameter;
554 if (recordType >= 1 && recordType <= 0x7a)
556 /* regular EMF record */
557 if (metafile->playback_dc)
559 ENHMETARECORD *record;
561 record = GdipAlloc(dataSize + 8);
563 if (record)
565 record->iType = recordType;
566 record->nSize = dataSize + 8;
567 memcpy(record->dParm, data, dataSize);
569 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
570 record, metafile->handle_count);
572 GdipFree(record);
574 else
575 return OutOfMemory;
578 else
580 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
582 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
584 switch(recordType)
586 case EmfPlusRecordTypeHeader:
587 case EmfPlusRecordTypeEndOfFile:
588 break;
589 case EmfPlusRecordTypeGetDC:
590 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
591 break;
592 case EmfPlusRecordTypeFillRects:
594 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
595 GpBrush *brush, *temp_brush=NULL;
596 GpRectF *rects, *temp_rects=NULL;
598 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
599 return InvalidParameter;
601 if (flags & 0x4000)
603 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
604 return InvalidParameter;
606 else
608 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
609 return InvalidParameter;
612 if (flags & 0x8000)
614 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
615 brush = temp_brush;
617 else
619 FIXME("brush deserialization not implemented\n");
620 return NotImplemented;
623 if (stat == Ok)
625 if (flags & 0x4000)
627 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
628 int i;
630 rects = temp_rects = GdipAlloc(sizeof(GpRectF) * record->Count);
631 if (rects)
633 for (i=0; i<record->Count; i++)
635 rects[i].X = int_rects[i].X;
636 rects[i].Y = int_rects[i].Y;
637 rects[i].Width = int_rects[i].Width;
638 rects[i].Height = int_rects[i].Height;
641 else
642 stat = OutOfMemory;
644 else
645 rects = (GpRectF*)(record+1);
648 if (stat == Ok)
650 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
653 GdipDeleteBrush(temp_brush);
654 GdipFree(temp_rects);
656 return stat;
658 default:
659 FIXME("Not implemented for record type %x\n", recordType);
660 return NotImplemented;
664 return Ok;
667 struct enum_metafile_data
669 EnumerateMetafileProc callback;
670 void *callback_data;
671 GpMetafile *metafile;
674 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
675 int nObj, LPARAM lpData)
677 BOOL ret;
678 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
679 const BYTE* pStr;
681 data->metafile->handle_table = lpHTable;
682 data->metafile->handle_count = nObj;
684 /* First check for an EMF+ record. */
685 if (lpEMFR->iType == EMR_GDICOMMENT)
687 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
689 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
691 int offset = 4;
693 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
695 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
697 if (record->DataSize)
698 pStr = (const BYTE*)(record+1);
699 else
700 pStr = NULL;
702 ret = data->callback(record->Type, record->Flags, record->DataSize,
703 pStr, data->callback_data);
705 if (!ret)
706 return 0;
708 offset += record->Size;
711 return 1;
715 if (lpEMFR->nSize != 8)
716 pStr = (const BYTE*)lpEMFR->dParm;
717 else
718 pStr = NULL;
720 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
721 pStr, data->callback_data);
724 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
725 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
726 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
727 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
729 struct enum_metafile_data data;
730 GpStatus stat;
731 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
732 GraphicsContainer state;
734 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
735 destPoints, count, srcRect, srcUnit, callback, callbackData,
736 imageAttributes);
738 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
739 return InvalidParameter;
741 if (!metafile->hemf)
742 return InvalidParameter;
744 if (metafile->playback_graphics)
745 return ObjectBusy;
747 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
748 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
749 debugstr_pointf(&destPoints[2]));
751 data.callback = callback;
752 data.callback_data = callbackData;
753 data.metafile = real_metafile;
755 real_metafile->playback_graphics = graphics;
756 real_metafile->playback_dc = NULL;
757 real_metafile->src_rect = *srcRect;
759 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
760 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
762 if (stat == Ok)
763 stat = GdipBeginContainer2(graphics, &state);
765 if (stat == Ok)
767 stat = GdipSetPageScale(graphics, 1.0);
769 if (stat == Ok)
770 stat = GdipSetPageUnit(graphics, UnitPixel);
772 if (stat == Ok)
773 stat = GdipCreateMatrix(&real_metafile->world_transform);
775 if (stat == Ok)
777 real_metafile->page_unit = UnitPixel; /* FIXME: Use frame unit here? */
778 real_metafile->page_scale = 1.0;
779 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
782 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
783 metafile->metafile_type == MetafileTypeWmfPlaceable ||
784 metafile->metafile_type == MetafileTypeWmf))
785 stat = METAFILE_PlaybackGetDC(real_metafile);
787 if (stat == Ok)
788 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
790 METAFILE_PlaybackReleaseDC(real_metafile);
792 GdipDeleteMatrix(real_metafile->world_transform);
793 real_metafile->world_transform = NULL;
795 GdipEndContainer(graphics, state);
798 real_metafile->playback_graphics = NULL;
800 return stat;
803 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
804 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
805 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
807 GpPointF points[3];
809 if (!graphics || !metafile || !dest) return InvalidParameter;
811 points[0].X = points[2].X = dest->X;
812 points[0].Y = points[1].Y = dest->Y;
813 points[1].X = dest->X + dest->Width;
814 points[2].Y = dest->Y + dest->Height;
816 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
817 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
820 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
821 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
822 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
824 GpRectF destf;
826 if (!graphics || !metafile || !dest) return InvalidParameter;
828 destf.X = dest->X;
829 destf.Y = dest->Y;
830 destf.Width = dest->Width;
831 destf.Height = dest->Height;
833 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
836 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
837 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
838 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
840 GpRectF destf;
842 if (!graphics || !metafile || !dest) return InvalidParameter;
844 destf.X = dest->X;
845 destf.Y = dest->Y;
846 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
847 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
849 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
852 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
853 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
854 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
856 GpPointF ptf;
858 if (!graphics || !metafile || !dest) return InvalidParameter;
860 ptf.X = dest->X;
861 ptf.Y = dest->Y;
863 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
866 static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
867 int nObj, LPARAM lpData)
869 MetafileType *result = (MetafileType*)lpData;
871 if (lpEMFR->iType == EMR_GDICOMMENT)
873 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
875 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
877 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
879 if (4 + sizeof(EmfPlusRecordHeader) <= comment->cbData &&
880 header->Type == EmfPlusRecordTypeHeader)
882 if ((header->Flags & 1) == 1)
883 *result = MetafileTypeEmfPlusDual;
884 else
885 *result = MetafileTypeEmfPlusOnly;
888 else
889 *result = MetafileTypeEmf;
891 else
892 *result = MetafileTypeEmf;
894 return FALSE;
897 MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf)
899 MetafileType result = MetafileTypeInvalid;
900 EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL);
901 return result;