gdiplus: Implement recording/playback for RotateWorldTransform.
[wine.git] / dlls / gdiplus / metafile.c
blobaa0da214e0d0ae9791379df2f92c70eae6cdcb81
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 EmfPlusClear
62 EmfPlusRecordHeader Header;
63 DWORD Color;
64 } EmfPlusClear;
66 typedef struct EmfPlusFillRects
68 EmfPlusRecordHeader Header;
69 DWORD BrushID;
70 DWORD Count;
71 } EmfPlusFillRects;
73 typedef struct EmfPlusSetPageTransform
75 EmfPlusRecordHeader Header;
76 REAL PageScale;
77 } EmfPlusSetPageTransform;
79 typedef struct EmfPlusRect
81 SHORT X;
82 SHORT Y;
83 SHORT Width;
84 SHORT Height;
85 } EmfPlusRect;
87 typedef struct EmfPlusScaleWorldTransform
89 EmfPlusRecordHeader Header;
90 REAL Sx;
91 REAL Sy;
92 } EmfPlusScaleWorldTransform;
94 typedef struct EmfPlusMultiplyWorldTransform
96 EmfPlusRecordHeader Header;
97 REAL MatrixData[6];
98 } EmfPlusMultiplyWorldTransform;
100 typedef struct EmfPlusRotateWorldTransform
102 EmfPlusRecordHeader Header;
103 REAL Angle;
104 } EmfPlusRotateWorldTransform;
106 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
108 DWORD size_needed;
109 EmfPlusRecordHeader *record;
111 if (!metafile->comment_data_size)
113 DWORD data_size = max(256, size * 2 + 4);
114 metafile->comment_data = heap_alloc_zero(data_size);
116 if (!metafile->comment_data)
117 return OutOfMemory;
119 memcpy(metafile->comment_data, "EMF+", 4);
121 metafile->comment_data_size = data_size;
122 metafile->comment_data_length = 4;
125 size_needed = size + metafile->comment_data_length;
127 if (size_needed > metafile->comment_data_size)
129 DWORD data_size = size_needed * 2;
130 BYTE *new_data = heap_alloc_zero(data_size);
132 if (!new_data)
133 return OutOfMemory;
135 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
137 metafile->comment_data_size = data_size;
138 heap_free(metafile->comment_data);
139 metafile->comment_data = new_data;
142 *result = metafile->comment_data + metafile->comment_data_length;
143 metafile->comment_data_length += size;
145 record = (EmfPlusRecordHeader*)*result;
146 record->Size = size;
147 record->DataSize = size - sizeof(EmfPlusRecordHeader);
149 return Ok;
152 static void METAFILE_WriteRecords(GpMetafile *metafile)
154 if (metafile->comment_data_length > 4)
156 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
157 metafile->comment_data_length = 4;
161 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
163 GpStatus stat;
165 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
167 EmfPlusHeader *header;
169 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
170 if (stat != Ok)
171 return stat;
173 header->Header.Type = EmfPlusRecordTypeHeader;
175 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
176 header->Header.Flags = 1;
177 else
178 header->Header.Flags = 0;
180 header->Version = 0xDBC01002;
182 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
183 header->EmfPlusFlags = 1;
184 else
185 header->EmfPlusFlags = 0;
187 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
188 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
190 METAFILE_WriteRecords(metafile);
193 return Ok;
196 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
198 GpStatus stat;
200 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
202 EmfPlusRecordHeader *record;
204 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
205 if (stat != Ok)
206 return stat;
208 record->Type = EmfPlusRecordTypeEndOfFile;
209 record->Flags = 0;
211 METAFILE_WriteRecords(metafile);
214 return Ok;
217 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
218 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
220 HDC record_dc;
221 REAL dpix, dpiy;
222 REAL framerect_factor_x, framerect_factor_y;
223 RECT rc, *lprc;
224 GpStatus stat;
226 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
228 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
229 return InvalidParameter;
231 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
232 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
234 if (frameRect)
236 switch (frameUnit)
238 case MetafileFrameUnitPixel:
239 framerect_factor_x = 2540.0 / dpix;
240 framerect_factor_y = 2540.0 / dpiy;
241 break;
242 case MetafileFrameUnitPoint:
243 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
244 break;
245 case MetafileFrameUnitInch:
246 framerect_factor_x = framerect_factor_y = 2540.0;
247 break;
248 case MetafileFrameUnitDocument:
249 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
250 break;
251 case MetafileFrameUnitMillimeter:
252 framerect_factor_x = framerect_factor_y = 100.0;
253 break;
254 case MetafileFrameUnitGdi:
255 framerect_factor_x = framerect_factor_y = 1.0;
256 break;
257 default:
258 return InvalidParameter;
261 rc.left = framerect_factor_x * frameRect->X;
262 rc.top = framerect_factor_y * frameRect->Y;
263 rc.right = rc.left + framerect_factor_x * frameRect->Width;
264 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
266 lprc = &rc;
268 else
269 lprc = NULL;
271 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
273 if (!record_dc)
274 return GenericError;
276 *metafile = heap_alloc_zero(sizeof(GpMetafile));
277 if(!*metafile)
279 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
280 return OutOfMemory;
283 (*metafile)->image.type = ImageTypeMetafile;
284 (*metafile)->image.picture = NULL;
285 (*metafile)->image.flags = ImageFlagsNone;
286 (*metafile)->image.palette = NULL;
287 (*metafile)->image.xres = dpix;
288 (*metafile)->image.yres = dpiy;
289 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
290 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
291 (*metafile)->unit = UnitPixel;
292 (*metafile)->metafile_type = type;
293 (*metafile)->record_dc = record_dc;
294 (*metafile)->comment_data = NULL;
295 (*metafile)->comment_data_size = 0;
296 (*metafile)->comment_data_length = 0;
297 (*metafile)->hemf = NULL;
299 if (!frameRect)
301 (*metafile)->auto_frame = TRUE;
302 (*metafile)->auto_frame_min.X = 0;
303 (*metafile)->auto_frame_min.Y = 0;
304 (*metafile)->auto_frame_max.X = -1;
305 (*metafile)->auto_frame_max.Y = -1;
308 stat = METAFILE_WriteHeader(*metafile, hdc);
310 if (stat != Ok)
312 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
313 heap_free(*metafile);
314 *metafile = NULL;
315 return OutOfMemory;
318 return stat;
321 /*****************************************************************************
322 * GdipRecordMetafileI [GDIPLUS.@]
324 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
325 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
327 GpRectF frameRectF, *pFrameRectF;
329 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
331 if (frameRect)
333 frameRectF.X = frameRect->X;
334 frameRectF.Y = frameRect->Y;
335 frameRectF.Width = frameRect->Width;
336 frameRectF.Height = frameRect->Height;
337 pFrameRectF = &frameRectF;
339 else
340 pFrameRectF = NULL;
342 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
345 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
346 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
348 GpStatus stat;
350 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
352 if (!stream)
353 return InvalidParameter;
355 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
357 if (stat == Ok)
359 (*metafile)->record_stream = stream;
360 IStream_AddRef(stream);
363 return stat;
366 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
367 UINT num_points)
369 int i;
371 if (!metafile->auto_frame || !num_points)
372 return;
374 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
375 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
377 for (i=0; i<num_points; i++)
379 if (points[i].X < metafile->auto_frame_min.X)
380 metafile->auto_frame_min.X = points[i].X;
381 if (points[i].X > metafile->auto_frame_max.X)
382 metafile->auto_frame_max.X = points[i].X;
383 if (points[i].Y < metafile->auto_frame_min.Y)
384 metafile->auto_frame_min.Y = points[i].Y;
385 if (points[i].Y > metafile->auto_frame_max.Y)
386 metafile->auto_frame_max.Y = points[i].Y;
390 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
392 GpStatus stat;
394 if (!metafile->record_dc || metafile->record_graphics)
395 return InvalidParameter;
397 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
399 if (stat == Ok)
401 *result = metafile->record_graphics;
402 metafile->record_graphics->xres = 96.0;
403 metafile->record_graphics->yres = 96.0;
406 return stat;
409 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
411 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
413 EmfPlusRecordHeader *record;
414 GpStatus stat;
416 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
417 if (stat != Ok)
418 return stat;
420 record->Type = EmfPlusRecordTypeGetDC;
421 record->Flags = 0;
423 METAFILE_WriteRecords(metafile);
426 *hdc = metafile->record_dc;
428 return Ok;
431 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
433 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
435 EmfPlusClear *record;
436 GpStatus stat;
438 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
439 if (stat != Ok)
440 return stat;
442 record->Header.Type = EmfPlusRecordTypeClear;
443 record->Header.Flags = 0;
444 record->Color = color;
446 METAFILE_WriteRecords(metafile);
449 return Ok;
452 static BOOL is_integer_rect(const GpRectF *rect)
454 SHORT x, y, width, height;
455 x = rect->X;
456 y = rect->Y;
457 width = rect->Width;
458 height = rect->Height;
459 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
460 rect->Width != (REAL)width || rect->Height != (REAL)height)
461 return FALSE;
462 return TRUE;
465 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
466 GDIPCONST GpRectF* rects, INT count)
468 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
470 EmfPlusFillRects *record;
471 GpStatus stat;
472 BOOL integer_rects = TRUE;
473 int i;
474 DWORD brushid;
475 int flags = 0;
477 if (brush->bt == BrushTypeSolidColor)
479 flags |= 0x8000;
480 brushid = ((GpSolidFill*)brush)->color;
482 else
484 FIXME("brush serialization not implemented\n");
485 return NotImplemented;
488 for (i=0; i<count; i++)
490 if (!is_integer_rect(&rects[i]))
492 integer_rects = FALSE;
493 break;
497 if (integer_rects)
498 flags |= 0x4000;
500 stat = METAFILE_AllocateRecord(metafile,
501 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
502 (void**)&record);
503 if (stat != Ok)
504 return stat;
506 record->Header.Type = EmfPlusRecordTypeFillRects;
507 record->Header.Flags = flags;
508 record->BrushID = brushid;
509 record->Count = count;
511 if (integer_rects)
513 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
514 for (i=0; i<count; i++)
516 record_rects[i].X = (SHORT)rects[i].X;
517 record_rects[i].Y = (SHORT)rects[i].Y;
518 record_rects[i].Width = (SHORT)rects[i].Width;
519 record_rects[i].Height = (SHORT)rects[i].Height;
522 else
523 memcpy(record+1, rects, sizeof(GpRectF) * count);
525 METAFILE_WriteRecords(metafile);
528 if (metafile->auto_frame)
530 GpPointF corners[4];
531 int i;
533 for (i=0; i<count; i++)
535 corners[0].X = rects[i].X;
536 corners[0].Y = rects[i].Y;
537 corners[1].X = rects[i].X + rects[i].Width;
538 corners[1].Y = rects[i].Y;
539 corners[2].X = rects[i].X;
540 corners[2].Y = rects[i].Y + rects[i].Height;
541 corners[3].X = rects[i].X + rects[i].Width;
542 corners[3].Y = rects[i].Y + rects[i].Height;
544 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
545 CoordinateSpaceWorld, corners, 4);
547 METAFILE_AdjustFrame(metafile, corners, 4);
551 return Ok;
554 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
556 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
558 EmfPlusSetPageTransform *record;
559 GpStatus stat;
561 stat = METAFILE_AllocateRecord(metafile,
562 sizeof(EmfPlusSetPageTransform),
563 (void**)&record);
564 if (stat != Ok)
565 return stat;
567 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
568 record->Header.Flags = unit;
569 record->PageScale = scale;
571 METAFILE_WriteRecords(metafile);
574 return Ok;
577 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
579 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
581 EmfPlusScaleWorldTransform *record;
582 GpStatus stat;
584 stat = METAFILE_AllocateRecord(metafile,
585 sizeof(EmfPlusScaleWorldTransform),
586 (void**)&record);
587 if (stat != Ok)
588 return stat;
590 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
591 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
592 record->Sx = sx;
593 record->Sy = sy;
595 METAFILE_WriteRecords(metafile);
598 return Ok;
601 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
603 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
605 EmfPlusMultiplyWorldTransform *record;
606 GpStatus stat;
608 stat = METAFILE_AllocateRecord(metafile,
609 sizeof(EmfPlusMultiplyWorldTransform),
610 (void**)&record);
611 if (stat != Ok)
612 return stat;
614 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
615 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
616 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
618 METAFILE_WriteRecords(metafile);
621 return Ok;
624 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
626 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
628 EmfPlusRotateWorldTransform *record;
629 GpStatus stat;
631 stat = METAFILE_AllocateRecord(metafile,
632 sizeof(EmfPlusRotateWorldTransform),
633 (void**)&record);
634 if (stat != Ok)
635 return stat;
637 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
638 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
639 record->Angle = angle;
641 METAFILE_WriteRecords(metafile);
644 return Ok;
647 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
649 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
651 EmfPlusRecordHeader *record;
652 GpStatus stat;
654 stat = METAFILE_AllocateRecord(metafile,
655 sizeof(EmfPlusRecordHeader),
656 (void**)&record);
657 if (stat != Ok)
658 return stat;
660 record->Type = EmfPlusRecordTypeResetWorldTransform;
661 record->Flags = 0;
663 METAFILE_WriteRecords(metafile);
666 return Ok;
669 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
671 if (hdc != metafile->record_dc)
672 return InvalidParameter;
674 return Ok;
677 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
679 GpStatus stat;
681 stat = METAFILE_WriteEndOfFile(metafile);
682 metafile->record_graphics = NULL;
684 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
685 metafile->record_dc = NULL;
687 heap_free(metafile->comment_data);
688 metafile->comment_data = NULL;
689 metafile->comment_data_size = 0;
691 if (stat == Ok)
693 MetafileHeader header;
695 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
696 if (stat == Ok && metafile->auto_frame &&
697 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
699 RECTL bounds_rc, gdi_bounds_rc;
700 REAL x_scale = 2540.0 / header.DpiX;
701 REAL y_scale = 2540.0 / header.DpiY;
702 BYTE* buffer;
703 UINT buffer_size;
705 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
706 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
707 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
708 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
710 gdi_bounds_rc = header.EmfHeader.rclBounds;
711 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
713 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
714 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
715 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
716 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
719 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
720 buffer = heap_alloc(buffer_size);
721 if (buffer)
723 HENHMETAFILE new_hemf;
725 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
727 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
729 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
731 if (new_hemf)
733 DeleteEnhMetaFile(metafile->hemf);
734 metafile->hemf = new_hemf;
736 else
737 stat = OutOfMemory;
739 heap_free(buffer);
741 else
742 stat = OutOfMemory;
744 if (stat == Ok)
745 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
747 if (stat == Ok)
749 metafile->bounds.X = header.X;
750 metafile->bounds.Y = header.Y;
751 metafile->bounds.Width = header.Width;
752 metafile->bounds.Height = header.Height;
756 if (stat == Ok && metafile->record_stream)
758 BYTE *buffer;
759 UINT buffer_size;
761 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
763 buffer = heap_alloc(buffer_size);
764 if (buffer)
766 HRESULT hr;
768 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
770 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
772 if (FAILED(hr))
773 stat = hresult_to_status(hr);
775 heap_free(buffer);
777 else
778 stat = OutOfMemory;
781 if (metafile->record_stream)
783 IStream_Release(metafile->record_stream);
784 metafile->record_stream = NULL;
787 return stat;
790 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
792 TRACE("(%p,%p)\n", metafile, hEmf);
794 if (!metafile || !hEmf || !metafile->hemf)
795 return InvalidParameter;
797 *hEmf = metafile->hemf;
798 metafile->hemf = NULL;
800 return Ok;
803 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
805 GpStatus stat = Ok;
807 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
809 if (stat == Ok)
811 /* The result of GdipGetDC always expects device co-ordinates, but the
812 * device co-ordinates of the source metafile do not correspond to
813 * device co-ordinates of the destination. Therefore, we set up the DC
814 * so that the metafile's bounds map to the destination points where we
815 * are drawing this metafile. */
816 SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
818 SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
819 SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
821 SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
822 SetViewportExtEx(metafile->playback_dc,
823 metafile->playback_points[1].X - metafile->playback_points[0].X,
824 metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
827 return stat;
830 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
832 if (metafile->playback_dc)
834 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
835 metafile->playback_dc = NULL;
839 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
841 return GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
844 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
846 GpMatrix *real_transform;
847 GpStatus stat;
849 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
851 if (stat == Ok)
853 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
855 if (metafile->page_unit != UnitDisplay)
856 scale *= metafile->page_scale;
858 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
860 if (stat == Ok)
861 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
863 if (stat == Ok)
864 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
866 GdipDeleteMatrix(real_transform);
869 return stat;
872 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
873 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
875 GpStatus stat;
876 GpMetafile *real_metafile = (GpMetafile*)metafile;
878 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
880 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
881 return InvalidParameter;
883 if (recordType >= 1 && recordType <= 0x7a)
885 /* regular EMF record */
886 if (metafile->playback_dc)
888 ENHMETARECORD *record;
890 record = heap_alloc_zero(dataSize + 8);
892 if (record)
894 record->iType = recordType;
895 record->nSize = dataSize + 8;
896 memcpy(record->dParm, data, dataSize);
898 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
899 record, metafile->handle_count);
901 heap_free(record);
903 else
904 return OutOfMemory;
907 else
909 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
911 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
913 switch(recordType)
915 case EmfPlusRecordTypeHeader:
916 case EmfPlusRecordTypeEndOfFile:
917 break;
918 case EmfPlusRecordTypeGetDC:
919 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
920 break;
921 case EmfPlusRecordTypeClear:
923 EmfPlusClear *record = (EmfPlusClear*)header;
925 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
927 case EmfPlusRecordTypeFillRects:
929 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
930 GpBrush *brush, *temp_brush=NULL;
931 GpRectF *rects, *temp_rects=NULL;
933 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
934 return InvalidParameter;
936 if (flags & 0x4000)
938 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
939 return InvalidParameter;
941 else
943 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
944 return InvalidParameter;
947 if (flags & 0x8000)
949 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
950 brush = temp_brush;
952 else
954 FIXME("brush deserialization not implemented\n");
955 return NotImplemented;
958 if (stat == Ok)
960 if (flags & 0x4000)
962 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
963 int i;
965 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
966 if (rects)
968 for (i=0; i<record->Count; i++)
970 rects[i].X = int_rects[i].X;
971 rects[i].Y = int_rects[i].Y;
972 rects[i].Width = int_rects[i].Width;
973 rects[i].Height = int_rects[i].Height;
976 else
977 stat = OutOfMemory;
979 else
980 rects = (GpRectF*)(record+1);
983 if (stat == Ok)
985 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
988 GdipDeleteBrush(temp_brush);
989 heap_free(temp_rects);
991 return stat;
993 case EmfPlusRecordTypeSetPageTransform:
995 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
996 GpUnit unit = (GpUnit)flags;
998 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
999 return InvalidParameter;
1001 real_metafile->page_unit = unit;
1002 real_metafile->page_scale = record->PageScale;
1004 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1006 case EmfPlusRecordTypeScaleWorldTransform:
1008 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
1009 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1011 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
1012 return InvalidParameter;
1014 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
1016 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1018 case EmfPlusRecordTypeMultiplyWorldTransform:
1020 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
1021 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1022 GpMatrix matrix;
1024 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
1025 return InvalidParameter;
1027 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
1029 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
1031 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1033 case EmfPlusRecordTypeRotateWorldTransform:
1035 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
1036 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1038 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
1039 return InvalidParameter;
1041 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
1043 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1045 case EmfPlusRecordTypeResetWorldTransform:
1047 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1049 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1051 default:
1052 FIXME("Not implemented for record type %x\n", recordType);
1053 return NotImplemented;
1057 return Ok;
1060 struct enum_metafile_data
1062 EnumerateMetafileProc callback;
1063 void *callback_data;
1064 GpMetafile *metafile;
1067 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1068 int nObj, LPARAM lpData)
1070 BOOL ret;
1071 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
1072 const BYTE* pStr;
1074 data->metafile->handle_table = lpHTable;
1075 data->metafile->handle_count = nObj;
1077 /* First check for an EMF+ record. */
1078 if (lpEMFR->iType == EMR_GDICOMMENT)
1080 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1082 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1084 int offset = 4;
1086 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
1088 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
1090 if (record->DataSize)
1091 pStr = (const BYTE*)(record+1);
1092 else
1093 pStr = NULL;
1095 ret = data->callback(record->Type, record->Flags, record->DataSize,
1096 pStr, data->callback_data);
1098 if (!ret)
1099 return 0;
1101 offset += record->Size;
1104 return 1;
1108 if (lpEMFR->nSize != 8)
1109 pStr = (const BYTE*)lpEMFR->dParm;
1110 else
1111 pStr = NULL;
1113 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
1114 pStr, data->callback_data);
1117 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
1118 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
1119 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
1120 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
1122 struct enum_metafile_data data;
1123 GpStatus stat;
1124 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
1125 GraphicsContainer state;
1126 GpPath *dst_path;
1128 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
1129 destPoints, count, srcRect, srcUnit, callback, callbackData,
1130 imageAttributes);
1132 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
1133 return InvalidParameter;
1135 if (!metafile->hemf)
1136 return InvalidParameter;
1138 if (metafile->playback_graphics)
1139 return ObjectBusy;
1141 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
1142 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
1143 debugstr_pointf(&destPoints[2]));
1145 data.callback = callback;
1146 data.callback_data = callbackData;
1147 data.metafile = real_metafile;
1149 real_metafile->playback_graphics = graphics;
1150 real_metafile->playback_dc = NULL;
1151 real_metafile->src_rect = *srcRect;
1153 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
1154 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
1156 if (stat == Ok)
1157 stat = GdipBeginContainer2(graphics, &state);
1159 if (stat == Ok)
1161 stat = GdipSetPageScale(graphics, 1.0);
1163 if (stat == Ok)
1164 stat = GdipSetPageUnit(graphics, UnitPixel);
1166 if (stat == Ok)
1167 stat = GdipResetWorldTransform(graphics);
1169 if (stat == Ok)
1170 stat = GdipCreateRegion(&real_metafile->base_clip);
1172 if (stat == Ok)
1173 stat = GdipGetClip(graphics, real_metafile->base_clip);
1175 if (stat == Ok)
1176 stat = GdipCreatePath(FillModeAlternate, &dst_path);
1178 if (stat == Ok)
1180 GpPointF clip_points[4];
1182 clip_points[0] = real_metafile->playback_points[0];
1183 clip_points[1] = real_metafile->playback_points[1];
1184 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
1185 - real_metafile->playback_points[0].X;
1186 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
1187 - real_metafile->playback_points[0].Y;
1188 clip_points[3] = real_metafile->playback_points[2];
1190 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
1192 if (stat == Ok)
1193 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
1195 GdipDeletePath(dst_path);
1198 if (stat == Ok)
1199 stat = GdipCreateMatrix(&real_metafile->world_transform);
1201 if (stat == Ok)
1203 real_metafile->page_unit = UnitDisplay;
1204 real_metafile->page_scale = 1.0;
1205 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1208 if (stat == Ok)
1210 stat = METAFILE_PlaybackUpdateClip(real_metafile);
1213 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
1214 metafile->metafile_type == MetafileTypeWmfPlaceable ||
1215 metafile->metafile_type == MetafileTypeWmf))
1216 stat = METAFILE_PlaybackGetDC(real_metafile);
1218 if (stat == Ok)
1219 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
1221 METAFILE_PlaybackReleaseDC(real_metafile);
1223 GdipDeleteMatrix(real_metafile->world_transform);
1224 real_metafile->world_transform = NULL;
1226 GdipDeleteRegion(real_metafile->base_clip);
1227 real_metafile->base_clip = NULL;
1229 GdipEndContainer(graphics, state);
1232 real_metafile->playback_graphics = NULL;
1234 return stat;
1237 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
1238 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
1239 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1241 GpPointF points[3];
1243 if (!graphics || !metafile || !dest) return InvalidParameter;
1245 points[0].X = points[2].X = dest->X;
1246 points[0].Y = points[1].Y = dest->Y;
1247 points[1].X = dest->X + dest->Width;
1248 points[2].Y = dest->Y + dest->Height;
1250 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
1251 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
1254 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
1255 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
1256 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1258 GpRectF destf;
1260 if (!graphics || !metafile || !dest) return InvalidParameter;
1262 destf.X = dest->X;
1263 destf.Y = dest->Y;
1264 destf.Width = dest->Width;
1265 destf.Height = dest->Height;
1267 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
1270 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
1271 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
1272 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1274 GpRectF destf;
1276 if (!graphics || !metafile || !dest) return InvalidParameter;
1278 destf.X = dest->X;
1279 destf.Y = dest->Y;
1280 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
1281 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
1283 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
1286 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
1287 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
1288 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1290 GpPointF ptf;
1292 if (!graphics || !metafile || !dest) return InvalidParameter;
1294 ptf.X = dest->X;
1295 ptf.Y = dest->Y;
1297 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
1300 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
1301 MetafileHeader * header)
1303 static int calls;
1305 TRACE("(%p, %p)\n", metafile, header);
1307 if(!metafile || !header)
1308 return InvalidParameter;
1310 if(!(calls++))
1311 FIXME("not implemented\n");
1313 memset(header, 0, sizeof(MetafileHeader));
1315 return Ok;
1318 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1319 int nObj, LPARAM lpData)
1321 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
1323 if (lpEMFR->iType == EMR_GDICOMMENT)
1325 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1327 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1329 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
1331 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
1332 header->Type == EmfPlusRecordTypeHeader)
1334 memcpy(dst_header, header, sizeof(*dst_header));
1338 else if (lpEMFR->iType == EMR_HEADER)
1339 return TRUE;
1341 return FALSE;
1344 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
1345 MetafileHeader *header)
1347 ENHMETAHEADER3 emfheader;
1348 EmfPlusHeader emfplusheader;
1349 MetafileType metafile_type;
1351 TRACE("(%p,%p)\n", hemf, header);
1353 if(!hemf || !header)
1354 return InvalidParameter;
1356 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
1357 return GenericError;
1359 emfplusheader.Header.Type = 0;
1361 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
1363 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
1365 if ((emfplusheader.Header.Flags & 1) == 1)
1366 metafile_type = MetafileTypeEmfPlusDual;
1367 else
1368 metafile_type = MetafileTypeEmfPlusOnly;
1370 else
1371 metafile_type = MetafileTypeEmf;
1373 header->Type = metafile_type;
1374 header->Size = emfheader.nBytes;
1375 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
1376 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
1377 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
1378 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
1379 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
1380 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
1381 header->EmfHeader = emfheader;
1383 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
1385 header->Version = emfplusheader.Version;
1386 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
1387 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
1388 header->LogicalDpiX = emfplusheader.LogicalDpiX;
1389 header->LogicalDpiY = emfplusheader.LogicalDpiY;
1391 else
1393 header->Version = emfheader.nVersion;
1394 header->EmfPlusFlags = 0;
1395 header->EmfPlusHeaderSize = 0;
1396 header->LogicalDpiX = 0;
1397 header->LogicalDpiY = 0;
1400 return Ok;
1403 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
1404 MetafileHeader *header)
1406 static int calls;
1408 TRACE("(%s,%p)\n", debugstr_w(filename), header);
1410 if(!filename || !header)
1411 return InvalidParameter;
1413 if(!(calls++))
1414 FIXME("not implemented\n");
1416 memset(header, 0, sizeof(MetafileHeader));
1418 return Ok;
1421 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
1422 MetafileHeader *header)
1424 static int calls;
1426 TRACE("(%p,%p)\n", stream, header);
1428 if(!stream || !header)
1429 return InvalidParameter;
1431 if(!(calls++))
1432 FIXME("not implemented\n");
1434 memset(header, 0, sizeof(MetafileHeader));
1436 return Ok;
1439 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
1440 GpMetafile **metafile)
1442 GpStatus stat;
1443 MetafileHeader header;
1445 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
1447 if(!hemf || !metafile)
1448 return InvalidParameter;
1450 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
1451 if (stat != Ok)
1452 return stat;
1454 *metafile = heap_alloc_zero(sizeof(GpMetafile));
1455 if (!*metafile)
1456 return OutOfMemory;
1458 (*metafile)->image.type = ImageTypeMetafile;
1459 (*metafile)->image.format = ImageFormatEMF;
1460 (*metafile)->image.frame_count = 1;
1461 (*metafile)->image.xres = header.DpiX;
1462 (*metafile)->image.yres = header.DpiY;
1463 (*metafile)->bounds.X = (REAL)header.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
1464 (*metafile)->bounds.Y = (REAL)header.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
1465 (*metafile)->bounds.Width = (REAL)(header.EmfHeader.rclFrame.right - header.EmfHeader.rclFrame.left)
1466 / 2540.0 * header.DpiX;
1467 (*metafile)->bounds.Height = (REAL)(header.EmfHeader.rclFrame.bottom - header.EmfHeader.rclFrame.top)
1468 / 2540.0 * header.DpiY;
1469 (*metafile)->unit = UnitPixel;
1470 (*metafile)->metafile_type = header.Type;
1471 (*metafile)->hemf = hemf;
1472 (*metafile)->preserve_hemf = !delete;
1474 TRACE("<-- %p\n", *metafile);
1476 return Ok;
1479 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
1480 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1482 UINT read;
1483 BYTE *copy;
1484 HENHMETAFILE hemf;
1485 GpStatus retval = Ok;
1487 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
1489 if(!hwmf || !metafile)
1490 return InvalidParameter;
1492 *metafile = NULL;
1493 read = GetMetaFileBitsEx(hwmf, 0, NULL);
1494 if(!read)
1495 return GenericError;
1496 copy = heap_alloc_zero(read);
1497 GetMetaFileBitsEx(hwmf, read, copy);
1499 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1500 heap_free(copy);
1502 /* FIXME: We should store and use hwmf instead of converting to hemf */
1503 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
1505 if (retval == Ok)
1507 if (placeable)
1509 (*metafile)->image.xres = (REAL)placeable->Inch;
1510 (*metafile)->image.yres = (REAL)placeable->Inch;
1511 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
1512 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
1513 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
1514 placeable->BoundingBox.Left);
1515 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
1516 placeable->BoundingBox.Top);
1517 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
1519 else
1520 (*metafile)->metafile_type = MetafileTypeWmf;
1521 (*metafile)->image.format = ImageFormatWMF;
1523 if (delete) DeleteMetaFile(hwmf);
1525 else
1526 DeleteEnhMetaFile(hemf);
1527 return retval;
1530 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1531 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1533 HMETAFILE hmf = GetMetaFileW(file);
1535 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1537 if(!hmf) return InvalidParameter;
1539 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1542 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
1543 GpMetafile **metafile)
1545 FIXME("(%p, %p): stub\n", file, metafile);
1546 return NotImplemented;
1549 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
1550 GpMetafile **metafile)
1552 FIXME("(%p, %p): stub\n", stream, metafile);
1553 return NotImplemented;
1556 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
1557 UINT limitDpi)
1559 TRACE("(%p,%u)\n", metafile, limitDpi);
1561 return Ok;
1564 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
1565 GpMetafile* metafile, BOOL* succ, EmfType emfType,
1566 const WCHAR* description, GpMetafile** out_metafile)
1568 static int calls;
1570 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
1571 debugstr_w(description), out_metafile);
1573 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
1574 return InvalidParameter;
1576 if(succ)
1577 *succ = FALSE;
1578 *out_metafile = NULL;
1580 if(!(calls++))
1581 FIXME("not implemented\n");
1583 return NotImplemented;
1586 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
1587 LPBYTE pData16, INT iMapMode, INT eFlags)
1589 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
1590 return NotImplemented;
1593 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
1594 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
1595 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
1596 GpMetafile **metafile)
1598 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1599 frameUnit, debugstr_w(desc), metafile);
1601 return NotImplemented;
1604 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
1605 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
1606 GDIPCONST WCHAR *desc, GpMetafile **metafile)
1608 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1609 frameUnit, debugstr_w(desc), metafile);
1611 return NotImplemented;
1614 /*****************************************************************************
1615 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
1618 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
1619 GpMetafile* metafile, BOOL* conversionSuccess,
1620 const WCHAR* filename, EmfType emfType,
1621 const WCHAR* description, GpMetafile** out_metafile)
1623 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
1624 return NotImplemented;