gdiplus/metafile: Implement playback for EmfPlusRecordTypeSetCompositingMode.
[wine.git] / dlls / gdiplus / metafile.c
blobd76441290dfa8618dbd25b5b03b15ab40fd055d8
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>
21 #include <assert.h>
23 #define NONAMELESSUNION
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "wine/unicode.h"
30 #define COBJMACROS
31 #include "objbase.h"
32 #include "ocidl.h"
33 #include "olectl.h"
34 #include "ole2.h"
36 #include "winreg.h"
37 #include "shlwapi.h"
39 #include "gdiplus.h"
40 #include "gdiplus_private.h"
41 #include "wine/debug.h"
42 #include "wine/list.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
46 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
48 typedef ARGB EmfPlusARGB;
50 typedef struct EmfPlusRecordHeader
52 WORD Type;
53 WORD Flags;
54 DWORD Size;
55 DWORD DataSize;
56 } EmfPlusRecordHeader;
58 typedef struct EmfPlusHeader
60 EmfPlusRecordHeader Header;
61 DWORD Version;
62 DWORD EmfPlusFlags;
63 DWORD LogicalDpiX;
64 DWORD LogicalDpiY;
65 } EmfPlusHeader;
67 typedef struct EmfPlusClear
69 EmfPlusRecordHeader Header;
70 DWORD Color;
71 } EmfPlusClear;
73 typedef struct EmfPlusFillRects
75 EmfPlusRecordHeader Header;
76 DWORD BrushID;
77 DWORD Count;
78 } EmfPlusFillRects;
80 typedef struct EmfPlusSetClipRect
82 EmfPlusRecordHeader Header;
83 GpRectF ClipRect;
84 } EmfPlusSetClipRect;
86 typedef struct EmfPlusSetPageTransform
88 EmfPlusRecordHeader Header;
89 REAL PageScale;
90 } EmfPlusSetPageTransform;
92 typedef struct EmfPlusRect
94 SHORT X;
95 SHORT Y;
96 SHORT Width;
97 SHORT Height;
98 } EmfPlusRect;
100 typedef struct EmfPlusSetWorldTransform
102 EmfPlusRecordHeader Header;
103 REAL MatrixData[6];
104 } EmfPlusSetWorldTransform;
106 typedef struct EmfPlusScaleWorldTransform
108 EmfPlusRecordHeader Header;
109 REAL Sx;
110 REAL Sy;
111 } EmfPlusScaleWorldTransform;
113 typedef struct EmfPlusMultiplyWorldTransform
115 EmfPlusRecordHeader Header;
116 REAL MatrixData[6];
117 } EmfPlusMultiplyWorldTransform;
119 typedef struct EmfPlusRotateWorldTransform
121 EmfPlusRecordHeader Header;
122 REAL Angle;
123 } EmfPlusRotateWorldTransform;
125 typedef struct EmfPlusTranslateWorldTransform
127 EmfPlusRecordHeader Header;
128 REAL dx;
129 REAL dy;
130 } EmfPlusTranslateWorldTransform;
132 typedef struct EmfPlusBeginContainer
134 EmfPlusRecordHeader Header;
135 GpRectF DestRect;
136 GpRectF SrcRect;
137 DWORD StackIndex;
138 } EmfPlusBeginContainer;
140 typedef struct EmfPlusContainerRecord
142 EmfPlusRecordHeader Header;
143 DWORD StackIndex;
144 } EmfPlusContainerRecord;
146 enum container_type
148 BEGIN_CONTAINER,
149 SAVE_GRAPHICS
152 typedef struct container
154 struct list entry;
155 DWORD id;
156 enum container_type type;
157 GraphicsContainer state;
158 GpMatrix world_transform;
159 GpUnit page_unit;
160 REAL page_scale;
161 GpRegion *clip;
162 } container;
164 enum PenDataFlags
166 PenDataTransform = 0x0001,
167 PenDataStartCap = 0x0002,
168 PenDataEndCap = 0x0004,
169 PenDataJoin = 0x0008,
170 PenDataMiterLimit = 0x0010,
171 PenDataLineStyle = 0x0020,
172 PenDataDashedLineCap = 0x0040,
173 PenDataDashedLineOffset = 0x0080,
174 PenDataDashedLine = 0x0100,
175 PenDataNonCenter = 0x0200,
176 PenDataCompoundLine = 0x0400,
177 PenDataCustomStartCap = 0x0800,
178 PenDataCustomEndCap = 0x1000
181 typedef struct EmfPlusTransformMatrix
183 REAL TransformMatrix[6];
184 } EmfPlusTransformMatrix;
186 enum LineStyle
188 LineStyleSolid,
189 LineStyleDash,
190 LineStyleDot,
191 LineStyleDashDot,
192 LineStyleDashDotDot,
193 LineStyleCustom
196 typedef struct EmfPlusDashedLineData
198 DWORD DashedLineDataSize;
199 BYTE data[1];
200 } EmfPlusDashedLineData;
202 typedef struct EmfPlusCompoundLineData
204 DWORD CompoundLineDataSize;
205 BYTE data[1];
206 } EmfPlusCompoundLineData;
208 typedef struct EmfPlusCustomStartCapData
210 DWORD CustomStartCapSize;
211 BYTE data[1];
212 } EmfPlusCustomStartCapData;
214 typedef struct EmfPlusCustomEndCapData
216 DWORD CustomEndCapSize;
217 BYTE data[1];
218 } EmfPlusCustomEndCapData;
220 typedef struct EmfPlusPenData
222 DWORD PenDataFlags;
223 DWORD PenUnit;
224 REAL PenWidth;
225 BYTE OptionalData[1];
226 } EmfPlusPenData;
228 enum BrushDataFlags
230 BrushDataPath = 1 << 0,
231 BrushDataTransform = 1 << 1,
232 BrushDataPresetColors = 1 << 2,
233 BrushDataBlendFactorsH = 1 << 3,
234 BrushDataBlendFactorsV = 1 << 4,
235 BrushDataFocusScales = 1 << 6,
236 BrushDataIsGammaCorrected = 1 << 7,
237 BrushDataDoNotTransform = 1 << 8,
240 typedef struct EmfPlusSolidBrushData
242 EmfPlusARGB SolidColor;
243 } EmfPlusSolidBrushData;
245 typedef struct EmfPlusHatchBrushData
247 DWORD HatchStyle;
248 EmfPlusARGB ForeColor;
249 EmfPlusARGB BackColor;
250 } EmfPlusHatchBrushData;
252 typedef struct EmfPlusTextureBrushData
254 DWORD BrushDataFlags;
255 INT WrapMode;
256 BYTE OptionalData[1];
257 } EmfPlusTextureBrushData;
259 typedef struct EmfPlusBrush
261 DWORD Version;
262 DWORD Type;
263 union {
264 EmfPlusSolidBrushData solid;
265 EmfPlusHatchBrushData hatch;
266 EmfPlusTextureBrushData texture;
267 } BrushData;
268 } EmfPlusBrush;
270 typedef struct EmfPlusPen
272 DWORD Version;
273 DWORD Type;
274 /* EmfPlusPenData */
275 /* EmfPlusBrush */
276 BYTE data[1];
277 } EmfPlusPen;
279 typedef struct EmfPlusPath
281 DWORD Version;
282 DWORD PathPointCount;
283 DWORD PathPointFlags;
284 /* PathPoints[] */
285 /* PathPointTypes[] */
286 /* AlignmentPadding */
287 BYTE data[1];
288 } EmfPlusPath;
290 typedef struct EmfPlusRegionNodePath
292 DWORD RegionNodePathLength;
293 EmfPlusPath RegionNodePath;
294 } EmfPlusRegionNodePath;
296 typedef struct EmfPlusRegion
298 DWORD Version;
299 DWORD RegionNodeCount;
300 BYTE RegionNode[1];
301 } EmfPlusRegion;
303 typedef struct EmfPlusPalette
305 DWORD PaletteStyleFlags;
306 DWORD PaletteCount;
307 BYTE PaletteEntries[1];
308 } EmfPlusPalette;
310 typedef enum
312 BitmapDataTypePixel,
313 BitmapDataTypeCompressed,
314 } BitmapDataType;
316 typedef struct EmfPlusBitmap
318 DWORD Width;
319 DWORD Height;
320 DWORD Stride;
321 DWORD PixelFormat;
322 DWORD Type;
323 BYTE BitmapData[1];
324 } EmfPlusBitmap;
326 typedef struct EmfPlusMetafile
328 DWORD Type;
329 DWORD MetafileDataSize;
330 BYTE MetafileData[1];
331 } EmfPlusMetafile;
333 typedef enum ImageDataType
335 ImageDataTypeUnknown,
336 ImageDataTypeBitmap,
337 ImageDataTypeMetafile,
338 } ImageDataType;
340 typedef struct EmfPlusImage
342 DWORD Version;
343 ImageDataType Type;
344 union
346 EmfPlusBitmap bitmap;
347 EmfPlusMetafile metafile;
348 } ImageData;
349 } EmfPlusImage;
351 typedef struct EmfPlusImageAttributes
353 DWORD Version;
354 DWORD Reserved1;
355 DWORD WrapMode;
356 EmfPlusARGB ClampColor;
357 DWORD ObjectClamp;
358 DWORD Reserved2;
359 } EmfPlusImageAttributes;
361 typedef struct EmfPlusObject
363 EmfPlusRecordHeader Header;
364 union
366 EmfPlusBrush brush;
367 EmfPlusPen pen;
368 EmfPlusPath path;
369 EmfPlusRegion region;
370 EmfPlusImage image;
371 EmfPlusImageAttributes image_attributes;
372 } ObjectData;
373 } EmfPlusObject;
375 typedef struct EmfPlusRectF
377 float X;
378 float Y;
379 float Width;
380 float Height;
381 } EmfPlusRectF;
383 typedef struct EmfPlusPointR7
385 BYTE X;
386 BYTE Y;
387 } EmfPlusPointR7;
389 typedef struct EmfPlusPoint
391 short X;
392 short Y;
393 } EmfPlusPoint;
395 typedef struct EmfPlusPointF
397 float X;
398 float Y;
399 } EmfPlusPointF;
401 typedef struct EmfPlusDrawImage
403 EmfPlusRecordHeader Header;
404 DWORD ImageAttributesID;
405 DWORD SrcUnit;
406 EmfPlusRectF SrcRect;
407 union
409 EmfPlusRect rect;
410 EmfPlusRectF rectF;
411 } RectData;
412 } EmfPlusDrawImage;
414 typedef struct EmfPlusDrawImagePoints
416 EmfPlusRecordHeader Header;
417 DWORD ImageAttributesID;
418 DWORD SrcUnit;
419 EmfPlusRectF SrcRect;
420 DWORD count;
421 union
423 EmfPlusPointR7 pointsR[3];
424 EmfPlusPoint points[3];
425 EmfPlusPointF pointsF[3];
426 } PointData;
427 } EmfPlusDrawImagePoints;
429 typedef struct EmfPlusDrawPath
431 EmfPlusRecordHeader Header;
432 DWORD PenId;
433 } EmfPlusDrawPath;
435 typedef struct EmfPlusDrawArc
437 EmfPlusRecordHeader Header;
438 float StartAngle;
439 float SweepAngle;
440 union
442 EmfPlusRect rect;
443 EmfPlusRectF rectF;
444 } RectData;
445 } EmfPlusDrawArc;
447 typedef struct EmfPlusDrawEllipse
449 EmfPlusRecordHeader Header;
450 union
452 EmfPlusRect rect;
453 EmfPlusRectF rectF;
454 } RectData;
455 } EmfPlusDrawEllipse;
457 typedef struct EmfPlusDrawPie
459 EmfPlusRecordHeader Header;
460 float StartAngle;
461 float SweepAngle;
462 union
464 EmfPlusRect rect;
465 EmfPlusRectF rectF;
466 } RectData;
467 } EmfPlusDrawPie;
469 typedef struct EmfPlusDrawRects
471 EmfPlusRecordHeader Header;
472 DWORD Count;
473 union
475 EmfPlusRect rect[1];
476 EmfPlusRectF rectF[1];
477 } RectData;
478 } EmfPlusDrawRects;
480 typedef struct EmfPlusFillPath
482 EmfPlusRecordHeader Header;
483 union
485 DWORD BrushId;
486 EmfPlusARGB Color;
487 } data;
488 } EmfPlusFillPath;
490 typedef struct EmfPlusFillClosedCurve
492 EmfPlusRecordHeader Header;
493 DWORD BrushId;
494 float Tension;
495 DWORD Count;
496 union
498 EmfPlusPointR7 pointsR[1];
499 EmfPlusPoint points[1];
500 EmfPlusPointF pointsF[1];
501 } PointData;
502 } EmfPlusFillClosedCurve;
504 typedef struct EmfPlusFillEllipse
506 EmfPlusRecordHeader Header;
507 DWORD BrushId;
508 union
510 EmfPlusRect rect;
511 EmfPlusRectF rectF;
512 } RectData;
513 } EmfPlusFillEllipse;
515 typedef struct EmfPlusFillPie
517 EmfPlusRecordHeader Header;
518 DWORD BrushId;
519 float StartAngle;
520 float SweepAngle;
521 union
523 EmfPlusRect rect;
524 EmfPlusRectF rectF;
525 } RectData;
526 } EmfPlusFillPie;
528 typedef struct EmfPlusFont
530 DWORD Version;
531 float EmSize;
532 DWORD SizeUnit;
533 DWORD FontStyleFlags;
534 DWORD Reserved;
535 DWORD Length;
536 WCHAR FamilyName[1];
537 } EmfPlusFont;
539 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
541 struct emfplus_object *object = &metafile->objtable[id];
543 switch (object->type)
545 case ObjectTypeInvalid:
546 break;
547 case ObjectTypeBrush:
548 GdipDeleteBrush(object->u.brush);
549 break;
550 case ObjectTypePen:
551 GdipDeletePen(object->u.pen);
552 break;
553 case ObjectTypePath:
554 GdipDeletePath(object->u.path);
555 break;
556 case ObjectTypeRegion:
557 GdipDeleteRegion(object->u.region);
558 break;
559 case ObjectTypeImage:
560 GdipDisposeImage(object->u.image);
561 break;
562 case ObjectTypeFont:
563 GdipDeleteFont(object->u.font);
564 break;
565 case ObjectTypeImageAttributes:
566 GdipDisposeImageAttributes(object->u.image_attributes);
567 break;
568 default:
569 FIXME("not implemented for object type %u.\n", object->type);
570 return;
573 object->type = ObjectTypeInvalid;
574 object->u.object = NULL;
577 void METAFILE_Free(GpMetafile *metafile)
579 unsigned int i;
581 heap_free(metafile->comment_data);
582 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
583 if (!metafile->preserve_hemf)
584 DeleteEnhMetaFile(metafile->hemf);
585 if (metafile->record_graphics)
587 WARN("metafile closed while recording\n");
588 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
589 metafile->record_graphics->image = NULL;
590 metafile->record_graphics->busy = TRUE;
593 if (metafile->record_stream)
594 IStream_Release(metafile->record_stream);
596 for (i = 0; i < sizeof(metafile->objtable)/sizeof(metafile->objtable[0]); i++)
597 metafile_free_object_table_entry(metafile, i);
600 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
602 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
605 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
607 DWORD size_needed;
608 EmfPlusRecordHeader *record;
610 if (!metafile->comment_data_size)
612 DWORD data_size = max(256, size * 2 + 4);
613 metafile->comment_data = heap_alloc_zero(data_size);
615 if (!metafile->comment_data)
616 return OutOfMemory;
618 memcpy(metafile->comment_data, "EMF+", 4);
620 metafile->comment_data_size = data_size;
621 metafile->comment_data_length = 4;
624 size_needed = size + metafile->comment_data_length;
626 if (size_needed > metafile->comment_data_size)
628 DWORD data_size = size_needed * 2;
629 BYTE *new_data = heap_alloc_zero(data_size);
631 if (!new_data)
632 return OutOfMemory;
634 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
636 metafile->comment_data_size = data_size;
637 heap_free(metafile->comment_data);
638 metafile->comment_data = new_data;
641 *result = metafile->comment_data + metafile->comment_data_length;
642 metafile->comment_data_length += size;
644 record = (EmfPlusRecordHeader*)*result;
645 record->Size = size;
646 record->DataSize = size - sizeof(EmfPlusRecordHeader);
648 return Ok;
651 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
653 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
654 metafile->comment_data_length -= record->Size;
657 static void METAFILE_WriteRecords(GpMetafile *metafile)
659 if (metafile->comment_data_length > 4)
661 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
662 metafile->comment_data_length = 4;
666 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
668 GpStatus stat;
670 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
672 EmfPlusHeader *header;
674 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
675 if (stat != Ok)
676 return stat;
678 header->Header.Type = EmfPlusRecordTypeHeader;
680 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
681 header->Header.Flags = 1;
682 else
683 header->Header.Flags = 0;
685 header->Version = VERSION_MAGIC2;
687 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
688 header->EmfPlusFlags = 1;
689 else
690 header->EmfPlusFlags = 0;
692 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
693 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
695 METAFILE_WriteRecords(metafile);
698 return Ok;
701 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
703 GpStatus stat;
705 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
707 EmfPlusRecordHeader *record;
709 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
710 if (stat != Ok)
711 return stat;
713 record->Type = EmfPlusRecordTypeEndOfFile;
714 record->Flags = 0;
716 METAFILE_WriteRecords(metafile);
719 return Ok;
722 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
723 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
725 HDC record_dc;
726 REAL dpix, dpiy;
727 REAL framerect_factor_x, framerect_factor_y;
728 RECT rc, *lprc;
729 GpStatus stat;
731 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
733 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
734 return InvalidParameter;
736 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
737 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
739 if (frameRect)
741 switch (frameUnit)
743 case MetafileFrameUnitPixel:
744 framerect_factor_x = 2540.0 / dpix;
745 framerect_factor_y = 2540.0 / dpiy;
746 break;
747 case MetafileFrameUnitPoint:
748 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
749 break;
750 case MetafileFrameUnitInch:
751 framerect_factor_x = framerect_factor_y = 2540.0;
752 break;
753 case MetafileFrameUnitDocument:
754 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
755 break;
756 case MetafileFrameUnitMillimeter:
757 framerect_factor_x = framerect_factor_y = 100.0;
758 break;
759 case MetafileFrameUnitGdi:
760 framerect_factor_x = framerect_factor_y = 1.0;
761 break;
762 default:
763 return InvalidParameter;
766 rc.left = framerect_factor_x * frameRect->X;
767 rc.top = framerect_factor_y * frameRect->Y;
768 rc.right = rc.left + framerect_factor_x * frameRect->Width;
769 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
771 lprc = &rc;
773 else
774 lprc = NULL;
776 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
778 if (!record_dc)
779 return GenericError;
781 *metafile = heap_alloc_zero(sizeof(GpMetafile));
782 if(!*metafile)
784 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
785 return OutOfMemory;
788 (*metafile)->image.type = ImageTypeMetafile;
789 (*metafile)->image.flags = ImageFlagsNone;
790 (*metafile)->image.palette = NULL;
791 (*metafile)->image.xres = dpix;
792 (*metafile)->image.yres = dpiy;
793 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
794 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
795 (*metafile)->unit = UnitPixel;
796 (*metafile)->metafile_type = type;
797 (*metafile)->record_dc = record_dc;
798 (*metafile)->comment_data = NULL;
799 (*metafile)->comment_data_size = 0;
800 (*metafile)->comment_data_length = 0;
801 (*metafile)->hemf = NULL;
802 list_init(&(*metafile)->containers);
804 if (!frameRect)
806 (*metafile)->auto_frame = TRUE;
807 (*metafile)->auto_frame_min.X = 0;
808 (*metafile)->auto_frame_min.Y = 0;
809 (*metafile)->auto_frame_max.X = -1;
810 (*metafile)->auto_frame_max.Y = -1;
813 stat = METAFILE_WriteHeader(*metafile, hdc);
815 if (stat != Ok)
817 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
818 heap_free(*metafile);
819 *metafile = NULL;
820 return OutOfMemory;
823 return stat;
826 /*****************************************************************************
827 * GdipRecordMetafileI [GDIPLUS.@]
829 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
830 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
832 GpRectF frameRectF, *pFrameRectF;
834 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
836 if (frameRect)
838 frameRectF.X = frameRect->X;
839 frameRectF.Y = frameRect->Y;
840 frameRectF.Width = frameRect->Width;
841 frameRectF.Height = frameRect->Height;
842 pFrameRectF = &frameRectF;
844 else
845 pFrameRectF = NULL;
847 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
850 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
851 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
853 GpStatus stat;
855 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
857 if (!stream)
858 return InvalidParameter;
860 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
862 if (stat == Ok)
864 (*metafile)->record_stream = stream;
865 IStream_AddRef(stream);
868 return stat;
871 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
872 UINT num_points)
874 int i;
876 if (!metafile->auto_frame || !num_points)
877 return;
879 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
880 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
882 for (i=0; i<num_points; i++)
884 if (points[i].X < metafile->auto_frame_min.X)
885 metafile->auto_frame_min.X = points[i].X;
886 if (points[i].X > metafile->auto_frame_max.X)
887 metafile->auto_frame_max.X = points[i].X;
888 if (points[i].Y < metafile->auto_frame_min.Y)
889 metafile->auto_frame_min.Y = points[i].Y;
890 if (points[i].Y > metafile->auto_frame_max.Y)
891 metafile->auto_frame_max.Y = points[i].Y;
895 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
897 GpStatus stat;
899 if (!metafile->record_dc || metafile->record_graphics)
900 return InvalidParameter;
902 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
904 if (stat == Ok)
906 *result = metafile->record_graphics;
907 metafile->record_graphics->xres = 96.0;
908 metafile->record_graphics->yres = 96.0;
911 return stat;
914 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
916 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
918 EmfPlusRecordHeader *record;
919 GpStatus stat;
921 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
922 if (stat != Ok)
923 return stat;
925 record->Type = EmfPlusRecordTypeGetDC;
926 record->Flags = 0;
928 METAFILE_WriteRecords(metafile);
931 *hdc = metafile->record_dc;
933 return Ok;
936 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
938 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
940 EmfPlusClear *record;
941 GpStatus stat;
943 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
944 if (stat != Ok)
945 return stat;
947 record->Header.Type = EmfPlusRecordTypeClear;
948 record->Header.Flags = 0;
949 record->Color = color;
951 METAFILE_WriteRecords(metafile);
954 return Ok;
957 static BOOL is_integer_rect(const GpRectF *rect)
959 SHORT x, y, width, height;
960 x = rect->X;
961 y = rect->Y;
962 width = rect->Width;
963 height = rect->Height;
964 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
965 rect->Width != (REAL)width || rect->Height != (REAL)height)
966 return FALSE;
967 return TRUE;
970 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
972 switch (brush->bt)
974 case BrushTypeSolidColor:
975 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
976 break;
977 case BrushTypeHatchFill:
978 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
979 break;
980 default:
981 FIXME("unsupported brush type: %d\n", brush->bt);
982 return NotImplemented;
985 return Ok;
988 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
990 data->Version = VERSION_MAGIC2;
991 data->Type = brush->bt;
993 switch (brush->bt)
995 case BrushTypeSolidColor:
997 GpSolidFill *solid = (GpSolidFill *)brush;
998 data->BrushData.solid.SolidColor = solid->color;
999 break;
1001 case BrushTypeHatchFill:
1003 GpHatch *hatch = (GpHatch *)brush;
1004 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
1005 data->BrushData.hatch.ForeColor = hatch->forecol;
1006 data->BrushData.hatch.BackColor = hatch->backcol;
1007 break;
1009 default:
1010 FIXME("unsupported brush type: %d\n", brush->bt);
1014 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
1016 EmfPlusObject *object_record;
1017 GpStatus stat;
1018 DWORD size;
1020 *id = -1;
1021 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1022 return Ok;
1024 stat = METAFILE_PrepareBrushData(brush, &size);
1025 if (stat != Ok) return stat;
1027 stat = METAFILE_AllocateRecord(metafile,
1028 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1029 if (stat != Ok) return stat;
1031 *id = METAFILE_AddObjectId(metafile);
1032 object_record->Header.Type = EmfPlusRecordTypeObject;
1033 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1034 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1035 return Ok;
1038 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1039 GDIPCONST GpRectF* rects, INT count)
1041 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1043 EmfPlusFillRects *record;
1044 GpStatus stat;
1045 BOOL integer_rects = TRUE;
1046 int i;
1047 DWORD brushid;
1048 int flags = 0;
1050 if (brush->bt == BrushTypeSolidColor)
1052 flags |= 0x8000;
1053 brushid = ((GpSolidFill*)brush)->color;
1055 else
1057 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1058 if (stat != Ok)
1059 return stat;
1062 for (i=0; i<count; i++)
1064 if (!is_integer_rect(&rects[i]))
1066 integer_rects = FALSE;
1067 break;
1071 if (integer_rects)
1072 flags |= 0x4000;
1074 stat = METAFILE_AllocateRecord(metafile,
1075 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1076 (void**)&record);
1077 if (stat != Ok)
1078 return stat;
1080 record->Header.Type = EmfPlusRecordTypeFillRects;
1081 record->Header.Flags = flags;
1082 record->BrushID = brushid;
1083 record->Count = count;
1085 if (integer_rects)
1087 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1088 for (i=0; i<count; i++)
1090 record_rects[i].X = (SHORT)rects[i].X;
1091 record_rects[i].Y = (SHORT)rects[i].Y;
1092 record_rects[i].Width = (SHORT)rects[i].Width;
1093 record_rects[i].Height = (SHORT)rects[i].Height;
1096 else
1097 memcpy(record+1, rects, sizeof(GpRectF) * count);
1099 METAFILE_WriteRecords(metafile);
1102 if (metafile->auto_frame)
1104 GpPointF corners[4];
1105 int i;
1107 for (i=0; i<count; i++)
1109 corners[0].X = rects[i].X;
1110 corners[0].Y = rects[i].Y;
1111 corners[1].X = rects[i].X + rects[i].Width;
1112 corners[1].Y = rects[i].Y;
1113 corners[2].X = rects[i].X;
1114 corners[2].Y = rects[i].Y + rects[i].Height;
1115 corners[3].X = rects[i].X + rects[i].Width;
1116 corners[3].Y = rects[i].Y + rects[i].Height;
1118 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1119 CoordinateSpaceWorld, corners, 4);
1121 METAFILE_AdjustFrame(metafile, corners, 4);
1125 return Ok;
1128 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1130 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1132 EmfPlusSetClipRect *record;
1133 GpStatus stat;
1135 stat = METAFILE_AllocateRecord(metafile,
1136 sizeof(EmfPlusSetClipRect),
1137 (void**)&record);
1138 if (stat != Ok)
1139 return stat;
1141 record->Header.Type = EmfPlusRecordTypeSetClipRect;
1142 record->Header.Flags = (mode & 0xf) << 8;
1143 record->ClipRect.X = x;
1144 record->ClipRect.Y = y;
1145 record->ClipRect.Width = width;
1146 record->ClipRect.Height = height;
1148 METAFILE_WriteRecords(metafile);
1151 return Ok;
1154 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1156 EmfPlusObject *object_record;
1157 DWORD size;
1158 GpStatus stat;
1160 *id = -1;
1161 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1162 return Ok;
1164 size = write_region_data(region, NULL);
1165 stat = METAFILE_AllocateRecord(metafile,
1166 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1167 if (stat != Ok) return stat;
1169 *id = METAFILE_AddObjectId(metafile);
1170 object_record->Header.Type = EmfPlusRecordTypeObject;
1171 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1172 write_region_data(region, &object_record->ObjectData.region);
1173 return Ok;
1176 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1178 EmfPlusRecordHeader *record;
1179 DWORD region_id;
1180 GpStatus stat;
1182 if (metafile->metafile_type == MetafileTypeEmf)
1184 FIXME("stub!\n");
1185 return NotImplemented;
1188 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1189 if (stat != Ok) return stat;
1191 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1192 if (stat != Ok) return stat;
1194 record->Type = EmfPlusRecordTypeSetClipRegion;
1195 record->Flags = region_id | mode << 8;
1197 METAFILE_WriteRecords(metafile);
1198 return Ok;
1201 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1203 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1205 EmfPlusSetPageTransform *record;
1206 GpStatus stat;
1208 stat = METAFILE_AllocateRecord(metafile,
1209 sizeof(EmfPlusSetPageTransform),
1210 (void**)&record);
1211 if (stat != Ok)
1212 return stat;
1214 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1215 record->Header.Flags = unit;
1216 record->PageScale = scale;
1218 METAFILE_WriteRecords(metafile);
1221 return Ok;
1224 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1226 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1228 EmfPlusSetWorldTransform *record;
1229 GpStatus stat;
1231 stat = METAFILE_AllocateRecord(metafile,
1232 sizeof(EmfPlusSetWorldTransform),
1233 (void**)&record);
1234 if (stat != Ok)
1235 return stat;
1237 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1238 record->Header.Flags = 0;
1239 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1241 METAFILE_WriteRecords(metafile);
1244 return Ok;
1247 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1249 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1251 EmfPlusScaleWorldTransform *record;
1252 GpStatus stat;
1254 stat = METAFILE_AllocateRecord(metafile,
1255 sizeof(EmfPlusScaleWorldTransform),
1256 (void**)&record);
1257 if (stat != Ok)
1258 return stat;
1260 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1261 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1262 record->Sx = sx;
1263 record->Sy = sy;
1265 METAFILE_WriteRecords(metafile);
1268 return Ok;
1271 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1273 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1275 EmfPlusMultiplyWorldTransform *record;
1276 GpStatus stat;
1278 stat = METAFILE_AllocateRecord(metafile,
1279 sizeof(EmfPlusMultiplyWorldTransform),
1280 (void**)&record);
1281 if (stat != Ok)
1282 return stat;
1284 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1285 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1286 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1288 METAFILE_WriteRecords(metafile);
1291 return Ok;
1294 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1296 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1298 EmfPlusRotateWorldTransform *record;
1299 GpStatus stat;
1301 stat = METAFILE_AllocateRecord(metafile,
1302 sizeof(EmfPlusRotateWorldTransform),
1303 (void**)&record);
1304 if (stat != Ok)
1305 return stat;
1307 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1308 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1309 record->Angle = angle;
1311 METAFILE_WriteRecords(metafile);
1314 return Ok;
1317 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1319 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1321 EmfPlusTranslateWorldTransform *record;
1322 GpStatus stat;
1324 stat = METAFILE_AllocateRecord(metafile,
1325 sizeof(EmfPlusTranslateWorldTransform),
1326 (void**)&record);
1327 if (stat != Ok)
1328 return stat;
1330 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1331 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1332 record->dx = dx;
1333 record->dy = dy;
1335 METAFILE_WriteRecords(metafile);
1338 return Ok;
1341 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1343 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1345 EmfPlusRecordHeader *record;
1346 GpStatus stat;
1348 stat = METAFILE_AllocateRecord(metafile,
1349 sizeof(EmfPlusRecordHeader),
1350 (void**)&record);
1351 if (stat != Ok)
1352 return stat;
1354 record->Type = EmfPlusRecordTypeResetWorldTransform;
1355 record->Flags = 0;
1357 METAFILE_WriteRecords(metafile);
1360 return Ok;
1363 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1364 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1366 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1368 EmfPlusBeginContainer *record;
1369 GpStatus stat;
1371 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1372 if (stat != Ok)
1373 return stat;
1375 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1376 record->Header.Flags = unit & 0xff;
1377 record->DestRect = *dstrect;
1378 record->SrcRect = *srcrect;
1379 record->StackIndex = StackIndex;
1381 METAFILE_WriteRecords(metafile);
1384 return Ok;
1387 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1389 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1391 EmfPlusContainerRecord *record;
1392 GpStatus stat;
1394 stat = METAFILE_AllocateRecord(metafile,
1395 sizeof(EmfPlusContainerRecord),
1396 (void**)&record);
1397 if (stat != Ok)
1398 return stat;
1400 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1401 record->Header.Flags = 0;
1402 record->StackIndex = StackIndex;
1404 METAFILE_WriteRecords(metafile);
1407 return Ok;
1410 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1412 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1414 EmfPlusContainerRecord *record;
1415 GpStatus stat;
1417 stat = METAFILE_AllocateRecord(metafile,
1418 sizeof(EmfPlusContainerRecord),
1419 (void**)&record);
1420 if (stat != Ok)
1421 return stat;
1423 record->Header.Type = EmfPlusRecordTypeEndContainer;
1424 record->Header.Flags = 0;
1425 record->StackIndex = StackIndex;
1427 METAFILE_WriteRecords(metafile);
1430 return Ok;
1433 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1435 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1437 EmfPlusContainerRecord *record;
1438 GpStatus stat;
1440 stat = METAFILE_AllocateRecord(metafile,
1441 sizeof(EmfPlusContainerRecord),
1442 (void**)&record);
1443 if (stat != Ok)
1444 return stat;
1446 record->Header.Type = EmfPlusRecordTypeSave;
1447 record->Header.Flags = 0;
1448 record->StackIndex = StackIndex;
1450 METAFILE_WriteRecords(metafile);
1453 return Ok;
1456 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1458 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1460 EmfPlusContainerRecord *record;
1461 GpStatus stat;
1463 stat = METAFILE_AllocateRecord(metafile,
1464 sizeof(EmfPlusContainerRecord),
1465 (void**)&record);
1466 if (stat != Ok)
1467 return stat;
1469 record->Header.Type = EmfPlusRecordTypeRestore;
1470 record->Header.Flags = 0;
1471 record->StackIndex = StackIndex;
1473 METAFILE_WriteRecords(metafile);
1476 return Ok;
1479 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1481 if (hdc != metafile->record_dc)
1482 return InvalidParameter;
1484 return Ok;
1487 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1489 GpStatus stat;
1491 stat = METAFILE_WriteEndOfFile(metafile);
1492 metafile->record_graphics = NULL;
1494 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1495 metafile->record_dc = NULL;
1497 heap_free(metafile->comment_data);
1498 metafile->comment_data = NULL;
1499 metafile->comment_data_size = 0;
1501 if (stat == Ok)
1503 MetafileHeader header;
1505 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1506 if (stat == Ok && metafile->auto_frame &&
1507 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1509 RECTL bounds_rc, gdi_bounds_rc;
1510 REAL x_scale = 2540.0 / header.DpiX;
1511 REAL y_scale = 2540.0 / header.DpiY;
1512 BYTE* buffer;
1513 UINT buffer_size;
1515 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1516 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1517 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1518 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1520 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1521 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1523 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1524 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1525 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1526 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1529 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1530 buffer = heap_alloc(buffer_size);
1531 if (buffer)
1533 HENHMETAFILE new_hemf;
1535 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1537 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1539 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1541 if (new_hemf)
1543 DeleteEnhMetaFile(metafile->hemf);
1544 metafile->hemf = new_hemf;
1546 else
1547 stat = OutOfMemory;
1549 heap_free(buffer);
1551 else
1552 stat = OutOfMemory;
1554 if (stat == Ok)
1555 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1557 if (stat == Ok)
1559 metafile->bounds.X = header.X;
1560 metafile->bounds.Y = header.Y;
1561 metafile->bounds.Width = header.Width;
1562 metafile->bounds.Height = header.Height;
1566 if (stat == Ok && metafile->record_stream)
1568 BYTE *buffer;
1569 UINT buffer_size;
1571 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1573 buffer = heap_alloc(buffer_size);
1574 if (buffer)
1576 HRESULT hr;
1578 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1580 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1582 if (FAILED(hr))
1583 stat = hresult_to_status(hr);
1585 heap_free(buffer);
1587 else
1588 stat = OutOfMemory;
1591 if (metafile->record_stream)
1593 IStream_Release(metafile->record_stream);
1594 metafile->record_stream = NULL;
1597 return stat;
1600 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1602 TRACE("(%p,%p)\n", metafile, hEmf);
1604 if (!metafile || !hEmf || !metafile->hemf)
1605 return InvalidParameter;
1607 *hEmf = metafile->hemf;
1608 metafile->hemf = NULL;
1610 return Ok;
1613 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1615 const GpRectF *rect;
1616 const GpPointF *pt;
1618 /* This transforms metafile device space to output points. */
1619 rect = &metafile->src_rect;
1620 pt = metafile->playback_points;
1621 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1622 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1623 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1624 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1625 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1626 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1629 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1631 XFORM combined, final;
1633 METAFILE_GetFinalGdiTransform(metafile, &final);
1635 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1637 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1638 SetWorldTransform(metafile->playback_dc, &combined);
1640 return Ok;
1643 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1645 GpStatus stat = Ok;
1647 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1649 if (stat == Ok)
1651 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1653 metafile->gdiworldtransform = identity;
1654 METAFILE_PlaybackUpdateGdiTransform(metafile);
1657 return stat;
1660 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1662 if (metafile->playback_dc)
1664 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1665 metafile->playback_dc = NULL;
1669 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1671 GpStatus stat;
1672 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1673 if (stat == Ok)
1674 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1675 return stat;
1678 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1680 GpMatrix *real_transform;
1681 GpStatus stat;
1683 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1685 if (stat == Ok)
1687 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1689 if (metafile->page_unit != UnitDisplay)
1690 scale *= metafile->page_scale;
1692 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1694 if (stat == Ok)
1695 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1697 if (stat == Ok)
1698 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1700 GdipDeleteMatrix(real_transform);
1703 return stat;
1706 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1708 metafile_free_object_table_entry(metafile, id);
1709 metafile->objtable[id].type = type;
1710 metafile->objtable[id].u.object = object;
1713 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1715 EmfPlusImage *data = (EmfPlusImage *)record_data;
1716 GpStatus status;
1718 *image = NULL;
1720 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1721 return InvalidParameter;
1722 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1724 switch (data->Type)
1726 case ImageDataTypeBitmap:
1728 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1730 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1731 return InvalidParameter;
1732 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1734 switch (bitmapdata->Type)
1736 case BitmapDataTypePixel:
1738 ColorPalette *palette;
1739 BYTE *scan0;
1741 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1743 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1744 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1746 if (data_size <= palette_size)
1747 return InvalidParameter;
1748 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1750 if (data_size < palette_size)
1751 return InvalidParameter;
1752 data_size -= palette_size;
1754 palette = (ColorPalette *)bitmapdata->BitmapData;
1755 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1757 else
1759 palette = NULL;
1760 scan0 = bitmapdata->BitmapData;
1763 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1764 return InvalidParameter;
1766 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1767 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1768 if (status == Ok && palette)
1770 status = GdipSetImagePalette(*image, palette);
1771 if (status != Ok)
1773 GdipDisposeImage(*image);
1774 *image = NULL;
1777 break;
1779 case BitmapDataTypeCompressed:
1781 IWICImagingFactory *factory;
1782 IWICStream *stream;
1783 HRESULT hr;
1785 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1786 return GenericError;
1788 hr = IWICImagingFactory_CreateStream(factory, &stream);
1789 IWICImagingFactory_Release(factory);
1790 if (hr != S_OK)
1791 return GenericError;
1793 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1794 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1795 else
1796 status = GenericError;
1798 IWICStream_Release(stream);
1799 break;
1801 default:
1802 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1803 return InvalidParameter;
1805 break;
1807 default:
1808 FIXME("image type %d not supported.\n", data->Type);
1809 return NotImplemented;
1812 return status;
1815 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1817 EmfPlusPath *data = (EmfPlusPath *)record_data;
1818 GpStatus status;
1819 BYTE *types;
1820 UINT size;
1821 DWORD i;
1823 *path = NULL;
1825 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1826 return InvalidParameter;
1827 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1829 if (data->PathPointFlags & 0x800) /* R */
1831 FIXME("RLE encoded path data is not supported.\n");
1832 return NotImplemented;
1834 else
1836 if (data->PathPointFlags & 0x4000) /* C */
1837 size = sizeof(EmfPlusPoint);
1838 else
1839 size = sizeof(EmfPlusPointF);
1840 size += sizeof(BYTE); /* EmfPlusPathPointType */
1841 size *= data->PathPointCount;
1844 if (data_size < size)
1845 return InvalidParameter;
1847 status = GdipCreatePath(FillModeAlternate, path);
1848 if (status != Ok)
1849 return status;
1851 (*path)->pathdata.Count = data->PathPointCount;
1852 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1853 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1854 (*path)->datalen = (*path)->pathdata.Count;
1856 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1858 GdipDeletePath(*path);
1859 return OutOfMemory;
1862 if (data->PathPointFlags & 0x4000) /* C */
1864 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1865 for (i = 0; i < data->PathPointCount; i++)
1867 (*path)->pathdata.Points[i].X = points[i].X;
1868 (*path)->pathdata.Points[i].Y = points[i].Y;
1870 types = (BYTE *)(points + i);
1872 else
1874 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1875 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1876 types = (BYTE *)(points + data->PathPointCount);
1879 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1881 return Ok;
1884 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1886 const DWORD *type;
1887 GpStatus status;
1889 type = buffer_read(mbuf, sizeof(*type));
1890 if (!type) return Ok;
1892 node->type = *type;
1894 switch (node->type)
1896 case CombineModeReplace:
1897 case CombineModeIntersect:
1898 case CombineModeUnion:
1899 case CombineModeXor:
1900 case CombineModeExclude:
1901 case CombineModeComplement:
1903 region_element *left, *right;
1905 left = heap_alloc_zero(sizeof(*left));
1906 if (!left)
1907 return OutOfMemory;
1909 right = heap_alloc_zero(sizeof(*right));
1910 if (!right)
1912 heap_free(left);
1913 return OutOfMemory;
1916 status = metafile_read_region_node(mbuf, region, left, count);
1917 if (status == Ok)
1919 status = metafile_read_region_node(mbuf, region, right, count);
1920 if (status == Ok)
1922 node->elementdata.combine.left = left;
1923 node->elementdata.combine.right = right;
1924 region->num_children += 2;
1925 return Ok;
1929 heap_free(left);
1930 heap_free(right);
1931 return status;
1933 case RegionDataRect:
1935 const EmfPlusRectF *rect;
1937 rect = buffer_read(mbuf, sizeof(*rect));
1938 if (!rect)
1939 return InvalidParameter;
1941 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1942 *count += 1;
1943 return Ok;
1945 case RegionDataPath:
1947 const BYTE *path_data;
1948 const UINT *data_size;
1949 GpPath *path;
1951 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1952 if (!data_size)
1953 return InvalidParameter;
1955 path_data = buffer_read(mbuf, *data_size);
1956 if (!path_data)
1957 return InvalidParameter;
1959 status = metafile_deserialize_path(path_data, *data_size, &path);
1960 if (status == Ok)
1962 node->elementdata.path = path;
1963 *count += 1;
1965 return Ok;
1967 case RegionDataEmptyRect:
1968 case RegionDataInfiniteRect:
1969 *count += 1;
1970 return Ok;
1971 default:
1972 FIXME("element type %#x is not supported\n", *type);
1973 break;
1976 return InvalidParameter;
1979 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1981 struct memory_buffer mbuf;
1982 GpStatus status;
1983 UINT count;
1985 *region = NULL;
1987 init_memory_buffer(&mbuf, record_data, data_size);
1989 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
1990 return InvalidParameter;
1992 status = GdipCreateRegion(region);
1993 if (status != Ok)
1994 return status;
1996 count = 0;
1997 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
1998 if (status == Ok && !count)
1999 status = InvalidParameter;
2001 if (status != Ok)
2003 GdipDeleteRegion(*region);
2004 *region = NULL;
2007 return status;
2010 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2012 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2013 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2014 GpStatus status;
2016 *brush = NULL;
2018 if (data_size < header_size)
2019 return InvalidParameter;
2021 switch (data->Type)
2023 case BrushTypeSolidColor:
2024 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2025 return InvalidParameter;
2027 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2028 break;
2029 case BrushTypeHatchFill:
2030 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2031 return InvalidParameter;
2033 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2034 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2035 break;
2036 case BrushTypeTextureFill:
2038 UINT offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2039 EmfPlusTransformMatrix *transform = NULL;
2040 DWORD brushflags;
2041 GpImage *image;
2043 if (data_size <= offset)
2044 return InvalidParameter;
2046 brushflags = data->BrushData.texture.BrushDataFlags;
2047 if (brushflags & BrushDataTransform)
2049 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2050 return InvalidParameter;
2051 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2052 offset += sizeof(EmfPlusTransformMatrix);
2055 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2056 if (status != Ok)
2057 return status;
2059 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2060 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2061 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2063 GdipDisposeImage(image);
2064 break;
2066 default:
2067 FIXME("brush type %u is not supported.\n", data->Type);
2068 return NotImplemented;
2071 return status;
2074 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2076 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2077 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2079 if (data_size <= offset)
2080 return InvalidParameter;
2082 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2083 if (data_size <= offset)
2084 return InvalidParameter;
2086 if (pendata->PenDataFlags & PenDataTransform)
2087 offset += sizeof(EmfPlusTransformMatrix);
2089 if (pendata->PenDataFlags & PenDataStartCap)
2090 offset += sizeof(DWORD);
2092 if (pendata->PenDataFlags & PenDataEndCap)
2093 offset += sizeof(DWORD);
2095 if (pendata->PenDataFlags & PenDataJoin)
2096 offset += sizeof(DWORD);
2098 if (pendata->PenDataFlags & PenDataMiterLimit)
2099 offset += sizeof(REAL);
2101 if (pendata->PenDataFlags & PenDataLineStyle)
2102 offset += sizeof(DWORD);
2104 if (pendata->PenDataFlags & PenDataDashedLineCap)
2105 offset += sizeof(DWORD);
2107 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2108 offset += sizeof(REAL);
2110 if (pendata->PenDataFlags & PenDataDashedLine)
2112 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2114 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2115 if (data_size <= offset)
2116 return InvalidParameter;
2118 offset += dashedline->DashedLineDataSize * sizeof(float);
2121 if (pendata->PenDataFlags & PenDataNonCenter)
2122 offset += sizeof(DWORD);
2124 if (pendata->PenDataFlags & PenDataCompoundLine)
2126 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2128 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2129 if (data_size <= offset)
2130 return InvalidParameter;
2132 offset += compoundline->CompoundLineDataSize * sizeof(float);
2135 if (pendata->PenDataFlags & PenDataCustomStartCap)
2137 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2139 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2140 if (data_size <= offset)
2141 return InvalidParameter;
2143 offset += startcap->CustomStartCapSize;
2146 if (pendata->PenDataFlags & PenDataCustomEndCap)
2148 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2150 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2151 if (data_size <= offset)
2152 return InvalidParameter;
2154 offset += endcap->CustomEndCapSize;
2157 *ret = offset;
2158 return Ok;
2161 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2163 BYTE type = (flags >> 8) & 0xff;
2164 BYTE id = flags & 0xff;
2165 void *object = NULL;
2166 GpStatus status;
2168 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2169 return InvalidParameter;
2171 switch (type)
2173 case ObjectTypeBrush:
2174 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2175 break;
2176 case ObjectTypePen:
2178 EmfPlusPen *data = (EmfPlusPen *)record_data;
2179 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2180 GpBrush *brush;
2181 DWORD offset;
2182 GpPen *pen;
2184 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2185 if (status != Ok)
2186 return status;
2188 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2189 if (status != Ok)
2190 return status;
2192 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2193 GdipDeleteBrush(brush);
2194 if (status != Ok)
2195 return status;
2197 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2199 if (pendata->PenDataFlags & PenDataTransform)
2201 FIXME("PenDataTransform is not supported.\n");
2202 offset += sizeof(EmfPlusTransformMatrix);
2205 if (pendata->PenDataFlags & PenDataStartCap)
2207 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2208 goto penfailed;
2209 offset += sizeof(DWORD);
2212 if (pendata->PenDataFlags & PenDataEndCap)
2214 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2215 goto penfailed;
2216 offset += sizeof(DWORD);
2219 if (pendata->PenDataFlags & PenDataJoin)
2221 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2222 goto penfailed;
2223 offset += sizeof(DWORD);
2226 if (pendata->PenDataFlags & PenDataMiterLimit)
2228 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2229 goto penfailed;
2230 offset += sizeof(REAL);
2233 if (pendata->PenDataFlags & PenDataLineStyle)
2235 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2236 goto penfailed;
2237 offset += sizeof(DWORD);
2240 if (pendata->PenDataFlags & PenDataDashedLineCap)
2242 FIXME("PenDataDashedLineCap is not supported.\n");
2243 offset += sizeof(DWORD);
2246 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2248 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2249 goto penfailed;
2250 offset += sizeof(REAL);
2253 if (pendata->PenDataFlags & PenDataDashedLine)
2255 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2256 FIXME("PenDataDashedLine is not supported.\n");
2257 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2260 if (pendata->PenDataFlags & PenDataNonCenter)
2262 FIXME("PenDataNonCenter is not supported.\n");
2263 offset += sizeof(DWORD);
2266 if (pendata->PenDataFlags & PenDataCompoundLine)
2268 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2269 FIXME("PenDataCompundLine is not supported.\n");
2270 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2273 if (pendata->PenDataFlags & PenDataCustomStartCap)
2275 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2276 FIXME("PenDataCustomStartCap is not supported.\n");
2277 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2280 if (pendata->PenDataFlags & PenDataCustomEndCap)
2282 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2283 FIXME("PenDataCustomEndCap is not supported.\n");
2284 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2287 object = pen;
2288 break;
2290 penfailed:
2291 GdipDeletePen(pen);
2292 return status;
2294 case ObjectTypePath:
2295 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2296 break;
2297 case ObjectTypeRegion:
2298 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2299 break;
2300 case ObjectTypeImage:
2301 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2302 break;
2303 case ObjectTypeFont:
2305 EmfPlusFont *data = (EmfPlusFont *)record_data;
2306 GpFontFamily *family;
2307 WCHAR *familyname;
2309 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2310 return InvalidParameter;
2311 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2313 if (data_size < data->Length * sizeof(WCHAR))
2314 return InvalidParameter;
2316 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2317 return OutOfMemory;
2319 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2320 familyname[data->Length] = 0;
2322 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2323 GdipFree(familyname);
2324 if (status != Ok)
2325 return InvalidParameter;
2327 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2328 GdipDeleteFontFamily(family);
2329 break;
2331 case ObjectTypeImageAttributes:
2333 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2334 GpImageAttributes *attributes = NULL;
2336 if (data_size != sizeof(*data))
2337 return InvalidParameter;
2339 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2340 return status;
2342 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2343 !!data->ObjectClamp);
2344 if (status == Ok)
2345 object = attributes;
2346 else
2347 GdipDisposeImageAttributes(attributes);
2348 break;
2350 default:
2351 FIXME("not implemented for object type %d.\n", type);
2352 return NotImplemented;
2355 if (status == Ok)
2356 metafile_set_object_table_entry(metafile, id, type, object);
2358 return status;
2361 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2363 GpMatrix world_to_device;
2365 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2367 GdipTransformRegion(region, &world_to_device);
2368 GdipCombineRegionRegion(metafile->clip, region, mode);
2370 return METAFILE_PlaybackUpdateClip(metafile);
2373 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2374 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2376 GpStatus stat;
2377 GpMetafile *real_metafile = (GpMetafile*)metafile;
2379 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2381 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2382 return InvalidParameter;
2384 if (recordType >= 1 && recordType <= 0x7a)
2386 /* regular EMF record */
2387 if (metafile->playback_dc)
2389 switch (recordType)
2391 case EMR_SETMAPMODE:
2392 case EMR_SAVEDC:
2393 case EMR_RESTOREDC:
2394 case EMR_SETWINDOWORGEX:
2395 case EMR_SETWINDOWEXTEX:
2396 case EMR_SETVIEWPORTORGEX:
2397 case EMR_SETVIEWPORTEXTEX:
2398 case EMR_SCALEVIEWPORTEXTEX:
2399 case EMR_SCALEWINDOWEXTEX:
2400 case EMR_MODIFYWORLDTRANSFORM:
2401 FIXME("not implemented for record type %x\n", recordType);
2402 break;
2403 case EMR_SETWORLDTRANSFORM:
2405 const XFORM* xform = (void*)data;
2406 real_metafile->gdiworldtransform = *xform;
2407 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
2408 break;
2410 case EMR_EXTSELECTCLIPRGN:
2412 DWORD rgndatasize = *(DWORD*)data;
2413 DWORD mode = *(DWORD*)(data + 4);
2414 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
2415 HRGN hrgn = NULL;
2417 if (dataSize > 8)
2419 XFORM final;
2421 METAFILE_GetFinalGdiTransform(metafile, &final);
2423 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
2426 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
2428 DeleteObject(hrgn);
2430 return Ok;
2432 default:
2434 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2436 if (record)
2438 record->iType = recordType;
2439 record->nSize = dataSize + 8;
2440 memcpy(record->dParm, data, dataSize);
2442 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2443 record, metafile->handle_count) == 0)
2444 ERR("PlayEnhMetaFileRecord failed\n");
2446 heap_free(record);
2448 else
2449 return OutOfMemory;
2451 break;
2456 else
2458 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2460 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2462 switch(recordType)
2464 case EmfPlusRecordTypeHeader:
2465 case EmfPlusRecordTypeEndOfFile:
2466 break;
2467 case EmfPlusRecordTypeGetDC:
2468 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2469 break;
2470 case EmfPlusRecordTypeClear:
2472 EmfPlusClear *record = (EmfPlusClear*)header;
2474 if (dataSize != sizeof(record->Color))
2475 return InvalidParameter;
2477 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2479 case EmfPlusRecordTypeFillRects:
2481 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2482 GpBrush *brush, *temp_brush=NULL;
2483 GpRectF *rects, *temp_rects=NULL;
2485 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2486 return InvalidParameter;
2488 if (flags & 0x4000)
2490 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2491 return InvalidParameter;
2493 else
2495 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2496 return InvalidParameter;
2499 if (flags & 0x8000)
2501 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2502 brush = temp_brush;
2504 else
2506 if (record->BrushID >= EmfPlusObjectTableSize ||
2507 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2508 return InvalidParameter;
2510 brush = real_metafile->objtable[record->BrushID].u.brush;
2511 stat = Ok;
2514 if (stat == Ok)
2516 if (flags & 0x4000)
2518 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2519 int i;
2521 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2522 if (rects)
2524 for (i=0; i<record->Count; i++)
2526 rects[i].X = int_rects[i].X;
2527 rects[i].Y = int_rects[i].Y;
2528 rects[i].Width = int_rects[i].Width;
2529 rects[i].Height = int_rects[i].Height;
2532 else
2533 stat = OutOfMemory;
2535 else
2536 rects = (GpRectF*)(record+1);
2539 if (stat == Ok)
2541 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2544 GdipDeleteBrush(temp_brush);
2545 heap_free(temp_rects);
2547 return stat;
2549 case EmfPlusRecordTypeSetClipRect:
2551 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2552 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2553 GpRegion *region;
2555 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2556 return InvalidParameter;
2558 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2560 if (stat == Ok)
2562 stat = metafile_set_clip_region(real_metafile, region, mode);
2563 GdipDeleteRegion(region);
2566 return stat;
2568 case EmfPlusRecordTypeSetClipRegion:
2570 CombineMode mode = (flags >> 8) & 0xf;
2571 BYTE regionid = flags & 0xff;
2572 GpRegion *region;
2574 if (dataSize != 0)
2575 return InvalidParameter;
2577 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2578 return InvalidParameter;
2580 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2581 if (stat == Ok)
2583 stat = metafile_set_clip_region(real_metafile, region, mode);
2584 GdipDeleteRegion(region);
2587 return stat;
2589 case EmfPlusRecordTypeSetClipPath:
2591 CombineMode mode = (flags >> 8) & 0xf;
2592 BYTE pathid = flags & 0xff;
2593 GpRegion *region;
2595 if (dataSize != 0)
2596 return InvalidParameter;
2598 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2599 return InvalidParameter;
2601 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2602 if (stat == Ok)
2604 stat = metafile_set_clip_region(real_metafile, region, mode);
2605 GdipDeleteRegion(region);
2608 return stat;
2610 case EmfPlusRecordTypeSetPageTransform:
2612 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2613 GpUnit unit = (GpUnit)flags;
2615 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2616 return InvalidParameter;
2618 real_metafile->page_unit = unit;
2619 real_metafile->page_scale = record->PageScale;
2621 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2623 case EmfPlusRecordTypeSetWorldTransform:
2625 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2627 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2628 return InvalidParameter;
2630 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2632 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2634 case EmfPlusRecordTypeScaleWorldTransform:
2636 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2637 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2639 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2640 return InvalidParameter;
2642 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2644 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2646 case EmfPlusRecordTypeMultiplyWorldTransform:
2648 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2649 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2650 GpMatrix matrix;
2652 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2653 return InvalidParameter;
2655 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2657 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2659 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2661 case EmfPlusRecordTypeRotateWorldTransform:
2663 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2664 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2666 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2667 return InvalidParameter;
2669 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2671 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2673 case EmfPlusRecordTypeTranslateWorldTransform:
2675 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2676 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2678 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2679 return InvalidParameter;
2681 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2683 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2685 case EmfPlusRecordTypeResetWorldTransform:
2687 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2689 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2691 case EmfPlusRecordTypeBeginContainer:
2693 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2694 container* cont;
2695 GpUnit unit;
2696 REAL scale_x, scale_y;
2697 GpRectF scaled_srcrect;
2698 GpMatrix transform;
2700 cont = heap_alloc_zero(sizeof(*cont));
2701 if (!cont)
2702 return OutOfMemory;
2704 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2705 if (stat != Ok)
2707 heap_free(cont);
2708 return stat;
2711 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2713 if (stat != Ok)
2715 GdipDeleteRegion(cont->clip);
2716 heap_free(cont);
2717 return stat;
2720 cont->id = record->StackIndex;
2721 cont->type = BEGIN_CONTAINER;
2722 cont->world_transform = *metafile->world_transform;
2723 cont->page_unit = metafile->page_unit;
2724 cont->page_scale = metafile->page_scale;
2725 list_add_head(&real_metafile->containers, &cont->entry);
2727 unit = record->Header.Flags & 0xff;
2729 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
2730 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
2732 scaled_srcrect.X = scale_x * record->SrcRect.X;
2733 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2734 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2735 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2737 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2738 transform.matrix[1] = 0.0;
2739 transform.matrix[2] = 0.0;
2740 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2741 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2742 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2744 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2746 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2748 case EmfPlusRecordTypeBeginContainerNoParams:
2749 case EmfPlusRecordTypeSave:
2751 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2752 container* cont;
2754 cont = heap_alloc_zero(sizeof(*cont));
2755 if (!cont)
2756 return OutOfMemory;
2758 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2759 if (stat != Ok)
2761 heap_free(cont);
2762 return stat;
2765 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2766 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2767 else
2768 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2770 if (stat != Ok)
2772 GdipDeleteRegion(cont->clip);
2773 heap_free(cont);
2774 return stat;
2777 cont->id = record->StackIndex;
2778 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2779 cont->type = BEGIN_CONTAINER;
2780 else
2781 cont->type = SAVE_GRAPHICS;
2782 cont->world_transform = *metafile->world_transform;
2783 cont->page_unit = metafile->page_unit;
2784 cont->page_scale = metafile->page_scale;
2785 list_add_head(&real_metafile->containers, &cont->entry);
2787 break;
2789 case EmfPlusRecordTypeEndContainer:
2790 case EmfPlusRecordTypeRestore:
2792 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2793 container* cont;
2794 enum container_type type;
2795 BOOL found=FALSE;
2797 if (recordType == EmfPlusRecordTypeEndContainer)
2798 type = BEGIN_CONTAINER;
2799 else
2800 type = SAVE_GRAPHICS;
2802 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2804 if (cont->id == record->StackIndex && cont->type == type)
2806 found = TRUE;
2807 break;
2811 if (found)
2813 container* cont2;
2815 /* pop any newer items on the stack */
2816 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2818 list_remove(&cont2->entry);
2819 GdipDeleteRegion(cont2->clip);
2820 heap_free(cont2);
2823 if (type == BEGIN_CONTAINER)
2824 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2825 else
2826 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2828 *real_metafile->world_transform = cont->world_transform;
2829 real_metafile->page_unit = cont->page_unit;
2830 real_metafile->page_scale = cont->page_scale;
2831 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2833 list_remove(&cont->entry);
2834 GdipDeleteRegion(cont->clip);
2835 heap_free(cont);
2838 break;
2840 case EmfPlusRecordTypeSetPixelOffsetMode:
2842 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2844 case EmfPlusRecordTypeSetCompositingQuality:
2846 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2848 case EmfPlusRecordTypeSetInterpolationMode:
2850 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2852 case EmfPlusRecordTypeSetTextRenderingHint:
2854 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2856 case EmfPlusRecordTypeSetAntiAliasMode:
2858 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2860 case EmfPlusRecordTypeSetCompositingMode:
2862 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2864 case EmfPlusRecordTypeObject:
2866 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2868 case EmfPlusRecordTypeDrawImage:
2870 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2871 BYTE image = flags & 0xff;
2872 GpPointF points[3];
2874 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2875 return InvalidParameter;
2877 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2878 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2879 return InvalidParameter;
2881 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2882 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2883 return InvalidParameter;
2885 if (flags & 0x4000) /* C */
2887 points[0].X = draw->RectData.rect.X;
2888 points[0].Y = draw->RectData.rect.Y;
2889 points[1].X = points[0].X + draw->RectData.rect.Width;
2890 points[1].Y = points[0].Y;
2891 points[2].X = points[1].X;
2892 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2894 else
2896 points[0].X = draw->RectData.rectF.X;
2897 points[0].Y = draw->RectData.rectF.Y;
2898 points[1].X = points[0].X + draw->RectData.rectF.Width;
2899 points[1].Y = points[0].Y;
2900 points[2].X = points[1].X;
2901 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2904 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2905 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2906 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2908 case EmfPlusRecordTypeDrawImagePoints:
2910 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2911 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2912 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2913 BYTE image = flags & 0xff;
2914 GpPointF points[3];
2915 unsigned int i;
2916 UINT size;
2918 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2919 return InvalidParameter;
2921 if (dataSize <= fixed_part_size)
2922 return InvalidParameter;
2923 dataSize -= fixed_part_size;
2925 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2926 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2927 return InvalidParameter;
2929 if (draw->count != 3)
2930 return InvalidParameter;
2932 if ((flags >> 13) & 1) /* E */
2933 FIXME("image effects are not supported.\n");
2935 if ((flags >> 11) & 1) /* P */
2936 size = sizeof(EmfPlusPointR7) * draw->count;
2937 else if ((flags >> 14) & 1) /* C */
2938 size = sizeof(EmfPlusPoint) * draw->count;
2939 else
2940 size = sizeof(EmfPlusPointF) * draw->count;
2942 if (dataSize != size)
2943 return InvalidParameter;
2945 if ((flags >> 11) & 1) /* P */
2947 points[0].X = draw->PointData.pointsR[0].X;
2948 points[0].Y = draw->PointData.pointsR[0].Y;
2949 for (i = 1; i < 3; i++)
2951 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
2952 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
2955 else if ((flags >> 14) & 1) /* C */
2957 for (i = 0; i < 3; i++)
2959 points[i].X = draw->PointData.points[i].X;
2960 points[i].Y = draw->PointData.points[i].Y;
2963 else
2964 memcpy(points, draw->PointData.pointsF, sizeof(points));
2966 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2967 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2968 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2970 case EmfPlusRecordTypeFillPath:
2972 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
2973 GpSolidFill *solidfill = NULL;
2974 BYTE path = flags & 0xff;
2975 GpBrush *brush;
2977 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
2978 return InvalidParameter;
2980 if (dataSize != sizeof(fill->data.BrushId))
2981 return InvalidParameter;
2983 if (flags & 0x8000)
2985 stat = GdipCreateSolidFill(fill->data.Color, (GpSolidFill **)&solidfill);
2986 if (stat != Ok)
2987 return stat;
2988 brush = (GpBrush *)solidfill;
2990 else
2992 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
2993 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
2994 return InvalidParameter;
2996 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
2999 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3000 GdipDeleteBrush((GpBrush *)solidfill);
3001 return stat;
3003 case EmfPlusRecordTypeFillClosedCurve:
3005 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3006 sizeof(EmfPlusRecordHeader);
3007 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3008 GpSolidFill *solidfill = NULL;
3009 GpFillMode mode;
3010 GpBrush *brush;
3011 UINT size, i;
3013 if (dataSize <= fixed_part_size)
3014 return InvalidParameter;
3016 if (fill->Count == 0)
3017 return InvalidParameter;
3019 if (flags & 0x800) /* P */
3020 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3021 else if (flags & 0x4000) /* C */
3022 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3023 else
3024 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3026 if (dataSize != size)
3027 return InvalidParameter;
3029 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3031 if (flags & 0x8000) /* S */
3033 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3034 if (stat != Ok)
3035 return stat;
3036 brush = (GpBrush *)solidfill;
3038 else
3040 if (fill->BrushId >= EmfPlusObjectTableSize ||
3041 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3042 return InvalidParameter;
3044 brush = real_metafile->objtable[fill->BrushId].u.brush;
3047 if (flags & (0x800 | 0x4000))
3049 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3050 if (points)
3052 if (flags & 0x800) /* P */
3054 for (i = 1; i < fill->Count; i++)
3056 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3057 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3060 else
3062 for (i = 0; i < fill->Count; i++)
3064 points[i].X = fill->PointData.points[i].X;
3065 points[i].Y = fill->PointData.points[i].Y;
3069 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3070 points, fill->Count, fill->Tension, mode);
3071 GdipFree(points);
3073 else
3074 stat = OutOfMemory;
3076 else
3077 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3078 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3080 GdipDeleteBrush((GpBrush *)solidfill);
3081 return stat;
3083 case EmfPlusRecordTypeFillEllipse:
3085 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3086 GpSolidFill *solidfill = NULL;
3087 GpBrush *brush;
3089 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3090 return InvalidParameter;
3091 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3093 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3094 return InvalidParameter;
3096 if (flags & 0x8000)
3098 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3099 if (stat != Ok)
3100 return stat;
3101 brush = (GpBrush *)solidfill;
3103 else
3105 if (fill->BrushId >= EmfPlusObjectTableSize ||
3106 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3107 return InvalidParameter;
3109 brush = real_metafile->objtable[fill->BrushId].u.brush;
3112 if (flags & 0x4000)
3113 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3114 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3115 else
3116 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3117 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3119 GdipDeleteBrush((GpBrush *)solidfill);
3120 return stat;
3122 case EmfPlusRecordTypeFillPie:
3124 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3125 GpSolidFill *solidfill = NULL;
3126 GpBrush *brush;
3128 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3129 return InvalidParameter;
3130 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3132 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3133 return InvalidParameter;
3135 if (flags & 0x8000) /* S */
3137 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3138 if (stat != Ok)
3139 return stat;
3140 brush = (GpBrush *)solidfill;
3142 else
3144 if (fill->BrushId >= EmfPlusObjectTableSize ||
3145 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3146 return InvalidParameter;
3148 brush = real_metafile->objtable[fill->BrushId].u.brush;
3151 if (flags & 0x4000) /* C */
3152 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3153 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3154 fill->StartAngle, fill->SweepAngle);
3155 else
3156 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3157 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3158 fill->StartAngle, fill->SweepAngle);
3160 GdipDeleteBrush((GpBrush *)solidfill);
3161 return stat;
3163 case EmfPlusRecordTypeDrawPath:
3165 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3166 BYTE path = flags & 0xff;
3168 if (dataSize != sizeof(draw->PenId))
3169 return InvalidParameter;
3171 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3172 return InvalidParameter;
3174 if (real_metafile->objtable[path].type != ObjectTypePath ||
3175 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3176 return InvalidParameter;
3178 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3179 real_metafile->objtable[path].u.path);
3181 case EmfPlusRecordTypeDrawArc:
3183 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3184 BYTE pen = flags & 0xff;
3186 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3187 return InvalidParameter;
3189 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3190 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3191 return InvalidParameter;
3193 if (flags & 0x4000) /* C */
3194 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3195 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3196 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3197 else
3198 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3199 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3200 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3202 case EmfPlusRecordTypeDrawEllipse:
3204 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3205 BYTE pen = flags & 0xff;
3207 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3208 return InvalidParameter;
3210 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3211 return InvalidParameter;
3213 if (flags & 0x4000) /* C */
3214 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3215 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3216 draw->RectData.rect.Height);
3217 else
3218 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3219 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3220 draw->RectData.rectF.Height);
3222 case EmfPlusRecordTypeDrawPie:
3224 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3225 BYTE pen = flags & 0xff;
3227 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3228 return InvalidParameter;
3230 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3231 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3232 return InvalidParameter;
3234 if (flags & 0x4000) /* C */
3235 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3236 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3237 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3238 else
3239 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3240 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3241 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3243 case EmfPlusRecordTypeDrawRects:
3245 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3246 BYTE pen = flags & 0xff;
3247 GpRectF *rects = NULL;
3249 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3250 return InvalidParameter;
3252 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3253 return InvalidParameter;
3254 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3256 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3257 return InvalidParameter;
3259 if (flags & 0x4000)
3261 DWORD i;
3263 rects = GdipAlloc(draw->Count * sizeof(*rects));
3264 if (!rects)
3265 return OutOfMemory;
3267 for (i = 0; i < draw->Count; i++)
3269 rects[i].X = draw->RectData.rect[i].X;
3270 rects[i].Y = draw->RectData.rect[i].Y;
3271 rects[i].Width = draw->RectData.rect[i].Width;
3272 rects[i].Height = draw->RectData.rect[i].Height;
3276 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3277 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3278 GdipFree(rects);
3279 return stat;
3281 default:
3282 FIXME("Not implemented for record type %x\n", recordType);
3283 return NotImplemented;
3287 return Ok;
3290 struct enum_metafile_data
3292 EnumerateMetafileProc callback;
3293 void *callback_data;
3294 GpMetafile *metafile;
3297 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3298 int nObj, LPARAM lpData)
3300 BOOL ret;
3301 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3302 const BYTE* pStr;
3304 data->metafile->handle_table = lpHTable;
3305 data->metafile->handle_count = nObj;
3307 /* First check for an EMF+ record. */
3308 if (lpEMFR->iType == EMR_GDICOMMENT)
3310 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3312 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3314 int offset = 4;
3316 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3318 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3320 if (record->DataSize)
3321 pStr = (const BYTE*)(record+1);
3322 else
3323 pStr = NULL;
3325 ret = data->callback(record->Type, record->Flags, record->DataSize,
3326 pStr, data->callback_data);
3328 if (!ret)
3329 return 0;
3331 offset += record->Size;
3334 return 1;
3338 if (lpEMFR->nSize != 8)
3339 pStr = (const BYTE*)lpEMFR->dParm;
3340 else
3341 pStr = NULL;
3343 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3344 pStr, data->callback_data);
3347 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3348 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3349 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3350 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3352 struct enum_metafile_data data;
3353 GpStatus stat;
3354 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3355 GraphicsContainer state;
3356 GpPath *dst_path;
3358 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3359 destPoints, count, srcRect, srcUnit, callback, callbackData,
3360 imageAttributes);
3362 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3363 return InvalidParameter;
3365 if (!metafile->hemf)
3366 return InvalidParameter;
3368 if (metafile->playback_graphics)
3369 return ObjectBusy;
3371 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3372 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3373 debugstr_pointf(&destPoints[2]));
3375 data.callback = callback;
3376 data.callback_data = callbackData;
3377 data.metafile = real_metafile;
3379 real_metafile->playback_graphics = graphics;
3380 real_metafile->playback_dc = NULL;
3381 real_metafile->src_rect = *srcRect;
3383 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3384 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3386 if (stat == Ok)
3387 stat = GdipBeginContainer2(graphics, &state);
3389 if (stat == Ok)
3391 stat = GdipSetPageScale(graphics, 1.0);
3393 if (stat == Ok)
3394 stat = GdipSetPageUnit(graphics, UnitPixel);
3396 if (stat == Ok)
3397 stat = GdipResetWorldTransform(graphics);
3399 if (stat == Ok)
3400 stat = GdipCreateRegion(&real_metafile->base_clip);
3402 if (stat == Ok)
3403 stat = GdipGetClip(graphics, real_metafile->base_clip);
3405 if (stat == Ok)
3406 stat = GdipCreateRegion(&real_metafile->clip);
3408 if (stat == Ok)
3409 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3411 if (stat == Ok)
3413 GpPointF clip_points[4];
3415 clip_points[0] = real_metafile->playback_points[0];
3416 clip_points[1] = real_metafile->playback_points[1];
3417 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3418 - real_metafile->playback_points[0].X;
3419 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3420 - real_metafile->playback_points[0].Y;
3421 clip_points[3] = real_metafile->playback_points[2];
3423 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3425 if (stat == Ok)
3426 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3428 GdipDeletePath(dst_path);
3431 if (stat == Ok)
3432 stat = GdipCreateMatrix(&real_metafile->world_transform);
3434 if (stat == Ok)
3436 real_metafile->page_unit = UnitDisplay;
3437 real_metafile->page_scale = 1.0;
3438 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3441 if (stat == Ok)
3443 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3446 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
3447 metafile->metafile_type == MetafileTypeWmfPlaceable ||
3448 metafile->metafile_type == MetafileTypeWmf))
3449 stat = METAFILE_PlaybackGetDC(real_metafile);
3451 if (stat == Ok)
3452 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
3454 METAFILE_PlaybackReleaseDC(real_metafile);
3456 GdipDeleteMatrix(real_metafile->world_transform);
3457 real_metafile->world_transform = NULL;
3459 GdipDeleteRegion(real_metafile->base_clip);
3460 real_metafile->base_clip = NULL;
3462 GdipDeleteRegion(real_metafile->clip);
3463 real_metafile->clip = NULL;
3465 while (list_head(&real_metafile->containers))
3467 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3468 list_remove(&cont->entry);
3469 GdipDeleteRegion(cont->clip);
3470 heap_free(cont);
3473 GdipEndContainer(graphics, state);
3476 real_metafile->playback_graphics = NULL;
3478 return stat;
3481 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3482 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3483 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3485 GpPointF points[3];
3487 if (!graphics || !metafile || !dest) return InvalidParameter;
3489 points[0].X = points[2].X = dest->X;
3490 points[0].Y = points[1].Y = dest->Y;
3491 points[1].X = dest->X + dest->Width;
3492 points[2].Y = dest->Y + dest->Height;
3494 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3495 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3498 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3499 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3500 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3502 GpRectF destf;
3504 if (!graphics || !metafile || !dest) return InvalidParameter;
3506 destf.X = dest->X;
3507 destf.Y = dest->Y;
3508 destf.Width = dest->Width;
3509 destf.Height = dest->Height;
3511 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3514 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3515 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3516 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3518 GpRectF destf;
3520 if (!graphics || !metafile || !dest) return InvalidParameter;
3522 destf.X = dest->X;
3523 destf.Y = dest->Y;
3524 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
3525 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
3527 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3530 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3531 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3532 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3534 GpPointF ptf;
3536 if (!graphics || !metafile || !dest) return InvalidParameter;
3538 ptf.X = dest->X;
3539 ptf.Y = dest->Y;
3541 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3544 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3545 MetafileHeader * header)
3547 GpStatus status;
3549 TRACE("(%p, %p)\n", metafile, header);
3551 if(!metafile || !header)
3552 return InvalidParameter;
3554 if (metafile->hemf)
3556 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3557 if (status != Ok) return status;
3559 else
3561 memset(header, 0, sizeof(*header));
3562 header->Version = VERSION_MAGIC2;
3565 header->Type = metafile->metafile_type;
3566 header->DpiX = metafile->image.xres;
3567 header->DpiY = metafile->image.yres;
3568 header->Width = gdip_round(metafile->bounds.Width);
3569 header->Height = gdip_round(metafile->bounds.Height);
3571 return Ok;
3574 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3575 int nObj, LPARAM lpData)
3577 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3579 if (lpEMFR->iType == EMR_GDICOMMENT)
3581 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3583 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3585 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3587 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3588 header->Type == EmfPlusRecordTypeHeader)
3590 memcpy(dst_header, header, sizeof(*dst_header));
3594 else if (lpEMFR->iType == EMR_HEADER)
3595 return TRUE;
3597 return FALSE;
3600 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3601 MetafileHeader *header)
3603 ENHMETAHEADER3 emfheader;
3604 EmfPlusHeader emfplusheader;
3605 MetafileType metafile_type;
3607 TRACE("(%p,%p)\n", hemf, header);
3609 if(!hemf || !header)
3610 return InvalidParameter;
3612 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3613 return GenericError;
3615 emfplusheader.Header.Type = 0;
3617 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3619 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3621 if ((emfplusheader.Header.Flags & 1) == 1)
3622 metafile_type = MetafileTypeEmfPlusDual;
3623 else
3624 metafile_type = MetafileTypeEmfPlusOnly;
3626 else
3627 metafile_type = MetafileTypeEmf;
3629 header->Type = metafile_type;
3630 header->Size = emfheader.nBytes;
3631 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3632 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3633 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3634 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3635 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3636 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3637 header->u.EmfHeader = emfheader;
3639 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3641 header->Version = emfplusheader.Version;
3642 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3643 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3644 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3645 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3647 else
3649 header->Version = emfheader.nVersion;
3650 header->EmfPlusFlags = 0;
3651 header->EmfPlusHeaderSize = 0;
3652 header->LogicalDpiX = 0;
3653 header->LogicalDpiY = 0;
3656 return Ok;
3659 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3660 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3662 GpStatus status;
3663 GpMetafile *metafile;
3665 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3667 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3668 if (status == Ok)
3670 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3671 GdipDisposeImage(&metafile->image);
3673 return status;
3676 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3677 MetafileHeader *header)
3679 GpStatus status;
3680 GpMetafile *metafile;
3682 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3684 if (!filename || !header)
3685 return InvalidParameter;
3687 status = GdipCreateMetafileFromFile(filename, &metafile);
3688 if (status == Ok)
3690 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3691 GdipDisposeImage(&metafile->image);
3693 return status;
3696 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3697 MetafileHeader *header)
3699 GpStatus status;
3700 GpMetafile *metafile;
3702 TRACE("(%p,%p)\n", stream, header);
3704 if (!stream || !header)
3705 return InvalidParameter;
3707 status = GdipCreateMetafileFromStream(stream, &metafile);
3708 if (status == Ok)
3710 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3711 GdipDisposeImage(&metafile->image);
3713 return status;
3716 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3717 GpMetafile **metafile)
3719 GpStatus stat;
3720 MetafileHeader header;
3722 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3724 if(!hemf || !metafile)
3725 return InvalidParameter;
3727 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3728 if (stat != Ok)
3729 return stat;
3731 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3732 if (!*metafile)
3733 return OutOfMemory;
3735 (*metafile)->image.type = ImageTypeMetafile;
3736 (*metafile)->image.format = ImageFormatEMF;
3737 (*metafile)->image.frame_count = 1;
3738 (*metafile)->image.xres = header.DpiX;
3739 (*metafile)->image.yres = header.DpiY;
3740 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3741 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3742 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3743 / 2540.0 * header.DpiX;
3744 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3745 / 2540.0 * header.DpiY;
3746 (*metafile)->unit = UnitPixel;
3747 (*metafile)->metafile_type = header.Type;
3748 (*metafile)->hemf = hemf;
3749 (*metafile)->preserve_hemf = !delete;
3750 list_init(&(*metafile)->containers);
3752 TRACE("<-- %p\n", *metafile);
3754 return Ok;
3757 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3758 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3760 UINT read;
3761 BYTE *copy;
3762 HENHMETAFILE hemf;
3763 GpStatus retval = Ok;
3765 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3767 if(!hwmf || !metafile)
3768 return InvalidParameter;
3770 *metafile = NULL;
3771 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3772 if(!read)
3773 return GenericError;
3774 copy = heap_alloc_zero(read);
3775 GetMetaFileBitsEx(hwmf, read, copy);
3777 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3778 heap_free(copy);
3780 /* FIXME: We should store and use hwmf instead of converting to hemf */
3781 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
3783 if (retval == Ok)
3785 if (placeable)
3787 (*metafile)->image.xres = (REAL)placeable->Inch;
3788 (*metafile)->image.yres = (REAL)placeable->Inch;
3789 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
3790 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
3791 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
3792 placeable->BoundingBox.Left);
3793 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
3794 placeable->BoundingBox.Top);
3795 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
3797 else
3798 (*metafile)->metafile_type = MetafileTypeWmf;
3799 (*metafile)->image.format = ImageFormatWMF;
3801 if (delete) DeleteMetaFile(hwmf);
3803 else
3804 DeleteEnhMetaFile(hemf);
3805 return retval;
3808 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
3809 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3811 HMETAFILE hmf;
3812 HENHMETAFILE emf;
3814 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
3816 hmf = GetMetaFileW(file);
3817 if(hmf)
3818 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
3820 emf = GetEnhMetaFileW(file);
3821 if(emf)
3822 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
3824 return GenericError;
3827 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
3828 GpMetafile **metafile)
3830 GpStatus status;
3831 IStream *stream;
3833 TRACE("(%p, %p)\n", file, metafile);
3835 if (!file || !metafile) return InvalidParameter;
3837 *metafile = NULL;
3839 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
3840 if (status == Ok)
3842 status = GdipCreateMetafileFromStream(stream, metafile);
3843 IStream_Release(stream);
3845 return status;
3848 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
3849 GpMetafile **metafile)
3851 GpStatus stat;
3853 TRACE("%p %p\n", stream, metafile);
3855 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
3856 if (stat != Ok) return stat;
3858 if ((*metafile)->image.type != ImageTypeMetafile)
3860 GdipDisposeImage(&(*metafile)->image);
3861 *metafile = NULL;
3862 return GenericError;
3865 return Ok;
3868 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3869 UINT limitDpi)
3871 TRACE("(%p,%u)\n", metafile, limitDpi);
3873 return Ok;
3876 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
3877 GpMetafile* metafile, BOOL* succ, EmfType emfType,
3878 const WCHAR* description, GpMetafile** out_metafile)
3880 static int calls;
3882 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
3883 debugstr_w(description), out_metafile);
3885 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
3886 return InvalidParameter;
3888 if(succ)
3889 *succ = FALSE;
3890 *out_metafile = NULL;
3892 if(!(calls++))
3893 FIXME("not implemented\n");
3895 return NotImplemented;
3898 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
3899 LPBYTE pData16, INT iMapMode, INT eFlags)
3901 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
3902 return NotImplemented;
3905 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
3906 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
3907 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
3908 GpMetafile **metafile)
3910 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3911 frameUnit, debugstr_w(desc), metafile);
3913 return NotImplemented;
3916 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
3917 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
3918 GDIPCONST WCHAR *desc, GpMetafile **metafile)
3920 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3921 frameUnit, debugstr_w(desc), metafile);
3923 return NotImplemented;
3926 /*****************************************************************************
3927 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
3930 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
3931 GpMetafile* metafile, BOOL* conversionSuccess,
3932 const WCHAR* filename, EmfType emfType,
3933 const WCHAR* description, GpMetafile** out_metafile)
3935 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
3936 return NotImplemented;
3939 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
3941 LARGE_INTEGER zero;
3942 STATSTG statstg;
3943 GpStatus stat;
3944 HRESULT hr;
3946 *size = 0;
3948 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
3949 if (FAILED(hr)) return hresult_to_status(hr);
3951 stat = encode_image_png(image, *stream, NULL);
3952 if (stat != Ok)
3954 IStream_Release(*stream);
3955 return stat;
3958 hr = IStream_Stat(*stream, &statstg, 1);
3959 if (FAILED(hr))
3961 IStream_Release(*stream);
3962 return hresult_to_status(hr);
3964 *size = statstg.cbSize.u.LowPart;
3966 zero.QuadPart = 0;
3967 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
3968 if (FAILED(hr))
3970 IStream_Release(*stream);
3971 return hresult_to_status(hr);
3974 return Ok;
3977 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
3979 HRESULT hr;
3981 record->Width = 0;
3982 record->Height = 0;
3983 record->Stride = 0;
3984 record->PixelFormat = 0;
3985 record->Type = BitmapDataTypeCompressed;
3987 hr = IStream_Read(stream, record->BitmapData, size, NULL);
3988 if (FAILED(hr)) return hresult_to_status(hr);
3989 return Ok;
3992 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
3994 EmfPlusObject *object_record;
3995 GpStatus stat;
3996 DWORD size;
3998 *id = -1;
4000 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4001 return Ok;
4003 if (image->type == ImageTypeBitmap)
4005 IStream *stream;
4006 DWORD aligned_size;
4008 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4009 if (stat != Ok) return stat;
4010 aligned_size = (size + 3) & ~3;
4012 stat = METAFILE_AllocateRecord(metafile,
4013 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4014 (void**)&object_record);
4015 if (stat != Ok)
4017 IStream_Release(stream);
4018 return stat;
4020 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4022 *id = METAFILE_AddObjectId(metafile);
4023 object_record->Header.Type = EmfPlusRecordTypeObject;
4024 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4025 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4026 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4028 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4029 IStream_Release(stream);
4030 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4031 return stat;
4033 else if (image->type == ImageTypeMetafile)
4035 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4036 EmfPlusMetafile *metafile_record;
4038 if (!hemf) return InvalidParameter;
4040 size = GetEnhMetaFileBits(hemf, 0, NULL);
4041 if (!size) return GenericError;
4043 stat = METAFILE_AllocateRecord(metafile,
4044 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4045 (void**)&object_record);
4046 if (stat != Ok) return stat;
4048 *id = METAFILE_AddObjectId(metafile);
4049 object_record->Header.Type = EmfPlusRecordTypeObject;
4050 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4051 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4052 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4053 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4054 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4055 metafile_record->MetafileDataSize = size;
4056 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4058 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4059 return GenericError;
4061 return Ok;
4063 else
4065 FIXME("not supported image type (%d)\n", image->type);
4066 return NotImplemented;
4070 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4072 EmfPlusObject *object_record;
4073 EmfPlusImageAttributes *attrs_record;
4074 GpStatus stat;
4076 *id = -1;
4078 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4079 return Ok;
4081 if (!attrs)
4082 return Ok;
4084 stat = METAFILE_AllocateRecord(metafile,
4085 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4086 (void**)&object_record);
4087 if (stat != Ok) return stat;
4089 *id = METAFILE_AddObjectId(metafile);
4090 object_record->Header.Type = EmfPlusRecordTypeObject;
4091 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4092 attrs_record = &object_record->ObjectData.image_attributes;
4093 attrs_record->Version = VERSION_MAGIC2;
4094 attrs_record->Reserved1 = 0;
4095 attrs_record->WrapMode = attrs->wrap;
4096 attrs_record->ClampColor = attrs->outside_color;
4097 attrs_record->ObjectClamp = attrs->clamp;
4098 attrs_record->Reserved2 = 0;
4099 return Ok;
4102 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4103 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4104 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4105 DrawImageAbort callback, VOID *callbackData)
4107 EmfPlusDrawImagePoints *draw_image_record;
4108 DWORD image_id, attributes_id;
4109 GpStatus stat;
4111 if (count != 3) return InvalidParameter;
4113 if (metafile->metafile_type == MetafileTypeEmf)
4115 FIXME("MetafileTypeEmf metafiles not supported\n");
4116 return NotImplemented;
4118 else
4119 FIXME("semi-stub\n");
4121 if (!imageAttributes)
4123 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4125 else if (image->type == ImageTypeBitmap)
4127 INT width = ((GpBitmap*)image)->width;
4128 INT height = ((GpBitmap*)image)->height;
4129 GpGraphics *graphics;
4130 GpBitmap *bitmap;
4132 stat = GdipCreateBitmapFromScan0(width, height,
4133 0, PixelFormat32bppARGB, NULL, &bitmap);
4134 if (stat != Ok) return stat;
4136 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4137 if (stat != Ok)
4139 GdipDisposeImage((GpImage*)bitmap);
4140 return stat;
4143 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4144 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4145 GdipDeleteGraphics(graphics);
4146 if (stat != Ok)
4148 GdipDisposeImage((GpImage*)bitmap);
4149 return stat;
4152 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4153 GdipDisposeImage((GpImage*)bitmap);
4155 else
4157 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4158 return NotImplemented;
4160 if (stat != Ok) return stat;
4162 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4163 if (stat != Ok) return stat;
4165 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
4166 if (stat != Ok) return stat;
4167 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
4168 draw_image_record->Header.Flags = image_id;
4169 draw_image_record->ImageAttributesID = attributes_id;
4170 draw_image_record->SrcUnit = UnitPixel;
4171 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
4172 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
4173 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
4174 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
4175 draw_image_record->count = 3;
4176 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4177 METAFILE_WriteRecords(metafile);
4178 return Ok;
4181 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4183 EmfPlusRecordHeader *record;
4184 GpStatus stat;
4186 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4187 return Ok;
4189 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
4190 if (stat != Ok) return stat;
4192 record->Type = prop;
4193 record->Flags = val;
4195 METAFILE_WriteRecords(metafile);
4196 return Ok;
4199 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4201 EmfPlusObject *object_record;
4202 GpStatus stat;
4203 DWORD size;
4205 *id = -1;
4206 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4207 return Ok;
4209 size = write_path_data(path, NULL);
4210 stat = METAFILE_AllocateRecord(metafile,
4211 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4212 (void**)&object_record);
4213 if (stat != Ok) return stat;
4215 *id = METAFILE_AddObjectId(metafile);
4216 object_record->Header.Type = EmfPlusRecordTypeObject;
4217 object_record->Header.Flags = *id | ObjectTypePath << 8;
4218 write_path_data(path, &object_record->ObjectData.path);
4219 return Ok;
4222 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4224 DWORD i, data_flags, pen_data_size, brush_size;
4225 EmfPlusObject *object_record;
4226 EmfPlusPenData *pen_data;
4227 GpStatus stat;
4228 BOOL result;
4230 *id = -1;
4231 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4232 return Ok;
4234 data_flags = 0;
4235 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4237 GdipIsMatrixIdentity(&pen->transform, &result);
4238 if (!result)
4240 data_flags |= PenDataTransform;
4241 pen_data_size += sizeof(EmfPlusTransformMatrix);
4243 if (pen->startcap != LineCapFlat)
4245 data_flags |= PenDataStartCap;
4246 pen_data_size += sizeof(DWORD);
4248 if (pen->endcap != LineCapFlat)
4250 data_flags |= PenDataEndCap;
4251 pen_data_size += sizeof(DWORD);
4253 if (pen->join != LineJoinMiter)
4255 data_flags |= PenDataJoin;
4256 pen_data_size += sizeof(DWORD);
4258 if (pen->miterlimit != 10.0)
4260 data_flags |= PenDataMiterLimit;
4261 pen_data_size += sizeof(REAL);
4263 if (pen->style != GP_DEFAULT_PENSTYLE)
4265 data_flags |= PenDataLineStyle;
4266 pen_data_size += sizeof(DWORD);
4268 if (pen->dashcap != DashCapFlat)
4270 data_flags |= PenDataDashedLineCap;
4271 pen_data_size += sizeof(DWORD);
4273 data_flags |= PenDataDashedLineOffset;
4274 pen_data_size += sizeof(REAL);
4275 if (pen->numdashes)
4277 data_flags |= PenDataDashedLine;
4278 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4280 if (pen->align != PenAlignmentCenter)
4282 data_flags |= PenDataNonCenter;
4283 pen_data_size += sizeof(DWORD);
4285 /* TODO: Add support for PenDataCompoundLine */
4286 if (pen->customstart)
4288 FIXME("ignoring custom start cup\n");
4290 if (pen->customend)
4292 FIXME("ignoring custom end cup\n");
4295 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4296 if (stat != Ok) return stat;
4298 stat = METAFILE_AllocateRecord(metafile,
4299 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4300 (void**)&object_record);
4301 if (stat != Ok) return stat;
4303 *id = METAFILE_AddObjectId(metafile);
4304 object_record->Header.Type = EmfPlusRecordTypeObject;
4305 object_record->Header.Flags = *id | ObjectTypePen << 8;
4306 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4307 object_record->ObjectData.pen.Type = 0;
4309 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4310 pen_data->PenDataFlags = data_flags;
4311 pen_data->PenUnit = pen->unit;
4312 pen_data->PenWidth = pen->width;
4314 i = 0;
4315 if (data_flags & PenDataTransform)
4317 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4318 memcpy(m, &pen->transform, sizeof(*m));
4319 i += sizeof(EmfPlusTransformMatrix);
4321 if (data_flags & PenDataStartCap)
4323 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4324 i += sizeof(DWORD);
4326 if (data_flags & PenDataEndCap)
4328 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4329 i += sizeof(DWORD);
4331 if (data_flags & PenDataJoin)
4333 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4334 i += sizeof(DWORD);
4336 if (data_flags & PenDataMiterLimit)
4338 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4339 i += sizeof(REAL);
4341 if (data_flags & PenDataLineStyle)
4343 switch (pen->style & PS_STYLE_MASK)
4345 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4346 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4347 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4348 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4349 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4350 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4352 i += sizeof(DWORD);
4354 if (data_flags & PenDataDashedLineCap)
4356 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4357 i += sizeof(DWORD);
4359 if (data_flags & PenDataDashedLineOffset)
4361 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4362 i += sizeof(REAL);
4364 if (data_flags & PenDataDashedLine)
4366 int j;
4368 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4369 i += sizeof(DWORD);
4371 for (j=0; j<pen->numdashes; j++)
4373 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4374 i += sizeof(REAL);
4377 if (data_flags & PenDataNonCenter)
4379 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4380 i += sizeof(DWORD);
4383 METAFILE_FillBrushData(pen->brush,
4384 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4385 return Ok;
4388 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4390 EmfPlusDrawPath *draw_path_record;
4391 DWORD path_id;
4392 DWORD pen_id;
4393 GpStatus stat;
4395 if (metafile->metafile_type == MetafileTypeEmf)
4397 FIXME("stub!\n");
4398 return NotImplemented;
4401 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4402 if (stat != Ok) return stat;
4404 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4405 if (stat != Ok) return stat;
4407 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4408 if (stat != Ok) return stat;
4409 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4410 draw_path_record->Header.Flags = path_id;
4411 draw_path_record->PenId = pen_id;
4413 METAFILE_WriteRecords(metafile);
4414 return Ok;
4417 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4419 EmfPlusFillPath *fill_path_record;
4420 DWORD brush_id = -1, path_id;
4421 BOOL inline_color;
4422 GpStatus stat;
4424 if (metafile->metafile_type == MetafileTypeEmf)
4426 FIXME("stub!\n");
4427 return NotImplemented;
4430 inline_color = brush->bt == BrushTypeSolidColor;
4431 if (!inline_color)
4433 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4434 if (stat != Ok) return stat;
4437 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4438 if (stat != Ok) return stat;
4440 stat = METAFILE_AllocateRecord(metafile,
4441 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4442 if (stat != Ok) return stat;
4443 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4444 if (inline_color)
4446 fill_path_record->Header.Flags = 0x8000 | path_id;
4447 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4449 else
4451 fill_path_record->Header.Flags = path_id;
4452 fill_path_record->data.BrushId = brush_id;
4455 METAFILE_WriteRecords(metafile);
4456 return Ok;