rasapi32: Update spec file.
[wine/zf.git] / dlls / gdiplus / metafile.c
blobf4824d4257b3a6e51c291747937d522cec7dca97
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"
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
35 #include "winreg.h"
36 #include "shlwapi.h"
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
45 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
47 typedef ARGB EmfPlusARGB;
49 typedef struct EmfPlusRecordHeader
51 WORD Type;
52 WORD Flags;
53 DWORD Size;
54 DWORD DataSize;
55 } EmfPlusRecordHeader;
57 typedef struct EmfPlusHeader
59 EmfPlusRecordHeader Header;
60 DWORD Version;
61 DWORD EmfPlusFlags;
62 DWORD LogicalDpiX;
63 DWORD LogicalDpiY;
64 } EmfPlusHeader;
66 typedef struct EmfPlusClear
68 EmfPlusRecordHeader Header;
69 DWORD Color;
70 } EmfPlusClear;
72 typedef struct EmfPlusFillRects
74 EmfPlusRecordHeader Header;
75 DWORD BrushID;
76 DWORD Count;
77 } EmfPlusFillRects;
79 typedef struct EmfPlusSetClipRect
81 EmfPlusRecordHeader Header;
82 GpRectF ClipRect;
83 } EmfPlusSetClipRect;
85 typedef struct EmfPlusSetPageTransform
87 EmfPlusRecordHeader Header;
88 REAL PageScale;
89 } EmfPlusSetPageTransform;
91 typedef struct EmfPlusRect
93 SHORT X;
94 SHORT Y;
95 SHORT Width;
96 SHORT Height;
97 } EmfPlusRect;
99 typedef struct EmfPlusSetWorldTransform
101 EmfPlusRecordHeader Header;
102 REAL MatrixData[6];
103 } EmfPlusSetWorldTransform;
105 typedef struct EmfPlusScaleWorldTransform
107 EmfPlusRecordHeader Header;
108 REAL Sx;
109 REAL Sy;
110 } EmfPlusScaleWorldTransform;
112 typedef struct EmfPlusMultiplyWorldTransform
114 EmfPlusRecordHeader Header;
115 REAL MatrixData[6];
116 } EmfPlusMultiplyWorldTransform;
118 typedef struct EmfPlusRotateWorldTransform
120 EmfPlusRecordHeader Header;
121 REAL Angle;
122 } EmfPlusRotateWorldTransform;
124 typedef struct EmfPlusTranslateWorldTransform
126 EmfPlusRecordHeader Header;
127 REAL dx;
128 REAL dy;
129 } EmfPlusTranslateWorldTransform;
131 typedef struct EmfPlusBeginContainer
133 EmfPlusRecordHeader Header;
134 GpRectF DestRect;
135 GpRectF SrcRect;
136 DWORD StackIndex;
137 } EmfPlusBeginContainer;
139 typedef struct EmfPlusContainerRecord
141 EmfPlusRecordHeader Header;
142 DWORD StackIndex;
143 } EmfPlusContainerRecord;
145 enum container_type
147 BEGIN_CONTAINER,
148 SAVE_GRAPHICS
151 typedef struct container
153 struct list entry;
154 DWORD id;
155 enum container_type type;
156 GraphicsContainer state;
157 GpMatrix world_transform;
158 GpUnit page_unit;
159 REAL page_scale;
160 GpRegion *clip;
161 } container;
163 enum PenDataFlags
165 PenDataTransform = 0x0001,
166 PenDataStartCap = 0x0002,
167 PenDataEndCap = 0x0004,
168 PenDataJoin = 0x0008,
169 PenDataMiterLimit = 0x0010,
170 PenDataLineStyle = 0x0020,
171 PenDataDashedLineCap = 0x0040,
172 PenDataDashedLineOffset = 0x0080,
173 PenDataDashedLine = 0x0100,
174 PenDataNonCenter = 0x0200,
175 PenDataCompoundLine = 0x0400,
176 PenDataCustomStartCap = 0x0800,
177 PenDataCustomEndCap = 0x1000
180 typedef struct EmfPlusTransformMatrix
182 REAL TransformMatrix[6];
183 } EmfPlusTransformMatrix;
185 enum LineStyle
187 LineStyleSolid,
188 LineStyleDash,
189 LineStyleDot,
190 LineStyleDashDot,
191 LineStyleDashDotDot,
192 LineStyleCustom
195 typedef struct EmfPlusDashedLineData
197 DWORD DashedLineDataSize;
198 BYTE data[1];
199 } EmfPlusDashedLineData;
201 typedef struct EmfPlusCompoundLineData
203 DWORD CompoundLineDataSize;
204 BYTE data[1];
205 } EmfPlusCompoundLineData;
207 typedef struct EmfPlusCustomStartCapData
209 DWORD CustomStartCapSize;
210 BYTE data[1];
211 } EmfPlusCustomStartCapData;
213 typedef struct EmfPlusCustomEndCapData
215 DWORD CustomEndCapSize;
216 BYTE data[1];
217 } EmfPlusCustomEndCapData;
219 typedef struct EmfPlusPenData
221 DWORD PenDataFlags;
222 DWORD PenUnit;
223 REAL PenWidth;
224 BYTE OptionalData[1];
225 } EmfPlusPenData;
227 enum BrushDataFlags
229 BrushDataPath = 1 << 0,
230 BrushDataTransform = 1 << 1,
231 BrushDataPresetColors = 1 << 2,
232 BrushDataBlendFactorsH = 1 << 3,
233 BrushDataBlendFactorsV = 1 << 4,
234 BrushDataFocusScales = 1 << 6,
235 BrushDataIsGammaCorrected = 1 << 7,
236 BrushDataDoNotTransform = 1 << 8,
239 typedef struct EmfPlusSolidBrushData
241 EmfPlusARGB SolidColor;
242 } EmfPlusSolidBrushData;
244 typedef struct EmfPlusHatchBrushData
246 DWORD HatchStyle;
247 EmfPlusARGB ForeColor;
248 EmfPlusARGB BackColor;
249 } EmfPlusHatchBrushData;
251 typedef struct EmfPlusTextureBrushData
253 DWORD BrushDataFlags;
254 INT WrapMode;
255 BYTE OptionalData[1];
256 } EmfPlusTextureBrushData;
258 typedef struct EmfPlusRectF
260 float X;
261 float Y;
262 float Width;
263 float Height;
264 } EmfPlusRectF;
266 typedef struct EmfPlusLinearGradientBrushData
268 DWORD BrushDataFlags;
269 INT WrapMode;
270 EmfPlusRectF RectF;
271 EmfPlusARGB StartColor;
272 EmfPlusARGB EndColor;
273 DWORD Reserved1;
274 DWORD Reserved2;
275 BYTE OptionalData[1];
276 } EmfPlusLinearGradientBrushData;
278 typedef struct EmfPlusBrush
280 DWORD Version;
281 DWORD Type;
282 union {
283 EmfPlusSolidBrushData solid;
284 EmfPlusHatchBrushData hatch;
285 EmfPlusTextureBrushData texture;
286 EmfPlusLinearGradientBrushData lineargradient;
287 } BrushData;
288 } EmfPlusBrush;
290 typedef struct EmfPlusPen
292 DWORD Version;
293 DWORD Type;
294 /* EmfPlusPenData */
295 /* EmfPlusBrush */
296 BYTE data[1];
297 } EmfPlusPen;
299 typedef struct EmfPlusPath
301 DWORD Version;
302 DWORD PathPointCount;
303 DWORD PathPointFlags;
304 /* PathPoints[] */
305 /* PathPointTypes[] */
306 /* AlignmentPadding */
307 BYTE data[1];
308 } EmfPlusPath;
310 typedef struct EmfPlusRegionNodePath
312 DWORD RegionNodePathLength;
313 EmfPlusPath RegionNodePath;
314 } EmfPlusRegionNodePath;
316 typedef struct EmfPlusRegion
318 DWORD Version;
319 DWORD RegionNodeCount;
320 BYTE RegionNode[1];
321 } EmfPlusRegion;
323 typedef struct EmfPlusPalette
325 DWORD PaletteStyleFlags;
326 DWORD PaletteCount;
327 BYTE PaletteEntries[1];
328 } EmfPlusPalette;
330 typedef enum
332 BitmapDataTypePixel,
333 BitmapDataTypeCompressed,
334 } BitmapDataType;
336 typedef struct EmfPlusBitmap
338 DWORD Width;
339 DWORD Height;
340 DWORD Stride;
341 DWORD PixelFormat;
342 DWORD Type;
343 BYTE BitmapData[1];
344 } EmfPlusBitmap;
346 typedef struct EmfPlusMetafile
348 DWORD Type;
349 DWORD MetafileDataSize;
350 BYTE MetafileData[1];
351 } EmfPlusMetafile;
353 typedef enum ImageDataType
355 ImageDataTypeUnknown,
356 ImageDataTypeBitmap,
357 ImageDataTypeMetafile,
358 } ImageDataType;
360 typedef struct EmfPlusImage
362 DWORD Version;
363 ImageDataType Type;
364 union
366 EmfPlusBitmap bitmap;
367 EmfPlusMetafile metafile;
368 } ImageData;
369 } EmfPlusImage;
371 typedef struct EmfPlusImageAttributes
373 DWORD Version;
374 DWORD Reserved1;
375 DWORD WrapMode;
376 EmfPlusARGB ClampColor;
377 DWORD ObjectClamp;
378 DWORD Reserved2;
379 } EmfPlusImageAttributes;
381 typedef struct EmfPlusFont
383 DWORD Version;
384 float EmSize;
385 DWORD SizeUnit;
386 DWORD FontStyleFlags;
387 DWORD Reserved;
388 DWORD Length;
389 WCHAR FamilyName[1];
390 } EmfPlusFont;
392 typedef struct EmfPlusObject
394 EmfPlusRecordHeader Header;
395 union
397 EmfPlusBrush brush;
398 EmfPlusPen pen;
399 EmfPlusPath path;
400 EmfPlusRegion region;
401 EmfPlusImage image;
402 EmfPlusImageAttributes image_attributes;
403 EmfPlusFont font;
404 } ObjectData;
405 } EmfPlusObject;
407 typedef struct EmfPlusPointR7
409 BYTE X;
410 BYTE Y;
411 } EmfPlusPointR7;
413 typedef struct EmfPlusPoint
415 short X;
416 short Y;
417 } EmfPlusPoint;
419 typedef struct EmfPlusPointF
421 float X;
422 float Y;
423 } EmfPlusPointF;
425 typedef struct EmfPlusDrawImage
427 EmfPlusRecordHeader Header;
428 DWORD ImageAttributesID;
429 DWORD SrcUnit;
430 EmfPlusRectF SrcRect;
431 union
433 EmfPlusRect rect;
434 EmfPlusRectF rectF;
435 } RectData;
436 } EmfPlusDrawImage;
438 typedef struct EmfPlusDrawImagePoints
440 EmfPlusRecordHeader Header;
441 DWORD ImageAttributesID;
442 DWORD SrcUnit;
443 EmfPlusRectF SrcRect;
444 DWORD count;
445 union
447 EmfPlusPointR7 pointsR[3];
448 EmfPlusPoint points[3];
449 EmfPlusPointF pointsF[3];
450 } PointData;
451 } EmfPlusDrawImagePoints;
453 typedef struct EmfPlusDrawPath
455 EmfPlusRecordHeader Header;
456 DWORD PenId;
457 } EmfPlusDrawPath;
459 typedef struct EmfPlusDrawArc
461 EmfPlusRecordHeader Header;
462 float StartAngle;
463 float SweepAngle;
464 union
466 EmfPlusRect rect;
467 EmfPlusRectF rectF;
468 } RectData;
469 } EmfPlusDrawArc;
471 typedef struct EmfPlusDrawEllipse
473 EmfPlusRecordHeader Header;
474 union
476 EmfPlusRect rect;
477 EmfPlusRectF rectF;
478 } RectData;
479 } EmfPlusDrawEllipse;
481 typedef struct EmfPlusDrawPie
483 EmfPlusRecordHeader Header;
484 float StartAngle;
485 float SweepAngle;
486 union
488 EmfPlusRect rect;
489 EmfPlusRectF rectF;
490 } RectData;
491 } EmfPlusDrawPie;
493 typedef struct EmfPlusDrawRects
495 EmfPlusRecordHeader Header;
496 DWORD Count;
497 union
499 EmfPlusRect rect[1];
500 EmfPlusRectF rectF[1];
501 } RectData;
502 } EmfPlusDrawRects;
504 typedef struct EmfPlusFillPath
506 EmfPlusRecordHeader Header;
507 union
509 DWORD BrushId;
510 EmfPlusARGB Color;
511 } data;
512 } EmfPlusFillPath;
514 typedef struct EmfPlusFillClosedCurve
516 EmfPlusRecordHeader Header;
517 DWORD BrushId;
518 float Tension;
519 DWORD Count;
520 union
522 EmfPlusPointR7 pointsR[1];
523 EmfPlusPoint points[1];
524 EmfPlusPointF pointsF[1];
525 } PointData;
526 } EmfPlusFillClosedCurve;
528 typedef struct EmfPlusFillEllipse
530 EmfPlusRecordHeader Header;
531 DWORD BrushId;
532 union
534 EmfPlusRect rect;
535 EmfPlusRectF rectF;
536 } RectData;
537 } EmfPlusFillEllipse;
539 typedef struct EmfPlusFillPie
541 EmfPlusRecordHeader Header;
542 DWORD BrushId;
543 float StartAngle;
544 float SweepAngle;
545 union
547 EmfPlusRect rect;
548 EmfPlusRectF rectF;
549 } RectData;
550 } EmfPlusFillPie;
552 typedef struct EmfPlusDrawDriverString
554 EmfPlusRecordHeader Header;
555 union
557 DWORD BrushId;
558 ARGB Color;
559 } brush;
560 DWORD DriverStringOptionsFlags;
561 DWORD MatrixPresent;
562 DWORD GlyphCount;
563 BYTE VariableData[1];
564 } EmfPlusDrawDriverString;
566 typedef struct EmfPlusFillRegion
568 EmfPlusRecordHeader Header;
569 union
571 DWORD BrushId;
572 EmfPlusARGB Color;
573 } data;
574 } EmfPlusFillRegion;
576 typedef struct EmfPlusOffsetClip
578 EmfPlusRecordHeader Header;
579 float dx;
580 float dy;
581 } EmfPlusOffsetClip;
583 typedef struct EmfPlusSetRenderingOrigin
585 EmfPlusRecordHeader Header;
586 INT x;
587 INT y;
588 } EmfPlusSetRenderingOrigin;
590 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
592 struct emfplus_object *object = &metafile->objtable[id];
594 switch (object->type)
596 case ObjectTypeInvalid:
597 break;
598 case ObjectTypeBrush:
599 GdipDeleteBrush(object->u.brush);
600 break;
601 case ObjectTypePen:
602 GdipDeletePen(object->u.pen);
603 break;
604 case ObjectTypePath:
605 GdipDeletePath(object->u.path);
606 break;
607 case ObjectTypeRegion:
608 GdipDeleteRegion(object->u.region);
609 break;
610 case ObjectTypeImage:
611 GdipDisposeImage(object->u.image);
612 break;
613 case ObjectTypeFont:
614 GdipDeleteFont(object->u.font);
615 break;
616 case ObjectTypeImageAttributes:
617 GdipDisposeImageAttributes(object->u.image_attributes);
618 break;
619 default:
620 FIXME("not implemented for object type %u.\n", object->type);
621 return;
624 object->type = ObjectTypeInvalid;
625 object->u.object = NULL;
628 void METAFILE_Free(GpMetafile *metafile)
630 unsigned int i;
632 heap_free(metafile->comment_data);
633 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
634 if (!metafile->preserve_hemf)
635 DeleteEnhMetaFile(metafile->hemf);
636 if (metafile->record_graphics)
638 WARN("metafile closed while recording\n");
639 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
640 metafile->record_graphics->image = NULL;
641 metafile->record_graphics->busy = TRUE;
644 if (metafile->record_stream)
645 IStream_Release(metafile->record_stream);
647 for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
648 metafile_free_object_table_entry(metafile, i);
651 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
653 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
656 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, EmfPlusRecordType record_type,
657 DWORD size, void **result)
659 DWORD size_needed;
660 EmfPlusRecordHeader *record;
662 if (!metafile->comment_data_size)
664 DWORD data_size = max(256, size * 2 + 4);
665 metafile->comment_data = heap_alloc_zero(data_size);
667 if (!metafile->comment_data)
668 return OutOfMemory;
670 memcpy(metafile->comment_data, "EMF+", 4);
672 metafile->comment_data_size = data_size;
673 metafile->comment_data_length = 4;
676 size_needed = size + metafile->comment_data_length;
678 if (size_needed > metafile->comment_data_size)
680 DWORD data_size = size_needed * 2;
681 BYTE *new_data = heap_alloc_zero(data_size);
683 if (!new_data)
684 return OutOfMemory;
686 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
688 metafile->comment_data_size = data_size;
689 heap_free(metafile->comment_data);
690 metafile->comment_data = new_data;
693 *result = metafile->comment_data + metafile->comment_data_length;
694 metafile->comment_data_length += size;
696 record = (EmfPlusRecordHeader*)*result;
697 record->Type = record_type;
698 record->Flags = 0;
699 record->Size = size;
700 record->DataSize = size - sizeof(EmfPlusRecordHeader);
702 return Ok;
705 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
707 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
708 metafile->comment_data_length -= record->Size;
711 static void METAFILE_WriteRecords(GpMetafile *metafile)
713 if (metafile->comment_data_length > 4)
715 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
716 metafile->comment_data_length = 4;
720 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
722 GpStatus stat;
724 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
726 EmfPlusHeader *header;
728 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeHeader, sizeof(EmfPlusHeader), (void**)&header);
729 if (stat != Ok)
730 return stat;
732 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
733 header->Header.Flags = 1;
735 header->Version = VERSION_MAGIC2;
737 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
738 header->EmfPlusFlags = 1;
739 else
740 header->EmfPlusFlags = 0;
742 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
743 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
745 METAFILE_WriteRecords(metafile);
748 return Ok;
751 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
753 GpStatus stat;
755 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
757 EmfPlusRecordHeader *record;
759 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndOfFile, sizeof(EmfPlusRecordHeader), (void**)&record);
760 if (stat != Ok)
761 return stat;
763 METAFILE_WriteRecords(metafile);
766 return Ok;
769 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
770 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
773 TRACE("(%p %d %s %d %p %p)\n", hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
775 return GdipRecordMetafileFileName(NULL, hdc, type, frameRect, frameUnit, desc, metafile);
778 /*****************************************************************************
779 * GdipRecordMetafileI [GDIPLUS.@]
781 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
782 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
784 GpRectF frameRectF, *pFrameRectF;
786 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
788 if (frameRect)
790 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
791 pFrameRectF = &frameRectF;
793 else
794 pFrameRectF = NULL;
796 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
799 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
800 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
802 GpRectF frameRectF, *pFrameRectF;
804 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
806 if (frameRect)
808 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
809 pFrameRectF = &frameRectF;
811 else
812 pFrameRectF = NULL;
814 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
817 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
818 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
820 GpStatus stat;
822 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
824 if (!stream)
825 return InvalidParameter;
827 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
829 if (stat == Ok)
831 (*metafile)->record_stream = stream;
832 IStream_AddRef(stream);
835 return stat;
838 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
839 UINT num_points)
841 int i;
843 if (!metafile->auto_frame || !num_points)
844 return;
846 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
847 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
849 for (i=0; i<num_points; i++)
851 if (points[i].X < metafile->auto_frame_min.X)
852 metafile->auto_frame_min.X = points[i].X;
853 if (points[i].X > metafile->auto_frame_max.X)
854 metafile->auto_frame_max.X = points[i].X;
855 if (points[i].Y < metafile->auto_frame_min.Y)
856 metafile->auto_frame_min.Y = points[i].Y;
857 if (points[i].Y > metafile->auto_frame_max.Y)
858 metafile->auto_frame_max.Y = points[i].Y;
862 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
864 GpStatus stat;
866 if (!metafile->record_dc || metafile->record_graphics)
867 return InvalidParameter;
869 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
871 if (stat == Ok)
873 *result = metafile->record_graphics;
874 metafile->record_graphics->xres = metafile->logical_dpix;
875 metafile->record_graphics->yres = metafile->logical_dpiy;
876 metafile->record_graphics->printer_display = metafile->printer_display;
879 return stat;
882 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
884 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
886 EmfPlusRecordHeader *record;
887 GpStatus stat;
889 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeGetDC, sizeof(EmfPlusRecordHeader), (void**)&record);
890 if (stat != Ok)
891 return stat;
893 METAFILE_WriteRecords(metafile);
896 *hdc = metafile->record_dc;
898 return Ok;
901 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
903 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
905 EmfPlusClear *record;
906 GpStatus stat;
908 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeClear, sizeof(EmfPlusClear), (void**)&record);
909 if (stat != Ok)
910 return stat;
912 record->Color = color;
914 METAFILE_WriteRecords(metafile);
917 return Ok;
920 static BOOL is_integer_rect(const GpRectF *rect)
922 SHORT x, y, width, height;
923 x = rect->X;
924 y = rect->Y;
925 width = rect->Width;
926 height = rect->Height;
927 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
928 rect->Width != (REAL)width || rect->Height != (REAL)height)
929 return FALSE;
930 return TRUE;
933 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
935 switch (brush->bt)
937 case BrushTypeSolidColor:
938 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
939 break;
940 case BrushTypeHatchFill:
941 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
942 break;
943 case BrushTypeLinearGradient:
945 BOOL ignore_xform;
946 GpLineGradient *gradient = (GpLineGradient*)brush;
948 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
950 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
951 if (!ignore_xform)
952 *size += sizeof(gradient->transform);
954 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
955 *size += sizeof(DWORD) + gradient->pblendcount *
956 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
957 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
958 *size += sizeof(DWORD) + gradient->blendcount *
959 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
961 break;
963 default:
964 FIXME("unsupported brush type: %d\n", brush->bt);
965 return NotImplemented;
968 return Ok;
971 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
973 data->Version = VERSION_MAGIC2;
974 data->Type = brush->bt;
976 switch (brush->bt)
978 case BrushTypeSolidColor:
980 GpSolidFill *solid = (GpSolidFill *)brush;
981 data->BrushData.solid.SolidColor = solid->color;
982 break;
984 case BrushTypeHatchFill:
986 GpHatch *hatch = (GpHatch *)brush;
987 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
988 data->BrushData.hatch.ForeColor = hatch->forecol;
989 data->BrushData.hatch.BackColor = hatch->backcol;
990 break;
992 case BrushTypeLinearGradient:
994 BYTE *cursor;
995 BOOL ignore_xform;
996 GpLineGradient *gradient = (GpLineGradient*)brush;
998 data->BrushData.lineargradient.BrushDataFlags = 0;
999 data->BrushData.lineargradient.WrapMode = gradient->wrap;
1000 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
1001 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
1002 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
1003 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
1004 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1005 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1006 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1007 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1009 if (gradient->gamma)
1010 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1012 cursor = &data->BrushData.lineargradient.OptionalData[0];
1014 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1015 if (!ignore_xform)
1017 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1018 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1019 cursor += sizeof(gradient->transform);
1022 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1024 const DWORD count = gradient->pblendcount;
1026 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1028 memcpy(cursor, &count, sizeof(count));
1029 cursor += sizeof(count);
1031 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1032 cursor += count * sizeof(*gradient->pblendpos);
1034 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1036 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1038 const DWORD count = gradient->blendcount;
1040 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1042 memcpy(cursor, &count, sizeof(count));
1043 cursor += sizeof(count);
1045 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1046 cursor += count * sizeof(*gradient->blendpos);
1048 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1051 break;
1053 default:
1054 FIXME("unsupported brush type: %d\n", brush->bt);
1058 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1060 EmfPlusObject *object_record;
1061 GpStatus stat;
1062 DWORD size;
1064 *id = -1;
1065 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1066 return Ok;
1068 stat = METAFILE_PrepareBrushData(brush, &size);
1069 if (stat != Ok) return stat;
1071 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1072 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1073 if (stat != Ok) return stat;
1075 *id = METAFILE_AddObjectId(metafile);
1076 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1077 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1078 return Ok;
1081 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1082 GDIPCONST GpRectF* rects, INT count)
1084 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1086 EmfPlusFillRects *record;
1087 GpStatus stat;
1088 BOOL integer_rects = TRUE;
1089 int i;
1090 DWORD brushid;
1091 int flags = 0;
1093 if (brush->bt == BrushTypeSolidColor)
1095 flags |= 0x8000;
1096 brushid = ((GpSolidFill*)brush)->color;
1098 else
1100 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1101 if (stat != Ok)
1102 return stat;
1105 for (i=0; i<count; i++)
1107 if (!is_integer_rect(&rects[i]))
1109 integer_rects = FALSE;
1110 break;
1114 if (integer_rects)
1115 flags |= 0x4000;
1117 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRects,
1118 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1119 (void**)&record);
1120 if (stat != Ok)
1121 return stat;
1123 record->Header.Flags = flags;
1124 record->BrushID = brushid;
1125 record->Count = count;
1127 if (integer_rects)
1129 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1130 for (i=0; i<count; i++)
1132 record_rects[i].X = (SHORT)rects[i].X;
1133 record_rects[i].Y = (SHORT)rects[i].Y;
1134 record_rects[i].Width = (SHORT)rects[i].Width;
1135 record_rects[i].Height = (SHORT)rects[i].Height;
1138 else
1139 memcpy(record+1, rects, sizeof(GpRectF) * count);
1141 METAFILE_WriteRecords(metafile);
1144 if (metafile->auto_frame)
1146 GpPointF corners[4];
1147 int i;
1149 for (i=0; i<count; i++)
1151 corners[0].X = rects[i].X;
1152 corners[0].Y = rects[i].Y;
1153 corners[1].X = rects[i].X + rects[i].Width;
1154 corners[1].Y = rects[i].Y;
1155 corners[2].X = rects[i].X;
1156 corners[2].Y = rects[i].Y + rects[i].Height;
1157 corners[3].X = rects[i].X + rects[i].Width;
1158 corners[3].Y = rects[i].Y + rects[i].Height;
1160 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1161 CoordinateSpaceWorld, corners, 4);
1163 METAFILE_AdjustFrame(metafile, corners, 4);
1167 return Ok;
1170 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1172 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1174 EmfPlusSetClipRect *record;
1175 GpStatus stat;
1177 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRect,
1178 sizeof(EmfPlusSetClipRect), (void **)&record);
1179 if (stat != Ok)
1180 return stat;
1182 record->Header.Flags = (mode & 0xf) << 8;
1183 record->ClipRect.X = x;
1184 record->ClipRect.Y = y;
1185 record->ClipRect.Width = width;
1186 record->ClipRect.Height = height;
1188 METAFILE_WriteRecords(metafile);
1191 return Ok;
1194 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1196 EmfPlusObject *object_record;
1197 DWORD size;
1198 GpStatus stat;
1200 *id = -1;
1201 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1202 return Ok;
1204 size = write_region_data(region, NULL);
1205 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1206 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1207 if (stat != Ok) return stat;
1209 *id = METAFILE_AddObjectId(metafile);
1210 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1211 write_region_data(region, &object_record->ObjectData.region);
1212 return Ok;
1215 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1217 EmfPlusRecordHeader *record;
1218 DWORD region_id;
1219 GpStatus stat;
1221 if (metafile->metafile_type == MetafileTypeEmf)
1223 FIXME("stub!\n");
1224 return NotImplemented;
1227 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1228 if (stat != Ok) return stat;
1230 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRegion, sizeof(*record), (void**)&record);
1231 if (stat != Ok) return stat;
1233 record->Flags = region_id | mode << 8;
1235 METAFILE_WriteRecords(metafile);
1236 return Ok;
1239 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1241 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1243 EmfPlusSetPageTransform *record;
1244 GpStatus stat;
1246 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetPageTransform,
1247 sizeof(EmfPlusSetPageTransform), (void **)&record);
1248 if (stat != Ok)
1249 return stat;
1251 record->Header.Flags = unit;
1252 record->PageScale = scale;
1254 METAFILE_WriteRecords(metafile);
1257 return Ok;
1260 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1262 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1264 EmfPlusSetWorldTransform *record;
1265 GpStatus stat;
1267 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetWorldTransform,
1268 sizeof(EmfPlusSetWorldTransform), (void **)&record);
1269 if (stat != Ok)
1270 return stat;
1272 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1274 METAFILE_WriteRecords(metafile);
1277 return Ok;
1280 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1282 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1284 EmfPlusScaleWorldTransform *record;
1285 GpStatus stat;
1287 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeScaleWorldTransform,
1288 sizeof(EmfPlusScaleWorldTransform), (void **)&record);
1289 if (stat != Ok)
1290 return stat;
1292 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1293 record->Sx = sx;
1294 record->Sy = sy;
1296 METAFILE_WriteRecords(metafile);
1299 return Ok;
1302 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1304 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1306 EmfPlusMultiplyWorldTransform *record;
1307 GpStatus stat;
1309 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeMultiplyWorldTransform,
1310 sizeof(EmfPlusMultiplyWorldTransform), (void **)&record);
1311 if (stat != Ok)
1312 return stat;
1314 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1315 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1317 METAFILE_WriteRecords(metafile);
1320 return Ok;
1323 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1325 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1327 EmfPlusRotateWorldTransform *record;
1328 GpStatus stat;
1330 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRotateWorldTransform,
1331 sizeof(EmfPlusRotateWorldTransform), (void **)&record);
1332 if (stat != Ok)
1333 return stat;
1335 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1336 record->Angle = angle;
1338 METAFILE_WriteRecords(metafile);
1341 return Ok;
1344 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1346 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1348 EmfPlusTranslateWorldTransform *record;
1349 GpStatus stat;
1351 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeTranslateWorldTransform,
1352 sizeof(EmfPlusTranslateWorldTransform), (void **)&record);
1353 if (stat != Ok)
1354 return stat;
1356 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1357 record->dx = dx;
1358 record->dy = dy;
1360 METAFILE_WriteRecords(metafile);
1363 return Ok;
1366 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1368 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1370 EmfPlusRecordHeader *record;
1371 GpStatus stat;
1373 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetWorldTransform,
1374 sizeof(EmfPlusRecordHeader), (void **)&record);
1375 if (stat != Ok)
1376 return stat;
1378 METAFILE_WriteRecords(metafile);
1381 return Ok;
1384 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1385 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1387 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1389 EmfPlusBeginContainer *record;
1390 GpStatus stat;
1392 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainer, sizeof(*record), (void**)&record);
1393 if (stat != Ok)
1394 return stat;
1396 record->Header.Flags = unit & 0xff;
1397 record->DestRect = *dstrect;
1398 record->SrcRect = *srcrect;
1399 record->StackIndex = StackIndex;
1401 METAFILE_WriteRecords(metafile);
1404 return Ok;
1407 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1409 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1411 EmfPlusContainerRecord *record;
1412 GpStatus stat;
1414 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainerNoParams,
1415 sizeof(EmfPlusContainerRecord), (void **)&record);
1416 if (stat != Ok)
1417 return stat;
1419 record->StackIndex = StackIndex;
1421 METAFILE_WriteRecords(metafile);
1424 return Ok;
1427 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1429 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1431 EmfPlusContainerRecord *record;
1432 GpStatus stat;
1434 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndContainer,
1435 sizeof(EmfPlusContainerRecord), (void **)&record);
1436 if (stat != Ok)
1437 return stat;
1439 record->StackIndex = StackIndex;
1441 METAFILE_WriteRecords(metafile);
1444 return Ok;
1447 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1449 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1451 EmfPlusContainerRecord *record;
1452 GpStatus stat;
1454 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSave,
1455 sizeof(EmfPlusContainerRecord), (void **)&record);
1456 if (stat != Ok)
1457 return stat;
1459 record->StackIndex = StackIndex;
1461 METAFILE_WriteRecords(metafile);
1464 return Ok;
1467 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1469 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1471 EmfPlusContainerRecord *record;
1472 GpStatus stat;
1474 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRestore,
1475 sizeof(EmfPlusContainerRecord), (void **)&record);
1476 if (stat != Ok)
1477 return stat;
1479 record->StackIndex = StackIndex;
1481 METAFILE_WriteRecords(metafile);
1484 return Ok;
1487 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1489 if (hdc != metafile->record_dc)
1490 return InvalidParameter;
1492 return Ok;
1495 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1497 GpStatus stat;
1499 stat = METAFILE_WriteEndOfFile(metafile);
1500 metafile->record_graphics = NULL;
1502 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1503 metafile->record_dc = NULL;
1505 heap_free(metafile->comment_data);
1506 metafile->comment_data = NULL;
1507 metafile->comment_data_size = 0;
1509 if (stat == Ok)
1511 MetafileHeader header;
1513 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1514 if (stat == Ok && metafile->auto_frame &&
1515 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1517 RECTL bounds_rc, gdi_bounds_rc;
1518 REAL x_scale = 2540.0 / header.DpiX;
1519 REAL y_scale = 2540.0 / header.DpiY;
1520 BYTE* buffer;
1521 UINT buffer_size;
1523 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1524 if (gdi_bounds_rc.right > gdi_bounds_rc.left &&
1525 gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1527 GpPointF *af_min = &metafile->auto_frame_min;
1528 GpPointF *af_max = &metafile->auto_frame_max;
1530 af_min->X = fmin(af_min->X, gdi_bounds_rc.left);
1531 af_min->Y = fmin(af_min->Y, gdi_bounds_rc.top);
1532 af_max->X = fmax(af_max->X, gdi_bounds_rc.right);
1533 af_max->Y = fmax(af_max->Y, gdi_bounds_rc.bottom);
1536 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1537 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1538 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1539 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1541 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1542 buffer = heap_alloc(buffer_size);
1543 if (buffer)
1545 HENHMETAFILE new_hemf;
1547 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1549 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1551 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1553 if (new_hemf)
1555 DeleteEnhMetaFile(metafile->hemf);
1556 metafile->hemf = new_hemf;
1558 else
1559 stat = OutOfMemory;
1561 heap_free(buffer);
1563 else
1564 stat = OutOfMemory;
1566 if (stat == Ok)
1567 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1569 if (stat == Ok)
1571 metafile->bounds.X = header.X;
1572 metafile->bounds.Y = header.Y;
1573 metafile->bounds.Width = header.Width;
1574 metafile->bounds.Height = header.Height;
1578 if (stat == Ok && metafile->record_stream)
1580 BYTE *buffer;
1581 UINT buffer_size;
1583 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1585 buffer = heap_alloc(buffer_size);
1586 if (buffer)
1588 HRESULT hr;
1590 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1592 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1594 if (FAILED(hr))
1595 stat = hresult_to_status(hr);
1597 heap_free(buffer);
1599 else
1600 stat = OutOfMemory;
1603 if (metafile->record_stream)
1605 IStream_Release(metafile->record_stream);
1606 metafile->record_stream = NULL;
1609 return stat;
1612 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1614 TRACE("(%p,%p)\n", metafile, hEmf);
1616 if (!metafile || !hEmf || !metafile->hemf)
1617 return InvalidParameter;
1619 *hEmf = metafile->hemf;
1620 metafile->hemf = NULL;
1622 return Ok;
1625 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1627 GpStatus stat = Ok;
1629 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1631 return stat;
1634 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1636 if (metafile->playback_dc)
1638 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1639 metafile->playback_dc = NULL;
1643 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1645 GpStatus stat;
1646 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1647 if (stat == Ok)
1648 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1649 return stat;
1652 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1654 GpMatrix *real_transform;
1655 GpStatus stat;
1657 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1659 if (stat == Ok)
1661 REAL scale_x = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpix, metafile->printer_display);
1662 REAL scale_y = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpiy, metafile->printer_display);
1664 if (metafile->page_unit != UnitDisplay)
1666 scale_x *= metafile->page_scale;
1667 scale_y *= metafile->page_scale;
1670 stat = GdipScaleMatrix(real_transform, scale_x, scale_y, MatrixOrderPrepend);
1672 if (stat == Ok)
1673 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1675 if (stat == Ok)
1676 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1678 GdipDeleteMatrix(real_transform);
1681 return stat;
1684 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1686 metafile_free_object_table_entry(metafile, id);
1687 metafile->objtable[id].type = type;
1688 metafile->objtable[id].u.object = object;
1691 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1693 EmfPlusImage *data = (EmfPlusImage *)record_data;
1694 GpStatus status;
1696 *image = NULL;
1698 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1699 return InvalidParameter;
1700 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1702 switch (data->Type)
1704 case ImageDataTypeBitmap:
1706 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1708 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1709 return InvalidParameter;
1710 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1712 switch (bitmapdata->Type)
1714 case BitmapDataTypePixel:
1716 ColorPalette *palette;
1717 BYTE *scan0;
1719 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1721 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1722 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1724 if (data_size <= palette_size)
1725 return InvalidParameter;
1726 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1728 if (data_size < palette_size)
1729 return InvalidParameter;
1730 data_size -= palette_size;
1732 palette = (ColorPalette *)bitmapdata->BitmapData;
1733 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1735 else
1737 palette = NULL;
1738 scan0 = bitmapdata->BitmapData;
1741 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1742 return InvalidParameter;
1744 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1745 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1746 if (status == Ok && palette)
1748 status = GdipSetImagePalette(*image, palette);
1749 if (status != Ok)
1751 GdipDisposeImage(*image);
1752 *image = NULL;
1755 break;
1757 case BitmapDataTypeCompressed:
1759 IWICImagingFactory *factory;
1760 IWICStream *stream;
1761 HRESULT hr;
1763 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1764 return GenericError;
1766 hr = IWICImagingFactory_CreateStream(factory, &stream);
1767 IWICImagingFactory_Release(factory);
1768 if (hr != S_OK)
1769 return GenericError;
1771 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1772 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1773 else
1774 status = GenericError;
1776 IWICStream_Release(stream);
1777 break;
1779 default:
1780 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1781 return InvalidParameter;
1783 break;
1785 case ImageDataTypeMetafile:
1787 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1789 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1790 return InvalidParameter;
1791 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1793 switch (metafiledata->Type) {
1794 case MetafileTypeEmf:
1795 case MetafileTypeEmfPlusOnly:
1796 case MetafileTypeEmfPlusDual:
1798 HENHMETAFILE hemf;
1800 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1802 if (!hemf)
1803 return GenericError;
1805 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1807 if (status != Ok)
1808 DeleteEnhMetaFile(hemf);
1810 break;
1812 default:
1813 FIXME("metafile type %d not supported.\n", metafiledata->Type);
1814 return NotImplemented;
1816 break;
1818 default:
1819 FIXME("image type %d not supported.\n", data->Type);
1820 return NotImplemented;
1823 return status;
1826 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1828 EmfPlusPath *data = (EmfPlusPath *)record_data;
1829 GpStatus status;
1830 BYTE *types;
1831 UINT size;
1832 DWORD i;
1834 *path = NULL;
1836 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1837 return InvalidParameter;
1838 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1840 if (data->PathPointFlags & 0x800) /* R */
1842 FIXME("RLE encoded path data is not supported.\n");
1843 return NotImplemented;
1845 else
1847 if (data->PathPointFlags & 0x4000) /* C */
1848 size = sizeof(EmfPlusPoint);
1849 else
1850 size = sizeof(EmfPlusPointF);
1851 size += sizeof(BYTE); /* EmfPlusPathPointType */
1852 size *= data->PathPointCount;
1855 if (data_size < size)
1856 return InvalidParameter;
1858 status = GdipCreatePath(FillModeAlternate, path);
1859 if (status != Ok)
1860 return status;
1862 (*path)->pathdata.Count = data->PathPointCount;
1863 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1864 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1865 (*path)->datalen = (*path)->pathdata.Count;
1867 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1869 GdipDeletePath(*path);
1870 return OutOfMemory;
1873 if (data->PathPointFlags & 0x4000) /* C */
1875 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1876 for (i = 0; i < data->PathPointCount; i++)
1878 (*path)->pathdata.Points[i].X = points[i].X;
1879 (*path)->pathdata.Points[i].Y = points[i].Y;
1881 types = (BYTE *)(points + i);
1883 else
1885 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1886 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1887 types = (BYTE *)(points + data->PathPointCount);
1890 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1892 return Ok;
1895 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1897 const DWORD *type;
1898 GpStatus status;
1900 type = buffer_read(mbuf, sizeof(*type));
1901 if (!type) return Ok;
1903 node->type = *type;
1905 switch (node->type)
1907 case CombineModeReplace:
1908 case CombineModeIntersect:
1909 case CombineModeUnion:
1910 case CombineModeXor:
1911 case CombineModeExclude:
1912 case CombineModeComplement:
1914 region_element *left, *right;
1916 left = heap_alloc_zero(sizeof(*left));
1917 if (!left)
1918 return OutOfMemory;
1920 right = heap_alloc_zero(sizeof(*right));
1921 if (!right)
1923 heap_free(left);
1924 return OutOfMemory;
1927 status = metafile_read_region_node(mbuf, region, left, count);
1928 if (status == Ok)
1930 status = metafile_read_region_node(mbuf, region, right, count);
1931 if (status == Ok)
1933 node->elementdata.combine.left = left;
1934 node->elementdata.combine.right = right;
1935 region->num_children += 2;
1936 return Ok;
1940 heap_free(left);
1941 heap_free(right);
1942 return status;
1944 case RegionDataRect:
1946 const EmfPlusRectF *rect;
1948 rect = buffer_read(mbuf, sizeof(*rect));
1949 if (!rect)
1950 return InvalidParameter;
1952 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1953 *count += 1;
1954 return Ok;
1956 case RegionDataPath:
1958 const BYTE *path_data;
1959 const UINT *data_size;
1960 GpPath *path;
1962 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1963 if (!data_size)
1964 return InvalidParameter;
1966 path_data = buffer_read(mbuf, *data_size);
1967 if (!path_data)
1968 return InvalidParameter;
1970 status = metafile_deserialize_path(path_data, *data_size, &path);
1971 if (status == Ok)
1973 node->elementdata.path = path;
1974 *count += 1;
1976 return Ok;
1978 case RegionDataEmptyRect:
1979 case RegionDataInfiniteRect:
1980 *count += 1;
1981 return Ok;
1982 default:
1983 FIXME("element type %#x is not supported\n", *type);
1984 break;
1987 return InvalidParameter;
1990 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1992 struct memory_buffer mbuf;
1993 GpStatus status;
1994 UINT count;
1996 *region = NULL;
1998 init_memory_buffer(&mbuf, record_data, data_size);
2000 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2001 return InvalidParameter;
2003 status = GdipCreateRegion(region);
2004 if (status != Ok)
2005 return status;
2007 count = 0;
2008 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2009 if (status == Ok && !count)
2010 status = InvalidParameter;
2012 if (status != Ok)
2014 GdipDeleteRegion(*region);
2015 *region = NULL;
2018 return status;
2021 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2023 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2024 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2025 EmfPlusTransformMatrix *transform = NULL;
2026 DWORD brushflags;
2027 GpStatus status;
2028 UINT offset;
2030 *brush = NULL;
2032 if (data_size < header_size)
2033 return InvalidParameter;
2035 switch (data->Type)
2037 case BrushTypeSolidColor:
2038 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2039 return InvalidParameter;
2041 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2042 break;
2043 case BrushTypeHatchFill:
2044 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2045 return InvalidParameter;
2047 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2048 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2049 break;
2050 case BrushTypeTextureFill:
2052 GpImage *image;
2054 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2055 if (data_size <= offset)
2056 return InvalidParameter;
2058 brushflags = data->BrushData.texture.BrushDataFlags;
2059 if (brushflags & BrushDataTransform)
2061 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2062 return InvalidParameter;
2063 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2064 offset += sizeof(EmfPlusTransformMatrix);
2067 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2068 if (status != Ok)
2069 return status;
2071 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2072 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2073 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2075 GdipDisposeImage(image);
2076 break;
2078 case BrushTypeLinearGradient:
2080 GpLineGradient *gradient = NULL;
2081 GpRectF rect;
2082 UINT position_count = 0;
2084 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2085 if (data_size < offset)
2086 return InvalidParameter;
2088 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2089 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2090 return InvalidParameter;
2092 if (brushflags & BrushDataTransform)
2094 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2095 return InvalidParameter;
2096 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2097 offset += sizeof(EmfPlusTransformMatrix);
2100 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2102 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2103 return InvalidParameter;
2104 position_count = *(DWORD *)(record_data + offset);
2105 offset += sizeof(DWORD);
2108 if (brushflags & BrushDataPresetColors)
2110 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2111 return InvalidParameter;
2113 else if (brushflags & BrushDataBlendFactorsH)
2115 if (data_size != offset + position_count * 2 * sizeof(float))
2116 return InvalidParameter;
2119 rect.X = data->BrushData.lineargradient.RectF.X;
2120 rect.Y = data->BrushData.lineargradient.RectF.Y;
2121 rect.Width = data->BrushData.lineargradient.RectF.Width;
2122 rect.Height = data->BrushData.lineargradient.RectF.Height;
2124 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2125 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2126 data->BrushData.lineargradient.WrapMode, &gradient);
2127 if (status == Ok)
2129 if (transform)
2130 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2132 if (status == Ok)
2134 if (brushflags & BrushDataPresetColors)
2135 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2136 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2137 else if (brushflags & BrushDataBlendFactorsH)
2138 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2139 (REAL *)(record_data + offset), position_count);
2141 if (brushflags & BrushDataIsGammaCorrected)
2142 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2146 if (status == Ok)
2147 *brush = (GpBrush *)gradient;
2148 else
2149 GdipDeleteBrush((GpBrush *)gradient);
2151 break;
2153 default:
2154 FIXME("brush type %u is not supported.\n", data->Type);
2155 return NotImplemented;
2158 return status;
2161 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2163 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2164 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2166 if (data_size <= offset)
2167 return InvalidParameter;
2169 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2170 if (data_size <= offset)
2171 return InvalidParameter;
2173 if (pendata->PenDataFlags & PenDataTransform)
2174 offset += sizeof(EmfPlusTransformMatrix);
2176 if (pendata->PenDataFlags & PenDataStartCap)
2177 offset += sizeof(DWORD);
2179 if (pendata->PenDataFlags & PenDataEndCap)
2180 offset += sizeof(DWORD);
2182 if (pendata->PenDataFlags & PenDataJoin)
2183 offset += sizeof(DWORD);
2185 if (pendata->PenDataFlags & PenDataMiterLimit)
2186 offset += sizeof(REAL);
2188 if (pendata->PenDataFlags & PenDataLineStyle)
2189 offset += sizeof(DWORD);
2191 if (pendata->PenDataFlags & PenDataDashedLineCap)
2192 offset += sizeof(DWORD);
2194 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2195 offset += sizeof(REAL);
2197 if (pendata->PenDataFlags & PenDataDashedLine)
2199 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2201 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2202 if (data_size <= offset)
2203 return InvalidParameter;
2205 offset += dashedline->DashedLineDataSize * sizeof(float);
2208 if (pendata->PenDataFlags & PenDataNonCenter)
2209 offset += sizeof(DWORD);
2211 if (pendata->PenDataFlags & PenDataCompoundLine)
2213 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2215 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2216 if (data_size <= offset)
2217 return InvalidParameter;
2219 offset += compoundline->CompoundLineDataSize * sizeof(float);
2222 if (pendata->PenDataFlags & PenDataCustomStartCap)
2224 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2226 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2227 if (data_size <= offset)
2228 return InvalidParameter;
2230 offset += startcap->CustomStartCapSize;
2233 if (pendata->PenDataFlags & PenDataCustomEndCap)
2235 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2237 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2238 if (data_size <= offset)
2239 return InvalidParameter;
2241 offset += endcap->CustomEndCapSize;
2244 *ret = offset;
2245 return Ok;
2248 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2250 BYTE type = (flags >> 8) & 0xff;
2251 BYTE id = flags & 0xff;
2252 void *object = NULL;
2253 GpStatus status;
2255 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2256 return InvalidParameter;
2258 switch (type)
2260 case ObjectTypeBrush:
2261 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2262 break;
2263 case ObjectTypePen:
2265 EmfPlusPen *data = (EmfPlusPen *)record_data;
2266 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2267 GpBrush *brush;
2268 DWORD offset;
2269 GpPen *pen;
2271 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2272 if (status != Ok)
2273 return status;
2275 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2276 if (status != Ok)
2277 return status;
2279 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2280 GdipDeleteBrush(brush);
2281 if (status != Ok)
2282 return status;
2284 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2286 if (pendata->PenDataFlags & PenDataTransform)
2288 FIXME("PenDataTransform is not supported.\n");
2289 offset += sizeof(EmfPlusTransformMatrix);
2292 if (pendata->PenDataFlags & PenDataStartCap)
2294 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2295 goto penfailed;
2296 offset += sizeof(DWORD);
2299 if (pendata->PenDataFlags & PenDataEndCap)
2301 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2302 goto penfailed;
2303 offset += sizeof(DWORD);
2306 if (pendata->PenDataFlags & PenDataJoin)
2308 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2309 goto penfailed;
2310 offset += sizeof(DWORD);
2313 if (pendata->PenDataFlags & PenDataMiterLimit)
2315 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2316 goto penfailed;
2317 offset += sizeof(REAL);
2320 if (pendata->PenDataFlags & PenDataLineStyle)
2322 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2323 goto penfailed;
2324 offset += sizeof(DWORD);
2327 if (pendata->PenDataFlags & PenDataDashedLineCap)
2329 FIXME("PenDataDashedLineCap is not supported.\n");
2330 offset += sizeof(DWORD);
2333 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2335 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2336 goto penfailed;
2337 offset += sizeof(REAL);
2340 if (pendata->PenDataFlags & PenDataDashedLine)
2342 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2343 FIXME("PenDataDashedLine is not supported.\n");
2344 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2347 if (pendata->PenDataFlags & PenDataNonCenter)
2349 FIXME("PenDataNonCenter is not supported.\n");
2350 offset += sizeof(DWORD);
2353 if (pendata->PenDataFlags & PenDataCompoundLine)
2355 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2356 FIXME("PenDataCompoundLine is not supported.\n");
2357 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2360 if (pendata->PenDataFlags & PenDataCustomStartCap)
2362 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2363 FIXME("PenDataCustomStartCap is not supported.\n");
2364 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2367 if (pendata->PenDataFlags & PenDataCustomEndCap)
2369 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2370 FIXME("PenDataCustomEndCap is not supported.\n");
2371 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2374 object = pen;
2375 break;
2377 penfailed:
2378 GdipDeletePen(pen);
2379 return status;
2381 case ObjectTypePath:
2382 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2383 break;
2384 case ObjectTypeRegion:
2385 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2386 break;
2387 case ObjectTypeImage:
2388 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2389 break;
2390 case ObjectTypeFont:
2392 EmfPlusFont *data = (EmfPlusFont *)record_data;
2393 GpFontFamily *family;
2394 WCHAR *familyname;
2396 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2397 return InvalidParameter;
2398 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2400 if (data_size < data->Length * sizeof(WCHAR))
2401 return InvalidParameter;
2403 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2404 return OutOfMemory;
2406 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2407 familyname[data->Length] = 0;
2409 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2410 GdipFree(familyname);
2412 /* If a font family cannot be created from family name, native
2413 falls back to a sans serif font. */
2414 if (status != Ok)
2415 status = GdipGetGenericFontFamilySansSerif(&family);
2416 if (status != Ok)
2417 return status;
2419 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2420 GdipDeleteFontFamily(family);
2421 break;
2423 case ObjectTypeImageAttributes:
2425 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2426 GpImageAttributes *attributes = NULL;
2428 if (data_size != sizeof(*data))
2429 return InvalidParameter;
2431 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2432 return status;
2434 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2435 !!data->ObjectClamp);
2436 if (status == Ok)
2437 object = attributes;
2438 else
2439 GdipDisposeImageAttributes(attributes);
2440 break;
2442 default:
2443 FIXME("not implemented for object type %d.\n", type);
2444 return NotImplemented;
2447 if (status == Ok)
2448 metafile_set_object_table_entry(metafile, id, type, object);
2450 return status;
2453 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2455 GpMatrix world_to_device;
2457 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2459 GdipTransformRegion(region, &world_to_device);
2460 GdipCombineRegionRegion(metafile->clip, region, mode);
2462 return METAFILE_PlaybackUpdateClip(metafile);
2465 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2466 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2468 GpStatus stat;
2469 GpMetafile *real_metafile = (GpMetafile*)metafile;
2471 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2473 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2474 return InvalidParameter;
2476 if (recordType >= 1 && recordType <= 0x7a)
2478 /* regular EMF record */
2479 if (metafile->playback_dc)
2481 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2483 if (record)
2485 record->iType = recordType;
2486 record->nSize = dataSize + 8;
2487 memcpy(record->dParm, data, dataSize);
2489 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2490 record, metafile->handle_count) == 0)
2491 ERR("PlayEnhMetaFileRecord failed\n");
2493 heap_free(record);
2495 else
2496 return OutOfMemory;
2499 else
2501 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2503 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2505 switch(recordType)
2507 case EmfPlusRecordTypeHeader:
2508 case EmfPlusRecordTypeEndOfFile:
2509 break;
2510 case EmfPlusRecordTypeGetDC:
2511 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2512 break;
2513 case EmfPlusRecordTypeClear:
2515 EmfPlusClear *record = (EmfPlusClear*)header;
2517 if (dataSize != sizeof(record->Color))
2518 return InvalidParameter;
2520 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2522 case EmfPlusRecordTypeFillRects:
2524 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2525 GpBrush *brush, *temp_brush=NULL;
2526 GpRectF *rects, *temp_rects=NULL;
2528 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2529 return InvalidParameter;
2531 if (flags & 0x4000)
2533 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2534 return InvalidParameter;
2536 else
2538 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2539 return InvalidParameter;
2542 if (flags & 0x8000)
2544 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2545 brush = temp_brush;
2547 else
2549 if (record->BrushID >= EmfPlusObjectTableSize ||
2550 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2551 return InvalidParameter;
2553 brush = real_metafile->objtable[record->BrushID].u.brush;
2554 stat = Ok;
2557 if (stat == Ok)
2559 if (flags & 0x4000)
2561 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2562 int i;
2564 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2565 if (rects)
2567 for (i=0; i<record->Count; i++)
2569 rects[i].X = int_rects[i].X;
2570 rects[i].Y = int_rects[i].Y;
2571 rects[i].Width = int_rects[i].Width;
2572 rects[i].Height = int_rects[i].Height;
2575 else
2576 stat = OutOfMemory;
2578 else
2579 rects = (GpRectF*)(record+1);
2582 if (stat == Ok)
2584 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2587 GdipDeleteBrush(temp_brush);
2588 heap_free(temp_rects);
2590 return stat;
2592 case EmfPlusRecordTypeSetClipRect:
2594 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2595 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2596 GpRegion *region;
2598 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2599 return InvalidParameter;
2601 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2603 if (stat == Ok)
2605 stat = metafile_set_clip_region(real_metafile, region, mode);
2606 GdipDeleteRegion(region);
2609 return stat;
2611 case EmfPlusRecordTypeSetClipRegion:
2613 CombineMode mode = (flags >> 8) & 0xf;
2614 BYTE regionid = flags & 0xff;
2615 GpRegion *region;
2617 if (dataSize != 0)
2618 return InvalidParameter;
2620 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2621 return InvalidParameter;
2623 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2624 if (stat == Ok)
2626 stat = metafile_set_clip_region(real_metafile, region, mode);
2627 GdipDeleteRegion(region);
2630 return stat;
2632 case EmfPlusRecordTypeSetClipPath:
2634 CombineMode mode = (flags >> 8) & 0xf;
2635 BYTE pathid = flags & 0xff;
2636 GpRegion *region;
2638 if (dataSize != 0)
2639 return InvalidParameter;
2641 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2642 return InvalidParameter;
2644 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2645 if (stat == Ok)
2647 stat = metafile_set_clip_region(real_metafile, region, mode);
2648 GdipDeleteRegion(region);
2651 return stat;
2653 case EmfPlusRecordTypeSetPageTransform:
2655 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2656 GpUnit unit = (GpUnit)flags;
2658 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2659 return InvalidParameter;
2661 real_metafile->page_unit = unit;
2662 real_metafile->page_scale = record->PageScale;
2664 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2666 case EmfPlusRecordTypeSetWorldTransform:
2668 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2670 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2671 return InvalidParameter;
2673 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2675 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2677 case EmfPlusRecordTypeScaleWorldTransform:
2679 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2680 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2682 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2683 return InvalidParameter;
2685 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2687 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2689 case EmfPlusRecordTypeMultiplyWorldTransform:
2691 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2692 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2693 GpMatrix matrix;
2695 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2696 return InvalidParameter;
2698 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2700 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2702 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2704 case EmfPlusRecordTypeRotateWorldTransform:
2706 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2707 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2709 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2710 return InvalidParameter;
2712 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2714 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2716 case EmfPlusRecordTypeTranslateWorldTransform:
2718 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2719 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2721 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2722 return InvalidParameter;
2724 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2726 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2728 case EmfPlusRecordTypeResetWorldTransform:
2730 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2732 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2734 case EmfPlusRecordTypeBeginContainer:
2736 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2737 container* cont;
2738 GpUnit unit;
2739 REAL scale_x, scale_y;
2740 GpRectF scaled_srcrect;
2741 GpMatrix transform;
2743 cont = heap_alloc_zero(sizeof(*cont));
2744 if (!cont)
2745 return OutOfMemory;
2747 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2748 if (stat != Ok)
2750 heap_free(cont);
2751 return stat;
2754 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2756 if (stat != Ok)
2758 GdipDeleteRegion(cont->clip);
2759 heap_free(cont);
2760 return stat;
2763 cont->id = record->StackIndex;
2764 cont->type = BEGIN_CONTAINER;
2765 cont->world_transform = *metafile->world_transform;
2766 cont->page_unit = metafile->page_unit;
2767 cont->page_scale = metafile->page_scale;
2768 list_add_head(&real_metafile->containers, &cont->entry);
2770 unit = record->Header.Flags & 0xff;
2772 scale_x = units_to_pixels(1.0, unit, metafile->image.xres, metafile->printer_display);
2773 scale_y = units_to_pixels(1.0, unit, metafile->image.yres, metafile->printer_display);
2775 scaled_srcrect.X = scale_x * record->SrcRect.X;
2776 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2777 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2778 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2780 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2781 transform.matrix[1] = 0.0;
2782 transform.matrix[2] = 0.0;
2783 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2784 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2785 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2787 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2789 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2791 case EmfPlusRecordTypeBeginContainerNoParams:
2792 case EmfPlusRecordTypeSave:
2794 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2795 container* cont;
2797 cont = heap_alloc_zero(sizeof(*cont));
2798 if (!cont)
2799 return OutOfMemory;
2801 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2802 if (stat != Ok)
2804 heap_free(cont);
2805 return stat;
2808 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2809 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2810 else
2811 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2813 if (stat != Ok)
2815 GdipDeleteRegion(cont->clip);
2816 heap_free(cont);
2817 return stat;
2820 cont->id = record->StackIndex;
2821 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2822 cont->type = BEGIN_CONTAINER;
2823 else
2824 cont->type = SAVE_GRAPHICS;
2825 cont->world_transform = *metafile->world_transform;
2826 cont->page_unit = metafile->page_unit;
2827 cont->page_scale = metafile->page_scale;
2828 list_add_head(&real_metafile->containers, &cont->entry);
2830 break;
2832 case EmfPlusRecordTypeEndContainer:
2833 case EmfPlusRecordTypeRestore:
2835 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2836 container* cont;
2837 enum container_type type;
2838 BOOL found=FALSE;
2840 if (recordType == EmfPlusRecordTypeEndContainer)
2841 type = BEGIN_CONTAINER;
2842 else
2843 type = SAVE_GRAPHICS;
2845 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2847 if (cont->id == record->StackIndex && cont->type == type)
2849 found = TRUE;
2850 break;
2854 if (found)
2856 container* cont2;
2858 /* pop any newer items on the stack */
2859 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2861 list_remove(&cont2->entry);
2862 GdipDeleteRegion(cont2->clip);
2863 heap_free(cont2);
2866 if (type == BEGIN_CONTAINER)
2867 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2868 else
2869 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2871 *real_metafile->world_transform = cont->world_transform;
2872 real_metafile->page_unit = cont->page_unit;
2873 real_metafile->page_scale = cont->page_scale;
2874 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2876 list_remove(&cont->entry);
2877 GdipDeleteRegion(cont->clip);
2878 heap_free(cont);
2881 break;
2883 case EmfPlusRecordTypeSetPixelOffsetMode:
2885 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2887 case EmfPlusRecordTypeSetCompositingQuality:
2889 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2891 case EmfPlusRecordTypeSetInterpolationMode:
2893 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2895 case EmfPlusRecordTypeSetTextRenderingHint:
2897 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2899 case EmfPlusRecordTypeSetAntiAliasMode:
2901 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2903 case EmfPlusRecordTypeSetCompositingMode:
2905 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2907 case EmfPlusRecordTypeObject:
2909 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2911 case EmfPlusRecordTypeDrawImage:
2913 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2914 BYTE image = flags & 0xff;
2915 GpPointF points[3];
2917 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2918 return InvalidParameter;
2920 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2921 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2922 return InvalidParameter;
2924 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2925 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2926 return InvalidParameter;
2928 if (flags & 0x4000) /* C */
2930 points[0].X = draw->RectData.rect.X;
2931 points[0].Y = draw->RectData.rect.Y;
2932 points[1].X = points[0].X + draw->RectData.rect.Width;
2933 points[1].Y = points[0].Y;
2934 points[2].X = points[1].X;
2935 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2937 else
2939 points[0].X = draw->RectData.rectF.X;
2940 points[0].Y = draw->RectData.rectF.Y;
2941 points[1].X = points[0].X + draw->RectData.rectF.Width;
2942 points[1].Y = points[0].Y;
2943 points[2].X = points[1].X;
2944 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2947 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2948 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2949 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2951 case EmfPlusRecordTypeDrawImagePoints:
2953 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2954 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2955 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2956 BYTE image = flags & 0xff;
2957 GpPointF points[3];
2958 unsigned int i;
2959 UINT size;
2961 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2962 return InvalidParameter;
2964 if (dataSize <= fixed_part_size)
2965 return InvalidParameter;
2966 dataSize -= fixed_part_size;
2968 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2969 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2970 return InvalidParameter;
2972 if (draw->count != 3)
2973 return InvalidParameter;
2975 if ((flags >> 13) & 1) /* E */
2976 FIXME("image effects are not supported.\n");
2978 if ((flags >> 11) & 1) /* P */
2979 size = sizeof(EmfPlusPointR7) * draw->count;
2980 else if ((flags >> 14) & 1) /* C */
2981 size = sizeof(EmfPlusPoint) * draw->count;
2982 else
2983 size = sizeof(EmfPlusPointF) * draw->count;
2985 if (dataSize != size)
2986 return InvalidParameter;
2988 if ((flags >> 11) & 1) /* P */
2990 points[0].X = draw->PointData.pointsR[0].X;
2991 points[0].Y = draw->PointData.pointsR[0].Y;
2992 for (i = 1; i < 3; i++)
2994 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
2995 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
2998 else if ((flags >> 14) & 1) /* C */
3000 for (i = 0; i < 3; i++)
3002 points[i].X = draw->PointData.points[i].X;
3003 points[i].Y = draw->PointData.points[i].Y;
3006 else
3007 memcpy(points, draw->PointData.pointsF, sizeof(points));
3009 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3010 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3011 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3013 case EmfPlusRecordTypeFillPath:
3015 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3016 GpSolidFill *solidfill = NULL;
3017 BYTE path = flags & 0xff;
3018 GpBrush *brush;
3020 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3021 return InvalidParameter;
3023 if (dataSize != sizeof(fill->data.BrushId))
3024 return InvalidParameter;
3026 if (flags & 0x8000)
3028 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3029 if (stat != Ok)
3030 return stat;
3031 brush = (GpBrush *)solidfill;
3033 else
3035 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3036 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3037 return InvalidParameter;
3039 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3042 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3043 GdipDeleteBrush((GpBrush *)solidfill);
3044 return stat;
3046 case EmfPlusRecordTypeFillClosedCurve:
3048 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3049 sizeof(EmfPlusRecordHeader);
3050 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3051 GpSolidFill *solidfill = NULL;
3052 GpFillMode mode;
3053 GpBrush *brush;
3054 UINT size, i;
3056 if (dataSize <= fixed_part_size)
3057 return InvalidParameter;
3059 if (fill->Count == 0)
3060 return InvalidParameter;
3062 if (flags & 0x800) /* P */
3063 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3064 else if (flags & 0x4000) /* C */
3065 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3066 else
3067 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3069 if (dataSize != size)
3070 return InvalidParameter;
3072 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3074 if (flags & 0x8000) /* S */
3076 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3077 if (stat != Ok)
3078 return stat;
3079 brush = (GpBrush *)solidfill;
3081 else
3083 if (fill->BrushId >= EmfPlusObjectTableSize ||
3084 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3085 return InvalidParameter;
3087 brush = real_metafile->objtable[fill->BrushId].u.brush;
3090 if (flags & (0x800 | 0x4000))
3092 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3093 if (points)
3095 if (flags & 0x800) /* P */
3097 for (i = 1; i < fill->Count; i++)
3099 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3100 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3103 else
3105 for (i = 0; i < fill->Count; i++)
3107 points[i].X = fill->PointData.points[i].X;
3108 points[i].Y = fill->PointData.points[i].Y;
3112 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3113 points, fill->Count, fill->Tension, mode);
3114 GdipFree(points);
3116 else
3117 stat = OutOfMemory;
3119 else
3120 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3121 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3123 GdipDeleteBrush((GpBrush *)solidfill);
3124 return stat;
3126 case EmfPlusRecordTypeFillEllipse:
3128 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3129 GpSolidFill *solidfill = NULL;
3130 GpBrush *brush;
3132 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3133 return InvalidParameter;
3134 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3136 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3137 return InvalidParameter;
3139 if (flags & 0x8000)
3141 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3142 if (stat != Ok)
3143 return stat;
3144 brush = (GpBrush *)solidfill;
3146 else
3148 if (fill->BrushId >= EmfPlusObjectTableSize ||
3149 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3150 return InvalidParameter;
3152 brush = real_metafile->objtable[fill->BrushId].u.brush;
3155 if (flags & 0x4000)
3156 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3157 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3158 else
3159 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3160 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3162 GdipDeleteBrush((GpBrush *)solidfill);
3163 return stat;
3165 case EmfPlusRecordTypeFillPie:
3167 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3168 GpSolidFill *solidfill = NULL;
3169 GpBrush *brush;
3171 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3172 return InvalidParameter;
3173 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3175 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3176 return InvalidParameter;
3178 if (flags & 0x8000) /* S */
3180 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3181 if (stat != Ok)
3182 return stat;
3183 brush = (GpBrush *)solidfill;
3185 else
3187 if (fill->BrushId >= EmfPlusObjectTableSize ||
3188 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3189 return InvalidParameter;
3191 brush = real_metafile->objtable[fill->BrushId].u.brush;
3194 if (flags & 0x4000) /* C */
3195 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3196 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3197 fill->StartAngle, fill->SweepAngle);
3198 else
3199 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3200 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3201 fill->StartAngle, fill->SweepAngle);
3203 GdipDeleteBrush((GpBrush *)solidfill);
3204 return stat;
3206 case EmfPlusRecordTypeDrawPath:
3208 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3209 BYTE path = flags & 0xff;
3211 if (dataSize != sizeof(draw->PenId))
3212 return InvalidParameter;
3214 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3215 return InvalidParameter;
3217 if (real_metafile->objtable[path].type != ObjectTypePath ||
3218 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3219 return InvalidParameter;
3221 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3222 real_metafile->objtable[path].u.path);
3224 case EmfPlusRecordTypeDrawArc:
3226 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3227 BYTE pen = flags & 0xff;
3229 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3230 return InvalidParameter;
3232 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3233 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3234 return InvalidParameter;
3236 if (flags & 0x4000) /* C */
3237 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3238 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3239 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3240 else
3241 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3242 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3243 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3245 case EmfPlusRecordTypeDrawEllipse:
3247 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3248 BYTE pen = flags & 0xff;
3250 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3251 return InvalidParameter;
3253 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3254 return InvalidParameter;
3256 if (flags & 0x4000) /* C */
3257 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3258 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3259 draw->RectData.rect.Height);
3260 else
3261 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3262 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3263 draw->RectData.rectF.Height);
3265 case EmfPlusRecordTypeDrawPie:
3267 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3268 BYTE pen = flags & 0xff;
3270 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3271 return InvalidParameter;
3273 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3274 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3275 return InvalidParameter;
3277 if (flags & 0x4000) /* C */
3278 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3279 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3280 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3281 else
3282 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3283 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3284 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3286 case EmfPlusRecordTypeDrawRects:
3288 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3289 BYTE pen = flags & 0xff;
3290 GpRectF *rects = NULL;
3292 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3293 return InvalidParameter;
3295 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3296 return InvalidParameter;
3297 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3299 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3300 return InvalidParameter;
3302 if (flags & 0x4000)
3304 DWORD i;
3306 rects = GdipAlloc(draw->Count * sizeof(*rects));
3307 if (!rects)
3308 return OutOfMemory;
3310 for (i = 0; i < draw->Count; i++)
3312 rects[i].X = draw->RectData.rect[i].X;
3313 rects[i].Y = draw->RectData.rect[i].Y;
3314 rects[i].Width = draw->RectData.rect[i].Width;
3315 rects[i].Height = draw->RectData.rect[i].Height;
3319 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3320 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3321 GdipFree(rects);
3322 return stat;
3324 case EmfPlusRecordTypeDrawDriverString:
3326 GpBrush *brush;
3327 DWORD expected_size;
3328 UINT16 *text;
3329 PointF *positions;
3330 GpSolidFill *solidfill = NULL;
3331 void* alignedmem = NULL;
3332 GpMatrix *matrix = NULL;
3333 BYTE font = flags & 0xff;
3334 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3336 if (font >= EmfPlusObjectTableSize ||
3337 real_metafile->objtable[font].type != ObjectTypeFont)
3338 return InvalidParameter;
3340 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3341 sizeof(EmfPlusRecordHeader);
3342 if (dataSize < expected_size || draw->GlyphCount <= 0)
3343 return InvalidParameter;
3345 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3346 if (draw->MatrixPresent)
3347 expected_size += sizeof(*matrix);
3349 /* Pad expected size to DWORD alignment. */
3350 expected_size = (expected_size + 3) & ~3;
3352 if (dataSize != expected_size)
3353 return InvalidParameter;
3355 if (flags & 0x8000)
3357 stat = GdipCreateSolidFill(draw->brush.Color, &solidfill);
3359 if (stat != Ok)
3360 return InvalidParameter;
3362 brush = (GpBrush*)solidfill;
3364 else
3366 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3367 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3368 return InvalidParameter;
3370 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3373 text = (UINT16*)&draw->VariableData[0];
3375 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3376 aligned rather than 4-byte aligned, which may lead to access
3377 issues. Handle this case by making our own copy of positions. */
3378 if (draw->GlyphCount % 2)
3380 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3382 if (draw->MatrixPresent)
3383 alloc_size += sizeof(*matrix);
3385 positions = alignedmem = heap_alloc(alloc_size);
3386 if (!positions)
3388 GdipDeleteBrush((GpBrush*)solidfill);
3389 return OutOfMemory;
3392 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3394 else
3395 positions = (PointF*)&text[draw->GlyphCount];
3397 if (draw->MatrixPresent)
3398 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3400 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3401 real_metafile->objtable[font].u.font, brush, positions,
3402 draw->DriverStringOptionsFlags, matrix);
3404 GdipDeleteBrush((GpBrush*)solidfill);
3405 heap_free(alignedmem);
3407 return stat;
3409 case EmfPlusRecordTypeFillRegion:
3411 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3412 GpSolidFill *solidfill = NULL;
3413 GpBrush *brush;
3414 BYTE region = flags & 0xff;
3416 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3417 return InvalidParameter;
3419 if (region >= EmfPlusObjectTableSize ||
3420 real_metafile->objtable[region].type != ObjectTypeRegion)
3421 return InvalidParameter;
3423 if (flags & 0x8000)
3425 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3426 if (stat != Ok)
3427 return stat;
3428 brush = (GpBrush*)solidfill;
3430 else
3432 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3433 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3434 return InvalidParameter;
3436 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3439 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3440 real_metafile->objtable[region].u.region);
3441 GdipDeleteBrush((GpBrush*)solidfill);
3443 return stat;
3445 default:
3446 FIXME("Not implemented for record type %x\n", recordType);
3447 return NotImplemented;
3451 return Ok;
3454 struct enum_metafile_data
3456 EnumerateMetafileProc callback;
3457 void *callback_data;
3458 GpMetafile *metafile;
3461 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3462 int nObj, LPARAM lpData)
3464 BOOL ret;
3465 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3466 const BYTE* pStr;
3468 data->metafile->handle_table = lpHTable;
3469 data->metafile->handle_count = nObj;
3471 /* First check for an EMF+ record. */
3472 if (lpEMFR->iType == EMR_GDICOMMENT)
3474 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3476 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3478 int offset = 4;
3480 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3482 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3484 if (record->DataSize)
3485 pStr = (const BYTE*)(record+1);
3486 else
3487 pStr = NULL;
3489 ret = data->callback(record->Type, record->Flags, record->DataSize,
3490 pStr, data->callback_data);
3492 if (!ret)
3493 return 0;
3495 offset += record->Size;
3498 return 1;
3502 if (lpEMFR->nSize != 8)
3503 pStr = (const BYTE*)lpEMFR->dParm;
3504 else
3505 pStr = NULL;
3507 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3508 pStr, data->callback_data);
3511 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3512 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3513 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3514 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3516 struct enum_metafile_data data;
3517 GpStatus stat;
3518 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3519 GraphicsContainer state;
3520 GpPath *dst_path;
3521 RECT dst_bounds;
3523 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3524 destPoints, count, srcRect, srcUnit, callback, callbackData,
3525 imageAttributes);
3527 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3528 return InvalidParameter;
3530 if (!metafile->hemf)
3531 return InvalidParameter;
3533 if (metafile->playback_graphics)
3534 return ObjectBusy;
3536 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3537 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3538 debugstr_pointf(&destPoints[2]));
3540 data.callback = callback;
3541 data.callback_data = callbackData;
3542 data.metafile = real_metafile;
3544 real_metafile->playback_graphics = graphics;
3545 real_metafile->playback_dc = NULL;
3546 real_metafile->src_rect = *srcRect;
3548 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3549 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3551 if (stat == Ok)
3552 stat = GdipBeginContainer2(graphics, &state);
3554 if (stat == Ok)
3556 stat = GdipSetPageScale(graphics, 1.0);
3558 if (stat == Ok)
3559 stat = GdipSetPageUnit(graphics, UnitPixel);
3561 if (stat == Ok)
3562 stat = GdipResetWorldTransform(graphics);
3564 if (stat == Ok)
3565 stat = GdipCreateRegion(&real_metafile->base_clip);
3567 if (stat == Ok)
3568 stat = GdipGetClip(graphics, real_metafile->base_clip);
3570 if (stat == Ok)
3571 stat = GdipCreateRegion(&real_metafile->clip);
3573 if (stat == Ok)
3574 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3576 if (stat == Ok)
3578 GpPointF clip_points[4];
3580 clip_points[0] = real_metafile->playback_points[0];
3581 clip_points[1] = real_metafile->playback_points[1];
3582 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3583 - real_metafile->playback_points[0].X;
3584 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3585 - real_metafile->playback_points[0].Y;
3586 clip_points[3] = real_metafile->playback_points[2];
3588 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3590 if (stat == Ok)
3591 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3593 GdipDeletePath(dst_path);
3596 if (stat == Ok)
3597 stat = GdipCreateMatrix(&real_metafile->world_transform);
3599 if (stat == Ok)
3601 real_metafile->page_unit = UnitDisplay;
3602 real_metafile->page_scale = 1.0;
3603 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3606 if (stat == Ok)
3608 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3611 if (stat == Ok)
3613 stat = METAFILE_PlaybackGetDC(real_metafile);
3615 dst_bounds.left = real_metafile->playback_points[0].X;
3616 dst_bounds.right = real_metafile->playback_points[1].X;
3617 dst_bounds.top = real_metafile->playback_points[0].Y;
3618 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3621 if (stat == Ok)
3622 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3623 &data, &dst_bounds);
3625 METAFILE_PlaybackReleaseDC(real_metafile);
3627 GdipDeleteMatrix(real_metafile->world_transform);
3628 real_metafile->world_transform = NULL;
3630 GdipDeleteRegion(real_metafile->base_clip);
3631 real_metafile->base_clip = NULL;
3633 GdipDeleteRegion(real_metafile->clip);
3634 real_metafile->clip = NULL;
3636 while (list_head(&real_metafile->containers))
3638 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3639 list_remove(&cont->entry);
3640 GdipDeleteRegion(cont->clip);
3641 heap_free(cont);
3644 GdipEndContainer(graphics, state);
3647 real_metafile->playback_graphics = NULL;
3649 return stat;
3652 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRect( GpGraphics *graphics,
3653 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3654 GDIPCONST GpRectF *src, Unit srcUnit, EnumerateMetafileProc callback,
3655 VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3657 GpPointF points[3];
3659 if (!graphics || !metafile || !dest) return InvalidParameter;
3661 points[0].X = points[2].X = dest->X;
3662 points[0].Y = points[1].Y = dest->Y;
3663 points[1].X = dest->X + dest->Width;
3664 points[2].Y = dest->Y + dest->Height;
3666 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3667 src, srcUnit, callback, cb_data, attrs);
3670 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3671 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3672 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3674 GpPointF points[3];
3676 if (!graphics || !metafile || !dest) return InvalidParameter;
3678 points[0].X = points[2].X = dest->X;
3679 points[0].Y = points[1].Y = dest->Y;
3680 points[1].X = dest->X + dest->Width;
3681 points[2].Y = dest->Y + dest->Height;
3683 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3684 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3687 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3688 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3689 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3691 GpRectF destf;
3693 if (!graphics || !metafile || !dest) return InvalidParameter;
3695 destf.X = dest->X;
3696 destf.Y = dest->Y;
3697 destf.Width = dest->Width;
3698 destf.Height = dest->Height;
3700 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3703 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3704 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3705 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3707 GpRectF destf;
3709 if (!graphics || !metafile || !dest) return InvalidParameter;
3711 destf.X = dest->X;
3712 destf.Y = dest->Y;
3713 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit,
3714 metafile->image.xres, metafile->printer_display);
3715 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit,
3716 metafile->image.yres, metafile->printer_display);
3718 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3721 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3722 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3723 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3725 GpPointF ptf;
3727 if (!graphics || !metafile || !dest) return InvalidParameter;
3729 ptf.X = dest->X;
3730 ptf.Y = dest->Y;
3732 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3735 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3736 MetafileHeader * header)
3738 GpStatus status;
3740 TRACE("(%p, %p)\n", metafile, header);
3742 if(!metafile || !header)
3743 return InvalidParameter;
3745 if (metafile->hemf)
3747 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3748 if (status != Ok) return status;
3750 else
3752 memset(header, 0, sizeof(*header));
3753 header->Version = VERSION_MAGIC2;
3756 header->Type = metafile->metafile_type;
3757 header->DpiX = metafile->image.xres;
3758 header->DpiY = metafile->image.yres;
3759 header->Width = gdip_round(metafile->bounds.Width);
3760 header->Height = gdip_round(metafile->bounds.Height);
3762 return Ok;
3765 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3766 int nObj, LPARAM lpData)
3768 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3770 if (lpEMFR->iType == EMR_GDICOMMENT)
3772 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3774 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3776 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3778 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3779 header->Type == EmfPlusRecordTypeHeader)
3781 memcpy(dst_header, header, sizeof(*dst_header));
3785 else if (lpEMFR->iType == EMR_HEADER)
3786 return TRUE;
3788 return FALSE;
3791 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3792 MetafileHeader *header)
3794 ENHMETAHEADER3 emfheader;
3795 EmfPlusHeader emfplusheader;
3796 MetafileType metafile_type;
3798 TRACE("(%p,%p)\n", hemf, header);
3800 if(!hemf || !header)
3801 return InvalidParameter;
3803 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3804 return GenericError;
3806 emfplusheader.Header.Type = 0;
3808 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3810 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3812 if ((emfplusheader.Header.Flags & 1) == 1)
3813 metafile_type = MetafileTypeEmfPlusDual;
3814 else
3815 metafile_type = MetafileTypeEmfPlusOnly;
3817 else
3818 metafile_type = MetafileTypeEmf;
3820 header->Type = metafile_type;
3821 header->Size = emfheader.nBytes;
3822 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3823 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3824 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3825 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3826 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3827 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3828 header->u.EmfHeader = emfheader;
3830 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3832 header->Version = emfplusheader.Version;
3833 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3834 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3835 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3836 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3838 else
3840 header->Version = emfheader.nVersion;
3841 header->EmfPlusFlags = 0;
3842 header->EmfPlusHeaderSize = 0;
3843 header->LogicalDpiX = 0;
3844 header->LogicalDpiY = 0;
3847 return Ok;
3850 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3851 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3853 GpStatus status;
3854 GpMetafile *metafile;
3856 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3858 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3859 if (status == Ok)
3861 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3862 GdipDisposeImage(&metafile->image);
3864 return status;
3867 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3868 MetafileHeader *header)
3870 GpStatus status;
3871 GpMetafile *metafile;
3873 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3875 if (!filename || !header)
3876 return InvalidParameter;
3878 status = GdipCreateMetafileFromFile(filename, &metafile);
3879 if (status == Ok)
3881 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3882 GdipDisposeImage(&metafile->image);
3884 return status;
3887 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3888 MetafileHeader *header)
3890 GpStatus status;
3891 GpMetafile *metafile;
3893 TRACE("(%p,%p)\n", stream, header);
3895 if (!stream || !header)
3896 return InvalidParameter;
3898 status = GdipCreateMetafileFromStream(stream, &metafile);
3899 if (status == Ok)
3901 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3902 GdipDisposeImage(&metafile->image);
3904 return status;
3907 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3908 GpMetafile **metafile)
3910 GpStatus stat;
3911 MetafileHeader header;
3913 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3915 if(!hemf || !metafile)
3916 return InvalidParameter;
3918 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3919 if (stat != Ok)
3920 return stat;
3922 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3923 if (!*metafile)
3924 return OutOfMemory;
3926 (*metafile)->image.type = ImageTypeMetafile;
3927 (*metafile)->image.format = ImageFormatEMF;
3928 (*metafile)->image.frame_count = 1;
3929 (*metafile)->image.xres = header.DpiX;
3930 (*metafile)->image.yres = header.DpiY;
3931 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3932 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3933 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3934 / 2540.0 * header.DpiX;
3935 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3936 / 2540.0 * header.DpiY;
3937 (*metafile)->unit = UnitPixel;
3938 (*metafile)->metafile_type = header.Type;
3939 (*metafile)->hemf = hemf;
3940 (*metafile)->preserve_hemf = !delete;
3941 /* If the 31th bit of EmfPlusFlags was set, metafile was recorded with a DC for a video display.
3942 * If clear, metafile was recorded with a DC for a printer */
3943 (*metafile)->printer_display = !(header.EmfPlusFlags & (1u << 31));
3944 (*metafile)->logical_dpix = header.LogicalDpiX;
3945 (*metafile)->logical_dpiy = header.LogicalDpiY;
3946 list_init(&(*metafile)->containers);
3948 TRACE("<-- %p\n", *metafile);
3950 return Ok;
3953 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3954 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3956 UINT read;
3957 BYTE *copy;
3958 HENHMETAFILE hemf;
3959 GpStatus retval = Ok;
3961 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3963 if(!hwmf || !metafile)
3964 return InvalidParameter;
3966 *metafile = NULL;
3967 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3968 if(!read)
3969 return GenericError;
3970 copy = heap_alloc_zero(read);
3971 GetMetaFileBitsEx(hwmf, read, copy);
3973 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3974 heap_free(copy);
3976 /* FIXME: We should store and use hwmf instead of converting to hemf */
3977 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
3979 if (retval == Ok)
3981 if (placeable)
3983 (*metafile)->image.xres = (REAL)placeable->Inch;
3984 (*metafile)->image.yres = (REAL)placeable->Inch;
3985 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
3986 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
3987 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
3988 placeable->BoundingBox.Left);
3989 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
3990 placeable->BoundingBox.Top);
3991 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
3993 else
3994 (*metafile)->metafile_type = MetafileTypeWmf;
3995 (*metafile)->image.format = ImageFormatWMF;
3997 if (delete) DeleteMetaFile(hwmf);
3999 else
4000 DeleteEnhMetaFile(hemf);
4001 return retval;
4004 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4005 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4007 HMETAFILE hmf;
4008 HENHMETAFILE emf;
4010 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4012 hmf = GetMetaFileW(file);
4013 if(hmf)
4014 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4016 emf = GetEnhMetaFileW(file);
4017 if(emf)
4018 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4020 return GenericError;
4023 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4024 GpMetafile **metafile)
4026 GpStatus status;
4027 IStream *stream;
4029 TRACE("(%p, %p)\n", file, metafile);
4031 if (!file || !metafile) return InvalidParameter;
4033 *metafile = NULL;
4035 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4036 if (status == Ok)
4038 status = GdipCreateMetafileFromStream(stream, metafile);
4039 IStream_Release(stream);
4041 return status;
4044 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4045 GpMetafile **metafile)
4047 GpStatus stat;
4049 TRACE("%p %p\n", stream, metafile);
4051 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4052 if (stat != Ok) return stat;
4054 if ((*metafile)->image.type != ImageTypeMetafile)
4056 GdipDisposeImage(&(*metafile)->image);
4057 *metafile = NULL;
4058 return GenericError;
4061 return Ok;
4064 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4065 UINT *limitDpi)
4067 TRACE("(%p,%p)\n", metafile, limitDpi);
4069 if (!metafile || !limitDpi)
4070 return InvalidParameter;
4072 if (!metafile->record_dc)
4073 return WrongState;
4075 *limitDpi = metafile->limit_dpi;
4077 return Ok;
4080 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4081 UINT limitDpi)
4083 TRACE("(%p,%u)\n", metafile, limitDpi);
4085 if (limitDpi == 0)
4086 limitDpi = 96;
4088 if (!metafile || limitDpi < 10)
4089 return InvalidParameter;
4091 if (!metafile->record_dc)
4092 return WrongState;
4094 metafile->limit_dpi = limitDpi;
4096 return Ok;
4099 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4100 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4101 const WCHAR* description, GpMetafile** out_metafile)
4103 static int calls;
4105 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4106 debugstr_w(description), out_metafile);
4108 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4109 return InvalidParameter;
4111 if(succ)
4112 *succ = FALSE;
4113 *out_metafile = NULL;
4115 if(!(calls++))
4116 FIXME("not implemented\n");
4118 return NotImplemented;
4121 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4122 LPBYTE pData16, INT iMapMode, INT eFlags)
4124 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4125 return NotImplemented;
4128 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4129 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4130 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4131 GpMetafile **metafile)
4133 HDC record_dc;
4134 REAL dpix, dpiy;
4135 REAL framerect_factor_x, framerect_factor_y;
4136 RECT rc, *lprc;
4137 GpStatus stat;
4139 TRACE("%s %p %d %s %d %s %p\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4140 frameUnit, debugstr_w(desc), metafile);
4142 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
4143 return InvalidParameter;
4145 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
4146 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
4148 if (pFrameRect)
4150 switch (frameUnit)
4152 case MetafileFrameUnitPixel:
4153 framerect_factor_x = 2540.0 / dpix;
4154 framerect_factor_y = 2540.0 / dpiy;
4155 break;
4156 case MetafileFrameUnitPoint:
4157 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
4158 break;
4159 case MetafileFrameUnitInch:
4160 framerect_factor_x = framerect_factor_y = 2540.0;
4161 break;
4162 case MetafileFrameUnitDocument:
4163 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
4164 break;
4165 case MetafileFrameUnitMillimeter:
4166 framerect_factor_x = framerect_factor_y = 100.0;
4167 break;
4168 case MetafileFrameUnitGdi:
4169 framerect_factor_x = framerect_factor_y = 1.0;
4170 break;
4171 default:
4172 return InvalidParameter;
4175 rc.left = framerect_factor_x * pFrameRect->X;
4176 rc.top = framerect_factor_y * pFrameRect->Y;
4177 rc.right = rc.left + framerect_factor_x * pFrameRect->Width;
4178 rc.bottom = rc.top + framerect_factor_y * pFrameRect->Height;
4180 lprc = &rc;
4182 else
4183 lprc = NULL;
4185 record_dc = CreateEnhMetaFileW(hdc, fileName, lprc, desc);
4187 if (!record_dc)
4188 return GenericError;
4190 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4191 if(!*metafile)
4193 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4194 return OutOfMemory;
4197 (*metafile)->image.type = ImageTypeMetafile;
4198 (*metafile)->image.flags = ImageFlagsNone;
4199 (*metafile)->image.palette = NULL;
4200 (*metafile)->image.xres = dpix;
4201 (*metafile)->image.yres = dpiy;
4202 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
4203 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
4204 (*metafile)->unit = UnitPixel;
4205 (*metafile)->metafile_type = type;
4206 (*metafile)->record_dc = record_dc;
4207 (*metafile)->comment_data = NULL;
4208 (*metafile)->comment_data_size = 0;
4209 (*metafile)->comment_data_length = 0;
4210 (*metafile)->limit_dpi = 96;
4211 (*metafile)->hemf = NULL;
4212 (*metafile)->printer_display = (GetDeviceCaps(record_dc, TECHNOLOGY) == DT_RASPRINTER);
4213 (*metafile)->logical_dpix = (REAL)GetDeviceCaps(record_dc, LOGPIXELSX);
4214 (*metafile)->logical_dpiy = (REAL)GetDeviceCaps(record_dc, LOGPIXELSY);
4215 list_init(&(*metafile)->containers);
4217 if (!pFrameRect)
4219 (*metafile)->auto_frame = TRUE;
4220 (*metafile)->auto_frame_min.X = 0;
4221 (*metafile)->auto_frame_min.Y = 0;
4222 (*metafile)->auto_frame_max.X = -1;
4223 (*metafile)->auto_frame_max.Y = -1;
4226 stat = METAFILE_WriteHeader(*metafile, hdc);
4228 if (stat != Ok)
4230 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4231 heap_free(*metafile);
4232 *metafile = NULL;
4233 return OutOfMemory;
4236 return stat;
4239 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4240 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4241 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4243 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4244 frameUnit, debugstr_w(desc), metafile);
4246 return NotImplemented;
4249 /*****************************************************************************
4250 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4253 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4254 GpMetafile* metafile, BOOL* conversionSuccess,
4255 const WCHAR* filename, EmfType emfType,
4256 const WCHAR* description, GpMetafile** out_metafile)
4258 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4259 return NotImplemented;
4262 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4264 LARGE_INTEGER zero;
4265 STATSTG statstg;
4266 GpStatus stat;
4267 HRESULT hr;
4269 *size = 0;
4271 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4272 if (FAILED(hr)) return hresult_to_status(hr);
4274 stat = encode_image_png(image, *stream, NULL);
4275 if (stat != Ok)
4277 IStream_Release(*stream);
4278 return stat;
4281 hr = IStream_Stat(*stream, &statstg, 1);
4282 if (FAILED(hr))
4284 IStream_Release(*stream);
4285 return hresult_to_status(hr);
4287 *size = statstg.cbSize.u.LowPart;
4289 zero.QuadPart = 0;
4290 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4291 if (FAILED(hr))
4293 IStream_Release(*stream);
4294 return hresult_to_status(hr);
4297 return Ok;
4300 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4302 HRESULT hr;
4304 record->Width = 0;
4305 record->Height = 0;
4306 record->Stride = 0;
4307 record->PixelFormat = 0;
4308 record->Type = BitmapDataTypeCompressed;
4310 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4311 if (FAILED(hr)) return hresult_to_status(hr);
4312 return Ok;
4315 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4317 EmfPlusObject *object_record;
4318 GpStatus stat;
4319 DWORD size;
4321 *id = -1;
4323 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4324 return Ok;
4326 if (image->type == ImageTypeBitmap)
4328 IStream *stream;
4329 DWORD aligned_size;
4331 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4332 if (stat != Ok) return stat;
4333 aligned_size = (size + 3) & ~3;
4335 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4336 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4337 (void**)&object_record);
4338 if (stat != Ok)
4340 IStream_Release(stream);
4341 return stat;
4343 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4345 *id = METAFILE_AddObjectId(metafile);
4346 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4347 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4348 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4350 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4351 IStream_Release(stream);
4352 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4353 return stat;
4355 else if (image->type == ImageTypeMetafile)
4357 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4358 EmfPlusMetafile *metafile_record;
4360 if (!hemf) return InvalidParameter;
4362 size = GetEnhMetaFileBits(hemf, 0, NULL);
4363 if (!size) return GenericError;
4365 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4366 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4367 (void**)&object_record);
4368 if (stat != Ok) return stat;
4370 *id = METAFILE_AddObjectId(metafile);
4371 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4372 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4373 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4374 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4375 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4376 metafile_record->MetafileDataSize = size;
4377 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4379 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4380 return GenericError;
4382 return Ok;
4384 else
4386 FIXME("not supported image type (%d)\n", image->type);
4387 return NotImplemented;
4391 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4393 EmfPlusObject *object_record;
4394 EmfPlusImageAttributes *attrs_record;
4395 GpStatus stat;
4397 *id = -1;
4399 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4400 return Ok;
4402 if (!attrs)
4403 return Ok;
4405 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4406 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4407 (void**)&object_record);
4408 if (stat != Ok) return stat;
4410 *id = METAFILE_AddObjectId(metafile);
4411 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4412 attrs_record = &object_record->ObjectData.image_attributes;
4413 attrs_record->Version = VERSION_MAGIC2;
4414 attrs_record->Reserved1 = 0;
4415 attrs_record->WrapMode = attrs->wrap;
4416 attrs_record->ClampColor = attrs->outside_color;
4417 attrs_record->ObjectClamp = attrs->clamp;
4418 attrs_record->Reserved2 = 0;
4419 return Ok;
4422 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4423 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4424 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4425 DrawImageAbort callback, VOID *callbackData)
4427 EmfPlusDrawImagePoints *draw_image_record;
4428 DWORD image_id, attributes_id;
4429 GpStatus stat;
4431 if (count != 3) return InvalidParameter;
4433 if (metafile->metafile_type == MetafileTypeEmf)
4435 FIXME("MetafileTypeEmf metafiles not supported\n");
4436 return NotImplemented;
4438 else
4439 FIXME("semi-stub\n");
4441 if (!imageAttributes)
4443 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4445 else if (image->type == ImageTypeBitmap)
4447 INT width = ((GpBitmap*)image)->width;
4448 INT height = ((GpBitmap*)image)->height;
4449 GpGraphics *graphics;
4450 GpBitmap *bitmap;
4452 stat = GdipCreateBitmapFromScan0(width, height,
4453 0, PixelFormat32bppARGB, NULL, &bitmap);
4454 if (stat != Ok) return stat;
4456 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4457 if (stat != Ok)
4459 GdipDisposeImage((GpImage*)bitmap);
4460 return stat;
4463 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4464 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4465 GdipDeleteGraphics(graphics);
4466 if (stat != Ok)
4468 GdipDisposeImage((GpImage*)bitmap);
4469 return stat;
4472 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4473 GdipDisposeImage((GpImage*)bitmap);
4475 else
4477 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4478 return NotImplemented;
4480 if (stat != Ok) return stat;
4482 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4483 if (stat != Ok) return stat;
4485 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawImagePoints,
4486 sizeof(EmfPlusDrawImagePoints), (void **)&draw_image_record);
4487 if (stat != Ok) return stat;
4489 draw_image_record->Header.Flags = image_id;
4490 draw_image_record->ImageAttributesID = attributes_id;
4491 draw_image_record->SrcUnit = UnitPixel;
4492 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres, metafile->printer_display);
4493 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres, metafile->printer_display);
4494 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres, metafile->printer_display);
4495 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres, metafile->printer_display);
4496 draw_image_record->count = 3;
4497 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4498 METAFILE_WriteRecords(metafile);
4499 return Ok;
4502 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4504 EmfPlusRecordHeader *record;
4505 GpStatus stat;
4507 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4508 return Ok;
4510 stat = METAFILE_AllocateRecord(metafile, prop, sizeof(*record), (void**)&record);
4511 if (stat != Ok) return stat;
4513 record->Flags = val;
4515 METAFILE_WriteRecords(metafile);
4516 return Ok;
4519 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4521 EmfPlusObject *object_record;
4522 GpStatus stat;
4523 DWORD size;
4525 *id = -1;
4526 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4527 return Ok;
4529 size = write_path_data(path, NULL);
4530 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4531 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4532 (void**)&object_record);
4533 if (stat != Ok) return stat;
4535 *id = METAFILE_AddObjectId(metafile);
4536 object_record->Header.Flags = *id | ObjectTypePath << 8;
4537 write_path_data(path, &object_record->ObjectData.path);
4538 return Ok;
4541 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4543 DWORD i, data_flags, pen_data_size, brush_size;
4544 EmfPlusObject *object_record;
4545 EmfPlusPenData *pen_data;
4546 GpStatus stat;
4547 BOOL result;
4549 *id = -1;
4550 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4551 return Ok;
4553 data_flags = 0;
4554 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4556 GdipIsMatrixIdentity(&pen->transform, &result);
4557 if (!result)
4559 data_flags |= PenDataTransform;
4560 pen_data_size += sizeof(EmfPlusTransformMatrix);
4562 if (pen->startcap != LineCapFlat)
4564 data_flags |= PenDataStartCap;
4565 pen_data_size += sizeof(DWORD);
4567 if (pen->endcap != LineCapFlat)
4569 data_flags |= PenDataEndCap;
4570 pen_data_size += sizeof(DWORD);
4572 if (pen->join != LineJoinMiter)
4574 data_flags |= PenDataJoin;
4575 pen_data_size += sizeof(DWORD);
4577 if (pen->miterlimit != 10.0)
4579 data_flags |= PenDataMiterLimit;
4580 pen_data_size += sizeof(REAL);
4582 if (pen->style != GP_DEFAULT_PENSTYLE)
4584 data_flags |= PenDataLineStyle;
4585 pen_data_size += sizeof(DWORD);
4587 if (pen->dashcap != DashCapFlat)
4589 data_flags |= PenDataDashedLineCap;
4590 pen_data_size += sizeof(DWORD);
4592 data_flags |= PenDataDashedLineOffset;
4593 pen_data_size += sizeof(REAL);
4594 if (pen->numdashes)
4596 data_flags |= PenDataDashedLine;
4597 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4599 if (pen->align != PenAlignmentCenter)
4601 data_flags |= PenDataNonCenter;
4602 pen_data_size += sizeof(DWORD);
4604 /* TODO: Add support for PenDataCompoundLine */
4605 if (pen->customstart)
4607 FIXME("ignoring custom start cup\n");
4609 if (pen->customend)
4611 FIXME("ignoring custom end cup\n");
4614 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4615 if (stat != Ok) return stat;
4617 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4618 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4619 (void**)&object_record);
4620 if (stat != Ok) return stat;
4622 *id = METAFILE_AddObjectId(metafile);
4623 object_record->Header.Flags = *id | ObjectTypePen << 8;
4624 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4625 object_record->ObjectData.pen.Type = 0;
4627 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4628 pen_data->PenDataFlags = data_flags;
4629 pen_data->PenUnit = pen->unit;
4630 pen_data->PenWidth = pen->width;
4632 i = 0;
4633 if (data_flags & PenDataTransform)
4635 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4636 memcpy(m, &pen->transform, sizeof(*m));
4637 i += sizeof(EmfPlusTransformMatrix);
4639 if (data_flags & PenDataStartCap)
4641 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4642 i += sizeof(DWORD);
4644 if (data_flags & PenDataEndCap)
4646 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4647 i += sizeof(DWORD);
4649 if (data_flags & PenDataJoin)
4651 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4652 i += sizeof(DWORD);
4654 if (data_flags & PenDataMiterLimit)
4656 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4657 i += sizeof(REAL);
4659 if (data_flags & PenDataLineStyle)
4661 switch (pen->style & PS_STYLE_MASK)
4663 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4664 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4665 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4666 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4667 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4668 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4670 i += sizeof(DWORD);
4672 if (data_flags & PenDataDashedLineCap)
4674 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4675 i += sizeof(DWORD);
4677 if (data_flags & PenDataDashedLineOffset)
4679 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4680 i += sizeof(REAL);
4682 if (data_flags & PenDataDashedLine)
4684 int j;
4686 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4687 i += sizeof(DWORD);
4689 for (j=0; j<pen->numdashes; j++)
4691 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4692 i += sizeof(REAL);
4695 if (data_flags & PenDataNonCenter)
4697 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4698 i += sizeof(DWORD);
4701 METAFILE_FillBrushData(pen->brush,
4702 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4703 return Ok;
4706 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4708 EmfPlusDrawPath *draw_path_record;
4709 DWORD path_id;
4710 DWORD pen_id;
4711 GpStatus stat;
4713 if (metafile->metafile_type == MetafileTypeEmf)
4715 FIXME("stub!\n");
4716 return NotImplemented;
4719 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4720 if (stat != Ok) return stat;
4722 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4723 if (stat != Ok) return stat;
4725 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawPath,
4726 sizeof(EmfPlusDrawPath), (void **)&draw_path_record);
4727 if (stat != Ok) return stat;
4728 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4729 draw_path_record->Header.Flags = path_id;
4730 draw_path_record->PenId = pen_id;
4732 METAFILE_WriteRecords(metafile);
4733 return Ok;
4736 GpStatus METAFILE_DrawEllipse(GpMetafile *metafile, GpPen *pen, GpRectF *rect)
4738 EmfPlusDrawEllipse *record;
4739 GpStatus stat;
4740 DWORD pen_id;
4742 if (metafile->metafile_type == MetafileTypeEmf)
4744 FIXME("stub!\n");
4745 return NotImplemented;
4748 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4749 if (stat != Ok) return stat;
4751 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawEllipse,
4752 sizeof(EmfPlusDrawEllipse), (void **)&record);
4753 if (stat != Ok) return stat;
4754 record->Header.Type = EmfPlusRecordTypeDrawEllipse;
4755 record->Header.Flags = pen_id;
4756 if (is_integer_rect(rect))
4758 record->Header.Flags |= 0x4000;
4759 record->RectData.rect.X = (SHORT)rect->X;
4760 record->RectData.rect.Y = (SHORT)rect->Y;
4761 record->RectData.rect.Width = (SHORT)rect->Width;
4762 record->RectData.rect.Height = (SHORT)rect->Height;
4764 else
4765 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4767 METAFILE_WriteRecords(metafile);
4768 return Ok;
4771 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4773 EmfPlusFillPath *fill_path_record;
4774 DWORD brush_id = -1, path_id;
4775 BOOL inline_color;
4776 GpStatus stat;
4778 if (metafile->metafile_type == MetafileTypeEmf)
4780 FIXME("stub!\n");
4781 return NotImplemented;
4784 inline_color = brush->bt == BrushTypeSolidColor;
4785 if (!inline_color)
4787 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4788 if (stat != Ok) return stat;
4791 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4792 if (stat != Ok) return stat;
4794 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPath,
4795 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4796 if (stat != Ok) return stat;
4797 if (inline_color)
4799 fill_path_record->Header.Flags = 0x8000 | path_id;
4800 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4802 else
4804 fill_path_record->Header.Flags = path_id;
4805 fill_path_record->data.BrushId = brush_id;
4808 METAFILE_WriteRecords(metafile);
4809 return Ok;
4812 GpStatus METAFILE_FillEllipse(GpMetafile *metafile, GpBrush *brush, GpRectF *rect)
4814 EmfPlusFillEllipse *record;
4815 DWORD brush_id = -1;
4816 BOOL inline_color;
4817 GpStatus stat;
4819 if (metafile->metafile_type == MetafileTypeEmf)
4821 FIXME("stub!\n");
4822 return NotImplemented;
4825 inline_color = brush->bt == BrushTypeSolidColor;
4826 if (!inline_color)
4828 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4829 if (stat != Ok) return stat;
4832 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillEllipse, sizeof(EmfPlusFillEllipse), (void **)&record);
4833 if (stat != Ok) return stat;
4834 if (inline_color)
4836 record->Header.Flags = 0x8000;
4837 record->BrushId = ((GpSolidFill *)brush)->color;
4839 else
4840 record->BrushId = brush_id;
4842 if (is_integer_rect(rect))
4844 record->Header.Flags |= 0x4000;
4845 record->RectData.rect.X = (SHORT)rect->X;
4846 record->RectData.rect.Y = (SHORT)rect->Y;
4847 record->RectData.rect.Width = (SHORT)rect->Width;
4848 record->RectData.rect.Height = (SHORT)rect->Height;
4850 else
4851 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4853 METAFILE_WriteRecords(metafile);
4854 return Ok;
4857 GpStatus METAFILE_FillPie(GpMetafile *metafile, GpBrush *brush, const GpRectF *rect,
4858 REAL startAngle, REAL sweepAngle)
4860 BOOL is_int_rect, inline_color;
4861 EmfPlusFillPie *record;
4862 DWORD brush_id = -1;
4863 GpStatus stat;
4865 if (metafile->metafile_type == MetafileTypeEmf)
4867 FIXME("stub!\n");
4868 return NotImplemented;
4871 inline_color = brush->bt == BrushTypeSolidColor;
4872 if (!inline_color)
4874 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4875 if (stat != Ok) return stat;
4878 is_int_rect = is_integer_rect(rect);
4880 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPie,
4881 FIELD_OFFSET(EmfPlusFillPie, RectData) + is_int_rect ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF),
4882 (void **)&record);
4883 if (stat != Ok) return stat;
4884 if (inline_color)
4886 record->Header.Flags = 0x8000;
4887 record->BrushId = ((GpSolidFill *)brush)->color;
4889 else
4890 record->BrushId = brush_id;
4892 record->StartAngle = startAngle;
4893 record->SweepAngle = sweepAngle;
4895 if (is_int_rect)
4897 record->Header.Flags |= 0x4000;
4898 record->RectData.rect.X = (SHORT)rect->X;
4899 record->RectData.rect.Y = (SHORT)rect->Y;
4900 record->RectData.rect.Width = (SHORT)rect->Width;
4901 record->RectData.rect.Height = (SHORT)rect->Height;
4903 else
4904 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4906 METAFILE_WriteRecords(metafile);
4907 return Ok;
4910 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
4912 EmfPlusObject *object_record;
4913 EmfPlusFont *font_record;
4914 GpStatus stat;
4915 INT fn_len;
4916 INT style;
4918 *id = -1;
4920 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4921 metafile->metafile_type != MetafileTypeEmfPlusDual)
4922 return Ok;
4924 /* The following cast is ugly, but GdipGetFontStyle does treat
4925 its first parameter as const. */
4926 stat = GdipGetFontStyle((GpFont*)font, &style);
4927 if (stat != Ok)
4928 return stat;
4930 fn_len = lstrlenW(font->family->FamilyName);
4931 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4932 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
4933 (void**)&object_record);
4934 if (stat != Ok)
4935 return stat;
4937 *id = METAFILE_AddObjectId(metafile);
4939 object_record->Header.Flags = *id | ObjectTypeFont << 8;
4941 font_record = &object_record->ObjectData.font;
4942 font_record->Version = VERSION_MAGIC2;
4943 font_record->EmSize = font->emSize;
4944 font_record->SizeUnit = font->unit;
4945 font_record->FontStyleFlags = style;
4946 font_record->Reserved = 0;
4947 font_record->Length = fn_len;
4949 memcpy(font_record->FamilyName, font->family->FamilyName,
4950 fn_len * sizeof(*font->family->FamilyName));
4952 return Ok;
4955 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
4956 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
4957 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
4959 DWORD brush_id;
4960 DWORD font_id;
4961 DWORD alloc_size;
4962 GpStatus stat;
4963 EmfPlusDrawDriverString *draw_string_record;
4964 BYTE *cursor;
4965 BOOL inline_color;
4966 BOOL include_matrix = FALSE;
4968 if (length <= 0)
4969 return InvalidParameter;
4971 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4972 metafile->metafile_type != MetafileTypeEmfPlusDual)
4974 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
4975 return NotImplemented;
4978 stat = METAFILE_AddFontObject(metafile, font, &font_id);
4979 if (stat != Ok)
4980 return stat;
4982 inline_color = (brush->bt == BrushTypeSolidColor);
4983 if (!inline_color)
4985 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4986 if (stat != Ok)
4987 return stat;
4990 if (matrix)
4992 BOOL identity;
4994 stat = GdipIsMatrixIdentity(matrix, &identity);
4995 if (stat != Ok)
4996 return stat;
4998 include_matrix = !identity;
5001 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
5002 length * (sizeof(*text) + sizeof(*positions));
5004 if (include_matrix)
5005 alloc_size += sizeof(*matrix);
5007 /* Pad record to DWORD alignment. */
5008 alloc_size = (alloc_size + 3) & ~3;
5010 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawDriverString, alloc_size, (void**)&draw_string_record);
5011 if (stat != Ok)
5012 return stat;
5014 draw_string_record->Header.Flags = font_id;
5015 draw_string_record->DriverStringOptionsFlags = flags;
5016 draw_string_record->MatrixPresent = include_matrix;
5017 draw_string_record->GlyphCount = length;
5019 if (inline_color)
5021 draw_string_record->Header.Flags |= 0x8000;
5022 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
5024 else
5025 draw_string_record->brush.BrushId = brush_id;
5027 cursor = &draw_string_record->VariableData[0];
5029 memcpy(cursor, text, length * sizeof(*text));
5030 cursor += length * sizeof(*text);
5032 if (flags & DriverStringOptionsRealizedAdvance)
5034 static BOOL fixme_written = FALSE;
5036 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
5037 in the case of RealizedAdvance, each glyph position is computed
5038 and serialized.
5040 While native GDI+ is capable of playing back metafiles with this
5041 flag set, it is possible that some application might rely on
5042 metafiles produced from GDI+ not setting this flag. Ideally we
5043 would also compute the position of each glyph here, serialize those
5044 values, and not set DriverStringOptionsRealizedAdvance. */
5045 if (!fixme_written)
5047 fixme_written = TRUE;
5048 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
5051 *((PointF*)cursor) = *positions;
5053 else
5054 memcpy(cursor, positions, length * sizeof(*positions));
5056 if (include_matrix)
5058 cursor += length * sizeof(*positions);
5059 memcpy(cursor, matrix, sizeof(*matrix));
5062 METAFILE_WriteRecords(metafile);
5064 return Ok;
5067 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
5069 GpStatus stat;
5070 DWORD brush_id;
5071 DWORD region_id;
5072 EmfPlusFillRegion *fill_region_record;
5073 BOOL inline_color;
5075 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5076 metafile->metafile_type != MetafileTypeEmfPlusDual)
5078 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5079 return NotImplemented;
5082 inline_color = (brush->bt == BrushTypeSolidColor);
5083 if (!inline_color)
5085 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5086 if (stat != Ok)
5087 return stat;
5090 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
5091 if (stat != Ok)
5092 return stat;
5094 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRegion, sizeof(EmfPlusFillRegion),
5095 (void**)&fill_region_record);
5096 if (stat != Ok)
5097 return stat;
5099 fill_region_record->Header.Flags = region_id;
5101 if (inline_color)
5103 fill_region_record->Header.Flags |= 0x8000;
5104 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
5106 else
5107 fill_region_record->data.BrushId = brush_id;
5109 METAFILE_WriteRecords(metafile);
5111 return Ok;
5114 GpStatus METAFILE_DrawRectangles(GpMetafile *metafile, GpPen *pen, const GpRectF *rects, INT count)
5116 EmfPlusDrawRects *record;
5117 GpStatus stat;
5118 BOOL integer_rects = TRUE;
5119 DWORD pen_id;
5120 int i;
5122 if (metafile->metafile_type == MetafileTypeEmf)
5124 FIXME("stub!\n");
5125 return NotImplemented;
5128 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5129 if (stat != Ok) return stat;
5131 for (i = 0; i < count; i++)
5133 if (!is_integer_rect(&rects[i]))
5135 integer_rects = FALSE;
5136 break;
5140 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawRects, FIELD_OFFSET(EmfPlusDrawRects, RectData) +
5141 count * (integer_rects ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF)),
5142 (void **)&record);
5143 if (stat != Ok)
5144 return stat;
5146 record->Header.Flags = pen_id;
5147 if (integer_rects)
5148 record->Header.Flags |= 0x4000;
5149 record->Count = count;
5151 if (integer_rects)
5153 for (i = 0; i < count; i++)
5155 record->RectData.rect[i].X = (SHORT)rects[i].X;
5156 record->RectData.rect[i].Y = (SHORT)rects[i].Y;
5157 record->RectData.rect[i].Width = (SHORT)rects[i].Width;
5158 record->RectData.rect[i].Height = (SHORT)rects[i].Height;
5161 else
5162 memcpy(record->RectData.rectF, rects, sizeof(*rects) * count);
5164 METAFILE_WriteRecords(metafile);
5166 return Ok;
5169 GpStatus METAFILE_DrawArc(GpMetafile *metafile, GpPen *pen, const GpRectF *rect, REAL startAngle, REAL sweepAngle)
5171 EmfPlusDrawArc *record;
5172 GpStatus stat;
5173 BOOL integer_rect;
5174 DWORD pen_id;
5176 if (metafile->metafile_type == MetafileTypeEmf)
5178 FIXME("stub!\n");
5179 return NotImplemented;
5182 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5183 if (stat != Ok) return stat;
5185 integer_rect = is_integer_rect(rect);
5187 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawArc, FIELD_OFFSET(EmfPlusDrawArc, RectData) +
5188 integer_rect ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF),
5189 (void **)&record);
5190 if (stat != Ok)
5191 return stat;
5193 record->Header.Flags = pen_id;
5194 if (integer_rect)
5195 record->Header.Flags |= 0x4000;
5196 record->StartAngle = startAngle;
5197 record->SweepAngle = sweepAngle;
5199 if (integer_rect)
5201 record->RectData.rect.X = (SHORT)rect->X;
5202 record->RectData.rect.Y = (SHORT)rect->Y;
5203 record->RectData.rect.Width = (SHORT)rect->Width;
5204 record->RectData.rect.Height = (SHORT)rect->Height;
5206 else
5207 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5209 METAFILE_WriteRecords(metafile);
5211 return Ok;
5214 GpStatus METAFILE_OffsetClip(GpMetafile *metafile, REAL dx, REAL dy)
5216 EmfPlusOffsetClip *record;
5217 GpStatus stat;
5219 if (metafile->metafile_type == MetafileTypeEmf)
5221 FIXME("stub!\n");
5222 return NotImplemented;
5225 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeOffsetClip,
5226 sizeof(*record), (void **)&record);
5227 if (stat != Ok)
5228 return stat;
5230 record->dx = dx;
5231 record->dy = dy;
5233 METAFILE_WriteRecords(metafile);
5235 return Ok;
5238 GpStatus METAFILE_ResetClip(GpMetafile *metafile)
5240 EmfPlusRecordHeader *record;
5241 GpStatus stat;
5243 if (metafile->metafile_type == MetafileTypeEmf)
5245 FIXME("stub!\n");
5246 return NotImplemented;
5249 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetClip,
5250 sizeof(*record), (void **)&record);
5251 if (stat != Ok)
5252 return stat;
5254 METAFILE_WriteRecords(metafile);
5256 return Ok;
5259 GpStatus METAFILE_SetClipPath(GpMetafile *metafile, GpPath *path, CombineMode mode)
5261 EmfPlusRecordHeader *record;
5262 DWORD path_id;
5263 GpStatus stat;
5265 if (metafile->metafile_type == MetafileTypeEmf)
5267 FIXME("stub!\n");
5268 return NotImplemented;
5271 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5272 if (stat != Ok) return stat;
5274 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipPath,
5275 sizeof(*record), (void **)&record);
5276 if (stat != Ok)
5277 return stat;
5279 record->Flags = ((mode & 0xf) << 8) | path_id;
5281 METAFILE_WriteRecords(metafile);
5283 return Ok;
5286 GpStatus METAFILE_SetRenderingOrigin(GpMetafile *metafile, INT x, INT y)
5288 EmfPlusSetRenderingOrigin *record;
5289 GpStatus stat;
5291 if (metafile->metafile_type == MetafileTypeEmf)
5293 FIXME("stub!\n");
5294 return NotImplemented;
5297 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetRenderingOrigin,
5298 sizeof(*record), (void **)&record);
5299 if (stat != Ok)
5300 return stat;
5302 record->x = x;
5303 record->y = y;
5305 METAFILE_WriteRecords(metafile);
5307 return Ok;