gphoto2.ds: Set supported groups.
[wine.git] / dlls / gdiplus / metafile.c
blob77673a7ea3f2082216e7dc553422894718dca795
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 EmfPlusRectF
261 float X;
262 float Y;
263 float Width;
264 float Height;
265 } EmfPlusRectF;
267 typedef struct EmfPlusLinearGradientBrushData
269 DWORD BrushDataFlags;
270 INT WrapMode;
271 EmfPlusRectF RectF;
272 EmfPlusARGB StartColor;
273 EmfPlusARGB EndColor;
274 DWORD Reserved1;
275 DWORD Reserved2;
276 BYTE OptionalData[1];
277 } EmfPlusLinearGradientBrushData;
279 typedef struct EmfPlusBrush
281 DWORD Version;
282 DWORD Type;
283 union {
284 EmfPlusSolidBrushData solid;
285 EmfPlusHatchBrushData hatch;
286 EmfPlusTextureBrushData texture;
287 EmfPlusLinearGradientBrushData lineargradient;
288 } BrushData;
289 } EmfPlusBrush;
291 typedef struct EmfPlusPen
293 DWORD Version;
294 DWORD Type;
295 /* EmfPlusPenData */
296 /* EmfPlusBrush */
297 BYTE data[1];
298 } EmfPlusPen;
300 typedef struct EmfPlusPath
302 DWORD Version;
303 DWORD PathPointCount;
304 DWORD PathPointFlags;
305 /* PathPoints[] */
306 /* PathPointTypes[] */
307 /* AlignmentPadding */
308 BYTE data[1];
309 } EmfPlusPath;
311 typedef struct EmfPlusRegionNodePath
313 DWORD RegionNodePathLength;
314 EmfPlusPath RegionNodePath;
315 } EmfPlusRegionNodePath;
317 typedef struct EmfPlusRegion
319 DWORD Version;
320 DWORD RegionNodeCount;
321 BYTE RegionNode[1];
322 } EmfPlusRegion;
324 typedef struct EmfPlusPalette
326 DWORD PaletteStyleFlags;
327 DWORD PaletteCount;
328 BYTE PaletteEntries[1];
329 } EmfPlusPalette;
331 typedef enum
333 BitmapDataTypePixel,
334 BitmapDataTypeCompressed,
335 } BitmapDataType;
337 typedef struct EmfPlusBitmap
339 DWORD Width;
340 DWORD Height;
341 DWORD Stride;
342 DWORD PixelFormat;
343 DWORD Type;
344 BYTE BitmapData[1];
345 } EmfPlusBitmap;
347 typedef struct EmfPlusMetafile
349 DWORD Type;
350 DWORD MetafileDataSize;
351 BYTE MetafileData[1];
352 } EmfPlusMetafile;
354 typedef enum ImageDataType
356 ImageDataTypeUnknown,
357 ImageDataTypeBitmap,
358 ImageDataTypeMetafile,
359 } ImageDataType;
361 typedef struct EmfPlusImage
363 DWORD Version;
364 ImageDataType Type;
365 union
367 EmfPlusBitmap bitmap;
368 EmfPlusMetafile metafile;
369 } ImageData;
370 } EmfPlusImage;
372 typedef struct EmfPlusImageAttributes
374 DWORD Version;
375 DWORD Reserved1;
376 DWORD WrapMode;
377 EmfPlusARGB ClampColor;
378 DWORD ObjectClamp;
379 DWORD Reserved2;
380 } EmfPlusImageAttributes;
382 typedef struct EmfPlusObject
384 EmfPlusRecordHeader Header;
385 union
387 EmfPlusBrush brush;
388 EmfPlusPen pen;
389 EmfPlusPath path;
390 EmfPlusRegion region;
391 EmfPlusImage image;
392 EmfPlusImageAttributes image_attributes;
393 } ObjectData;
394 } EmfPlusObject;
396 typedef struct EmfPlusPointR7
398 BYTE X;
399 BYTE Y;
400 } EmfPlusPointR7;
402 typedef struct EmfPlusPoint
404 short X;
405 short Y;
406 } EmfPlusPoint;
408 typedef struct EmfPlusPointF
410 float X;
411 float Y;
412 } EmfPlusPointF;
414 typedef struct EmfPlusDrawImage
416 EmfPlusRecordHeader Header;
417 DWORD ImageAttributesID;
418 DWORD SrcUnit;
419 EmfPlusRectF SrcRect;
420 union
422 EmfPlusRect rect;
423 EmfPlusRectF rectF;
424 } RectData;
425 } EmfPlusDrawImage;
427 typedef struct EmfPlusDrawImagePoints
429 EmfPlusRecordHeader Header;
430 DWORD ImageAttributesID;
431 DWORD SrcUnit;
432 EmfPlusRectF SrcRect;
433 DWORD count;
434 union
436 EmfPlusPointR7 pointsR[3];
437 EmfPlusPoint points[3];
438 EmfPlusPointF pointsF[3];
439 } PointData;
440 } EmfPlusDrawImagePoints;
442 typedef struct EmfPlusDrawPath
444 EmfPlusRecordHeader Header;
445 DWORD PenId;
446 } EmfPlusDrawPath;
448 typedef struct EmfPlusDrawArc
450 EmfPlusRecordHeader Header;
451 float StartAngle;
452 float SweepAngle;
453 union
455 EmfPlusRect rect;
456 EmfPlusRectF rectF;
457 } RectData;
458 } EmfPlusDrawArc;
460 typedef struct EmfPlusDrawEllipse
462 EmfPlusRecordHeader Header;
463 union
465 EmfPlusRect rect;
466 EmfPlusRectF rectF;
467 } RectData;
468 } EmfPlusDrawEllipse;
470 typedef struct EmfPlusDrawPie
472 EmfPlusRecordHeader Header;
473 float StartAngle;
474 float SweepAngle;
475 union
477 EmfPlusRect rect;
478 EmfPlusRectF rectF;
479 } RectData;
480 } EmfPlusDrawPie;
482 typedef struct EmfPlusDrawRects
484 EmfPlusRecordHeader Header;
485 DWORD Count;
486 union
488 EmfPlusRect rect[1];
489 EmfPlusRectF rectF[1];
490 } RectData;
491 } EmfPlusDrawRects;
493 typedef struct EmfPlusFillPath
495 EmfPlusRecordHeader Header;
496 union
498 DWORD BrushId;
499 EmfPlusARGB Color;
500 } data;
501 } EmfPlusFillPath;
503 typedef struct EmfPlusFillClosedCurve
505 EmfPlusRecordHeader Header;
506 DWORD BrushId;
507 float Tension;
508 DWORD Count;
509 union
511 EmfPlusPointR7 pointsR[1];
512 EmfPlusPoint points[1];
513 EmfPlusPointF pointsF[1];
514 } PointData;
515 } EmfPlusFillClosedCurve;
517 typedef struct EmfPlusFillEllipse
519 EmfPlusRecordHeader Header;
520 DWORD BrushId;
521 union
523 EmfPlusRect rect;
524 EmfPlusRectF rectF;
525 } RectData;
526 } EmfPlusFillEllipse;
528 typedef struct EmfPlusFillPie
530 EmfPlusRecordHeader Header;
531 DWORD BrushId;
532 float StartAngle;
533 float SweepAngle;
534 union
536 EmfPlusRect rect;
537 EmfPlusRectF rectF;
538 } RectData;
539 } EmfPlusFillPie;
541 typedef struct EmfPlusFont
543 DWORD Version;
544 float EmSize;
545 DWORD SizeUnit;
546 DWORD FontStyleFlags;
547 DWORD Reserved;
548 DWORD Length;
549 WCHAR FamilyName[1];
550 } EmfPlusFont;
552 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
554 struct emfplus_object *object = &metafile->objtable[id];
556 switch (object->type)
558 case ObjectTypeInvalid:
559 break;
560 case ObjectTypeBrush:
561 GdipDeleteBrush(object->u.brush);
562 break;
563 case ObjectTypePen:
564 GdipDeletePen(object->u.pen);
565 break;
566 case ObjectTypePath:
567 GdipDeletePath(object->u.path);
568 break;
569 case ObjectTypeRegion:
570 GdipDeleteRegion(object->u.region);
571 break;
572 case ObjectTypeImage:
573 GdipDisposeImage(object->u.image);
574 break;
575 case ObjectTypeFont:
576 GdipDeleteFont(object->u.font);
577 break;
578 case ObjectTypeImageAttributes:
579 GdipDisposeImageAttributes(object->u.image_attributes);
580 break;
581 default:
582 FIXME("not implemented for object type %u.\n", object->type);
583 return;
586 object->type = ObjectTypeInvalid;
587 object->u.object = NULL;
590 void METAFILE_Free(GpMetafile *metafile)
592 unsigned int i;
594 heap_free(metafile->comment_data);
595 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
596 if (!metafile->preserve_hemf)
597 DeleteEnhMetaFile(metafile->hemf);
598 if (metafile->record_graphics)
600 WARN("metafile closed while recording\n");
601 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
602 metafile->record_graphics->image = NULL;
603 metafile->record_graphics->busy = TRUE;
606 if (metafile->record_stream)
607 IStream_Release(metafile->record_stream);
609 for (i = 0; i < sizeof(metafile->objtable)/sizeof(metafile->objtable[0]); i++)
610 metafile_free_object_table_entry(metafile, i);
613 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
615 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
618 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
620 DWORD size_needed;
621 EmfPlusRecordHeader *record;
623 if (!metafile->comment_data_size)
625 DWORD data_size = max(256, size * 2 + 4);
626 metafile->comment_data = heap_alloc_zero(data_size);
628 if (!metafile->comment_data)
629 return OutOfMemory;
631 memcpy(metafile->comment_data, "EMF+", 4);
633 metafile->comment_data_size = data_size;
634 metafile->comment_data_length = 4;
637 size_needed = size + metafile->comment_data_length;
639 if (size_needed > metafile->comment_data_size)
641 DWORD data_size = size_needed * 2;
642 BYTE *new_data = heap_alloc_zero(data_size);
644 if (!new_data)
645 return OutOfMemory;
647 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
649 metafile->comment_data_size = data_size;
650 heap_free(metafile->comment_data);
651 metafile->comment_data = new_data;
654 *result = metafile->comment_data + metafile->comment_data_length;
655 metafile->comment_data_length += size;
657 record = (EmfPlusRecordHeader*)*result;
658 record->Size = size;
659 record->DataSize = size - sizeof(EmfPlusRecordHeader);
661 return Ok;
664 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
666 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
667 metafile->comment_data_length -= record->Size;
670 static void METAFILE_WriteRecords(GpMetafile *metafile)
672 if (metafile->comment_data_length > 4)
674 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
675 metafile->comment_data_length = 4;
679 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
681 GpStatus stat;
683 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
685 EmfPlusHeader *header;
687 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
688 if (stat != Ok)
689 return stat;
691 header->Header.Type = EmfPlusRecordTypeHeader;
693 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
694 header->Header.Flags = 1;
695 else
696 header->Header.Flags = 0;
698 header->Version = VERSION_MAGIC2;
700 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
701 header->EmfPlusFlags = 1;
702 else
703 header->EmfPlusFlags = 0;
705 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
706 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
708 METAFILE_WriteRecords(metafile);
711 return Ok;
714 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
716 GpStatus stat;
718 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
720 EmfPlusRecordHeader *record;
722 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
723 if (stat != Ok)
724 return stat;
726 record->Type = EmfPlusRecordTypeEndOfFile;
727 record->Flags = 0;
729 METAFILE_WriteRecords(metafile);
732 return Ok;
735 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
736 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
738 HDC record_dc;
739 REAL dpix, dpiy;
740 REAL framerect_factor_x, framerect_factor_y;
741 RECT rc, *lprc;
742 GpStatus stat;
744 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
746 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
747 return InvalidParameter;
749 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
750 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
752 if (frameRect)
754 switch (frameUnit)
756 case MetafileFrameUnitPixel:
757 framerect_factor_x = 2540.0 / dpix;
758 framerect_factor_y = 2540.0 / dpiy;
759 break;
760 case MetafileFrameUnitPoint:
761 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
762 break;
763 case MetafileFrameUnitInch:
764 framerect_factor_x = framerect_factor_y = 2540.0;
765 break;
766 case MetafileFrameUnitDocument:
767 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
768 break;
769 case MetafileFrameUnitMillimeter:
770 framerect_factor_x = framerect_factor_y = 100.0;
771 break;
772 case MetafileFrameUnitGdi:
773 framerect_factor_x = framerect_factor_y = 1.0;
774 break;
775 default:
776 return InvalidParameter;
779 rc.left = framerect_factor_x * frameRect->X;
780 rc.top = framerect_factor_y * frameRect->Y;
781 rc.right = rc.left + framerect_factor_x * frameRect->Width;
782 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
784 lprc = &rc;
786 else
787 lprc = NULL;
789 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
791 if (!record_dc)
792 return GenericError;
794 *metafile = heap_alloc_zero(sizeof(GpMetafile));
795 if(!*metafile)
797 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
798 return OutOfMemory;
801 (*metafile)->image.type = ImageTypeMetafile;
802 (*metafile)->image.flags = ImageFlagsNone;
803 (*metafile)->image.palette = NULL;
804 (*metafile)->image.xres = dpix;
805 (*metafile)->image.yres = dpiy;
806 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
807 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
808 (*metafile)->unit = UnitPixel;
809 (*metafile)->metafile_type = type;
810 (*metafile)->record_dc = record_dc;
811 (*metafile)->comment_data = NULL;
812 (*metafile)->comment_data_size = 0;
813 (*metafile)->comment_data_length = 0;
814 (*metafile)->hemf = NULL;
815 list_init(&(*metafile)->containers);
817 if (!frameRect)
819 (*metafile)->auto_frame = TRUE;
820 (*metafile)->auto_frame_min.X = 0;
821 (*metafile)->auto_frame_min.Y = 0;
822 (*metafile)->auto_frame_max.X = -1;
823 (*metafile)->auto_frame_max.Y = -1;
826 stat = METAFILE_WriteHeader(*metafile, hdc);
828 if (stat != Ok)
830 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
831 heap_free(*metafile);
832 *metafile = NULL;
833 return OutOfMemory;
836 return stat;
839 /*****************************************************************************
840 * GdipRecordMetafileI [GDIPLUS.@]
842 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
843 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
845 GpRectF frameRectF, *pFrameRectF;
847 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
849 if (frameRect)
851 frameRectF.X = frameRect->X;
852 frameRectF.Y = frameRect->Y;
853 frameRectF.Width = frameRect->Width;
854 frameRectF.Height = frameRect->Height;
855 pFrameRectF = &frameRectF;
857 else
858 pFrameRectF = NULL;
860 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
863 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
864 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
866 GpStatus stat;
868 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
870 if (!stream)
871 return InvalidParameter;
873 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
875 if (stat == Ok)
877 (*metafile)->record_stream = stream;
878 IStream_AddRef(stream);
881 return stat;
884 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
885 UINT num_points)
887 int i;
889 if (!metafile->auto_frame || !num_points)
890 return;
892 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
893 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
895 for (i=0; i<num_points; i++)
897 if (points[i].X < metafile->auto_frame_min.X)
898 metafile->auto_frame_min.X = points[i].X;
899 if (points[i].X > metafile->auto_frame_max.X)
900 metafile->auto_frame_max.X = points[i].X;
901 if (points[i].Y < metafile->auto_frame_min.Y)
902 metafile->auto_frame_min.Y = points[i].Y;
903 if (points[i].Y > metafile->auto_frame_max.Y)
904 metafile->auto_frame_max.Y = points[i].Y;
908 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
910 GpStatus stat;
912 if (!metafile->record_dc || metafile->record_graphics)
913 return InvalidParameter;
915 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
917 if (stat == Ok)
919 *result = metafile->record_graphics;
920 metafile->record_graphics->xres = 96.0;
921 metafile->record_graphics->yres = 96.0;
924 return stat;
927 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
929 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
931 EmfPlusRecordHeader *record;
932 GpStatus stat;
934 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
935 if (stat != Ok)
936 return stat;
938 record->Type = EmfPlusRecordTypeGetDC;
939 record->Flags = 0;
941 METAFILE_WriteRecords(metafile);
944 *hdc = metafile->record_dc;
946 return Ok;
949 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
951 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
953 EmfPlusClear *record;
954 GpStatus stat;
956 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
957 if (stat != Ok)
958 return stat;
960 record->Header.Type = EmfPlusRecordTypeClear;
961 record->Header.Flags = 0;
962 record->Color = color;
964 METAFILE_WriteRecords(metafile);
967 return Ok;
970 static BOOL is_integer_rect(const GpRectF *rect)
972 SHORT x, y, width, height;
973 x = rect->X;
974 y = rect->Y;
975 width = rect->Width;
976 height = rect->Height;
977 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
978 rect->Width != (REAL)width || rect->Height != (REAL)height)
979 return FALSE;
980 return TRUE;
983 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
985 switch (brush->bt)
987 case BrushTypeSolidColor:
988 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
989 break;
990 case BrushTypeHatchFill:
991 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
992 break;
993 default:
994 FIXME("unsupported brush type: %d\n", brush->bt);
995 return NotImplemented;
998 return Ok;
1001 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
1003 data->Version = VERSION_MAGIC2;
1004 data->Type = brush->bt;
1006 switch (brush->bt)
1008 case BrushTypeSolidColor:
1010 GpSolidFill *solid = (GpSolidFill *)brush;
1011 data->BrushData.solid.SolidColor = solid->color;
1012 break;
1014 case BrushTypeHatchFill:
1016 GpHatch *hatch = (GpHatch *)brush;
1017 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
1018 data->BrushData.hatch.ForeColor = hatch->forecol;
1019 data->BrushData.hatch.BackColor = hatch->backcol;
1020 break;
1022 default:
1023 FIXME("unsupported brush type: %d\n", brush->bt);
1027 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
1029 EmfPlusObject *object_record;
1030 GpStatus stat;
1031 DWORD size;
1033 *id = -1;
1034 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1035 return Ok;
1037 stat = METAFILE_PrepareBrushData(brush, &size);
1038 if (stat != Ok) return stat;
1040 stat = METAFILE_AllocateRecord(metafile,
1041 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1042 if (stat != Ok) return stat;
1044 *id = METAFILE_AddObjectId(metafile);
1045 object_record->Header.Type = EmfPlusRecordTypeObject;
1046 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1047 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1048 return Ok;
1051 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1052 GDIPCONST GpRectF* rects, INT count)
1054 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1056 EmfPlusFillRects *record;
1057 GpStatus stat;
1058 BOOL integer_rects = TRUE;
1059 int i;
1060 DWORD brushid;
1061 int flags = 0;
1063 if (brush->bt == BrushTypeSolidColor)
1065 flags |= 0x8000;
1066 brushid = ((GpSolidFill*)brush)->color;
1068 else
1070 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1071 if (stat != Ok)
1072 return stat;
1075 for (i=0; i<count; i++)
1077 if (!is_integer_rect(&rects[i]))
1079 integer_rects = FALSE;
1080 break;
1084 if (integer_rects)
1085 flags |= 0x4000;
1087 stat = METAFILE_AllocateRecord(metafile,
1088 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1089 (void**)&record);
1090 if (stat != Ok)
1091 return stat;
1093 record->Header.Type = EmfPlusRecordTypeFillRects;
1094 record->Header.Flags = flags;
1095 record->BrushID = brushid;
1096 record->Count = count;
1098 if (integer_rects)
1100 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1101 for (i=0; i<count; i++)
1103 record_rects[i].X = (SHORT)rects[i].X;
1104 record_rects[i].Y = (SHORT)rects[i].Y;
1105 record_rects[i].Width = (SHORT)rects[i].Width;
1106 record_rects[i].Height = (SHORT)rects[i].Height;
1109 else
1110 memcpy(record+1, rects, sizeof(GpRectF) * count);
1112 METAFILE_WriteRecords(metafile);
1115 if (metafile->auto_frame)
1117 GpPointF corners[4];
1118 int i;
1120 for (i=0; i<count; i++)
1122 corners[0].X = rects[i].X;
1123 corners[0].Y = rects[i].Y;
1124 corners[1].X = rects[i].X + rects[i].Width;
1125 corners[1].Y = rects[i].Y;
1126 corners[2].X = rects[i].X;
1127 corners[2].Y = rects[i].Y + rects[i].Height;
1128 corners[3].X = rects[i].X + rects[i].Width;
1129 corners[3].Y = rects[i].Y + rects[i].Height;
1131 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1132 CoordinateSpaceWorld, corners, 4);
1134 METAFILE_AdjustFrame(metafile, corners, 4);
1138 return Ok;
1141 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1143 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1145 EmfPlusSetClipRect *record;
1146 GpStatus stat;
1148 stat = METAFILE_AllocateRecord(metafile,
1149 sizeof(EmfPlusSetClipRect),
1150 (void**)&record);
1151 if (stat != Ok)
1152 return stat;
1154 record->Header.Type = EmfPlusRecordTypeSetClipRect;
1155 record->Header.Flags = (mode & 0xf) << 8;
1156 record->ClipRect.X = x;
1157 record->ClipRect.Y = y;
1158 record->ClipRect.Width = width;
1159 record->ClipRect.Height = height;
1161 METAFILE_WriteRecords(metafile);
1164 return Ok;
1167 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1169 EmfPlusObject *object_record;
1170 DWORD size;
1171 GpStatus stat;
1173 *id = -1;
1174 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1175 return Ok;
1177 size = write_region_data(region, NULL);
1178 stat = METAFILE_AllocateRecord(metafile,
1179 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1180 if (stat != Ok) return stat;
1182 *id = METAFILE_AddObjectId(metafile);
1183 object_record->Header.Type = EmfPlusRecordTypeObject;
1184 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1185 write_region_data(region, &object_record->ObjectData.region);
1186 return Ok;
1189 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1191 EmfPlusRecordHeader *record;
1192 DWORD region_id;
1193 GpStatus stat;
1195 if (metafile->metafile_type == MetafileTypeEmf)
1197 FIXME("stub!\n");
1198 return NotImplemented;
1201 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1202 if (stat != Ok) return stat;
1204 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1205 if (stat != Ok) return stat;
1207 record->Type = EmfPlusRecordTypeSetClipRegion;
1208 record->Flags = region_id | mode << 8;
1210 METAFILE_WriteRecords(metafile);
1211 return Ok;
1214 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1216 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1218 EmfPlusSetPageTransform *record;
1219 GpStatus stat;
1221 stat = METAFILE_AllocateRecord(metafile,
1222 sizeof(EmfPlusSetPageTransform),
1223 (void**)&record);
1224 if (stat != Ok)
1225 return stat;
1227 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1228 record->Header.Flags = unit;
1229 record->PageScale = scale;
1231 METAFILE_WriteRecords(metafile);
1234 return Ok;
1237 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1239 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1241 EmfPlusSetWorldTransform *record;
1242 GpStatus stat;
1244 stat = METAFILE_AllocateRecord(metafile,
1245 sizeof(EmfPlusSetWorldTransform),
1246 (void**)&record);
1247 if (stat != Ok)
1248 return stat;
1250 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1251 record->Header.Flags = 0;
1252 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1254 METAFILE_WriteRecords(metafile);
1257 return Ok;
1260 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1262 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1264 EmfPlusScaleWorldTransform *record;
1265 GpStatus stat;
1267 stat = METAFILE_AllocateRecord(metafile,
1268 sizeof(EmfPlusScaleWorldTransform),
1269 (void**)&record);
1270 if (stat != Ok)
1271 return stat;
1273 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1274 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1275 record->Sx = sx;
1276 record->Sy = sy;
1278 METAFILE_WriteRecords(metafile);
1281 return Ok;
1284 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1286 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1288 EmfPlusMultiplyWorldTransform *record;
1289 GpStatus stat;
1291 stat = METAFILE_AllocateRecord(metafile,
1292 sizeof(EmfPlusMultiplyWorldTransform),
1293 (void**)&record);
1294 if (stat != Ok)
1295 return stat;
1297 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1298 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1299 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1301 METAFILE_WriteRecords(metafile);
1304 return Ok;
1307 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1309 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1311 EmfPlusRotateWorldTransform *record;
1312 GpStatus stat;
1314 stat = METAFILE_AllocateRecord(metafile,
1315 sizeof(EmfPlusRotateWorldTransform),
1316 (void**)&record);
1317 if (stat != Ok)
1318 return stat;
1320 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1321 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1322 record->Angle = angle;
1324 METAFILE_WriteRecords(metafile);
1327 return Ok;
1330 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1332 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1334 EmfPlusTranslateWorldTransform *record;
1335 GpStatus stat;
1337 stat = METAFILE_AllocateRecord(metafile,
1338 sizeof(EmfPlusTranslateWorldTransform),
1339 (void**)&record);
1340 if (stat != Ok)
1341 return stat;
1343 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1344 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1345 record->dx = dx;
1346 record->dy = dy;
1348 METAFILE_WriteRecords(metafile);
1351 return Ok;
1354 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1356 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1358 EmfPlusRecordHeader *record;
1359 GpStatus stat;
1361 stat = METAFILE_AllocateRecord(metafile,
1362 sizeof(EmfPlusRecordHeader),
1363 (void**)&record);
1364 if (stat != Ok)
1365 return stat;
1367 record->Type = EmfPlusRecordTypeResetWorldTransform;
1368 record->Flags = 0;
1370 METAFILE_WriteRecords(metafile);
1373 return Ok;
1376 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1377 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1379 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1381 EmfPlusBeginContainer *record;
1382 GpStatus stat;
1384 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1385 if (stat != Ok)
1386 return stat;
1388 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1389 record->Header.Flags = unit & 0xff;
1390 record->DestRect = *dstrect;
1391 record->SrcRect = *srcrect;
1392 record->StackIndex = StackIndex;
1394 METAFILE_WriteRecords(metafile);
1397 return Ok;
1400 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1402 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1404 EmfPlusContainerRecord *record;
1405 GpStatus stat;
1407 stat = METAFILE_AllocateRecord(metafile,
1408 sizeof(EmfPlusContainerRecord),
1409 (void**)&record);
1410 if (stat != Ok)
1411 return stat;
1413 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1414 record->Header.Flags = 0;
1415 record->StackIndex = StackIndex;
1417 METAFILE_WriteRecords(metafile);
1420 return Ok;
1423 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1425 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1427 EmfPlusContainerRecord *record;
1428 GpStatus stat;
1430 stat = METAFILE_AllocateRecord(metafile,
1431 sizeof(EmfPlusContainerRecord),
1432 (void**)&record);
1433 if (stat != Ok)
1434 return stat;
1436 record->Header.Type = EmfPlusRecordTypeEndContainer;
1437 record->Header.Flags = 0;
1438 record->StackIndex = StackIndex;
1440 METAFILE_WriteRecords(metafile);
1443 return Ok;
1446 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1448 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1450 EmfPlusContainerRecord *record;
1451 GpStatus stat;
1453 stat = METAFILE_AllocateRecord(metafile,
1454 sizeof(EmfPlusContainerRecord),
1455 (void**)&record);
1456 if (stat != Ok)
1457 return stat;
1459 record->Header.Type = EmfPlusRecordTypeSave;
1460 record->Header.Flags = 0;
1461 record->StackIndex = StackIndex;
1463 METAFILE_WriteRecords(metafile);
1466 return Ok;
1469 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1471 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1473 EmfPlusContainerRecord *record;
1474 GpStatus stat;
1476 stat = METAFILE_AllocateRecord(metafile,
1477 sizeof(EmfPlusContainerRecord),
1478 (void**)&record);
1479 if (stat != Ok)
1480 return stat;
1482 record->Header.Type = EmfPlusRecordTypeRestore;
1483 record->Header.Flags = 0;
1484 record->StackIndex = StackIndex;
1486 METAFILE_WriteRecords(metafile);
1489 return Ok;
1492 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1494 if (hdc != metafile->record_dc)
1495 return InvalidParameter;
1497 return Ok;
1500 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1502 GpStatus stat;
1504 stat = METAFILE_WriteEndOfFile(metafile);
1505 metafile->record_graphics = NULL;
1507 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1508 metafile->record_dc = NULL;
1510 heap_free(metafile->comment_data);
1511 metafile->comment_data = NULL;
1512 metafile->comment_data_size = 0;
1514 if (stat == Ok)
1516 MetafileHeader header;
1518 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1519 if (stat == Ok && metafile->auto_frame &&
1520 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1522 RECTL bounds_rc, gdi_bounds_rc;
1523 REAL x_scale = 2540.0 / header.DpiX;
1524 REAL y_scale = 2540.0 / header.DpiY;
1525 BYTE* buffer;
1526 UINT buffer_size;
1528 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1529 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1530 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1531 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1533 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1534 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1536 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1537 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1538 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1539 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1542 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1543 buffer = heap_alloc(buffer_size);
1544 if (buffer)
1546 HENHMETAFILE new_hemf;
1548 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1550 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1552 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1554 if (new_hemf)
1556 DeleteEnhMetaFile(metafile->hemf);
1557 metafile->hemf = new_hemf;
1559 else
1560 stat = OutOfMemory;
1562 heap_free(buffer);
1564 else
1565 stat = OutOfMemory;
1567 if (stat == Ok)
1568 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1570 if (stat == Ok)
1572 metafile->bounds.X = header.X;
1573 metafile->bounds.Y = header.Y;
1574 metafile->bounds.Width = header.Width;
1575 metafile->bounds.Height = header.Height;
1579 if (stat == Ok && metafile->record_stream)
1581 BYTE *buffer;
1582 UINT buffer_size;
1584 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1586 buffer = heap_alloc(buffer_size);
1587 if (buffer)
1589 HRESULT hr;
1591 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1593 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1595 if (FAILED(hr))
1596 stat = hresult_to_status(hr);
1598 heap_free(buffer);
1600 else
1601 stat = OutOfMemory;
1604 if (metafile->record_stream)
1606 IStream_Release(metafile->record_stream);
1607 metafile->record_stream = NULL;
1610 return stat;
1613 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1615 TRACE("(%p,%p)\n", metafile, hEmf);
1617 if (!metafile || !hEmf || !metafile->hemf)
1618 return InvalidParameter;
1620 *hEmf = metafile->hemf;
1621 metafile->hemf = NULL;
1623 return Ok;
1626 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1628 const GpRectF *rect;
1629 const GpPointF *pt;
1631 /* This transforms metafile device space to output points. */
1632 rect = &metafile->src_rect;
1633 pt = metafile->playback_points;
1634 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1635 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1636 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1637 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1638 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1639 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1642 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1644 XFORM combined, final;
1646 METAFILE_GetFinalGdiTransform(metafile, &final);
1648 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1650 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1651 SetWorldTransform(metafile->playback_dc, &combined);
1653 return Ok;
1656 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1658 GpStatus stat = Ok;
1660 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1662 if (stat == Ok)
1664 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1666 metafile->gdiworldtransform = identity;
1667 METAFILE_PlaybackUpdateGdiTransform(metafile);
1670 return stat;
1673 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1675 if (metafile->playback_dc)
1677 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1678 metafile->playback_dc = NULL;
1682 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1684 GpStatus stat;
1685 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1686 if (stat == Ok)
1687 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1688 return stat;
1691 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1693 GpMatrix *real_transform;
1694 GpStatus stat;
1696 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1698 if (stat == Ok)
1700 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1702 if (metafile->page_unit != UnitDisplay)
1703 scale *= metafile->page_scale;
1705 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1707 if (stat == Ok)
1708 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1710 if (stat == Ok)
1711 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1713 GdipDeleteMatrix(real_transform);
1716 return stat;
1719 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1721 metafile_free_object_table_entry(metafile, id);
1722 metafile->objtable[id].type = type;
1723 metafile->objtable[id].u.object = object;
1726 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1728 EmfPlusImage *data = (EmfPlusImage *)record_data;
1729 GpStatus status;
1731 *image = NULL;
1733 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1734 return InvalidParameter;
1735 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1737 switch (data->Type)
1739 case ImageDataTypeBitmap:
1741 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1743 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1744 return InvalidParameter;
1745 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1747 switch (bitmapdata->Type)
1749 case BitmapDataTypePixel:
1751 ColorPalette *palette;
1752 BYTE *scan0;
1754 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1756 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1757 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1759 if (data_size <= palette_size)
1760 return InvalidParameter;
1761 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1763 if (data_size < palette_size)
1764 return InvalidParameter;
1765 data_size -= palette_size;
1767 palette = (ColorPalette *)bitmapdata->BitmapData;
1768 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1770 else
1772 palette = NULL;
1773 scan0 = bitmapdata->BitmapData;
1776 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1777 return InvalidParameter;
1779 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1780 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1781 if (status == Ok && palette)
1783 status = GdipSetImagePalette(*image, palette);
1784 if (status != Ok)
1786 GdipDisposeImage(*image);
1787 *image = NULL;
1790 break;
1792 case BitmapDataTypeCompressed:
1794 IWICImagingFactory *factory;
1795 IWICStream *stream;
1796 HRESULT hr;
1798 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1799 return GenericError;
1801 hr = IWICImagingFactory_CreateStream(factory, &stream);
1802 IWICImagingFactory_Release(factory);
1803 if (hr != S_OK)
1804 return GenericError;
1806 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1807 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1808 else
1809 status = GenericError;
1811 IWICStream_Release(stream);
1812 break;
1814 default:
1815 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1816 return InvalidParameter;
1818 break;
1820 default:
1821 FIXME("image type %d not supported.\n", data->Type);
1822 return NotImplemented;
1825 return status;
1828 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1830 EmfPlusPath *data = (EmfPlusPath *)record_data;
1831 GpStatus status;
1832 BYTE *types;
1833 UINT size;
1834 DWORD i;
1836 *path = NULL;
1838 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1839 return InvalidParameter;
1840 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1842 if (data->PathPointFlags & 0x800) /* R */
1844 FIXME("RLE encoded path data is not supported.\n");
1845 return NotImplemented;
1847 else
1849 if (data->PathPointFlags & 0x4000) /* C */
1850 size = sizeof(EmfPlusPoint);
1851 else
1852 size = sizeof(EmfPlusPointF);
1853 size += sizeof(BYTE); /* EmfPlusPathPointType */
1854 size *= data->PathPointCount;
1857 if (data_size < size)
1858 return InvalidParameter;
1860 status = GdipCreatePath(FillModeAlternate, path);
1861 if (status != Ok)
1862 return status;
1864 (*path)->pathdata.Count = data->PathPointCount;
1865 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1866 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1867 (*path)->datalen = (*path)->pathdata.Count;
1869 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1871 GdipDeletePath(*path);
1872 return OutOfMemory;
1875 if (data->PathPointFlags & 0x4000) /* C */
1877 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1878 for (i = 0; i < data->PathPointCount; i++)
1880 (*path)->pathdata.Points[i].X = points[i].X;
1881 (*path)->pathdata.Points[i].Y = points[i].Y;
1883 types = (BYTE *)(points + i);
1885 else
1887 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1888 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1889 types = (BYTE *)(points + data->PathPointCount);
1892 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1894 return Ok;
1897 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1899 const DWORD *type;
1900 GpStatus status;
1902 type = buffer_read(mbuf, sizeof(*type));
1903 if (!type) return Ok;
1905 node->type = *type;
1907 switch (node->type)
1909 case CombineModeReplace:
1910 case CombineModeIntersect:
1911 case CombineModeUnion:
1912 case CombineModeXor:
1913 case CombineModeExclude:
1914 case CombineModeComplement:
1916 region_element *left, *right;
1918 left = heap_alloc_zero(sizeof(*left));
1919 if (!left)
1920 return OutOfMemory;
1922 right = heap_alloc_zero(sizeof(*right));
1923 if (!right)
1925 heap_free(left);
1926 return OutOfMemory;
1929 status = metafile_read_region_node(mbuf, region, left, count);
1930 if (status == Ok)
1932 status = metafile_read_region_node(mbuf, region, right, count);
1933 if (status == Ok)
1935 node->elementdata.combine.left = left;
1936 node->elementdata.combine.right = right;
1937 region->num_children += 2;
1938 return Ok;
1942 heap_free(left);
1943 heap_free(right);
1944 return status;
1946 case RegionDataRect:
1948 const EmfPlusRectF *rect;
1950 rect = buffer_read(mbuf, sizeof(*rect));
1951 if (!rect)
1952 return InvalidParameter;
1954 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1955 *count += 1;
1956 return Ok;
1958 case RegionDataPath:
1960 const BYTE *path_data;
1961 const UINT *data_size;
1962 GpPath *path;
1964 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1965 if (!data_size)
1966 return InvalidParameter;
1968 path_data = buffer_read(mbuf, *data_size);
1969 if (!path_data)
1970 return InvalidParameter;
1972 status = metafile_deserialize_path(path_data, *data_size, &path);
1973 if (status == Ok)
1975 node->elementdata.path = path;
1976 *count += 1;
1978 return Ok;
1980 case RegionDataEmptyRect:
1981 case RegionDataInfiniteRect:
1982 *count += 1;
1983 return Ok;
1984 default:
1985 FIXME("element type %#x is not supported\n", *type);
1986 break;
1989 return InvalidParameter;
1992 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1994 struct memory_buffer mbuf;
1995 GpStatus status;
1996 UINT count;
1998 *region = NULL;
2000 init_memory_buffer(&mbuf, record_data, data_size);
2002 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2003 return InvalidParameter;
2005 status = GdipCreateRegion(region);
2006 if (status != Ok)
2007 return status;
2009 count = 0;
2010 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2011 if (status == Ok && !count)
2012 status = InvalidParameter;
2014 if (status != Ok)
2016 GdipDeleteRegion(*region);
2017 *region = NULL;
2020 return status;
2023 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2025 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2026 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2027 EmfPlusTransformMatrix *transform = NULL;
2028 DWORD brushflags;
2029 GpStatus status;
2030 UINT offset;
2032 *brush = NULL;
2034 if (data_size < header_size)
2035 return InvalidParameter;
2037 switch (data->Type)
2039 case BrushTypeSolidColor:
2040 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2041 return InvalidParameter;
2043 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2044 break;
2045 case BrushTypeHatchFill:
2046 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2047 return InvalidParameter;
2049 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2050 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2051 break;
2052 case BrushTypeTextureFill:
2054 GpImage *image;
2056 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2057 if (data_size <= offset)
2058 return InvalidParameter;
2060 brushflags = data->BrushData.texture.BrushDataFlags;
2061 if (brushflags & BrushDataTransform)
2063 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2064 return InvalidParameter;
2065 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2066 offset += sizeof(EmfPlusTransformMatrix);
2069 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2070 if (status != Ok)
2071 return status;
2073 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2074 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2075 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2077 GdipDisposeImage(image);
2078 break;
2080 case BrushTypeLinearGradient:
2082 GpLineGradient *gradient = NULL;
2083 GpPointF startpoint, endpoint;
2084 UINT position_count = 0;
2086 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2087 if (data_size <= offset)
2088 return InvalidParameter;
2090 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2091 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2092 return InvalidParameter;
2094 if (brushflags & BrushDataTransform)
2096 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2097 return InvalidParameter;
2098 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2099 offset += sizeof(EmfPlusTransformMatrix);
2102 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2104 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2105 return InvalidParameter;
2106 position_count = *(DWORD *)(record_data + offset);
2107 offset += sizeof(DWORD);
2110 if (brushflags & BrushDataPresetColors)
2112 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2113 return InvalidParameter;
2115 else if (brushflags & BrushDataBlendFactorsH)
2117 if (data_size != offset + position_count * 2 * sizeof(float))
2118 return InvalidParameter;
2121 startpoint.X = data->BrushData.lineargradient.RectF.X;
2122 startpoint.Y = data->BrushData.lineargradient.RectF.Y;
2123 endpoint.X = startpoint.X + data->BrushData.lineargradient.RectF.Width;
2124 endpoint.Y = startpoint.Y + data->BrushData.lineargradient.RectF.Height;
2126 status = GdipCreateLineBrush(&startpoint, &endpoint, data->BrushData.lineargradient.StartColor,
2127 data->BrushData.lineargradient.EndColor, data->BrushData.lineargradient.WrapMode, &gradient);
2128 if (status == Ok)
2130 if (transform)
2131 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2133 if (status == Ok)
2135 if (brushflags & BrushDataPresetColors)
2136 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2137 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2138 else if (brushflags & BrushDataBlendFactorsH)
2139 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2140 (REAL *)(record_data + offset), position_count);
2142 if (brushflags & BrushDataIsGammaCorrected)
2143 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2147 if (status == Ok)
2148 *brush = (GpBrush *)gradient;
2149 else
2150 GdipDeleteBrush((GpBrush *)gradient);
2152 break;
2154 default:
2155 FIXME("brush type %u is not supported.\n", data->Type);
2156 return NotImplemented;
2159 return status;
2162 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2164 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2165 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2167 if (data_size <= offset)
2168 return InvalidParameter;
2170 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2171 if (data_size <= offset)
2172 return InvalidParameter;
2174 if (pendata->PenDataFlags & PenDataTransform)
2175 offset += sizeof(EmfPlusTransformMatrix);
2177 if (pendata->PenDataFlags & PenDataStartCap)
2178 offset += sizeof(DWORD);
2180 if (pendata->PenDataFlags & PenDataEndCap)
2181 offset += sizeof(DWORD);
2183 if (pendata->PenDataFlags & PenDataJoin)
2184 offset += sizeof(DWORD);
2186 if (pendata->PenDataFlags & PenDataMiterLimit)
2187 offset += sizeof(REAL);
2189 if (pendata->PenDataFlags & PenDataLineStyle)
2190 offset += sizeof(DWORD);
2192 if (pendata->PenDataFlags & PenDataDashedLineCap)
2193 offset += sizeof(DWORD);
2195 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2196 offset += sizeof(REAL);
2198 if (pendata->PenDataFlags & PenDataDashedLine)
2200 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2202 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2203 if (data_size <= offset)
2204 return InvalidParameter;
2206 offset += dashedline->DashedLineDataSize * sizeof(float);
2209 if (pendata->PenDataFlags & PenDataNonCenter)
2210 offset += sizeof(DWORD);
2212 if (pendata->PenDataFlags & PenDataCompoundLine)
2214 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2216 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2217 if (data_size <= offset)
2218 return InvalidParameter;
2220 offset += compoundline->CompoundLineDataSize * sizeof(float);
2223 if (pendata->PenDataFlags & PenDataCustomStartCap)
2225 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2227 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2228 if (data_size <= offset)
2229 return InvalidParameter;
2231 offset += startcap->CustomStartCapSize;
2234 if (pendata->PenDataFlags & PenDataCustomEndCap)
2236 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2238 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2239 if (data_size <= offset)
2240 return InvalidParameter;
2242 offset += endcap->CustomEndCapSize;
2245 *ret = offset;
2246 return Ok;
2249 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2251 BYTE type = (flags >> 8) & 0xff;
2252 BYTE id = flags & 0xff;
2253 void *object = NULL;
2254 GpStatus status;
2256 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2257 return InvalidParameter;
2259 switch (type)
2261 case ObjectTypeBrush:
2262 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2263 break;
2264 case ObjectTypePen:
2266 EmfPlusPen *data = (EmfPlusPen *)record_data;
2267 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2268 GpBrush *brush;
2269 DWORD offset;
2270 GpPen *pen;
2272 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2273 if (status != Ok)
2274 return status;
2276 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2277 if (status != Ok)
2278 return status;
2280 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2281 GdipDeleteBrush(brush);
2282 if (status != Ok)
2283 return status;
2285 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2287 if (pendata->PenDataFlags & PenDataTransform)
2289 FIXME("PenDataTransform is not supported.\n");
2290 offset += sizeof(EmfPlusTransformMatrix);
2293 if (pendata->PenDataFlags & PenDataStartCap)
2295 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2296 goto penfailed;
2297 offset += sizeof(DWORD);
2300 if (pendata->PenDataFlags & PenDataEndCap)
2302 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2303 goto penfailed;
2304 offset += sizeof(DWORD);
2307 if (pendata->PenDataFlags & PenDataJoin)
2309 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2310 goto penfailed;
2311 offset += sizeof(DWORD);
2314 if (pendata->PenDataFlags & PenDataMiterLimit)
2316 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2317 goto penfailed;
2318 offset += sizeof(REAL);
2321 if (pendata->PenDataFlags & PenDataLineStyle)
2323 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2324 goto penfailed;
2325 offset += sizeof(DWORD);
2328 if (pendata->PenDataFlags & PenDataDashedLineCap)
2330 FIXME("PenDataDashedLineCap is not supported.\n");
2331 offset += sizeof(DWORD);
2334 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2336 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2337 goto penfailed;
2338 offset += sizeof(REAL);
2341 if (pendata->PenDataFlags & PenDataDashedLine)
2343 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2344 FIXME("PenDataDashedLine is not supported.\n");
2345 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2348 if (pendata->PenDataFlags & PenDataNonCenter)
2350 FIXME("PenDataNonCenter is not supported.\n");
2351 offset += sizeof(DWORD);
2354 if (pendata->PenDataFlags & PenDataCompoundLine)
2356 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2357 FIXME("PenDataCompundLine is not supported.\n");
2358 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2361 if (pendata->PenDataFlags & PenDataCustomStartCap)
2363 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2364 FIXME("PenDataCustomStartCap is not supported.\n");
2365 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2368 if (pendata->PenDataFlags & PenDataCustomEndCap)
2370 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2371 FIXME("PenDataCustomEndCap is not supported.\n");
2372 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2375 object = pen;
2376 break;
2378 penfailed:
2379 GdipDeletePen(pen);
2380 return status;
2382 case ObjectTypePath:
2383 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2384 break;
2385 case ObjectTypeRegion:
2386 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2387 break;
2388 case ObjectTypeImage:
2389 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2390 break;
2391 case ObjectTypeFont:
2393 EmfPlusFont *data = (EmfPlusFont *)record_data;
2394 GpFontFamily *family;
2395 WCHAR *familyname;
2397 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2398 return InvalidParameter;
2399 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2401 if (data_size < data->Length * sizeof(WCHAR))
2402 return InvalidParameter;
2404 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2405 return OutOfMemory;
2407 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2408 familyname[data->Length] = 0;
2410 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2411 GdipFree(familyname);
2412 if (status != Ok)
2413 return InvalidParameter;
2415 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2416 GdipDeleteFontFamily(family);
2417 break;
2419 case ObjectTypeImageAttributes:
2421 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2422 GpImageAttributes *attributes = NULL;
2424 if (data_size != sizeof(*data))
2425 return InvalidParameter;
2427 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2428 return status;
2430 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2431 !!data->ObjectClamp);
2432 if (status == Ok)
2433 object = attributes;
2434 else
2435 GdipDisposeImageAttributes(attributes);
2436 break;
2438 default:
2439 FIXME("not implemented for object type %d.\n", type);
2440 return NotImplemented;
2443 if (status == Ok)
2444 metafile_set_object_table_entry(metafile, id, type, object);
2446 return status;
2449 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2451 GpMatrix world_to_device;
2453 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2455 GdipTransformRegion(region, &world_to_device);
2456 GdipCombineRegionRegion(metafile->clip, region, mode);
2458 return METAFILE_PlaybackUpdateClip(metafile);
2461 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2462 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2464 GpStatus stat;
2465 GpMetafile *real_metafile = (GpMetafile*)metafile;
2467 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2469 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2470 return InvalidParameter;
2472 if (recordType >= 1 && recordType <= 0x7a)
2474 /* regular EMF record */
2475 if (metafile->playback_dc)
2477 switch (recordType)
2479 case EMR_SETMAPMODE:
2480 case EMR_SAVEDC:
2481 case EMR_RESTOREDC:
2482 case EMR_SETWINDOWORGEX:
2483 case EMR_SETWINDOWEXTEX:
2484 case EMR_SETVIEWPORTORGEX:
2485 case EMR_SETVIEWPORTEXTEX:
2486 case EMR_SCALEVIEWPORTEXTEX:
2487 case EMR_SCALEWINDOWEXTEX:
2488 case EMR_MODIFYWORLDTRANSFORM:
2489 FIXME("not implemented for record type %x\n", recordType);
2490 break;
2491 case EMR_SETWORLDTRANSFORM:
2493 const XFORM* xform = (void*)data;
2494 real_metafile->gdiworldtransform = *xform;
2495 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
2496 break;
2498 case EMR_EXTSELECTCLIPRGN:
2500 DWORD rgndatasize = *(DWORD*)data;
2501 DWORD mode = *(DWORD*)(data + 4);
2502 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
2503 HRGN hrgn = NULL;
2505 if (dataSize > 8)
2507 XFORM final;
2509 METAFILE_GetFinalGdiTransform(metafile, &final);
2511 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
2514 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
2516 DeleteObject(hrgn);
2518 return Ok;
2520 default:
2522 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2524 if (record)
2526 record->iType = recordType;
2527 record->nSize = dataSize + 8;
2528 memcpy(record->dParm, data, dataSize);
2530 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2531 record, metafile->handle_count) == 0)
2532 ERR("PlayEnhMetaFileRecord failed\n");
2534 heap_free(record);
2536 else
2537 return OutOfMemory;
2539 break;
2544 else
2546 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2548 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2550 switch(recordType)
2552 case EmfPlusRecordTypeHeader:
2553 case EmfPlusRecordTypeEndOfFile:
2554 break;
2555 case EmfPlusRecordTypeGetDC:
2556 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2557 break;
2558 case EmfPlusRecordTypeClear:
2560 EmfPlusClear *record = (EmfPlusClear*)header;
2562 if (dataSize != sizeof(record->Color))
2563 return InvalidParameter;
2565 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2567 case EmfPlusRecordTypeFillRects:
2569 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2570 GpBrush *brush, *temp_brush=NULL;
2571 GpRectF *rects, *temp_rects=NULL;
2573 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2574 return InvalidParameter;
2576 if (flags & 0x4000)
2578 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2579 return InvalidParameter;
2581 else
2583 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2584 return InvalidParameter;
2587 if (flags & 0x8000)
2589 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2590 brush = temp_brush;
2592 else
2594 if (record->BrushID >= EmfPlusObjectTableSize ||
2595 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2596 return InvalidParameter;
2598 brush = real_metafile->objtable[record->BrushID].u.brush;
2599 stat = Ok;
2602 if (stat == Ok)
2604 if (flags & 0x4000)
2606 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2607 int i;
2609 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2610 if (rects)
2612 for (i=0; i<record->Count; i++)
2614 rects[i].X = int_rects[i].X;
2615 rects[i].Y = int_rects[i].Y;
2616 rects[i].Width = int_rects[i].Width;
2617 rects[i].Height = int_rects[i].Height;
2620 else
2621 stat = OutOfMemory;
2623 else
2624 rects = (GpRectF*)(record+1);
2627 if (stat == Ok)
2629 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2632 GdipDeleteBrush(temp_brush);
2633 heap_free(temp_rects);
2635 return stat;
2637 case EmfPlusRecordTypeSetClipRect:
2639 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2640 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2641 GpRegion *region;
2643 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2644 return InvalidParameter;
2646 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2648 if (stat == Ok)
2650 stat = metafile_set_clip_region(real_metafile, region, mode);
2651 GdipDeleteRegion(region);
2654 return stat;
2656 case EmfPlusRecordTypeSetClipRegion:
2658 CombineMode mode = (flags >> 8) & 0xf;
2659 BYTE regionid = flags & 0xff;
2660 GpRegion *region;
2662 if (dataSize != 0)
2663 return InvalidParameter;
2665 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2666 return InvalidParameter;
2668 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2669 if (stat == Ok)
2671 stat = metafile_set_clip_region(real_metafile, region, mode);
2672 GdipDeleteRegion(region);
2675 return stat;
2677 case EmfPlusRecordTypeSetClipPath:
2679 CombineMode mode = (flags >> 8) & 0xf;
2680 BYTE pathid = flags & 0xff;
2681 GpRegion *region;
2683 if (dataSize != 0)
2684 return InvalidParameter;
2686 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2687 return InvalidParameter;
2689 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2690 if (stat == Ok)
2692 stat = metafile_set_clip_region(real_metafile, region, mode);
2693 GdipDeleteRegion(region);
2696 return stat;
2698 case EmfPlusRecordTypeSetPageTransform:
2700 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2701 GpUnit unit = (GpUnit)flags;
2703 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2704 return InvalidParameter;
2706 real_metafile->page_unit = unit;
2707 real_metafile->page_scale = record->PageScale;
2709 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2711 case EmfPlusRecordTypeSetWorldTransform:
2713 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2715 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2716 return InvalidParameter;
2718 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2720 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2722 case EmfPlusRecordTypeScaleWorldTransform:
2724 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2725 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2727 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2728 return InvalidParameter;
2730 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2732 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2734 case EmfPlusRecordTypeMultiplyWorldTransform:
2736 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2737 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2738 GpMatrix matrix;
2740 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2741 return InvalidParameter;
2743 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2745 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2747 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2749 case EmfPlusRecordTypeRotateWorldTransform:
2751 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2752 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2754 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2755 return InvalidParameter;
2757 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2759 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2761 case EmfPlusRecordTypeTranslateWorldTransform:
2763 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2764 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2766 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2767 return InvalidParameter;
2769 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2771 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2773 case EmfPlusRecordTypeResetWorldTransform:
2775 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2777 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2779 case EmfPlusRecordTypeBeginContainer:
2781 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2782 container* cont;
2783 GpUnit unit;
2784 REAL scale_x, scale_y;
2785 GpRectF scaled_srcrect;
2786 GpMatrix transform;
2788 cont = heap_alloc_zero(sizeof(*cont));
2789 if (!cont)
2790 return OutOfMemory;
2792 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2793 if (stat != Ok)
2795 heap_free(cont);
2796 return stat;
2799 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2801 if (stat != Ok)
2803 GdipDeleteRegion(cont->clip);
2804 heap_free(cont);
2805 return stat;
2808 cont->id = record->StackIndex;
2809 cont->type = BEGIN_CONTAINER;
2810 cont->world_transform = *metafile->world_transform;
2811 cont->page_unit = metafile->page_unit;
2812 cont->page_scale = metafile->page_scale;
2813 list_add_head(&real_metafile->containers, &cont->entry);
2815 unit = record->Header.Flags & 0xff;
2817 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
2818 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
2820 scaled_srcrect.X = scale_x * record->SrcRect.X;
2821 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2822 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2823 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2825 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2826 transform.matrix[1] = 0.0;
2827 transform.matrix[2] = 0.0;
2828 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2829 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2830 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2832 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2834 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2836 case EmfPlusRecordTypeBeginContainerNoParams:
2837 case EmfPlusRecordTypeSave:
2839 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2840 container* cont;
2842 cont = heap_alloc_zero(sizeof(*cont));
2843 if (!cont)
2844 return OutOfMemory;
2846 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2847 if (stat != Ok)
2849 heap_free(cont);
2850 return stat;
2853 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2854 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2855 else
2856 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2858 if (stat != Ok)
2860 GdipDeleteRegion(cont->clip);
2861 heap_free(cont);
2862 return stat;
2865 cont->id = record->StackIndex;
2866 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2867 cont->type = BEGIN_CONTAINER;
2868 else
2869 cont->type = SAVE_GRAPHICS;
2870 cont->world_transform = *metafile->world_transform;
2871 cont->page_unit = metafile->page_unit;
2872 cont->page_scale = metafile->page_scale;
2873 list_add_head(&real_metafile->containers, &cont->entry);
2875 break;
2877 case EmfPlusRecordTypeEndContainer:
2878 case EmfPlusRecordTypeRestore:
2880 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2881 container* cont;
2882 enum container_type type;
2883 BOOL found=FALSE;
2885 if (recordType == EmfPlusRecordTypeEndContainer)
2886 type = BEGIN_CONTAINER;
2887 else
2888 type = SAVE_GRAPHICS;
2890 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2892 if (cont->id == record->StackIndex && cont->type == type)
2894 found = TRUE;
2895 break;
2899 if (found)
2901 container* cont2;
2903 /* pop any newer items on the stack */
2904 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2906 list_remove(&cont2->entry);
2907 GdipDeleteRegion(cont2->clip);
2908 heap_free(cont2);
2911 if (type == BEGIN_CONTAINER)
2912 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2913 else
2914 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2916 *real_metafile->world_transform = cont->world_transform;
2917 real_metafile->page_unit = cont->page_unit;
2918 real_metafile->page_scale = cont->page_scale;
2919 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2921 list_remove(&cont->entry);
2922 GdipDeleteRegion(cont->clip);
2923 heap_free(cont);
2926 break;
2928 case EmfPlusRecordTypeSetPixelOffsetMode:
2930 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2932 case EmfPlusRecordTypeSetCompositingQuality:
2934 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2936 case EmfPlusRecordTypeSetInterpolationMode:
2938 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2940 case EmfPlusRecordTypeSetTextRenderingHint:
2942 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2944 case EmfPlusRecordTypeSetAntiAliasMode:
2946 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2948 case EmfPlusRecordTypeSetCompositingMode:
2950 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2952 case EmfPlusRecordTypeObject:
2954 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2956 case EmfPlusRecordTypeDrawImage:
2958 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2959 BYTE image = flags & 0xff;
2960 GpPointF points[3];
2962 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2963 return InvalidParameter;
2965 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2966 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2967 return InvalidParameter;
2969 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2970 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2971 return InvalidParameter;
2973 if (flags & 0x4000) /* C */
2975 points[0].X = draw->RectData.rect.X;
2976 points[0].Y = draw->RectData.rect.Y;
2977 points[1].X = points[0].X + draw->RectData.rect.Width;
2978 points[1].Y = points[0].Y;
2979 points[2].X = points[1].X;
2980 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2982 else
2984 points[0].X = draw->RectData.rectF.X;
2985 points[0].Y = draw->RectData.rectF.Y;
2986 points[1].X = points[0].X + draw->RectData.rectF.Width;
2987 points[1].Y = points[0].Y;
2988 points[2].X = points[1].X;
2989 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2992 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2993 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2994 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2996 case EmfPlusRecordTypeDrawImagePoints:
2998 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2999 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
3000 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
3001 BYTE image = flags & 0xff;
3002 GpPointF points[3];
3003 unsigned int i;
3004 UINT size;
3006 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3007 return InvalidParameter;
3009 if (dataSize <= fixed_part_size)
3010 return InvalidParameter;
3011 dataSize -= fixed_part_size;
3013 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3014 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3015 return InvalidParameter;
3017 if (draw->count != 3)
3018 return InvalidParameter;
3020 if ((flags >> 13) & 1) /* E */
3021 FIXME("image effects are not supported.\n");
3023 if ((flags >> 11) & 1) /* P */
3024 size = sizeof(EmfPlusPointR7) * draw->count;
3025 else if ((flags >> 14) & 1) /* C */
3026 size = sizeof(EmfPlusPoint) * draw->count;
3027 else
3028 size = sizeof(EmfPlusPointF) * draw->count;
3030 if (dataSize != size)
3031 return InvalidParameter;
3033 if ((flags >> 11) & 1) /* P */
3035 points[0].X = draw->PointData.pointsR[0].X;
3036 points[0].Y = draw->PointData.pointsR[0].Y;
3037 for (i = 1; i < 3; i++)
3039 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3040 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3043 else if ((flags >> 14) & 1) /* C */
3045 for (i = 0; i < 3; i++)
3047 points[i].X = draw->PointData.points[i].X;
3048 points[i].Y = draw->PointData.points[i].Y;
3051 else
3052 memcpy(points, draw->PointData.pointsF, sizeof(points));
3054 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3055 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3056 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3058 case EmfPlusRecordTypeFillPath:
3060 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3061 GpSolidFill *solidfill = NULL;
3062 BYTE path = flags & 0xff;
3063 GpBrush *brush;
3065 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3066 return InvalidParameter;
3068 if (dataSize != sizeof(fill->data.BrushId))
3069 return InvalidParameter;
3071 if (flags & 0x8000)
3073 stat = GdipCreateSolidFill(fill->data.Color, (GpSolidFill **)&solidfill);
3074 if (stat != Ok)
3075 return stat;
3076 brush = (GpBrush *)solidfill;
3078 else
3080 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3081 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3082 return InvalidParameter;
3084 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3087 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3088 GdipDeleteBrush((GpBrush *)solidfill);
3089 return stat;
3091 case EmfPlusRecordTypeFillClosedCurve:
3093 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3094 sizeof(EmfPlusRecordHeader);
3095 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3096 GpSolidFill *solidfill = NULL;
3097 GpFillMode mode;
3098 GpBrush *brush;
3099 UINT size, i;
3101 if (dataSize <= fixed_part_size)
3102 return InvalidParameter;
3104 if (fill->Count == 0)
3105 return InvalidParameter;
3107 if (flags & 0x800) /* P */
3108 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3109 else if (flags & 0x4000) /* C */
3110 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3111 else
3112 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3114 if (dataSize != size)
3115 return InvalidParameter;
3117 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3119 if (flags & 0x8000) /* S */
3121 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3122 if (stat != Ok)
3123 return stat;
3124 brush = (GpBrush *)solidfill;
3126 else
3128 if (fill->BrushId >= EmfPlusObjectTableSize ||
3129 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3130 return InvalidParameter;
3132 brush = real_metafile->objtable[fill->BrushId].u.brush;
3135 if (flags & (0x800 | 0x4000))
3137 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3138 if (points)
3140 if (flags & 0x800) /* P */
3142 for (i = 1; i < fill->Count; i++)
3144 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3145 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3148 else
3150 for (i = 0; i < fill->Count; i++)
3152 points[i].X = fill->PointData.points[i].X;
3153 points[i].Y = fill->PointData.points[i].Y;
3157 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3158 points, fill->Count, fill->Tension, mode);
3159 GdipFree(points);
3161 else
3162 stat = OutOfMemory;
3164 else
3165 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3166 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3168 GdipDeleteBrush((GpBrush *)solidfill);
3169 return stat;
3171 case EmfPlusRecordTypeFillEllipse:
3173 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3174 GpSolidFill *solidfill = NULL;
3175 GpBrush *brush;
3177 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3178 return InvalidParameter;
3179 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3181 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3182 return InvalidParameter;
3184 if (flags & 0x8000)
3186 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3187 if (stat != Ok)
3188 return stat;
3189 brush = (GpBrush *)solidfill;
3191 else
3193 if (fill->BrushId >= EmfPlusObjectTableSize ||
3194 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3195 return InvalidParameter;
3197 brush = real_metafile->objtable[fill->BrushId].u.brush;
3200 if (flags & 0x4000)
3201 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3202 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3203 else
3204 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3205 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3207 GdipDeleteBrush((GpBrush *)solidfill);
3208 return stat;
3210 case EmfPlusRecordTypeFillPie:
3212 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3213 GpSolidFill *solidfill = NULL;
3214 GpBrush *brush;
3216 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3217 return InvalidParameter;
3218 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3220 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3221 return InvalidParameter;
3223 if (flags & 0x8000) /* S */
3225 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3226 if (stat != Ok)
3227 return stat;
3228 brush = (GpBrush *)solidfill;
3230 else
3232 if (fill->BrushId >= EmfPlusObjectTableSize ||
3233 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3234 return InvalidParameter;
3236 brush = real_metafile->objtable[fill->BrushId].u.brush;
3239 if (flags & 0x4000) /* C */
3240 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3241 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3242 fill->StartAngle, fill->SweepAngle);
3243 else
3244 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3245 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3246 fill->StartAngle, fill->SweepAngle);
3248 GdipDeleteBrush((GpBrush *)solidfill);
3249 return stat;
3251 case EmfPlusRecordTypeDrawPath:
3253 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3254 BYTE path = flags & 0xff;
3256 if (dataSize != sizeof(draw->PenId))
3257 return InvalidParameter;
3259 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3260 return InvalidParameter;
3262 if (real_metafile->objtable[path].type != ObjectTypePath ||
3263 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3264 return InvalidParameter;
3266 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3267 real_metafile->objtable[path].u.path);
3269 case EmfPlusRecordTypeDrawArc:
3271 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3272 BYTE pen = flags & 0xff;
3274 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3275 return InvalidParameter;
3277 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3278 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3279 return InvalidParameter;
3281 if (flags & 0x4000) /* C */
3282 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3283 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3284 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3285 else
3286 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3287 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3288 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3290 case EmfPlusRecordTypeDrawEllipse:
3292 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3293 BYTE pen = flags & 0xff;
3295 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3296 return InvalidParameter;
3298 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3299 return InvalidParameter;
3301 if (flags & 0x4000) /* C */
3302 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3303 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3304 draw->RectData.rect.Height);
3305 else
3306 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3307 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3308 draw->RectData.rectF.Height);
3310 case EmfPlusRecordTypeDrawPie:
3312 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3313 BYTE pen = flags & 0xff;
3315 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3316 return InvalidParameter;
3318 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3319 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3320 return InvalidParameter;
3322 if (flags & 0x4000) /* C */
3323 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3324 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3325 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3326 else
3327 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3328 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3329 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3331 case EmfPlusRecordTypeDrawRects:
3333 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3334 BYTE pen = flags & 0xff;
3335 GpRectF *rects = NULL;
3337 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3338 return InvalidParameter;
3340 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3341 return InvalidParameter;
3342 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3344 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3345 return InvalidParameter;
3347 if (flags & 0x4000)
3349 DWORD i;
3351 rects = GdipAlloc(draw->Count * sizeof(*rects));
3352 if (!rects)
3353 return OutOfMemory;
3355 for (i = 0; i < draw->Count; i++)
3357 rects[i].X = draw->RectData.rect[i].X;
3358 rects[i].Y = draw->RectData.rect[i].Y;
3359 rects[i].Width = draw->RectData.rect[i].Width;
3360 rects[i].Height = draw->RectData.rect[i].Height;
3364 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3365 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3366 GdipFree(rects);
3367 return stat;
3369 default:
3370 FIXME("Not implemented for record type %x\n", recordType);
3371 return NotImplemented;
3375 return Ok;
3378 struct enum_metafile_data
3380 EnumerateMetafileProc callback;
3381 void *callback_data;
3382 GpMetafile *metafile;
3385 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3386 int nObj, LPARAM lpData)
3388 BOOL ret;
3389 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3390 const BYTE* pStr;
3392 data->metafile->handle_table = lpHTable;
3393 data->metafile->handle_count = nObj;
3395 /* First check for an EMF+ record. */
3396 if (lpEMFR->iType == EMR_GDICOMMENT)
3398 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3400 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3402 int offset = 4;
3404 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3406 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3408 if (record->DataSize)
3409 pStr = (const BYTE*)(record+1);
3410 else
3411 pStr = NULL;
3413 ret = data->callback(record->Type, record->Flags, record->DataSize,
3414 pStr, data->callback_data);
3416 if (!ret)
3417 return 0;
3419 offset += record->Size;
3422 return 1;
3426 if (lpEMFR->nSize != 8)
3427 pStr = (const BYTE*)lpEMFR->dParm;
3428 else
3429 pStr = NULL;
3431 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3432 pStr, data->callback_data);
3435 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3436 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3437 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3438 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3440 struct enum_metafile_data data;
3441 GpStatus stat;
3442 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3443 GraphicsContainer state;
3444 GpPath *dst_path;
3446 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3447 destPoints, count, srcRect, srcUnit, callback, callbackData,
3448 imageAttributes);
3450 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3451 return InvalidParameter;
3453 if (!metafile->hemf)
3454 return InvalidParameter;
3456 if (metafile->playback_graphics)
3457 return ObjectBusy;
3459 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3460 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3461 debugstr_pointf(&destPoints[2]));
3463 data.callback = callback;
3464 data.callback_data = callbackData;
3465 data.metafile = real_metafile;
3467 real_metafile->playback_graphics = graphics;
3468 real_metafile->playback_dc = NULL;
3469 real_metafile->src_rect = *srcRect;
3471 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3472 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3474 if (stat == Ok)
3475 stat = GdipBeginContainer2(graphics, &state);
3477 if (stat == Ok)
3479 stat = GdipSetPageScale(graphics, 1.0);
3481 if (stat == Ok)
3482 stat = GdipSetPageUnit(graphics, UnitPixel);
3484 if (stat == Ok)
3485 stat = GdipResetWorldTransform(graphics);
3487 if (stat == Ok)
3488 stat = GdipCreateRegion(&real_metafile->base_clip);
3490 if (stat == Ok)
3491 stat = GdipGetClip(graphics, real_metafile->base_clip);
3493 if (stat == Ok)
3494 stat = GdipCreateRegion(&real_metafile->clip);
3496 if (stat == Ok)
3497 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3499 if (stat == Ok)
3501 GpPointF clip_points[4];
3503 clip_points[0] = real_metafile->playback_points[0];
3504 clip_points[1] = real_metafile->playback_points[1];
3505 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3506 - real_metafile->playback_points[0].X;
3507 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3508 - real_metafile->playback_points[0].Y;
3509 clip_points[3] = real_metafile->playback_points[2];
3511 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3513 if (stat == Ok)
3514 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3516 GdipDeletePath(dst_path);
3519 if (stat == Ok)
3520 stat = GdipCreateMatrix(&real_metafile->world_transform);
3522 if (stat == Ok)
3524 real_metafile->page_unit = UnitDisplay;
3525 real_metafile->page_scale = 1.0;
3526 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3529 if (stat == Ok)
3531 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3534 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
3535 metafile->metafile_type == MetafileTypeWmfPlaceable ||
3536 metafile->metafile_type == MetafileTypeWmf))
3537 stat = METAFILE_PlaybackGetDC(real_metafile);
3539 if (stat == Ok)
3540 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
3542 METAFILE_PlaybackReleaseDC(real_metafile);
3544 GdipDeleteMatrix(real_metafile->world_transform);
3545 real_metafile->world_transform = NULL;
3547 GdipDeleteRegion(real_metafile->base_clip);
3548 real_metafile->base_clip = NULL;
3550 GdipDeleteRegion(real_metafile->clip);
3551 real_metafile->clip = NULL;
3553 while (list_head(&real_metafile->containers))
3555 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3556 list_remove(&cont->entry);
3557 GdipDeleteRegion(cont->clip);
3558 heap_free(cont);
3561 GdipEndContainer(graphics, state);
3564 real_metafile->playback_graphics = NULL;
3566 return stat;
3569 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3570 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3571 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3573 GpPointF points[3];
3575 if (!graphics || !metafile || !dest) return InvalidParameter;
3577 points[0].X = points[2].X = dest->X;
3578 points[0].Y = points[1].Y = dest->Y;
3579 points[1].X = dest->X + dest->Width;
3580 points[2].Y = dest->Y + dest->Height;
3582 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3583 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3586 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3587 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3588 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3590 GpRectF destf;
3592 if (!graphics || !metafile || !dest) return InvalidParameter;
3594 destf.X = dest->X;
3595 destf.Y = dest->Y;
3596 destf.Width = dest->Width;
3597 destf.Height = dest->Height;
3599 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3602 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3603 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3604 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3606 GpRectF destf;
3608 if (!graphics || !metafile || !dest) return InvalidParameter;
3610 destf.X = dest->X;
3611 destf.Y = dest->Y;
3612 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
3613 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
3615 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3618 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3619 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3620 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3622 GpPointF ptf;
3624 if (!graphics || !metafile || !dest) return InvalidParameter;
3626 ptf.X = dest->X;
3627 ptf.Y = dest->Y;
3629 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3632 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3633 MetafileHeader * header)
3635 GpStatus status;
3637 TRACE("(%p, %p)\n", metafile, header);
3639 if(!metafile || !header)
3640 return InvalidParameter;
3642 if (metafile->hemf)
3644 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3645 if (status != Ok) return status;
3647 else
3649 memset(header, 0, sizeof(*header));
3650 header->Version = VERSION_MAGIC2;
3653 header->Type = metafile->metafile_type;
3654 header->DpiX = metafile->image.xres;
3655 header->DpiY = metafile->image.yres;
3656 header->Width = gdip_round(metafile->bounds.Width);
3657 header->Height = gdip_round(metafile->bounds.Height);
3659 return Ok;
3662 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3663 int nObj, LPARAM lpData)
3665 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3667 if (lpEMFR->iType == EMR_GDICOMMENT)
3669 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3671 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3673 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3675 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3676 header->Type == EmfPlusRecordTypeHeader)
3678 memcpy(dst_header, header, sizeof(*dst_header));
3682 else if (lpEMFR->iType == EMR_HEADER)
3683 return TRUE;
3685 return FALSE;
3688 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3689 MetafileHeader *header)
3691 ENHMETAHEADER3 emfheader;
3692 EmfPlusHeader emfplusheader;
3693 MetafileType metafile_type;
3695 TRACE("(%p,%p)\n", hemf, header);
3697 if(!hemf || !header)
3698 return InvalidParameter;
3700 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3701 return GenericError;
3703 emfplusheader.Header.Type = 0;
3705 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3707 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3709 if ((emfplusheader.Header.Flags & 1) == 1)
3710 metafile_type = MetafileTypeEmfPlusDual;
3711 else
3712 metafile_type = MetafileTypeEmfPlusOnly;
3714 else
3715 metafile_type = MetafileTypeEmf;
3717 header->Type = metafile_type;
3718 header->Size = emfheader.nBytes;
3719 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3720 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3721 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3722 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3723 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3724 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3725 header->u.EmfHeader = emfheader;
3727 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3729 header->Version = emfplusheader.Version;
3730 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3731 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3732 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3733 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3735 else
3737 header->Version = emfheader.nVersion;
3738 header->EmfPlusFlags = 0;
3739 header->EmfPlusHeaderSize = 0;
3740 header->LogicalDpiX = 0;
3741 header->LogicalDpiY = 0;
3744 return Ok;
3747 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3748 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3750 GpStatus status;
3751 GpMetafile *metafile;
3753 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3755 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3756 if (status == Ok)
3758 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3759 GdipDisposeImage(&metafile->image);
3761 return status;
3764 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3765 MetafileHeader *header)
3767 GpStatus status;
3768 GpMetafile *metafile;
3770 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3772 if (!filename || !header)
3773 return InvalidParameter;
3775 status = GdipCreateMetafileFromFile(filename, &metafile);
3776 if (status == Ok)
3778 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3779 GdipDisposeImage(&metafile->image);
3781 return status;
3784 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3785 MetafileHeader *header)
3787 GpStatus status;
3788 GpMetafile *metafile;
3790 TRACE("(%p,%p)\n", stream, header);
3792 if (!stream || !header)
3793 return InvalidParameter;
3795 status = GdipCreateMetafileFromStream(stream, &metafile);
3796 if (status == Ok)
3798 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3799 GdipDisposeImage(&metafile->image);
3801 return status;
3804 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3805 GpMetafile **metafile)
3807 GpStatus stat;
3808 MetafileHeader header;
3810 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3812 if(!hemf || !metafile)
3813 return InvalidParameter;
3815 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3816 if (stat != Ok)
3817 return stat;
3819 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3820 if (!*metafile)
3821 return OutOfMemory;
3823 (*metafile)->image.type = ImageTypeMetafile;
3824 (*metafile)->image.format = ImageFormatEMF;
3825 (*metafile)->image.frame_count = 1;
3826 (*metafile)->image.xres = header.DpiX;
3827 (*metafile)->image.yres = header.DpiY;
3828 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3829 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3830 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3831 / 2540.0 * header.DpiX;
3832 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3833 / 2540.0 * header.DpiY;
3834 (*metafile)->unit = UnitPixel;
3835 (*metafile)->metafile_type = header.Type;
3836 (*metafile)->hemf = hemf;
3837 (*metafile)->preserve_hemf = !delete;
3838 list_init(&(*metafile)->containers);
3840 TRACE("<-- %p\n", *metafile);
3842 return Ok;
3845 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3846 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3848 UINT read;
3849 BYTE *copy;
3850 HENHMETAFILE hemf;
3851 GpStatus retval = Ok;
3853 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3855 if(!hwmf || !metafile)
3856 return InvalidParameter;
3858 *metafile = NULL;
3859 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3860 if(!read)
3861 return GenericError;
3862 copy = heap_alloc_zero(read);
3863 GetMetaFileBitsEx(hwmf, read, copy);
3865 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3866 heap_free(copy);
3868 /* FIXME: We should store and use hwmf instead of converting to hemf */
3869 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
3871 if (retval == Ok)
3873 if (placeable)
3875 (*metafile)->image.xres = (REAL)placeable->Inch;
3876 (*metafile)->image.yres = (REAL)placeable->Inch;
3877 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
3878 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
3879 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
3880 placeable->BoundingBox.Left);
3881 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
3882 placeable->BoundingBox.Top);
3883 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
3885 else
3886 (*metafile)->metafile_type = MetafileTypeWmf;
3887 (*metafile)->image.format = ImageFormatWMF;
3889 if (delete) DeleteMetaFile(hwmf);
3891 else
3892 DeleteEnhMetaFile(hemf);
3893 return retval;
3896 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
3897 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3899 HMETAFILE hmf;
3900 HENHMETAFILE emf;
3902 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
3904 hmf = GetMetaFileW(file);
3905 if(hmf)
3906 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
3908 emf = GetEnhMetaFileW(file);
3909 if(emf)
3910 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
3912 return GenericError;
3915 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
3916 GpMetafile **metafile)
3918 GpStatus status;
3919 IStream *stream;
3921 TRACE("(%p, %p)\n", file, metafile);
3923 if (!file || !metafile) return InvalidParameter;
3925 *metafile = NULL;
3927 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
3928 if (status == Ok)
3930 status = GdipCreateMetafileFromStream(stream, metafile);
3931 IStream_Release(stream);
3933 return status;
3936 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
3937 GpMetafile **metafile)
3939 GpStatus stat;
3941 TRACE("%p %p\n", stream, metafile);
3943 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
3944 if (stat != Ok) return stat;
3946 if ((*metafile)->image.type != ImageTypeMetafile)
3948 GdipDisposeImage(&(*metafile)->image);
3949 *metafile = NULL;
3950 return GenericError;
3953 return Ok;
3956 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3957 UINT limitDpi)
3959 TRACE("(%p,%u)\n", metafile, limitDpi);
3961 return Ok;
3964 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
3965 GpMetafile* metafile, BOOL* succ, EmfType emfType,
3966 const WCHAR* description, GpMetafile** out_metafile)
3968 static int calls;
3970 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
3971 debugstr_w(description), out_metafile);
3973 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
3974 return InvalidParameter;
3976 if(succ)
3977 *succ = FALSE;
3978 *out_metafile = NULL;
3980 if(!(calls++))
3981 FIXME("not implemented\n");
3983 return NotImplemented;
3986 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
3987 LPBYTE pData16, INT iMapMode, INT eFlags)
3989 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
3990 return NotImplemented;
3993 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
3994 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
3995 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
3996 GpMetafile **metafile)
3998 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3999 frameUnit, debugstr_w(desc), metafile);
4001 return NotImplemented;
4004 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4005 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4006 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4008 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4009 frameUnit, debugstr_w(desc), metafile);
4011 return NotImplemented;
4014 /*****************************************************************************
4015 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4018 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4019 GpMetafile* metafile, BOOL* conversionSuccess,
4020 const WCHAR* filename, EmfType emfType,
4021 const WCHAR* description, GpMetafile** out_metafile)
4023 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4024 return NotImplemented;
4027 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4029 LARGE_INTEGER zero;
4030 STATSTG statstg;
4031 GpStatus stat;
4032 HRESULT hr;
4034 *size = 0;
4036 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4037 if (FAILED(hr)) return hresult_to_status(hr);
4039 stat = encode_image_png(image, *stream, NULL);
4040 if (stat != Ok)
4042 IStream_Release(*stream);
4043 return stat;
4046 hr = IStream_Stat(*stream, &statstg, 1);
4047 if (FAILED(hr))
4049 IStream_Release(*stream);
4050 return hresult_to_status(hr);
4052 *size = statstg.cbSize.u.LowPart;
4054 zero.QuadPart = 0;
4055 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4056 if (FAILED(hr))
4058 IStream_Release(*stream);
4059 return hresult_to_status(hr);
4062 return Ok;
4065 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4067 HRESULT hr;
4069 record->Width = 0;
4070 record->Height = 0;
4071 record->Stride = 0;
4072 record->PixelFormat = 0;
4073 record->Type = BitmapDataTypeCompressed;
4075 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4076 if (FAILED(hr)) return hresult_to_status(hr);
4077 return Ok;
4080 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4082 EmfPlusObject *object_record;
4083 GpStatus stat;
4084 DWORD size;
4086 *id = -1;
4088 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4089 return Ok;
4091 if (image->type == ImageTypeBitmap)
4093 IStream *stream;
4094 DWORD aligned_size;
4096 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4097 if (stat != Ok) return stat;
4098 aligned_size = (size + 3) & ~3;
4100 stat = METAFILE_AllocateRecord(metafile,
4101 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4102 (void**)&object_record);
4103 if (stat != Ok)
4105 IStream_Release(stream);
4106 return stat;
4108 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4110 *id = METAFILE_AddObjectId(metafile);
4111 object_record->Header.Type = EmfPlusRecordTypeObject;
4112 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4113 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4114 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4116 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4117 IStream_Release(stream);
4118 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4119 return stat;
4121 else if (image->type == ImageTypeMetafile)
4123 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4124 EmfPlusMetafile *metafile_record;
4126 if (!hemf) return InvalidParameter;
4128 size = GetEnhMetaFileBits(hemf, 0, NULL);
4129 if (!size) return GenericError;
4131 stat = METAFILE_AllocateRecord(metafile,
4132 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4133 (void**)&object_record);
4134 if (stat != Ok) return stat;
4136 *id = METAFILE_AddObjectId(metafile);
4137 object_record->Header.Type = EmfPlusRecordTypeObject;
4138 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4139 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4140 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4141 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4142 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4143 metafile_record->MetafileDataSize = size;
4144 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4146 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4147 return GenericError;
4149 return Ok;
4151 else
4153 FIXME("not supported image type (%d)\n", image->type);
4154 return NotImplemented;
4158 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4160 EmfPlusObject *object_record;
4161 EmfPlusImageAttributes *attrs_record;
4162 GpStatus stat;
4164 *id = -1;
4166 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4167 return Ok;
4169 if (!attrs)
4170 return Ok;
4172 stat = METAFILE_AllocateRecord(metafile,
4173 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4174 (void**)&object_record);
4175 if (stat != Ok) return stat;
4177 *id = METAFILE_AddObjectId(metafile);
4178 object_record->Header.Type = EmfPlusRecordTypeObject;
4179 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4180 attrs_record = &object_record->ObjectData.image_attributes;
4181 attrs_record->Version = VERSION_MAGIC2;
4182 attrs_record->Reserved1 = 0;
4183 attrs_record->WrapMode = attrs->wrap;
4184 attrs_record->ClampColor = attrs->outside_color;
4185 attrs_record->ObjectClamp = attrs->clamp;
4186 attrs_record->Reserved2 = 0;
4187 return Ok;
4190 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4191 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4192 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4193 DrawImageAbort callback, VOID *callbackData)
4195 EmfPlusDrawImagePoints *draw_image_record;
4196 DWORD image_id, attributes_id;
4197 GpStatus stat;
4199 if (count != 3) return InvalidParameter;
4201 if (metafile->metafile_type == MetafileTypeEmf)
4203 FIXME("MetafileTypeEmf metafiles not supported\n");
4204 return NotImplemented;
4206 else
4207 FIXME("semi-stub\n");
4209 if (!imageAttributes)
4211 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4213 else if (image->type == ImageTypeBitmap)
4215 INT width = ((GpBitmap*)image)->width;
4216 INT height = ((GpBitmap*)image)->height;
4217 GpGraphics *graphics;
4218 GpBitmap *bitmap;
4220 stat = GdipCreateBitmapFromScan0(width, height,
4221 0, PixelFormat32bppARGB, NULL, &bitmap);
4222 if (stat != Ok) return stat;
4224 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4225 if (stat != Ok)
4227 GdipDisposeImage((GpImage*)bitmap);
4228 return stat;
4231 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4232 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4233 GdipDeleteGraphics(graphics);
4234 if (stat != Ok)
4236 GdipDisposeImage((GpImage*)bitmap);
4237 return stat;
4240 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4241 GdipDisposeImage((GpImage*)bitmap);
4243 else
4245 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4246 return NotImplemented;
4248 if (stat != Ok) return stat;
4250 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4251 if (stat != Ok) return stat;
4253 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
4254 if (stat != Ok) return stat;
4255 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
4256 draw_image_record->Header.Flags = image_id;
4257 draw_image_record->ImageAttributesID = attributes_id;
4258 draw_image_record->SrcUnit = UnitPixel;
4259 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
4260 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
4261 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
4262 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
4263 draw_image_record->count = 3;
4264 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4265 METAFILE_WriteRecords(metafile);
4266 return Ok;
4269 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4271 EmfPlusRecordHeader *record;
4272 GpStatus stat;
4274 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4275 return Ok;
4277 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
4278 if (stat != Ok) return stat;
4280 record->Type = prop;
4281 record->Flags = val;
4283 METAFILE_WriteRecords(metafile);
4284 return Ok;
4287 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4289 EmfPlusObject *object_record;
4290 GpStatus stat;
4291 DWORD size;
4293 *id = -1;
4294 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4295 return Ok;
4297 size = write_path_data(path, NULL);
4298 stat = METAFILE_AllocateRecord(metafile,
4299 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + 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 | ObjectTypePath << 8;
4306 write_path_data(path, &object_record->ObjectData.path);
4307 return Ok;
4310 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4312 DWORD i, data_flags, pen_data_size, brush_size;
4313 EmfPlusObject *object_record;
4314 EmfPlusPenData *pen_data;
4315 GpStatus stat;
4316 BOOL result;
4318 *id = -1;
4319 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4320 return Ok;
4322 data_flags = 0;
4323 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4325 GdipIsMatrixIdentity(&pen->transform, &result);
4326 if (!result)
4328 data_flags |= PenDataTransform;
4329 pen_data_size += sizeof(EmfPlusTransformMatrix);
4331 if (pen->startcap != LineCapFlat)
4333 data_flags |= PenDataStartCap;
4334 pen_data_size += sizeof(DWORD);
4336 if (pen->endcap != LineCapFlat)
4338 data_flags |= PenDataEndCap;
4339 pen_data_size += sizeof(DWORD);
4341 if (pen->join != LineJoinMiter)
4343 data_flags |= PenDataJoin;
4344 pen_data_size += sizeof(DWORD);
4346 if (pen->miterlimit != 10.0)
4348 data_flags |= PenDataMiterLimit;
4349 pen_data_size += sizeof(REAL);
4351 if (pen->style != GP_DEFAULT_PENSTYLE)
4353 data_flags |= PenDataLineStyle;
4354 pen_data_size += sizeof(DWORD);
4356 if (pen->dashcap != DashCapFlat)
4358 data_flags |= PenDataDashedLineCap;
4359 pen_data_size += sizeof(DWORD);
4361 data_flags |= PenDataDashedLineOffset;
4362 pen_data_size += sizeof(REAL);
4363 if (pen->numdashes)
4365 data_flags |= PenDataDashedLine;
4366 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4368 if (pen->align != PenAlignmentCenter)
4370 data_flags |= PenDataNonCenter;
4371 pen_data_size += sizeof(DWORD);
4373 /* TODO: Add support for PenDataCompoundLine */
4374 if (pen->customstart)
4376 FIXME("ignoring custom start cup\n");
4378 if (pen->customend)
4380 FIXME("ignoring custom end cup\n");
4383 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4384 if (stat != Ok) return stat;
4386 stat = METAFILE_AllocateRecord(metafile,
4387 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4388 (void**)&object_record);
4389 if (stat != Ok) return stat;
4391 *id = METAFILE_AddObjectId(metafile);
4392 object_record->Header.Type = EmfPlusRecordTypeObject;
4393 object_record->Header.Flags = *id | ObjectTypePen << 8;
4394 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4395 object_record->ObjectData.pen.Type = 0;
4397 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4398 pen_data->PenDataFlags = data_flags;
4399 pen_data->PenUnit = pen->unit;
4400 pen_data->PenWidth = pen->width;
4402 i = 0;
4403 if (data_flags & PenDataTransform)
4405 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4406 memcpy(m, &pen->transform, sizeof(*m));
4407 i += sizeof(EmfPlusTransformMatrix);
4409 if (data_flags & PenDataStartCap)
4411 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4412 i += sizeof(DWORD);
4414 if (data_flags & PenDataEndCap)
4416 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4417 i += sizeof(DWORD);
4419 if (data_flags & PenDataJoin)
4421 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4422 i += sizeof(DWORD);
4424 if (data_flags & PenDataMiterLimit)
4426 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4427 i += sizeof(REAL);
4429 if (data_flags & PenDataLineStyle)
4431 switch (pen->style & PS_STYLE_MASK)
4433 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4434 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4435 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4436 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4437 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4438 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4440 i += sizeof(DWORD);
4442 if (data_flags & PenDataDashedLineCap)
4444 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4445 i += sizeof(DWORD);
4447 if (data_flags & PenDataDashedLineOffset)
4449 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4450 i += sizeof(REAL);
4452 if (data_flags & PenDataDashedLine)
4454 int j;
4456 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4457 i += sizeof(DWORD);
4459 for (j=0; j<pen->numdashes; j++)
4461 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4462 i += sizeof(REAL);
4465 if (data_flags & PenDataNonCenter)
4467 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4468 i += sizeof(DWORD);
4471 METAFILE_FillBrushData(pen->brush,
4472 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4473 return Ok;
4476 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4478 EmfPlusDrawPath *draw_path_record;
4479 DWORD path_id;
4480 DWORD pen_id;
4481 GpStatus stat;
4483 if (metafile->metafile_type == MetafileTypeEmf)
4485 FIXME("stub!\n");
4486 return NotImplemented;
4489 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4490 if (stat != Ok) return stat;
4492 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4493 if (stat != Ok) return stat;
4495 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4496 if (stat != Ok) return stat;
4497 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4498 draw_path_record->Header.Flags = path_id;
4499 draw_path_record->PenId = pen_id;
4501 METAFILE_WriteRecords(metafile);
4502 return Ok;
4505 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4507 EmfPlusFillPath *fill_path_record;
4508 DWORD brush_id = -1, path_id;
4509 BOOL inline_color;
4510 GpStatus stat;
4512 if (metafile->metafile_type == MetafileTypeEmf)
4514 FIXME("stub!\n");
4515 return NotImplemented;
4518 inline_color = brush->bt == BrushTypeSolidColor;
4519 if (!inline_color)
4521 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4522 if (stat != Ok) return stat;
4525 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4526 if (stat != Ok) return stat;
4528 stat = METAFILE_AllocateRecord(metafile,
4529 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4530 if (stat != Ok) return stat;
4531 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4532 if (inline_color)
4534 fill_path_record->Header.Flags = 0x8000 | path_id;
4535 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4537 else
4539 fill_path_record->Header.Flags = path_id;
4540 fill_path_record->data.BrushId = brush_id;
4543 METAFILE_WriteRecords(metafile);
4544 return Ok;