makefiles: Always use the global SOURCES variable for .l files.
[wine.git] / dlls / gdiplus / metafile.c
blob0664aa5fc89f694334116162118e2ce6e408ddee
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 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "ocidl.h"
30 #include "olectl.h"
31 #include "ole2.h"
33 #include "winreg.h"
34 #include "shlwapi.h"
36 #include "gdiplus.h"
37 #include "gdiplus_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
45 typedef ARGB EmfPlusARGB;
47 typedef struct EmfPlusPointF
49 float X;
50 float Y;
51 } EmfPlusPointF;
53 typedef struct EmfPlusRecordHeader
55 WORD Type;
56 WORD Flags;
57 DWORD Size;
58 DWORD DataSize;
59 } EmfPlusRecordHeader;
61 typedef struct EmfPlusHeader
63 EmfPlusRecordHeader Header;
64 DWORD Version;
65 DWORD EmfPlusFlags;
66 DWORD LogicalDpiX;
67 DWORD LogicalDpiY;
68 } EmfPlusHeader;
70 typedef struct EmfPlusClear
72 EmfPlusRecordHeader Header;
73 DWORD Color;
74 } EmfPlusClear;
76 typedef struct EmfPlusFillRects
78 EmfPlusRecordHeader Header;
79 DWORD BrushID;
80 DWORD Count;
81 } EmfPlusFillRects;
83 typedef struct EmfPlusSetClipRect
85 EmfPlusRecordHeader Header;
86 GpRectF ClipRect;
87 } EmfPlusSetClipRect;
89 typedef struct EmfPlusSetPageTransform
91 EmfPlusRecordHeader Header;
92 REAL PageScale;
93 } EmfPlusSetPageTransform;
95 typedef struct EmfPlusRect
97 SHORT X;
98 SHORT Y;
99 SHORT Width;
100 SHORT Height;
101 } EmfPlusRect;
103 typedef struct EmfPlusSetWorldTransform
105 EmfPlusRecordHeader Header;
106 REAL MatrixData[6];
107 } EmfPlusSetWorldTransform;
109 typedef struct EmfPlusScaleWorldTransform
111 EmfPlusRecordHeader Header;
112 REAL Sx;
113 REAL Sy;
114 } EmfPlusScaleWorldTransform;
116 typedef struct EmfPlusMultiplyWorldTransform
118 EmfPlusRecordHeader Header;
119 REAL MatrixData[6];
120 } EmfPlusMultiplyWorldTransform;
122 typedef struct EmfPlusRotateWorldTransform
124 EmfPlusRecordHeader Header;
125 REAL Angle;
126 } EmfPlusRotateWorldTransform;
128 typedef struct EmfPlusTranslateWorldTransform
130 EmfPlusRecordHeader Header;
131 REAL dx;
132 REAL dy;
133 } EmfPlusTranslateWorldTransform;
135 typedef struct EmfPlusBeginContainer
137 EmfPlusRecordHeader Header;
138 GpRectF DestRect;
139 GpRectF SrcRect;
140 DWORD StackIndex;
141 } EmfPlusBeginContainer;
143 typedef struct EmfPlusContainerRecord
145 EmfPlusRecordHeader Header;
146 DWORD StackIndex;
147 } EmfPlusContainerRecord;
149 enum container_type
151 BEGIN_CONTAINER,
152 SAVE_GRAPHICS
155 typedef struct container
157 struct list entry;
158 DWORD id;
159 enum container_type type;
160 GraphicsContainer state;
161 GpMatrix world_transform;
162 GpUnit page_unit;
163 REAL page_scale;
164 GpRegion *clip;
165 } container;
167 enum PenDataFlags
169 PenDataTransform = 0x0001,
170 PenDataStartCap = 0x0002,
171 PenDataEndCap = 0x0004,
172 PenDataJoin = 0x0008,
173 PenDataMiterLimit = 0x0010,
174 PenDataLineStyle = 0x0020,
175 PenDataDashedLineCap = 0x0040,
176 PenDataDashedLineOffset = 0x0080,
177 PenDataDashedLine = 0x0100,
178 PenDataNonCenter = 0x0200,
179 PenDataCompoundLine = 0x0400,
180 PenDataCustomStartCap = 0x0800,
181 PenDataCustomEndCap = 0x1000
184 enum CustomLineCapData
186 CustomLineCapDataFillPath = 0x1,
187 CustomLineCapDataLinePath = 0x2,
190 typedef struct EmfPlusTransformMatrix
192 REAL TransformMatrix[6];
193 } EmfPlusTransformMatrix;
195 enum LineStyle
197 LineStyleSolid,
198 LineStyleDash,
199 LineStyleDot,
200 LineStyleDashDot,
201 LineStyleDashDotDot,
202 LineStyleCustom
205 typedef struct EmfPlusDashedLineData
207 DWORD DashedLineDataSize;
208 BYTE data[1];
209 } EmfPlusDashedLineData;
211 typedef struct EmfPlusCompoundLineData
213 DWORD CompoundLineDataSize;
214 BYTE data[1];
215 } EmfPlusCompoundLineData;
217 typedef struct EmfPlusCustomStartCapData
219 DWORD CustomStartCapSize;
220 BYTE data[1];
221 } EmfPlusCustomStartCapData;
223 typedef struct EmfPlusCustomEndCapData
225 DWORD CustomEndCapSize;
226 BYTE data[1];
227 } EmfPlusCustomEndCapData;
229 typedef struct EmfPlusPenData
231 DWORD PenDataFlags;
232 DWORD PenUnit;
233 REAL PenWidth;
234 BYTE OptionalData[1];
235 } EmfPlusPenData;
237 enum BrushDataFlags
239 BrushDataPath = 1 << 0,
240 BrushDataTransform = 1 << 1,
241 BrushDataPresetColors = 1 << 2,
242 BrushDataBlendFactorsH = 1 << 3,
243 BrushDataBlendFactorsV = 1 << 4,
244 BrushDataFocusScales = 1 << 6,
245 BrushDataIsGammaCorrected = 1 << 7,
246 BrushDataDoNotTransform = 1 << 8,
249 typedef struct EmfPlusSolidBrushData
251 EmfPlusARGB SolidColor;
252 } EmfPlusSolidBrushData;
254 typedef struct EmfPlusHatchBrushData
256 DWORD HatchStyle;
257 EmfPlusARGB ForeColor;
258 EmfPlusARGB BackColor;
259 } EmfPlusHatchBrushData;
261 typedef struct EmfPlusTextureBrushData
263 DWORD BrushDataFlags;
264 INT WrapMode;
265 BYTE OptionalData[1];
266 } EmfPlusTextureBrushData;
268 typedef struct EmfPlusRectF
270 float X;
271 float Y;
272 float Width;
273 float Height;
274 } EmfPlusRectF;
276 typedef struct EmfPlusLinearGradientBrushData
278 DWORD BrushDataFlags;
279 INT WrapMode;
280 EmfPlusRectF RectF;
281 EmfPlusARGB StartColor;
282 EmfPlusARGB EndColor;
283 DWORD Reserved1;
284 DWORD Reserved2;
285 BYTE OptionalData[1];
286 } EmfPlusLinearGradientBrushData;
288 typedef struct EmfPlusBrush
290 DWORD Version;
291 DWORD Type;
292 union {
293 EmfPlusSolidBrushData solid;
294 EmfPlusHatchBrushData hatch;
295 EmfPlusTextureBrushData texture;
296 EmfPlusLinearGradientBrushData lineargradient;
297 } BrushData;
298 } EmfPlusBrush;
300 typedef struct EmfPlusCustomLineCapArrowData
302 REAL Width;
303 REAL Height;
304 REAL MiddleInset;
305 BOOL FillState;
306 DWORD LineStartCap;
307 DWORD LineEndCap;
308 DWORD LineJoin;
309 REAL LineMiterLimit;
310 REAL WidthScale;
311 EmfPlusPointF FillHotSpot;
312 EmfPlusPointF LineHotSpot;
313 } EmfPlusCustomLineCapArrowData;
315 typedef struct EmfPlusPath
317 DWORD Version;
318 DWORD PathPointCount;
319 DWORD PathPointFlags;
320 /* PathPoints[] */
321 /* PathPointTypes[] */
322 /* AlignmentPadding */
323 BYTE data[1];
324 } EmfPlusPath;
326 typedef struct EmfPlusCustomLineCapDataFillPath
328 INT FillPathLength;
329 /* EmfPlusPath */
330 BYTE FillPath[1];
331 } EmfPlusCustomLineCapDataFillPath;
333 typedef struct EmfPlusCustomLineCapDataLinePath
335 INT LinePathLength;
336 /* EmfPlusPath */
337 BYTE LinePath[1];
338 } EmfPlusCustomLineCapDataLinePath;
340 typedef struct EmfPlusCustomLineCapData
342 DWORD CustomLineCapDataFlags;
343 DWORD BaseCap;
344 REAL BaseInset;
345 DWORD StrokeStartCap;
346 DWORD StrokeEndCap;
347 DWORD StrokeJoin;
348 REAL StrokeMiterLimit;
349 REAL WidthScale;
350 EmfPlusPointF FillHotSpot;
351 EmfPlusPointF LineHotSpot;
352 /* EmfPlusCustomLineCapDataFillPath */
353 /* EmfPlusCustomLineCapDataLinePath */
354 BYTE OptionalData[1];
355 } EmfPlusCustomLineCapData;
357 typedef struct EmfPlusCustomLineCap
359 DWORD Version;
360 DWORD Type;
361 /* EmfPlusCustomLineCapArrowData */
362 /* EmfPlusCustomLineCapData */
363 BYTE CustomLineCapData[1];
364 } EmfPlusCustomLineCap;
366 typedef struct EmfPlusPen
368 DWORD Version;
369 DWORD Type;
370 /* EmfPlusPenData */
371 /* EmfPlusBrush */
372 BYTE data[1];
373 } EmfPlusPen;
375 typedef struct EmfPlusRegionNodePath
377 DWORD RegionNodePathLength;
378 EmfPlusPath RegionNodePath;
379 } EmfPlusRegionNodePath;
381 typedef struct EmfPlusRegion
383 DWORD Version;
384 DWORD RegionNodeCount;
385 BYTE RegionNode[1];
386 } EmfPlusRegion;
388 typedef struct EmfPlusPalette
390 DWORD PaletteStyleFlags;
391 DWORD PaletteCount;
392 BYTE PaletteEntries[1];
393 } EmfPlusPalette;
395 typedef enum
397 BitmapDataTypePixel,
398 BitmapDataTypeCompressed,
399 } BitmapDataType;
401 typedef struct EmfPlusBitmap
403 DWORD Width;
404 DWORD Height;
405 DWORD Stride;
406 DWORD PixelFormat;
407 DWORD Type;
408 BYTE BitmapData[1];
409 } EmfPlusBitmap;
411 typedef struct EmfPlusMetafile
413 DWORD Type;
414 DWORD MetafileDataSize;
415 BYTE MetafileData[1];
416 } EmfPlusMetafile;
418 typedef enum ImageDataType
420 ImageDataTypeUnknown,
421 ImageDataTypeBitmap,
422 ImageDataTypeMetafile,
423 } ImageDataType;
425 typedef struct EmfPlusImage
427 DWORD Version;
428 ImageDataType Type;
429 union
431 EmfPlusBitmap bitmap;
432 EmfPlusMetafile metafile;
433 } ImageData;
434 } EmfPlusImage;
436 typedef struct EmfPlusImageAttributes
438 DWORD Version;
439 DWORD Reserved1;
440 DWORD WrapMode;
441 EmfPlusARGB ClampColor;
442 DWORD ObjectClamp;
443 DWORD Reserved2;
444 } EmfPlusImageAttributes;
446 typedef struct EmfPlusFont
448 DWORD Version;
449 float EmSize;
450 DWORD SizeUnit;
451 DWORD FontStyleFlags;
452 DWORD Reserved;
453 DWORD Length;
454 WCHAR FamilyName[1];
455 } EmfPlusFont;
457 typedef struct EmfPlusObject
459 EmfPlusRecordHeader Header;
460 union
462 EmfPlusBrush brush;
463 EmfPlusPen pen;
464 EmfPlusPath path;
465 EmfPlusRegion region;
466 EmfPlusImage image;
467 EmfPlusImageAttributes image_attributes;
468 EmfPlusFont font;
469 } ObjectData;
470 } EmfPlusObject;
472 typedef struct EmfPlusPointR7
474 BYTE X;
475 BYTE Y;
476 } EmfPlusPointR7;
478 typedef struct EmfPlusPoint
480 short X;
481 short Y;
482 } EmfPlusPoint;
484 typedef struct EmfPlusDrawImage
486 EmfPlusRecordHeader Header;
487 DWORD ImageAttributesID;
488 DWORD SrcUnit;
489 EmfPlusRectF SrcRect;
490 union
492 EmfPlusRect rect;
493 EmfPlusRectF rectF;
494 } RectData;
495 } EmfPlusDrawImage;
497 typedef struct EmfPlusDrawImagePoints
499 EmfPlusRecordHeader Header;
500 DWORD ImageAttributesID;
501 DWORD SrcUnit;
502 EmfPlusRectF SrcRect;
503 DWORD count;
504 union
506 EmfPlusPointR7 pointsR[3];
507 EmfPlusPoint points[3];
508 EmfPlusPointF pointsF[3];
509 } PointData;
510 } EmfPlusDrawImagePoints;
512 typedef struct EmfPlusDrawPath
514 EmfPlusRecordHeader Header;
515 DWORD PenId;
516 } EmfPlusDrawPath;
518 typedef struct EmfPlusDrawArc
520 EmfPlusRecordHeader Header;
521 float StartAngle;
522 float SweepAngle;
523 union
525 EmfPlusRect rect;
526 EmfPlusRectF rectF;
527 } RectData;
528 } EmfPlusDrawArc;
530 typedef struct EmfPlusDrawEllipse
532 EmfPlusRecordHeader Header;
533 union
535 EmfPlusRect rect;
536 EmfPlusRectF rectF;
537 } RectData;
538 } EmfPlusDrawEllipse;
540 typedef struct EmfPlusDrawPie
542 EmfPlusRecordHeader Header;
543 float StartAngle;
544 float SweepAngle;
545 union
547 EmfPlusRect rect;
548 EmfPlusRectF rectF;
549 } RectData;
550 } EmfPlusDrawPie;
552 typedef struct EmfPlusDrawRects
554 EmfPlusRecordHeader Header;
555 DWORD Count;
556 union
558 EmfPlusRect rect[1];
559 EmfPlusRectF rectF[1];
560 } RectData;
561 } EmfPlusDrawRects;
563 typedef struct EmfPlusFillPath
565 EmfPlusRecordHeader Header;
566 union
568 DWORD BrushId;
569 EmfPlusARGB Color;
570 } data;
571 } EmfPlusFillPath;
573 typedef struct EmfPlusFillClosedCurve
575 EmfPlusRecordHeader Header;
576 DWORD BrushId;
577 float Tension;
578 DWORD Count;
579 union
581 EmfPlusPointR7 pointsR[1];
582 EmfPlusPoint points[1];
583 EmfPlusPointF pointsF[1];
584 } PointData;
585 } EmfPlusFillClosedCurve;
587 typedef struct EmfPlusFillEllipse
589 EmfPlusRecordHeader Header;
590 DWORD BrushId;
591 union
593 EmfPlusRect rect;
594 EmfPlusRectF rectF;
595 } RectData;
596 } EmfPlusFillEllipse;
598 typedef struct EmfPlusFillPie
600 EmfPlusRecordHeader Header;
601 DWORD BrushId;
602 float StartAngle;
603 float SweepAngle;
604 union
606 EmfPlusRect rect;
607 EmfPlusRectF rectF;
608 } RectData;
609 } EmfPlusFillPie;
611 typedef struct EmfPlusDrawDriverString
613 EmfPlusRecordHeader Header;
614 union
616 DWORD BrushId;
617 ARGB Color;
618 } brush;
619 DWORD DriverStringOptionsFlags;
620 DWORD MatrixPresent;
621 DWORD GlyphCount;
622 BYTE VariableData[1];
623 } EmfPlusDrawDriverString;
625 typedef struct EmfPlusFillRegion
627 EmfPlusRecordHeader Header;
628 union
630 DWORD BrushId;
631 EmfPlusARGB Color;
632 } data;
633 } EmfPlusFillRegion;
635 typedef struct EmfPlusOffsetClip
637 EmfPlusRecordHeader Header;
638 float dx;
639 float dy;
640 } EmfPlusOffsetClip;
642 typedef struct EmfPlusSetRenderingOrigin
644 EmfPlusRecordHeader Header;
645 INT x;
646 INT y;
647 } EmfPlusSetRenderingOrigin;
649 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
651 struct emfplus_object *object = &metafile->objtable[id];
653 switch (object->type)
655 case ObjectTypeInvalid:
656 break;
657 case ObjectTypeBrush:
658 GdipDeleteBrush(object->u.brush);
659 break;
660 case ObjectTypePen:
661 GdipDeletePen(object->u.pen);
662 break;
663 case ObjectTypePath:
664 GdipDeletePath(object->u.path);
665 break;
666 case ObjectTypeRegion:
667 GdipDeleteRegion(object->u.region);
668 break;
669 case ObjectTypeImage:
670 GdipDisposeImage(object->u.image);
671 break;
672 case ObjectTypeFont:
673 GdipDeleteFont(object->u.font);
674 break;
675 case ObjectTypeImageAttributes:
676 GdipDisposeImageAttributes(object->u.image_attributes);
677 break;
678 default:
679 FIXME("not implemented for object type %u.\n", object->type);
680 return;
683 object->type = ObjectTypeInvalid;
684 object->u.object = NULL;
687 void METAFILE_Free(GpMetafile *metafile)
689 unsigned int i;
691 heap_free(metafile->comment_data);
692 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
693 if (!metafile->preserve_hemf)
694 DeleteEnhMetaFile(metafile->hemf);
695 if (metafile->record_graphics)
697 WARN("metafile closed while recording\n");
698 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
699 metafile->record_graphics->image = NULL;
700 metafile->record_graphics->busy = TRUE;
703 if (metafile->record_stream)
704 IStream_Release(metafile->record_stream);
706 for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
707 metafile_free_object_table_entry(metafile, i);
710 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
712 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
715 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, EmfPlusRecordType record_type,
716 DWORD size, void **result)
718 DWORD size_needed;
719 EmfPlusRecordHeader *record;
721 if (!metafile->comment_data_size)
723 DWORD data_size = max(256, size * 2 + 4);
724 metafile->comment_data = heap_alloc_zero(data_size);
726 if (!metafile->comment_data)
727 return OutOfMemory;
729 memcpy(metafile->comment_data, "EMF+", 4);
731 metafile->comment_data_size = data_size;
732 metafile->comment_data_length = 4;
735 size_needed = size + metafile->comment_data_length;
737 if (size_needed > metafile->comment_data_size)
739 DWORD data_size = size_needed * 2;
740 BYTE *new_data = heap_alloc_zero(data_size);
742 if (!new_data)
743 return OutOfMemory;
745 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
747 metafile->comment_data_size = data_size;
748 heap_free(metafile->comment_data);
749 metafile->comment_data = new_data;
752 *result = metafile->comment_data + metafile->comment_data_length;
753 metafile->comment_data_length += size;
755 record = (EmfPlusRecordHeader*)*result;
756 record->Type = record_type;
757 record->Flags = 0;
758 record->Size = size;
759 record->DataSize = size - sizeof(EmfPlusRecordHeader);
761 return Ok;
764 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
766 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
767 metafile->comment_data_length -= record->Size;
770 static void METAFILE_WriteRecords(GpMetafile *metafile)
772 if (metafile->comment_data_length > 4)
774 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
775 metafile->comment_data_length = 4;
779 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
781 GpStatus stat;
783 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
785 EmfPlusHeader *header;
787 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeHeader, sizeof(EmfPlusHeader), (void**)&header);
788 if (stat != Ok)
789 return stat;
791 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
792 header->Header.Flags = 1;
794 header->Version = VERSION_MAGIC2;
796 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
797 header->EmfPlusFlags = 1;
798 else
799 header->EmfPlusFlags = 0;
801 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
802 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
804 METAFILE_WriteRecords(metafile);
807 return Ok;
810 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
812 GpStatus stat;
814 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
816 EmfPlusRecordHeader *record;
818 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndOfFile, sizeof(EmfPlusRecordHeader), (void**)&record);
819 if (stat != Ok)
820 return stat;
822 METAFILE_WriteRecords(metafile);
825 return Ok;
828 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
829 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
832 TRACE("(%p %d %s %d %p %p)\n", hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
834 return GdipRecordMetafileFileName(NULL, hdc, type, frameRect, frameUnit, desc, metafile);
837 /*****************************************************************************
838 * GdipRecordMetafileI [GDIPLUS.@]
840 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
841 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
843 GpRectF frameRectF, *pFrameRectF;
845 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
847 if (frameRect)
849 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
850 pFrameRectF = &frameRectF;
852 else
853 pFrameRectF = NULL;
855 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
858 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
859 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
861 GpRectF frameRectF, *pFrameRectF;
863 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
865 if (frameRect)
867 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
868 pFrameRectF = &frameRectF;
870 else
871 pFrameRectF = NULL;
873 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
876 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
877 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
879 GpStatus stat;
881 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
883 if (!stream)
884 return InvalidParameter;
886 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
888 if (stat == Ok)
890 (*metafile)->record_stream = stream;
891 IStream_AddRef(stream);
894 return stat;
897 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
898 UINT num_points)
900 int i;
902 if (!metafile->auto_frame || !num_points)
903 return;
905 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
906 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
908 for (i=0; i<num_points; i++)
910 if (points[i].X < metafile->auto_frame_min.X)
911 metafile->auto_frame_min.X = points[i].X;
912 if (points[i].X > metafile->auto_frame_max.X)
913 metafile->auto_frame_max.X = points[i].X;
914 if (points[i].Y < metafile->auto_frame_min.Y)
915 metafile->auto_frame_min.Y = points[i].Y;
916 if (points[i].Y > metafile->auto_frame_max.Y)
917 metafile->auto_frame_max.Y = points[i].Y;
921 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
923 GpStatus stat;
925 if (!metafile->record_dc || metafile->record_graphics)
926 return InvalidParameter;
928 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
930 if (stat == Ok)
932 *result = metafile->record_graphics;
933 metafile->record_graphics->xres = metafile->logical_dpix;
934 metafile->record_graphics->yres = metafile->logical_dpiy;
935 metafile->record_graphics->printer_display = metafile->printer_display;
938 return stat;
941 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
943 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
945 EmfPlusRecordHeader *record;
946 GpStatus stat;
948 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeGetDC, sizeof(EmfPlusRecordHeader), (void**)&record);
949 if (stat != Ok)
950 return stat;
952 METAFILE_WriteRecords(metafile);
955 *hdc = metafile->record_dc;
957 return Ok;
960 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
962 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
964 EmfPlusClear *record;
965 GpStatus stat;
967 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeClear, sizeof(EmfPlusClear), (void**)&record);
968 if (stat != Ok)
969 return stat;
971 record->Color = color;
973 METAFILE_WriteRecords(metafile);
976 return Ok;
979 static BOOL is_integer_rect(const GpRectF *rect)
981 SHORT x, y, width, height;
982 x = rect->X;
983 y = rect->Y;
984 width = rect->Width;
985 height = rect->Height;
986 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
987 rect->Width != (REAL)width || rect->Height != (REAL)height)
988 return FALSE;
989 return TRUE;
992 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
994 switch (brush->bt)
996 case BrushTypeSolidColor:
997 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
998 break;
999 case BrushTypeHatchFill:
1000 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
1001 break;
1002 case BrushTypeLinearGradient:
1004 BOOL ignore_xform;
1005 GpLineGradient *gradient = (GpLineGradient*)brush;
1007 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
1009 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1010 if (!ignore_xform)
1011 *size += sizeof(gradient->transform);
1013 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1014 *size += sizeof(DWORD) + gradient->pblendcount *
1015 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
1016 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1017 *size += sizeof(DWORD) + gradient->blendcount *
1018 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
1020 break;
1022 default:
1023 FIXME("unsupported brush type: %d\n", brush->bt);
1024 return NotImplemented;
1027 return Ok;
1030 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
1032 data->Version = VERSION_MAGIC2;
1033 data->Type = brush->bt;
1035 switch (brush->bt)
1037 case BrushTypeSolidColor:
1039 GpSolidFill *solid = (GpSolidFill *)brush;
1040 data->BrushData.solid.SolidColor = solid->color;
1041 break;
1043 case BrushTypeHatchFill:
1045 GpHatch *hatch = (GpHatch *)brush;
1046 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
1047 data->BrushData.hatch.ForeColor = hatch->forecol;
1048 data->BrushData.hatch.BackColor = hatch->backcol;
1049 break;
1051 case BrushTypeLinearGradient:
1053 BYTE *cursor;
1054 BOOL ignore_xform;
1055 GpLineGradient *gradient = (GpLineGradient*)brush;
1057 data->BrushData.lineargradient.BrushDataFlags = 0;
1058 data->BrushData.lineargradient.WrapMode = gradient->wrap;
1059 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
1060 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
1061 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
1062 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
1063 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1064 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1065 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1066 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1068 if (gradient->gamma)
1069 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1071 cursor = &data->BrushData.lineargradient.OptionalData[0];
1073 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1074 if (!ignore_xform)
1076 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1077 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1078 cursor += sizeof(gradient->transform);
1081 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1083 const DWORD count = gradient->pblendcount;
1085 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1087 memcpy(cursor, &count, sizeof(count));
1088 cursor += sizeof(count);
1090 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1091 cursor += count * sizeof(*gradient->pblendpos);
1093 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1095 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1097 const DWORD count = gradient->blendcount;
1099 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1101 memcpy(cursor, &count, sizeof(count));
1102 cursor += sizeof(count);
1104 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1105 cursor += count * sizeof(*gradient->blendpos);
1107 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1110 break;
1112 default:
1113 FIXME("unsupported brush type: %d\n", brush->bt);
1117 static void METAFILE_PrepareCustomLineCapData(GDIPCONST GpCustomLineCap *cap, DWORD *ret_cap_size,
1118 DWORD *ret_cap_data_size, DWORD *ret_path_size)
1120 DWORD cap_size, path_size = 0;
1122 /* EmfPlusCustomStartCapData */
1123 cap_size = FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1124 /* -> EmfPlusCustomLineCap */
1125 cap_size += FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData);
1126 /* -> EmfPlusCustomLineCapArrowData */
1127 if (cap->type == CustomLineCapTypeAdjustableArrow)
1128 cap_size += sizeof(EmfPlusCustomLineCapArrowData);
1129 /* -> EmfPlusCustomLineCapData */
1130 else
1132 /* -> EmfPlusCustomLineCapOptionalData */
1133 cap_size += FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData);
1134 if (cap->fill)
1135 /* -> EmfPlusCustomLineCapDataFillPath */
1136 cap_size += FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath);
1137 else
1138 /* -> EmfPlusCustomLineCapDataLinePath */
1139 cap_size += FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath);
1141 /* -> EmfPlusPath in EmfPlusCustomLineCapDataFillPath and EmfPlusCustomLineCapDataLinePath */
1142 path_size = FIELD_OFFSET(EmfPlusPath, data);
1143 path_size += sizeof(PointF) * cap->pathdata.Count;
1144 path_size += sizeof(BYTE) * cap->pathdata.Count;
1145 path_size = (path_size + 3) & ~3;
1147 cap_size += path_size;
1150 *ret_cap_size = cap_size;
1151 *ret_cap_data_size = cap_size - FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1152 *ret_path_size = path_size;
1155 static void METAFILE_FillCustomLineCapData(GDIPCONST GpCustomLineCap *cap, BYTE *ptr,
1156 REAL line_miter_limit, DWORD data_size, DWORD path_size)
1158 EmfPlusCustomStartCapData *cap_data;
1159 EmfPlusCustomLineCap *line_cap;
1160 DWORD i;
1162 cap_data = (EmfPlusCustomStartCapData *)ptr;
1163 cap_data->CustomStartCapSize = data_size;
1164 i = FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1166 line_cap = (EmfPlusCustomLineCap *)(ptr + i);
1167 line_cap->Version = VERSION_MAGIC2;
1168 line_cap->Type = cap->type;
1169 i += FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData);
1171 if (cap->type == CustomLineCapTypeAdjustableArrow)
1173 EmfPlusCustomLineCapArrowData *arrow_data;
1174 GpAdjustableArrowCap *arrow_cap;
1176 arrow_data = (EmfPlusCustomLineCapArrowData *)(ptr + i);
1177 arrow_cap = (GpAdjustableArrowCap *)cap;
1178 arrow_data->Width = arrow_cap->width;
1179 arrow_data->Height = arrow_cap->height;
1180 arrow_data->MiddleInset = arrow_cap->middle_inset;
1181 arrow_data->FillState = arrow_cap->cap.fill;
1182 arrow_data->LineStartCap = arrow_cap->cap.strokeStartCap;
1183 arrow_data->LineEndCap = arrow_cap->cap.strokeEndCap;
1184 arrow_data->LineJoin = arrow_cap->cap.join;
1185 arrow_data->LineMiterLimit = line_miter_limit;
1186 arrow_data->WidthScale = arrow_cap->cap.scale;
1187 arrow_data->FillHotSpot.X = 0;
1188 arrow_data->FillHotSpot.Y = 0;
1189 arrow_data->LineHotSpot.X = 0;
1190 arrow_data->LineHotSpot.Y = 0;
1192 else
1194 EmfPlusCustomLineCapData *line_cap_data = (EmfPlusCustomLineCapData *)(ptr + i);
1195 EmfPlusPath *path;
1197 if (cap->fill)
1198 line_cap_data->CustomLineCapDataFlags = CustomLineCapDataFillPath;
1199 else
1200 line_cap_data->CustomLineCapDataFlags = CustomLineCapDataLinePath;
1201 line_cap_data->BaseCap = cap->basecap;
1202 line_cap_data->BaseInset = cap->inset;
1203 line_cap_data->StrokeStartCap = cap->strokeStartCap;
1204 line_cap_data->StrokeEndCap = cap->strokeEndCap;
1205 line_cap_data->StrokeJoin = cap->join;
1206 line_cap_data->StrokeMiterLimit = line_miter_limit;
1207 line_cap_data->WidthScale = cap->scale;
1208 line_cap_data->FillHotSpot.X = 0;
1209 line_cap_data->FillHotSpot.Y = 0;
1210 line_cap_data->LineHotSpot.X = 0;
1211 line_cap_data->LineHotSpot.Y = 0;
1212 i += FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData);
1214 if (cap->fill)
1216 EmfPlusCustomLineCapDataFillPath *fill_path = (EmfPlusCustomLineCapDataFillPath *)(ptr + i);
1217 fill_path->FillPathLength = path_size;
1218 i += FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath);
1220 else
1222 EmfPlusCustomLineCapDataLinePath *line_path = (EmfPlusCustomLineCapDataLinePath *)(ptr + i);
1223 line_path->LinePathLength = path_size;
1224 i += FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath);
1227 path = (EmfPlusPath *)(ptr + i);
1228 path->Version = VERSION_MAGIC2;
1229 path->PathPointCount = cap->pathdata.Count;
1230 path->PathPointFlags = 0;
1231 i += FIELD_OFFSET(EmfPlusPath, data);
1232 memcpy(ptr + i, cap->pathdata.Points, cap->pathdata.Count * sizeof(PointF));
1233 i += cap->pathdata.Count * sizeof(PointF);
1234 memcpy(ptr + i, cap->pathdata.Types, cap->pathdata.Count * sizeof(BYTE));
1238 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1240 EmfPlusObject *object_record;
1241 GpStatus stat;
1242 DWORD size;
1244 *id = -1;
1245 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1246 return Ok;
1248 stat = METAFILE_PrepareBrushData(brush, &size);
1249 if (stat != Ok) return stat;
1251 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1252 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1253 if (stat != Ok) return stat;
1255 *id = METAFILE_AddObjectId(metafile);
1256 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1257 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1258 return Ok;
1261 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1262 GDIPCONST GpRectF* rects, INT count)
1264 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1266 EmfPlusFillRects *record;
1267 GpStatus stat;
1268 BOOL integer_rects = TRUE;
1269 int i;
1270 DWORD brushid;
1271 int flags = 0;
1273 if (brush->bt == BrushTypeSolidColor)
1275 flags |= 0x8000;
1276 brushid = ((GpSolidFill*)brush)->color;
1278 else
1280 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1281 if (stat != Ok)
1282 return stat;
1285 for (i=0; i<count; i++)
1287 if (!is_integer_rect(&rects[i]))
1289 integer_rects = FALSE;
1290 break;
1294 if (integer_rects)
1295 flags |= 0x4000;
1297 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRects,
1298 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1299 (void**)&record);
1300 if (stat != Ok)
1301 return stat;
1303 record->Header.Flags = flags;
1304 record->BrushID = brushid;
1305 record->Count = count;
1307 if (integer_rects)
1309 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1310 for (i=0; i<count; i++)
1312 record_rects[i].X = (SHORT)rects[i].X;
1313 record_rects[i].Y = (SHORT)rects[i].Y;
1314 record_rects[i].Width = (SHORT)rects[i].Width;
1315 record_rects[i].Height = (SHORT)rects[i].Height;
1318 else
1319 memcpy(record+1, rects, sizeof(GpRectF) * count);
1321 METAFILE_WriteRecords(metafile);
1324 if (metafile->auto_frame)
1326 GpPointF corners[4];
1327 int i;
1329 for (i=0; i<count; i++)
1331 corners[0].X = rects[i].X;
1332 corners[0].Y = rects[i].Y;
1333 corners[1].X = rects[i].X + rects[i].Width;
1334 corners[1].Y = rects[i].Y;
1335 corners[2].X = rects[i].X;
1336 corners[2].Y = rects[i].Y + rects[i].Height;
1337 corners[3].X = rects[i].X + rects[i].Width;
1338 corners[3].Y = rects[i].Y + rects[i].Height;
1340 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1341 CoordinateSpaceWorld, corners, 4);
1343 METAFILE_AdjustFrame(metafile, corners, 4);
1347 return Ok;
1350 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1352 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1354 EmfPlusSetClipRect *record;
1355 GpStatus stat;
1357 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRect,
1358 sizeof(EmfPlusSetClipRect), (void **)&record);
1359 if (stat != Ok)
1360 return stat;
1362 record->Header.Flags = (mode & 0xf) << 8;
1363 record->ClipRect.X = x;
1364 record->ClipRect.Y = y;
1365 record->ClipRect.Width = width;
1366 record->ClipRect.Height = height;
1368 METAFILE_WriteRecords(metafile);
1371 return Ok;
1374 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1376 EmfPlusObject *object_record;
1377 DWORD size;
1378 GpStatus stat;
1380 *id = -1;
1381 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1382 return Ok;
1384 size = write_region_data(region, NULL);
1385 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1386 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1387 if (stat != Ok) return stat;
1389 *id = METAFILE_AddObjectId(metafile);
1390 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1391 write_region_data(region, &object_record->ObjectData.region);
1392 return Ok;
1395 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1397 EmfPlusRecordHeader *record;
1398 DWORD region_id;
1399 GpStatus stat;
1401 if (metafile->metafile_type == MetafileTypeEmf)
1403 FIXME("stub!\n");
1404 return NotImplemented;
1407 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1408 if (stat != Ok) return stat;
1410 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRegion, sizeof(*record), (void**)&record);
1411 if (stat != Ok) return stat;
1413 record->Flags = region_id | mode << 8;
1415 METAFILE_WriteRecords(metafile);
1416 return Ok;
1419 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1421 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1423 EmfPlusSetPageTransform *record;
1424 GpStatus stat;
1426 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetPageTransform,
1427 sizeof(EmfPlusSetPageTransform), (void **)&record);
1428 if (stat != Ok)
1429 return stat;
1431 record->Header.Flags = unit;
1432 record->PageScale = scale;
1434 METAFILE_WriteRecords(metafile);
1437 return Ok;
1440 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1442 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1444 EmfPlusSetWorldTransform *record;
1445 GpStatus stat;
1447 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetWorldTransform,
1448 sizeof(EmfPlusSetWorldTransform), (void **)&record);
1449 if (stat != Ok)
1450 return stat;
1452 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1454 METAFILE_WriteRecords(metafile);
1457 return Ok;
1460 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1462 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1464 EmfPlusScaleWorldTransform *record;
1465 GpStatus stat;
1467 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeScaleWorldTransform,
1468 sizeof(EmfPlusScaleWorldTransform), (void **)&record);
1469 if (stat != Ok)
1470 return stat;
1472 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1473 record->Sx = sx;
1474 record->Sy = sy;
1476 METAFILE_WriteRecords(metafile);
1479 return Ok;
1482 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1484 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1486 EmfPlusMultiplyWorldTransform *record;
1487 GpStatus stat;
1489 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeMultiplyWorldTransform,
1490 sizeof(EmfPlusMultiplyWorldTransform), (void **)&record);
1491 if (stat != Ok)
1492 return stat;
1494 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1495 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1497 METAFILE_WriteRecords(metafile);
1500 return Ok;
1503 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1505 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1507 EmfPlusRotateWorldTransform *record;
1508 GpStatus stat;
1510 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRotateWorldTransform,
1511 sizeof(EmfPlusRotateWorldTransform), (void **)&record);
1512 if (stat != Ok)
1513 return stat;
1515 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1516 record->Angle = angle;
1518 METAFILE_WriteRecords(metafile);
1521 return Ok;
1524 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1526 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1528 EmfPlusTranslateWorldTransform *record;
1529 GpStatus stat;
1531 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeTranslateWorldTransform,
1532 sizeof(EmfPlusTranslateWorldTransform), (void **)&record);
1533 if (stat != Ok)
1534 return stat;
1536 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1537 record->dx = dx;
1538 record->dy = dy;
1540 METAFILE_WriteRecords(metafile);
1543 return Ok;
1546 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1548 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1550 EmfPlusRecordHeader *record;
1551 GpStatus stat;
1553 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetWorldTransform,
1554 sizeof(EmfPlusRecordHeader), (void **)&record);
1555 if (stat != Ok)
1556 return stat;
1558 METAFILE_WriteRecords(metafile);
1561 return Ok;
1564 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1565 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1567 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1569 EmfPlusBeginContainer *record;
1570 GpStatus stat;
1572 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainer, sizeof(*record), (void**)&record);
1573 if (stat != Ok)
1574 return stat;
1576 record->Header.Flags = unit & 0xff;
1577 record->DestRect = *dstrect;
1578 record->SrcRect = *srcrect;
1579 record->StackIndex = StackIndex;
1581 METAFILE_WriteRecords(metafile);
1584 return Ok;
1587 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1589 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1591 EmfPlusContainerRecord *record;
1592 GpStatus stat;
1594 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainerNoParams,
1595 sizeof(EmfPlusContainerRecord), (void **)&record);
1596 if (stat != Ok)
1597 return stat;
1599 record->StackIndex = StackIndex;
1601 METAFILE_WriteRecords(metafile);
1604 return Ok;
1607 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1609 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1611 EmfPlusContainerRecord *record;
1612 GpStatus stat;
1614 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndContainer,
1615 sizeof(EmfPlusContainerRecord), (void **)&record);
1616 if (stat != Ok)
1617 return stat;
1619 record->StackIndex = StackIndex;
1621 METAFILE_WriteRecords(metafile);
1624 return Ok;
1627 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1629 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1631 EmfPlusContainerRecord *record;
1632 GpStatus stat;
1634 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSave,
1635 sizeof(EmfPlusContainerRecord), (void **)&record);
1636 if (stat != Ok)
1637 return stat;
1639 record->StackIndex = StackIndex;
1641 METAFILE_WriteRecords(metafile);
1644 return Ok;
1647 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1649 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1651 EmfPlusContainerRecord *record;
1652 GpStatus stat;
1654 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRestore,
1655 sizeof(EmfPlusContainerRecord), (void **)&record);
1656 if (stat != Ok)
1657 return stat;
1659 record->StackIndex = StackIndex;
1661 METAFILE_WriteRecords(metafile);
1664 return Ok;
1667 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1669 if (hdc != metafile->record_dc)
1670 return InvalidParameter;
1672 return Ok;
1675 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1677 GpStatus stat;
1679 stat = METAFILE_WriteEndOfFile(metafile);
1680 metafile->record_graphics = NULL;
1682 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1683 metafile->record_dc = NULL;
1685 heap_free(metafile->comment_data);
1686 metafile->comment_data = NULL;
1687 metafile->comment_data_size = 0;
1689 if (stat == Ok)
1691 MetafileHeader header;
1693 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1694 if (stat == Ok && metafile->auto_frame &&
1695 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1697 RECTL bounds_rc, gdi_bounds_rc;
1698 REAL x_scale = 2540.0 / header.DpiX;
1699 REAL y_scale = 2540.0 / header.DpiY;
1700 BYTE* buffer;
1701 UINT buffer_size;
1703 gdi_bounds_rc = header.EmfHeader.rclBounds;
1704 if (gdi_bounds_rc.right > gdi_bounds_rc.left &&
1705 gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1707 GpPointF *af_min = &metafile->auto_frame_min;
1708 GpPointF *af_max = &metafile->auto_frame_max;
1710 af_min->X = fmin(af_min->X, gdi_bounds_rc.left);
1711 af_min->Y = fmin(af_min->Y, gdi_bounds_rc.top);
1712 af_max->X = fmax(af_max->X, gdi_bounds_rc.right);
1713 af_max->Y = fmax(af_max->Y, gdi_bounds_rc.bottom);
1716 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1717 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1718 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1719 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1721 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1722 buffer = heap_alloc(buffer_size);
1723 if (buffer)
1725 HENHMETAFILE new_hemf;
1727 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1729 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1731 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1733 if (new_hemf)
1735 DeleteEnhMetaFile(metafile->hemf);
1736 metafile->hemf = new_hemf;
1738 else
1739 stat = OutOfMemory;
1741 heap_free(buffer);
1743 else
1744 stat = OutOfMemory;
1746 if (stat == Ok)
1747 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1749 if (stat == Ok)
1751 metafile->bounds.X = header.X;
1752 metafile->bounds.Y = header.Y;
1753 metafile->bounds.Width = header.Width;
1754 metafile->bounds.Height = header.Height;
1758 if (stat == Ok && metafile->record_stream)
1760 BYTE *buffer;
1761 UINT buffer_size;
1763 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1765 buffer = heap_alloc(buffer_size);
1766 if (buffer)
1768 HRESULT hr;
1770 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1772 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1774 if (FAILED(hr))
1775 stat = hresult_to_status(hr);
1777 heap_free(buffer);
1779 else
1780 stat = OutOfMemory;
1783 if (metafile->record_stream)
1785 IStream_Release(metafile->record_stream);
1786 metafile->record_stream = NULL;
1789 return stat;
1792 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1794 TRACE("(%p,%p)\n", metafile, hEmf);
1796 if (!metafile || !hEmf || !metafile->hemf)
1797 return InvalidParameter;
1799 *hEmf = metafile->hemf;
1800 metafile->hemf = NULL;
1802 return Ok;
1805 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1807 GpStatus stat = Ok;
1809 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1811 return stat;
1814 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1816 if (metafile->playback_dc)
1818 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1819 metafile->playback_dc = NULL;
1823 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1825 GpStatus stat;
1826 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1827 if (stat == Ok)
1828 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1829 return stat;
1832 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1834 GpMatrix *real_transform;
1835 GpStatus stat;
1837 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1839 if (stat == Ok)
1841 REAL scale_x = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpix, metafile->printer_display);
1842 REAL scale_y = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpiy, metafile->printer_display);
1844 if (metafile->page_unit != UnitDisplay)
1846 scale_x *= metafile->page_scale;
1847 scale_y *= metafile->page_scale;
1850 stat = GdipScaleMatrix(real_transform, scale_x, scale_y, MatrixOrderPrepend);
1852 if (stat == Ok)
1853 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1855 if (stat == Ok)
1856 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1858 GdipDeleteMatrix(real_transform);
1861 return stat;
1864 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1866 metafile_free_object_table_entry(metafile, id);
1867 metafile->objtable[id].type = type;
1868 metafile->objtable[id].u.object = object;
1871 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1873 EmfPlusImage *data = (EmfPlusImage *)record_data;
1874 GpStatus status;
1876 *image = NULL;
1878 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1879 return InvalidParameter;
1880 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1882 switch (data->Type)
1884 case ImageDataTypeBitmap:
1886 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1888 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1889 return InvalidParameter;
1890 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1892 switch (bitmapdata->Type)
1894 case BitmapDataTypePixel:
1896 ColorPalette *palette;
1897 BYTE *scan0;
1899 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1901 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1902 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1904 if (data_size <= palette_size)
1905 return InvalidParameter;
1906 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1908 if (data_size < palette_size)
1909 return InvalidParameter;
1910 data_size -= palette_size;
1912 palette = (ColorPalette *)bitmapdata->BitmapData;
1913 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1915 else
1917 palette = NULL;
1918 scan0 = bitmapdata->BitmapData;
1921 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1922 return InvalidParameter;
1924 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1925 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1926 if (status == Ok && palette)
1928 status = GdipSetImagePalette(*image, palette);
1929 if (status != Ok)
1931 GdipDisposeImage(*image);
1932 *image = NULL;
1935 break;
1937 case BitmapDataTypeCompressed:
1939 IWICImagingFactory *factory;
1940 IWICStream *stream;
1941 HRESULT hr;
1943 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1944 return GenericError;
1946 hr = IWICImagingFactory_CreateStream(factory, &stream);
1947 IWICImagingFactory_Release(factory);
1948 if (hr != S_OK)
1949 return GenericError;
1951 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1952 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1953 else
1954 status = GenericError;
1956 IWICStream_Release(stream);
1957 break;
1959 default:
1960 WARN("Invalid bitmap type %ld.\n", bitmapdata->Type);
1961 return InvalidParameter;
1963 break;
1965 case ImageDataTypeMetafile:
1967 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1969 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1970 return InvalidParameter;
1971 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1973 switch (metafiledata->Type) {
1974 case MetafileTypeEmf:
1975 case MetafileTypeEmfPlusOnly:
1976 case MetafileTypeEmfPlusDual:
1978 HENHMETAFILE hemf;
1980 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1982 if (!hemf)
1983 return GenericError;
1985 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1987 if (status != Ok)
1988 DeleteEnhMetaFile(hemf);
1990 break;
1992 default:
1993 FIXME("metafile type %ld not supported.\n", metafiledata->Type);
1994 return NotImplemented;
1996 break;
1998 default:
1999 FIXME("image type %d not supported.\n", data->Type);
2000 return NotImplemented;
2003 return status;
2006 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
2008 EmfPlusPath *data = (EmfPlusPath *)record_data;
2009 GpStatus status;
2010 BYTE *types;
2011 UINT size;
2012 DWORD i;
2014 *path = NULL;
2016 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
2017 return InvalidParameter;
2018 data_size -= FIELD_OFFSET(EmfPlusPath, data);
2020 if (data->PathPointFlags & 0x800) /* R */
2022 FIXME("RLE encoded path data is not supported.\n");
2023 return NotImplemented;
2025 else
2027 if (data->PathPointFlags & 0x4000) /* C */
2028 size = sizeof(EmfPlusPoint);
2029 else
2030 size = sizeof(EmfPlusPointF);
2031 size += sizeof(BYTE); /* EmfPlusPathPointType */
2032 size *= data->PathPointCount;
2035 if (data_size < size)
2036 return InvalidParameter;
2038 status = GdipCreatePath(FillModeAlternate, path);
2039 if (status != Ok)
2040 return status;
2042 (*path)->pathdata.Count = data->PathPointCount;
2043 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
2044 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
2045 (*path)->datalen = (*path)->pathdata.Count;
2047 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
2049 GdipDeletePath(*path);
2050 return OutOfMemory;
2053 if (data->PathPointFlags & 0x4000) /* C */
2055 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
2056 for (i = 0; i < data->PathPointCount; i++)
2058 (*path)->pathdata.Points[i].X = points[i].X;
2059 (*path)->pathdata.Points[i].Y = points[i].Y;
2061 types = (BYTE *)(points + i);
2063 else
2065 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
2066 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
2067 types = (BYTE *)(points + data->PathPointCount);
2070 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
2072 return Ok;
2075 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
2077 const DWORD *type;
2078 GpStatus status;
2080 type = buffer_read(mbuf, sizeof(*type));
2081 if (!type) return Ok;
2083 node->type = *type;
2085 switch (node->type)
2087 case CombineModeReplace:
2088 case CombineModeIntersect:
2089 case CombineModeUnion:
2090 case CombineModeXor:
2091 case CombineModeExclude:
2092 case CombineModeComplement:
2094 region_element *left, *right;
2096 left = heap_alloc_zero(sizeof(*left));
2097 if (!left)
2098 return OutOfMemory;
2100 right = heap_alloc_zero(sizeof(*right));
2101 if (!right)
2103 heap_free(left);
2104 return OutOfMemory;
2107 status = metafile_read_region_node(mbuf, region, left, count);
2108 if (status == Ok)
2110 status = metafile_read_region_node(mbuf, region, right, count);
2111 if (status == Ok)
2113 node->elementdata.combine.left = left;
2114 node->elementdata.combine.right = right;
2115 region->num_children += 2;
2116 return Ok;
2120 heap_free(left);
2121 heap_free(right);
2122 return status;
2124 case RegionDataRect:
2126 const EmfPlusRectF *rect;
2128 rect = buffer_read(mbuf, sizeof(*rect));
2129 if (!rect)
2130 return InvalidParameter;
2132 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
2133 *count += 1;
2134 return Ok;
2136 case RegionDataPath:
2138 const BYTE *path_data;
2139 const UINT *data_size;
2140 GpPath *path;
2142 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
2143 if (!data_size)
2144 return InvalidParameter;
2146 path_data = buffer_read(mbuf, *data_size);
2147 if (!path_data)
2148 return InvalidParameter;
2150 status = metafile_deserialize_path(path_data, *data_size, &path);
2151 if (status == Ok)
2153 node->elementdata.path = path;
2154 *count += 1;
2156 return Ok;
2158 case RegionDataEmptyRect:
2159 case RegionDataInfiniteRect:
2160 *count += 1;
2161 return Ok;
2162 default:
2163 FIXME("element type %#lx is not supported\n", *type);
2164 break;
2167 return InvalidParameter;
2170 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
2172 struct memory_buffer mbuf;
2173 GpStatus status;
2174 UINT count;
2176 *region = NULL;
2178 init_memory_buffer(&mbuf, record_data, data_size);
2180 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2181 return InvalidParameter;
2183 status = GdipCreateRegion(region);
2184 if (status != Ok)
2185 return status;
2187 count = 0;
2188 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2189 if (status == Ok && !count)
2190 status = InvalidParameter;
2192 if (status != Ok)
2194 GdipDeleteRegion(*region);
2195 *region = NULL;
2198 return status;
2201 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2203 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2204 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2205 EmfPlusTransformMatrix *transform = NULL;
2206 DWORD brushflags;
2207 GpStatus status;
2208 UINT offset;
2210 *brush = NULL;
2212 if (data_size < header_size)
2213 return InvalidParameter;
2215 switch (data->Type)
2217 case BrushTypeSolidColor:
2218 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2219 return InvalidParameter;
2221 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2222 break;
2223 case BrushTypeHatchFill:
2224 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2225 return InvalidParameter;
2227 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2228 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2229 break;
2230 case BrushTypeTextureFill:
2232 GpImage *image;
2234 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2235 if (data_size <= offset)
2236 return InvalidParameter;
2238 brushflags = data->BrushData.texture.BrushDataFlags;
2239 if (brushflags & BrushDataTransform)
2241 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2242 return InvalidParameter;
2243 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2244 offset += sizeof(EmfPlusTransformMatrix);
2247 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2248 if (status != Ok)
2249 return status;
2251 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2252 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2253 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2255 GdipDisposeImage(image);
2256 break;
2258 case BrushTypeLinearGradient:
2260 GpLineGradient *gradient = NULL;
2261 GpRectF rect;
2262 UINT position_count = 0;
2264 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2265 if (data_size < offset)
2266 return InvalidParameter;
2268 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2269 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2270 return InvalidParameter;
2272 if (brushflags & BrushDataTransform)
2274 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2275 return InvalidParameter;
2276 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2277 offset += sizeof(EmfPlusTransformMatrix);
2280 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2282 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2283 return InvalidParameter;
2284 position_count = *(DWORD *)(record_data + offset);
2285 offset += sizeof(DWORD);
2288 if (brushflags & BrushDataPresetColors)
2290 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2291 return InvalidParameter;
2293 else if (brushflags & BrushDataBlendFactorsH)
2295 if (data_size != offset + position_count * 2 * sizeof(float))
2296 return InvalidParameter;
2299 rect.X = data->BrushData.lineargradient.RectF.X;
2300 rect.Y = data->BrushData.lineargradient.RectF.Y;
2301 rect.Width = data->BrushData.lineargradient.RectF.Width;
2302 rect.Height = data->BrushData.lineargradient.RectF.Height;
2304 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2305 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2306 data->BrushData.lineargradient.WrapMode, &gradient);
2307 if (status == Ok)
2309 if (transform)
2310 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2312 if (status == Ok)
2314 if (brushflags & BrushDataPresetColors)
2315 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2316 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2317 else if (brushflags & BrushDataBlendFactorsH)
2318 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2319 (REAL *)(record_data + offset), position_count);
2321 if (brushflags & BrushDataIsGammaCorrected)
2322 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2326 if (status == Ok)
2327 *brush = (GpBrush *)gradient;
2328 else
2329 GdipDeleteBrush((GpBrush *)gradient);
2331 break;
2333 default:
2334 FIXME("brush type %lu is not supported.\n", data->Type);
2335 return NotImplemented;
2338 return status;
2341 static GpStatus metafile_deserialize_custom_line_cap(const BYTE *record_data, UINT data_size, GpCustomLineCap **cap)
2343 EmfPlusCustomStartCapData *custom_cap_data = (EmfPlusCustomStartCapData *)record_data;
2344 EmfPlusCustomLineCap *line_cap;
2345 GpStatus status;
2346 UINT offset;
2348 *cap = NULL;
2350 if (data_size < FIELD_OFFSET(EmfPlusCustomStartCapData, data))
2351 return InvalidParameter;
2352 if (data_size < FIELD_OFFSET(EmfPlusCustomStartCapData, data) + custom_cap_data->CustomStartCapSize)
2353 return InvalidParameter;
2354 offset = FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2355 line_cap = (EmfPlusCustomLineCap *)(record_data + offset);
2357 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData))
2358 return InvalidParameter;
2359 offset += FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData);
2361 if (line_cap->Type == CustomLineCapTypeAdjustableArrow)
2363 EmfPlusCustomLineCapArrowData *arrow_data;
2364 GpAdjustableArrowCap *arrow_cap;
2366 arrow_data = (EmfPlusCustomLineCapArrowData *)(record_data + offset);
2368 if (data_size < offset + sizeof(EmfPlusCustomLineCapArrowData))
2369 return InvalidParameter;
2371 if ((status = GdipCreateAdjustableArrowCap(arrow_data->Height, arrow_data->Width,
2372 arrow_data->FillState, &arrow_cap)))
2373 return status;
2375 if ((status = GdipSetAdjustableArrowCapMiddleInset(arrow_cap, arrow_data->MiddleInset)))
2376 goto arrow_cap_failed;
2377 if ((status = GdipSetCustomLineCapStrokeCaps((GpCustomLineCap *)arrow_cap, arrow_data->LineStartCap, arrow_data->LineEndCap)))
2378 goto arrow_cap_failed;
2379 if ((status = GdipSetCustomLineCapStrokeJoin((GpCustomLineCap *)arrow_cap, arrow_data->LineJoin)))
2380 goto arrow_cap_failed;
2381 if ((status = GdipSetCustomLineCapWidthScale((GpCustomLineCap *)arrow_cap, arrow_data->WidthScale)))
2382 goto arrow_cap_failed;
2384 *cap = (GpCustomLineCap *)arrow_cap;
2385 return Ok;
2387 arrow_cap_failed:
2388 GdipDeleteCustomLineCap((GpCustomLineCap *)arrow_cap);
2389 return status;
2391 else
2393 GpPath *path, *fill_path = NULL, *stroke_path = NULL;
2394 EmfPlusCustomLineCapData *line_cap_data;
2395 GpCustomLineCap *line_cap = NULL;
2396 GpStatus status;
2398 line_cap_data = (EmfPlusCustomLineCapData *)(record_data + offset);
2400 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData))
2401 return InvalidParameter;
2402 offset += FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData);
2404 if (line_cap_data->CustomLineCapDataFlags == CustomLineCapDataFillPath)
2406 EmfPlusCustomLineCapDataFillPath *fill_path = (EmfPlusCustomLineCapDataFillPath *)(record_data + offset);
2408 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath))
2409 return InvalidParameter;
2410 if (data_size < offset + fill_path->FillPathLength)
2411 return InvalidParameter;
2413 offset += FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath);
2415 else
2417 EmfPlusCustomLineCapDataLinePath *line_path = (EmfPlusCustomLineCapDataLinePath *)(record_data + offset);
2419 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath))
2420 return InvalidParameter;
2421 if (data_size < offset + line_path->LinePathLength)
2422 return InvalidParameter;
2424 offset += FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath);
2427 if ((status = metafile_deserialize_path(record_data + offset, data_size - offset, &path)))
2428 return status;
2430 if (line_cap_data->CustomLineCapDataFlags == CustomLineCapDataFillPath)
2431 fill_path = path;
2432 else
2433 stroke_path = path;
2435 if ((status = GdipCreateCustomLineCap(fill_path, stroke_path, line_cap_data->BaseCap,
2436 line_cap_data->BaseInset, &line_cap)))
2437 goto default_cap_failed;
2438 if ((status = GdipSetCustomLineCapStrokeCaps(line_cap, line_cap_data->StrokeStartCap, line_cap_data->StrokeEndCap)))
2439 goto default_cap_failed;
2440 if ((status = GdipSetCustomLineCapStrokeJoin(line_cap, line_cap_data->StrokeJoin)))
2441 goto default_cap_failed;
2442 if ((status = GdipSetCustomLineCapWidthScale(line_cap, line_cap_data->WidthScale)))
2443 goto default_cap_failed;
2445 GdipDeletePath(path);
2446 *cap = line_cap;
2447 return Ok;
2449 default_cap_failed:
2450 if (line_cap)
2451 GdipDeleteCustomLineCap(line_cap);
2452 GdipDeletePath(path);
2453 return status;
2457 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2459 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2460 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2462 if (data_size <= offset)
2463 return InvalidParameter;
2465 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2466 if (data_size <= offset)
2467 return InvalidParameter;
2469 if (pendata->PenDataFlags & PenDataTransform)
2470 offset += sizeof(EmfPlusTransformMatrix);
2472 if (pendata->PenDataFlags & PenDataStartCap)
2473 offset += sizeof(DWORD);
2475 if (pendata->PenDataFlags & PenDataEndCap)
2476 offset += sizeof(DWORD);
2478 if (pendata->PenDataFlags & PenDataJoin)
2479 offset += sizeof(DWORD);
2481 if (pendata->PenDataFlags & PenDataMiterLimit)
2482 offset += sizeof(REAL);
2484 if (pendata->PenDataFlags & PenDataLineStyle)
2485 offset += sizeof(DWORD);
2487 if (pendata->PenDataFlags & PenDataDashedLineCap)
2488 offset += sizeof(DWORD);
2490 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2491 offset += sizeof(REAL);
2493 if (pendata->PenDataFlags & PenDataDashedLine)
2495 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2497 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2498 if (data_size <= offset)
2499 return InvalidParameter;
2501 offset += dashedline->DashedLineDataSize * sizeof(float);
2504 if (pendata->PenDataFlags & PenDataNonCenter)
2505 offset += sizeof(DWORD);
2507 if (pendata->PenDataFlags & PenDataCompoundLine)
2509 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2511 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2512 if (data_size <= offset)
2513 return InvalidParameter;
2515 offset += compoundline->CompoundLineDataSize * sizeof(float);
2518 if (pendata->PenDataFlags & PenDataCustomStartCap)
2520 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2522 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2523 if (data_size <= offset)
2524 return InvalidParameter;
2526 offset += startcap->CustomStartCapSize;
2529 if (pendata->PenDataFlags & PenDataCustomEndCap)
2531 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2533 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2534 if (data_size <= offset)
2535 return InvalidParameter;
2537 offset += endcap->CustomEndCapSize;
2540 *ret = offset;
2541 return Ok;
2544 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2546 BYTE type = (flags >> 8) & 0xff;
2547 BYTE id = flags & 0xff;
2548 void *object = NULL;
2549 GpStatus status;
2551 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2552 return InvalidParameter;
2554 switch (type)
2556 case ObjectTypeBrush:
2557 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2558 break;
2559 case ObjectTypePen:
2561 EmfPlusPen *data = (EmfPlusPen *)record_data;
2562 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2563 GpCustomLineCap *custom_line_cap;
2564 GpBrush *brush;
2565 DWORD offset;
2566 GpPen *pen;
2568 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2569 if (status != Ok)
2570 return status;
2572 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2573 if (status != Ok)
2574 return status;
2576 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2577 GdipDeleteBrush(brush);
2578 if (status != Ok)
2579 return status;
2581 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2583 if (pendata->PenDataFlags & PenDataTransform)
2585 FIXME("PenDataTransform is not supported.\n");
2586 offset += sizeof(EmfPlusTransformMatrix);
2589 if (pendata->PenDataFlags & PenDataStartCap)
2591 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2592 goto penfailed;
2593 offset += sizeof(DWORD);
2596 if (pendata->PenDataFlags & PenDataEndCap)
2598 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2599 goto penfailed;
2600 offset += sizeof(DWORD);
2603 if (pendata->PenDataFlags & PenDataJoin)
2605 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2606 goto penfailed;
2607 offset += sizeof(DWORD);
2610 if (pendata->PenDataFlags & PenDataMiterLimit)
2612 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2613 goto penfailed;
2614 offset += sizeof(REAL);
2617 if (pendata->PenDataFlags & PenDataLineStyle)
2619 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2620 goto penfailed;
2621 offset += sizeof(DWORD);
2624 if (pendata->PenDataFlags & PenDataDashedLineCap)
2626 FIXME("PenDataDashedLineCap is not supported.\n");
2627 offset += sizeof(DWORD);
2630 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2632 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2633 goto penfailed;
2634 offset += sizeof(REAL);
2637 if (pendata->PenDataFlags & PenDataDashedLine)
2639 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2640 FIXME("PenDataDashedLine is not supported.\n");
2641 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2644 if (pendata->PenDataFlags & PenDataNonCenter)
2646 FIXME("PenDataNonCenter is not supported.\n");
2647 offset += sizeof(DWORD);
2650 if (pendata->PenDataFlags & PenDataCompoundLine)
2652 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2653 FIXME("PenDataCompoundLine is not supported.\n");
2654 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2657 if (pendata->PenDataFlags & PenDataCustomStartCap)
2659 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2660 if ((status = metafile_deserialize_custom_line_cap((BYTE *)startcap, data_size, &custom_line_cap)) != Ok)
2661 goto penfailed;
2662 status = GdipSetPenCustomStartCap(pen, custom_line_cap);
2663 GdipDeleteCustomLineCap(custom_line_cap);
2664 if (status != Ok)
2665 goto penfailed;
2666 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2669 if (pendata->PenDataFlags & PenDataCustomEndCap)
2671 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2672 if ((status = metafile_deserialize_custom_line_cap((BYTE *)endcap, data_size, &custom_line_cap)) != Ok)
2673 goto penfailed;
2674 status = GdipSetPenCustomEndCap(pen, custom_line_cap);
2675 GdipDeleteCustomLineCap(custom_line_cap);
2676 if (status != Ok)
2677 goto penfailed;
2678 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2681 object = pen;
2682 break;
2684 penfailed:
2685 GdipDeletePen(pen);
2686 return status;
2688 case ObjectTypePath:
2689 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2690 break;
2691 case ObjectTypeRegion:
2692 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2693 break;
2694 case ObjectTypeImage:
2695 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2696 break;
2697 case ObjectTypeFont:
2699 EmfPlusFont *data = (EmfPlusFont *)record_data;
2700 GpFontFamily *family;
2701 WCHAR *familyname;
2703 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2704 return InvalidParameter;
2705 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2707 if (data_size < data->Length * sizeof(WCHAR))
2708 return InvalidParameter;
2710 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2711 return OutOfMemory;
2713 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2714 familyname[data->Length] = 0;
2716 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2717 GdipFree(familyname);
2719 /* If a font family cannot be created from family name, native
2720 falls back to a sans serif font. */
2721 if (status != Ok)
2722 status = GdipGetGenericFontFamilySansSerif(&family);
2723 if (status != Ok)
2724 return status;
2726 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2727 GdipDeleteFontFamily(family);
2728 break;
2730 case ObjectTypeImageAttributes:
2732 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2733 GpImageAttributes *attributes = NULL;
2735 if (data_size != sizeof(*data))
2736 return InvalidParameter;
2738 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2739 return status;
2741 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2742 !!data->ObjectClamp);
2743 if (status == Ok)
2744 object = attributes;
2745 else
2746 GdipDisposeImageAttributes(attributes);
2747 break;
2749 default:
2750 FIXME("not implemented for object type %d.\n", type);
2751 return NotImplemented;
2754 if (status == Ok)
2755 metafile_set_object_table_entry(metafile, id, type, object);
2757 return status;
2760 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2762 GpMatrix world_to_device;
2764 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2766 GdipTransformRegion(region, &world_to_device);
2767 GdipCombineRegionRegion(metafile->clip, region, mode);
2769 return METAFILE_PlaybackUpdateClip(metafile);
2772 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2773 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2775 GpStatus stat;
2776 GpMetafile *real_metafile = (GpMetafile*)metafile;
2778 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2780 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2781 return InvalidParameter;
2783 if (recordType >= 1 && recordType <= 0x7a)
2785 /* regular EMF record */
2786 if (metafile->playback_dc)
2788 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2790 if (record)
2792 record->iType = recordType;
2793 record->nSize = dataSize + 8;
2794 memcpy(record->dParm, data, dataSize);
2796 if (record->iType == EMR_BITBLT || record->iType == EMR_STRETCHBLT)
2797 SetStretchBltMode(metafile->playback_dc, STRETCH_HALFTONE);
2799 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2800 record, metafile->handle_count) == 0)
2801 ERR("PlayEnhMetaFileRecord failed\n");
2803 heap_free(record);
2805 else
2806 return OutOfMemory;
2809 else
2811 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2813 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2815 switch(recordType)
2817 case EmfPlusRecordTypeHeader:
2818 case EmfPlusRecordTypeEndOfFile:
2819 break;
2820 case EmfPlusRecordTypeGetDC:
2821 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2822 break;
2823 case EmfPlusRecordTypeClear:
2825 EmfPlusClear *record = (EmfPlusClear*)header;
2827 if (dataSize != sizeof(record->Color))
2828 return InvalidParameter;
2830 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2832 case EmfPlusRecordTypeFillRects:
2834 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2835 GpBrush *brush, *temp_brush=NULL;
2836 GpRectF *rects, *temp_rects=NULL;
2838 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2839 return InvalidParameter;
2841 if (flags & 0x4000)
2843 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2844 return InvalidParameter;
2846 else
2848 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2849 return InvalidParameter;
2852 if (flags & 0x8000)
2854 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2855 brush = temp_brush;
2857 else
2859 if (record->BrushID >= EmfPlusObjectTableSize ||
2860 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2861 return InvalidParameter;
2863 brush = real_metafile->objtable[record->BrushID].u.brush;
2864 stat = Ok;
2867 if (stat == Ok)
2869 if (flags & 0x4000)
2871 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2872 int i;
2874 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2875 if (rects)
2877 for (i=0; i<record->Count; i++)
2879 rects[i].X = int_rects[i].X;
2880 rects[i].Y = int_rects[i].Y;
2881 rects[i].Width = int_rects[i].Width;
2882 rects[i].Height = int_rects[i].Height;
2885 else
2886 stat = OutOfMemory;
2888 else
2889 rects = (GpRectF*)(record+1);
2892 if (stat == Ok)
2894 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2897 GdipDeleteBrush(temp_brush);
2898 heap_free(temp_rects);
2900 return stat;
2902 case EmfPlusRecordTypeSetClipRect:
2904 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2905 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2906 GpRegion *region;
2908 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2909 return InvalidParameter;
2911 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2913 if (stat == Ok)
2915 stat = metafile_set_clip_region(real_metafile, region, mode);
2916 GdipDeleteRegion(region);
2919 return stat;
2921 case EmfPlusRecordTypeSetClipRegion:
2923 CombineMode mode = (flags >> 8) & 0xf;
2924 BYTE regionid = flags & 0xff;
2925 GpRegion *region;
2927 if (dataSize != 0)
2928 return InvalidParameter;
2930 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2931 return InvalidParameter;
2933 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2934 if (stat == Ok)
2936 stat = metafile_set_clip_region(real_metafile, region, mode);
2937 GdipDeleteRegion(region);
2940 return stat;
2942 case EmfPlusRecordTypeSetClipPath:
2944 CombineMode mode = (flags >> 8) & 0xf;
2945 BYTE pathid = flags & 0xff;
2946 GpRegion *region;
2948 if (dataSize != 0)
2949 return InvalidParameter;
2951 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2952 return InvalidParameter;
2954 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2955 if (stat == Ok)
2957 stat = metafile_set_clip_region(real_metafile, region, mode);
2958 GdipDeleteRegion(region);
2961 return stat;
2963 case EmfPlusRecordTypeSetPageTransform:
2965 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2966 GpUnit unit = (GpUnit)flags;
2968 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2969 return InvalidParameter;
2971 real_metafile->page_unit = unit;
2972 real_metafile->page_scale = record->PageScale;
2974 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2976 case EmfPlusRecordTypeSetWorldTransform:
2978 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2980 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2981 return InvalidParameter;
2983 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2985 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2987 case EmfPlusRecordTypeScaleWorldTransform:
2989 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2990 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2992 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2993 return InvalidParameter;
2995 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2997 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2999 case EmfPlusRecordTypeMultiplyWorldTransform:
3001 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
3002 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
3003 GpMatrix matrix;
3005 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
3006 return InvalidParameter;
3008 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
3010 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
3012 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3014 case EmfPlusRecordTypeRotateWorldTransform:
3016 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
3017 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
3019 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
3020 return InvalidParameter;
3022 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
3024 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3026 case EmfPlusRecordTypeTranslateWorldTransform:
3028 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
3029 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
3031 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
3032 return InvalidParameter;
3034 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
3036 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3038 case EmfPlusRecordTypeResetWorldTransform:
3040 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
3042 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3044 case EmfPlusRecordTypeBeginContainer:
3046 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
3047 container* cont;
3048 GpUnit unit;
3049 REAL scale_x, scale_y;
3050 GpRectF scaled_srcrect;
3051 GpMatrix transform;
3053 cont = heap_alloc_zero(sizeof(*cont));
3054 if (!cont)
3055 return OutOfMemory;
3057 stat = GdipCloneRegion(metafile->clip, &cont->clip);
3058 if (stat != Ok)
3060 heap_free(cont);
3061 return stat;
3064 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
3066 if (stat != Ok)
3068 GdipDeleteRegion(cont->clip);
3069 heap_free(cont);
3070 return stat;
3073 cont->id = record->StackIndex;
3074 cont->type = BEGIN_CONTAINER;
3075 cont->world_transform = *metafile->world_transform;
3076 cont->page_unit = metafile->page_unit;
3077 cont->page_scale = metafile->page_scale;
3078 list_add_head(&real_metafile->containers, &cont->entry);
3080 unit = record->Header.Flags & 0xff;
3082 scale_x = units_to_pixels(1.0, unit, metafile->image.xres, metafile->printer_display);
3083 scale_y = units_to_pixels(1.0, unit, metafile->image.yres, metafile->printer_display);
3085 scaled_srcrect.X = scale_x * record->SrcRect.X;
3086 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
3087 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
3088 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
3090 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
3091 transform.matrix[1] = 0.0;
3092 transform.matrix[2] = 0.0;
3093 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
3094 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
3095 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
3097 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
3099 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3101 case EmfPlusRecordTypeBeginContainerNoParams:
3102 case EmfPlusRecordTypeSave:
3104 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
3105 container* cont;
3107 cont = heap_alloc_zero(sizeof(*cont));
3108 if (!cont)
3109 return OutOfMemory;
3111 stat = GdipCloneRegion(metafile->clip, &cont->clip);
3112 if (stat != Ok)
3114 heap_free(cont);
3115 return stat;
3118 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
3119 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
3120 else
3121 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
3123 if (stat != Ok)
3125 GdipDeleteRegion(cont->clip);
3126 heap_free(cont);
3127 return stat;
3130 cont->id = record->StackIndex;
3131 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
3132 cont->type = BEGIN_CONTAINER;
3133 else
3134 cont->type = SAVE_GRAPHICS;
3135 cont->world_transform = *metafile->world_transform;
3136 cont->page_unit = metafile->page_unit;
3137 cont->page_scale = metafile->page_scale;
3138 list_add_head(&real_metafile->containers, &cont->entry);
3140 break;
3142 case EmfPlusRecordTypeEndContainer:
3143 case EmfPlusRecordTypeRestore:
3145 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
3146 container* cont;
3147 enum container_type type;
3148 BOOL found=FALSE;
3150 if (recordType == EmfPlusRecordTypeEndContainer)
3151 type = BEGIN_CONTAINER;
3152 else
3153 type = SAVE_GRAPHICS;
3155 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
3157 if (cont->id == record->StackIndex && cont->type == type)
3159 found = TRUE;
3160 break;
3164 if (found)
3166 container* cont2;
3168 /* pop any newer items on the stack */
3169 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
3171 list_remove(&cont2->entry);
3172 GdipDeleteRegion(cont2->clip);
3173 heap_free(cont2);
3176 if (type == BEGIN_CONTAINER)
3177 GdipEndContainer(real_metafile->playback_graphics, cont->state);
3178 else
3179 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
3181 *real_metafile->world_transform = cont->world_transform;
3182 real_metafile->page_unit = cont->page_unit;
3183 real_metafile->page_scale = cont->page_scale;
3184 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
3186 list_remove(&cont->entry);
3187 GdipDeleteRegion(cont->clip);
3188 heap_free(cont);
3191 break;
3193 case EmfPlusRecordTypeSetPixelOffsetMode:
3195 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
3197 case EmfPlusRecordTypeSetCompositingQuality:
3199 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
3201 case EmfPlusRecordTypeSetInterpolationMode:
3203 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
3205 case EmfPlusRecordTypeSetTextRenderingHint:
3207 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
3209 case EmfPlusRecordTypeSetAntiAliasMode:
3211 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
3213 case EmfPlusRecordTypeSetCompositingMode:
3215 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
3217 case EmfPlusRecordTypeObject:
3219 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
3221 case EmfPlusRecordTypeDrawImage:
3223 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
3224 BYTE image = flags & 0xff;
3225 GpPointF points[3];
3227 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3228 return InvalidParameter;
3230 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
3231 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3232 return InvalidParameter;
3234 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3235 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3236 return InvalidParameter;
3238 if (flags & 0x4000) /* C */
3240 points[0].X = draw->RectData.rect.X;
3241 points[0].Y = draw->RectData.rect.Y;
3242 points[1].X = points[0].X + draw->RectData.rect.Width;
3243 points[1].Y = points[0].Y;
3244 points[2].X = points[1].X;
3245 points[2].Y = points[1].Y + draw->RectData.rect.Height;
3247 else
3249 points[0].X = draw->RectData.rectF.X;
3250 points[0].Y = draw->RectData.rectF.Y;
3251 points[1].X = points[0].X + draw->RectData.rectF.Width;
3252 points[1].Y = points[0].Y;
3253 points[2].X = points[1].X;
3254 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
3257 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3258 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3259 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3261 case EmfPlusRecordTypeDrawImagePoints:
3263 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
3264 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
3265 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
3266 BYTE image = flags & 0xff;
3267 GpPointF points[3];
3268 unsigned int i;
3269 UINT size;
3271 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3272 return InvalidParameter;
3274 if (dataSize <= fixed_part_size)
3275 return InvalidParameter;
3276 dataSize -= fixed_part_size;
3278 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3279 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3280 return InvalidParameter;
3282 if (draw->count != 3)
3283 return InvalidParameter;
3285 if ((flags >> 13) & 1) /* E */
3286 FIXME("image effects are not supported.\n");
3288 if ((flags >> 11) & 1) /* P */
3289 size = sizeof(EmfPlusPointR7) * draw->count;
3290 else if ((flags >> 14) & 1) /* C */
3291 size = sizeof(EmfPlusPoint) * draw->count;
3292 else
3293 size = sizeof(EmfPlusPointF) * draw->count;
3295 if (dataSize != size)
3296 return InvalidParameter;
3298 if ((flags >> 11) & 1) /* P */
3300 points[0].X = draw->PointData.pointsR[0].X;
3301 points[0].Y = draw->PointData.pointsR[0].Y;
3302 for (i = 1; i < 3; i++)
3304 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3305 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3308 else if ((flags >> 14) & 1) /* C */
3310 for (i = 0; i < 3; i++)
3312 points[i].X = draw->PointData.points[i].X;
3313 points[i].Y = draw->PointData.points[i].Y;
3316 else
3317 memcpy(points, draw->PointData.pointsF, sizeof(points));
3319 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3320 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3321 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3323 case EmfPlusRecordTypeFillPath:
3325 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3326 GpSolidFill *solidfill = NULL;
3327 BYTE path = flags & 0xff;
3328 GpBrush *brush;
3330 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3331 return InvalidParameter;
3333 if (dataSize != sizeof(fill->data.BrushId))
3334 return InvalidParameter;
3336 if (flags & 0x8000)
3338 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3339 if (stat != Ok)
3340 return stat;
3341 brush = (GpBrush *)solidfill;
3343 else
3345 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3346 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3347 return InvalidParameter;
3349 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3352 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3353 GdipDeleteBrush((GpBrush *)solidfill);
3354 return stat;
3356 case EmfPlusRecordTypeFillClosedCurve:
3358 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3359 sizeof(EmfPlusRecordHeader);
3360 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3361 GpSolidFill *solidfill = NULL;
3362 GpFillMode mode;
3363 GpBrush *brush;
3364 UINT size, i;
3366 if (dataSize <= fixed_part_size)
3367 return InvalidParameter;
3369 if (fill->Count == 0)
3370 return InvalidParameter;
3372 if (flags & 0x800) /* P */
3373 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3374 else if (flags & 0x4000) /* C */
3375 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3376 else
3377 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3379 if (dataSize != size)
3380 return InvalidParameter;
3382 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3384 if (flags & 0x8000) /* S */
3386 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3387 if (stat != Ok)
3388 return stat;
3389 brush = (GpBrush *)solidfill;
3391 else
3393 if (fill->BrushId >= EmfPlusObjectTableSize ||
3394 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3395 return InvalidParameter;
3397 brush = real_metafile->objtable[fill->BrushId].u.brush;
3400 if (flags & (0x800 | 0x4000))
3402 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3403 if (points)
3405 if (flags & 0x800) /* P */
3407 for (i = 1; i < fill->Count; i++)
3409 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3410 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3413 else
3415 for (i = 0; i < fill->Count; i++)
3417 points[i].X = fill->PointData.points[i].X;
3418 points[i].Y = fill->PointData.points[i].Y;
3422 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3423 points, fill->Count, fill->Tension, mode);
3424 GdipFree(points);
3426 else
3427 stat = OutOfMemory;
3429 else
3430 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3431 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3433 GdipDeleteBrush((GpBrush *)solidfill);
3434 return stat;
3436 case EmfPlusRecordTypeFillEllipse:
3438 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3439 GpSolidFill *solidfill = NULL;
3440 GpBrush *brush;
3442 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3443 return InvalidParameter;
3444 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3446 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3447 return InvalidParameter;
3449 if (flags & 0x8000)
3451 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3452 if (stat != Ok)
3453 return stat;
3454 brush = (GpBrush *)solidfill;
3456 else
3458 if (fill->BrushId >= EmfPlusObjectTableSize ||
3459 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3460 return InvalidParameter;
3462 brush = real_metafile->objtable[fill->BrushId].u.brush;
3465 if (flags & 0x4000)
3466 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3467 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3468 else
3469 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3470 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3472 GdipDeleteBrush((GpBrush *)solidfill);
3473 return stat;
3475 case EmfPlusRecordTypeFillPie:
3477 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3478 GpSolidFill *solidfill = NULL;
3479 GpBrush *brush;
3481 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3482 return InvalidParameter;
3483 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3485 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3486 return InvalidParameter;
3488 if (flags & 0x8000) /* S */
3490 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3491 if (stat != Ok)
3492 return stat;
3493 brush = (GpBrush *)solidfill;
3495 else
3497 if (fill->BrushId >= EmfPlusObjectTableSize ||
3498 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3499 return InvalidParameter;
3501 brush = real_metafile->objtable[fill->BrushId].u.brush;
3504 if (flags & 0x4000) /* C */
3505 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3506 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3507 fill->StartAngle, fill->SweepAngle);
3508 else
3509 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3510 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3511 fill->StartAngle, fill->SweepAngle);
3513 GdipDeleteBrush((GpBrush *)solidfill);
3514 return stat;
3516 case EmfPlusRecordTypeDrawPath:
3518 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3519 BYTE path = flags & 0xff;
3521 if (dataSize != sizeof(draw->PenId))
3522 return InvalidParameter;
3524 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3525 return InvalidParameter;
3527 if (real_metafile->objtable[path].type != ObjectTypePath ||
3528 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3529 return InvalidParameter;
3531 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3532 real_metafile->objtable[path].u.path);
3534 case EmfPlusRecordTypeDrawArc:
3536 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3537 BYTE pen = flags & 0xff;
3539 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3540 return InvalidParameter;
3542 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3543 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3544 return InvalidParameter;
3546 if (flags & 0x4000) /* C */
3547 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3548 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3549 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3550 else
3551 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3552 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3553 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3555 case EmfPlusRecordTypeDrawEllipse:
3557 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3558 BYTE pen = flags & 0xff;
3560 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3561 return InvalidParameter;
3563 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3564 return InvalidParameter;
3566 if (flags & 0x4000) /* C */
3567 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3568 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3569 draw->RectData.rect.Height);
3570 else
3571 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3572 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3573 draw->RectData.rectF.Height);
3575 case EmfPlusRecordTypeDrawPie:
3577 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3578 BYTE pen = flags & 0xff;
3580 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3581 return InvalidParameter;
3583 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3584 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3585 return InvalidParameter;
3587 if (flags & 0x4000) /* C */
3588 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3589 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3590 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3591 else
3592 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3593 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3594 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3596 case EmfPlusRecordTypeDrawRects:
3598 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3599 BYTE pen = flags & 0xff;
3600 GpRectF *rects = NULL;
3602 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3603 return InvalidParameter;
3605 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3606 return InvalidParameter;
3607 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3609 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3610 return InvalidParameter;
3612 if (flags & 0x4000)
3614 DWORD i;
3616 rects = GdipAlloc(draw->Count * sizeof(*rects));
3617 if (!rects)
3618 return OutOfMemory;
3620 for (i = 0; i < draw->Count; i++)
3622 rects[i].X = draw->RectData.rect[i].X;
3623 rects[i].Y = draw->RectData.rect[i].Y;
3624 rects[i].Width = draw->RectData.rect[i].Width;
3625 rects[i].Height = draw->RectData.rect[i].Height;
3629 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3630 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3631 GdipFree(rects);
3632 return stat;
3634 case EmfPlusRecordTypeDrawDriverString:
3636 GpBrush *brush;
3637 DWORD expected_size;
3638 UINT16 *text;
3639 PointF *positions;
3640 GpSolidFill *solidfill = NULL;
3641 void* alignedmem = NULL;
3642 GpMatrix *matrix = NULL;
3643 BYTE font = flags & 0xff;
3644 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3646 if (font >= EmfPlusObjectTableSize ||
3647 real_metafile->objtable[font].type != ObjectTypeFont)
3648 return InvalidParameter;
3650 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3651 sizeof(EmfPlusRecordHeader);
3652 if (dataSize < expected_size || draw->GlyphCount <= 0)
3653 return InvalidParameter;
3655 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3656 if (draw->MatrixPresent)
3657 expected_size += sizeof(*matrix);
3659 /* Pad expected size to DWORD alignment. */
3660 expected_size = (expected_size + 3) & ~3;
3662 if (dataSize != expected_size)
3663 return InvalidParameter;
3665 if (flags & 0x8000)
3667 stat = GdipCreateSolidFill(draw->brush.Color, &solidfill);
3669 if (stat != Ok)
3670 return InvalidParameter;
3672 brush = (GpBrush*)solidfill;
3674 else
3676 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3677 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3678 return InvalidParameter;
3680 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3683 text = (UINT16*)&draw->VariableData[0];
3685 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3686 aligned rather than 4-byte aligned, which may lead to access
3687 issues. Handle this case by making our own copy of positions. */
3688 if (draw->GlyphCount % 2)
3690 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3692 if (draw->MatrixPresent)
3693 alloc_size += sizeof(*matrix);
3695 positions = alignedmem = heap_alloc(alloc_size);
3696 if (!positions)
3698 GdipDeleteBrush((GpBrush*)solidfill);
3699 return OutOfMemory;
3702 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3704 else
3705 positions = (PointF*)&text[draw->GlyphCount];
3707 if (draw->MatrixPresent)
3708 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3710 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3711 real_metafile->objtable[font].u.font, brush, positions,
3712 draw->DriverStringOptionsFlags, matrix);
3714 GdipDeleteBrush((GpBrush*)solidfill);
3715 heap_free(alignedmem);
3717 return stat;
3719 case EmfPlusRecordTypeFillRegion:
3721 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3722 GpSolidFill *solidfill = NULL;
3723 GpBrush *brush;
3724 BYTE region = flags & 0xff;
3726 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3727 return InvalidParameter;
3729 if (region >= EmfPlusObjectTableSize ||
3730 real_metafile->objtable[region].type != ObjectTypeRegion)
3731 return InvalidParameter;
3733 if (flags & 0x8000)
3735 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3736 if (stat != Ok)
3737 return stat;
3738 brush = (GpBrush*)solidfill;
3740 else
3742 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3743 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3744 return InvalidParameter;
3746 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3749 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3750 real_metafile->objtable[region].u.region);
3751 GdipDeleteBrush((GpBrush*)solidfill);
3753 return stat;
3755 default:
3756 FIXME("Not implemented for record type %x\n", recordType);
3757 return NotImplemented;
3761 return Ok;
3764 struct enum_metafile_data
3766 EnumerateMetafileProc callback;
3767 void *callback_data;
3768 GpMetafile *metafile;
3771 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3772 int nObj, LPARAM lpData)
3774 BOOL ret;
3775 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3776 const BYTE* pStr;
3778 data->metafile->handle_table = lpHTable;
3779 data->metafile->handle_count = nObj;
3781 /* First check for an EMF+ record. */
3782 if (lpEMFR->iType == EMR_GDICOMMENT)
3784 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3786 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3788 int offset = 4;
3790 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3792 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3794 if (record->DataSize)
3795 pStr = (const BYTE*)(record+1);
3796 else
3797 pStr = NULL;
3799 ret = data->callback(record->Type, record->Flags, record->DataSize,
3800 pStr, data->callback_data);
3802 if (!ret)
3803 return 0;
3805 offset += record->Size;
3808 return 1;
3812 if (lpEMFR->nSize != 8)
3813 pStr = (const BYTE*)lpEMFR->dParm;
3814 else
3815 pStr = NULL;
3817 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3818 pStr, data->callback_data);
3821 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3822 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3823 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3824 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3826 struct enum_metafile_data data;
3827 GpStatus stat;
3828 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3829 GraphicsContainer state;
3830 GpPath *dst_path;
3831 RECT dst_bounds;
3833 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3834 destPoints, count, srcRect, srcUnit, callback, callbackData,
3835 imageAttributes);
3837 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3838 return InvalidParameter;
3840 if (!metafile->hemf)
3841 return InvalidParameter;
3843 if (metafile->playback_graphics)
3844 return ObjectBusy;
3846 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3847 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3848 debugstr_pointf(&destPoints[2]));
3850 data.callback = callback;
3851 data.callback_data = callbackData;
3852 data.metafile = real_metafile;
3854 real_metafile->playback_graphics = graphics;
3855 real_metafile->playback_dc = NULL;
3856 real_metafile->src_rect = *srcRect;
3858 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3859 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3861 if (stat == Ok)
3862 stat = GdipBeginContainer2(graphics, &state);
3864 if (stat == Ok)
3866 stat = GdipSetPageScale(graphics, 1.0);
3868 if (stat == Ok)
3869 stat = GdipSetPageUnit(graphics, UnitPixel);
3871 if (stat == Ok)
3872 stat = GdipResetWorldTransform(graphics);
3874 if (stat == Ok)
3875 stat = GdipCreateRegion(&real_metafile->base_clip);
3877 if (stat == Ok)
3878 stat = GdipGetClip(graphics, real_metafile->base_clip);
3880 if (stat == Ok)
3881 stat = GdipCreateRegion(&real_metafile->clip);
3883 if (stat == Ok)
3884 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3886 if (stat == Ok)
3888 GpPointF clip_points[4];
3890 clip_points[0] = real_metafile->playback_points[0];
3891 clip_points[1] = real_metafile->playback_points[1];
3892 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3893 - real_metafile->playback_points[0].X;
3894 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3895 - real_metafile->playback_points[0].Y;
3896 clip_points[3] = real_metafile->playback_points[2];
3898 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3900 if (stat == Ok)
3901 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3903 GdipDeletePath(dst_path);
3906 if (stat == Ok)
3907 stat = GdipCreateMatrix(&real_metafile->world_transform);
3909 if (stat == Ok)
3911 real_metafile->page_unit = UnitDisplay;
3912 real_metafile->page_scale = 1.0;
3913 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3916 if (stat == Ok)
3918 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3921 if (stat == Ok)
3923 stat = METAFILE_PlaybackGetDC(real_metafile);
3925 dst_bounds.left = real_metafile->playback_points[0].X;
3926 dst_bounds.right = real_metafile->playback_points[1].X;
3927 dst_bounds.top = real_metafile->playback_points[0].Y;
3928 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3931 if (stat == Ok)
3932 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3933 &data, &dst_bounds);
3935 METAFILE_PlaybackReleaseDC(real_metafile);
3937 GdipDeleteMatrix(real_metafile->world_transform);
3938 real_metafile->world_transform = NULL;
3940 GdipDeleteRegion(real_metafile->base_clip);
3941 real_metafile->base_clip = NULL;
3943 GdipDeleteRegion(real_metafile->clip);
3944 real_metafile->clip = NULL;
3946 while (list_head(&real_metafile->containers))
3948 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3949 list_remove(&cont->entry);
3950 GdipDeleteRegion(cont->clip);
3951 heap_free(cont);
3954 GdipEndContainer(graphics, state);
3957 real_metafile->playback_graphics = NULL;
3959 return stat;
3962 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRect( GpGraphics *graphics,
3963 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3964 GDIPCONST GpRectF *src, Unit srcUnit, EnumerateMetafileProc callback,
3965 VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3967 GpPointF points[3];
3969 if (!graphics || !metafile || !dest) return InvalidParameter;
3971 points[0].X = points[2].X = dest->X;
3972 points[0].Y = points[1].Y = dest->Y;
3973 points[1].X = dest->X + dest->Width;
3974 points[2].Y = dest->Y + dest->Height;
3976 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3977 src, srcUnit, callback, cb_data, attrs);
3979 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRectI( GpGraphics * graphics,
3980 GDIPCONST GpMetafile *metafile, GDIPCONST Rect *destRect,
3981 GDIPCONST Rect *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3982 VOID *cb_data, GDIPCONST GpImageAttributes *attrs )
3984 GpRectF destRectF, srcRectF;
3986 destRectF.X = destRect->X;
3987 destRectF.Y = destRect->Y;
3988 destRectF.Width = destRect->Width;
3989 destRectF.Height = destRect->Height;
3991 srcRectF.X = srcRect->X;
3992 srcRectF.Y = srcRect->Y;
3993 srcRectF.Width = srcRect->Width;
3994 srcRectF.Height = srcRect->Height;
3996 return GdipEnumerateMetafileSrcRectDestRect( graphics, metafile, &destRectF, &srcRectF, srcUnit, callback, cb_data, attrs);
3999 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
4000 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
4001 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4003 GpPointF points[3];
4005 if (!graphics || !metafile || !dest) return InvalidParameter;
4007 points[0].X = points[2].X = dest->X;
4008 points[0].Y = points[1].Y = dest->Y;
4009 points[1].X = dest->X + dest->Width;
4010 points[2].Y = dest->Y + dest->Height;
4012 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
4013 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
4016 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
4017 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
4018 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4020 GpRectF destf;
4022 if (!graphics || !metafile || !dest) return InvalidParameter;
4024 destf.X = dest->X;
4025 destf.Y = dest->Y;
4026 destf.Width = dest->Width;
4027 destf.Height = dest->Height;
4029 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
4032 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
4033 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
4034 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4036 GpRectF destf;
4038 if (!graphics || !metafile || !dest) return InvalidParameter;
4040 destf.X = dest->X;
4041 destf.Y = dest->Y;
4042 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit,
4043 metafile->image.xres, metafile->printer_display);
4044 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit,
4045 metafile->image.yres, metafile->printer_display);
4047 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
4050 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
4051 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
4052 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4054 GpPointF ptf;
4056 if (!graphics || !metafile || !dest) return InvalidParameter;
4058 ptf.X = dest->X;
4059 ptf.Y = dest->Y;
4061 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
4064 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
4065 MetafileHeader * header)
4067 GpStatus status;
4069 TRACE("(%p, %p)\n", metafile, header);
4071 if(!metafile || !header)
4072 return InvalidParameter;
4074 if (metafile->hemf)
4076 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
4077 if (status != Ok) return status;
4079 else
4081 memset(header, 0, sizeof(*header));
4082 header->Version = VERSION_MAGIC2;
4085 header->Type = metafile->metafile_type;
4086 header->DpiX = metafile->image.xres;
4087 header->DpiY = metafile->image.yres;
4088 header->Width = gdip_round(metafile->bounds.Width);
4089 header->Height = gdip_round(metafile->bounds.Height);
4091 return Ok;
4094 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
4095 int nObj, LPARAM lpData)
4097 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
4099 if (lpEMFR->iType == EMR_GDICOMMENT)
4101 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
4103 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
4105 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
4107 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
4108 header->Type == EmfPlusRecordTypeHeader)
4110 memcpy(dst_header, header, sizeof(*dst_header));
4114 else if (lpEMFR->iType == EMR_HEADER)
4115 return TRUE;
4117 return FALSE;
4120 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
4121 MetafileHeader *header)
4123 ENHMETAHEADER3 emfheader;
4124 EmfPlusHeader emfplusheader;
4125 MetafileType metafile_type;
4127 TRACE("(%p,%p)\n", hemf, header);
4129 if(!hemf || !header)
4130 return InvalidParameter;
4132 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
4133 return GenericError;
4135 emfplusheader.Header.Type = 0;
4137 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
4139 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
4141 if ((emfplusheader.Header.Flags & 1) == 1)
4142 metafile_type = MetafileTypeEmfPlusDual;
4143 else
4144 metafile_type = MetafileTypeEmfPlusOnly;
4146 else
4147 metafile_type = MetafileTypeEmf;
4149 header->Type = metafile_type;
4150 header->Size = emfheader.nBytes;
4151 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
4152 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
4153 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
4154 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
4155 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
4156 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
4157 header->EmfHeader = emfheader;
4159 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
4161 header->Version = emfplusheader.Version;
4162 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
4163 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
4164 header->LogicalDpiX = emfplusheader.LogicalDpiX;
4165 header->LogicalDpiY = emfplusheader.LogicalDpiY;
4167 else
4169 header->Version = emfheader.nVersion;
4170 header->EmfPlusFlags = 0;
4171 header->EmfPlusHeaderSize = 0;
4172 header->LogicalDpiX = 0;
4173 header->LogicalDpiY = 0;
4176 return Ok;
4179 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
4180 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
4182 GpStatus status;
4183 GpMetafile *metafile;
4185 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
4187 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
4188 if (status == Ok)
4190 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4191 GdipDisposeImage(&metafile->image);
4193 return status;
4196 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
4197 MetafileHeader *header)
4199 GpStatus status;
4200 GpMetafile *metafile;
4202 TRACE("(%s,%p)\n", debugstr_w(filename), header);
4204 if (!filename || !header)
4205 return InvalidParameter;
4207 status = GdipCreateMetafileFromFile(filename, &metafile);
4208 if (status == Ok)
4210 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4211 GdipDisposeImage(&metafile->image);
4213 return status;
4216 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
4217 MetafileHeader *header)
4219 GpStatus status;
4220 GpMetafile *metafile;
4222 TRACE("(%p,%p)\n", stream, header);
4224 if (!stream || !header)
4225 return InvalidParameter;
4227 status = GdipCreateMetafileFromStream(stream, &metafile);
4228 if (status == Ok)
4230 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4231 GdipDisposeImage(&metafile->image);
4233 return status;
4236 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
4237 GpMetafile **metafile)
4239 GpStatus stat;
4240 MetafileHeader header;
4242 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
4244 if(!hemf || !metafile)
4245 return InvalidParameter;
4247 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
4248 if (stat != Ok)
4249 return stat;
4251 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4252 if (!*metafile)
4253 return OutOfMemory;
4255 (*metafile)->image.type = ImageTypeMetafile;
4256 (*metafile)->image.format = ImageFormatEMF;
4257 (*metafile)->image.frame_count = 1;
4258 (*metafile)->image.xres = header.DpiX;
4259 (*metafile)->image.yres = header.DpiY;
4260 (*metafile)->bounds.X = (REAL)header.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
4261 (*metafile)->bounds.Y = (REAL)header.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
4262 (*metafile)->bounds.Width = (REAL)(header.EmfHeader.rclFrame.right - header.EmfHeader.rclFrame.left)
4263 / 2540.0 * header.DpiX;
4264 (*metafile)->bounds.Height = (REAL)(header.EmfHeader.rclFrame.bottom - header.EmfHeader.rclFrame.top)
4265 / 2540.0 * header.DpiY;
4266 (*metafile)->unit = UnitPixel;
4267 (*metafile)->metafile_type = header.Type;
4268 (*metafile)->hemf = hemf;
4269 (*metafile)->preserve_hemf = !delete;
4270 /* If the 31th bit of EmfPlusFlags was set, metafile was recorded with a DC for a video display.
4271 * If clear, metafile was recorded with a DC for a printer */
4272 (*metafile)->printer_display = !(header.EmfPlusFlags & (1u << 31));
4273 (*metafile)->logical_dpix = header.LogicalDpiX;
4274 (*metafile)->logical_dpiy = header.LogicalDpiY;
4275 list_init(&(*metafile)->containers);
4277 TRACE("<-- %p\n", *metafile);
4279 return Ok;
4282 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
4283 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4285 UINT read;
4286 BYTE *copy;
4287 HENHMETAFILE hemf;
4288 GpStatus retval = Ok;
4290 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
4292 if(!hwmf || !metafile)
4293 return InvalidParameter;
4295 *metafile = NULL;
4296 read = GetMetaFileBitsEx(hwmf, 0, NULL);
4297 if(!read)
4298 return GenericError;
4299 copy = heap_alloc_zero(read);
4300 GetMetaFileBitsEx(hwmf, read, copy);
4302 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
4303 heap_free(copy);
4305 /* FIXME: We should store and use hwmf instead of converting to hemf */
4306 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4308 if (retval == Ok)
4310 if (placeable)
4312 (*metafile)->image.xres = (REAL)placeable->Inch;
4313 (*metafile)->image.yres = (REAL)placeable->Inch;
4314 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
4315 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
4316 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
4317 placeable->BoundingBox.Left);
4318 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
4319 placeable->BoundingBox.Top);
4320 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
4322 else
4323 (*metafile)->metafile_type = MetafileTypeWmf;
4324 (*metafile)->image.format = ImageFormatWMF;
4326 if (delete) DeleteMetaFile(hwmf);
4328 else
4329 DeleteEnhMetaFile(hemf);
4330 return retval;
4333 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4334 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4336 HMETAFILE hmf;
4337 HENHMETAFILE emf;
4339 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4341 hmf = GetMetaFileW(file);
4342 if(hmf)
4343 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4345 emf = GetEnhMetaFileW(file);
4346 if(emf)
4347 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4349 return GenericError;
4352 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4353 GpMetafile **metafile)
4355 GpStatus status;
4356 IStream *stream;
4358 TRACE("(%p, %p)\n", file, metafile);
4360 if (!file || !metafile) return InvalidParameter;
4362 *metafile = NULL;
4364 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4365 if (status == Ok)
4367 status = GdipCreateMetafileFromStream(stream, metafile);
4368 IStream_Release(stream);
4370 return status;
4373 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4374 GpMetafile **metafile)
4376 GpStatus stat;
4378 TRACE("%p %p\n", stream, metafile);
4380 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4381 if (stat != Ok) return stat;
4383 if ((*metafile)->image.type != ImageTypeMetafile)
4385 GdipDisposeImage(&(*metafile)->image);
4386 *metafile = NULL;
4387 return GenericError;
4390 return Ok;
4393 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4394 UINT *limitDpi)
4396 TRACE("(%p,%p)\n", metafile, limitDpi);
4398 if (!metafile || !limitDpi)
4399 return InvalidParameter;
4401 if (!metafile->record_dc)
4402 return WrongState;
4404 *limitDpi = metafile->limit_dpi;
4406 return Ok;
4409 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4410 UINT limitDpi)
4412 TRACE("(%p,%u)\n", metafile, limitDpi);
4414 if (limitDpi == 0)
4415 limitDpi = 96;
4417 if (!metafile || limitDpi < 10)
4418 return InvalidParameter;
4420 if (!metafile->record_dc)
4421 return WrongState;
4423 metafile->limit_dpi = limitDpi;
4425 return Ok;
4428 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4429 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4430 const WCHAR* description, GpMetafile** out_metafile)
4432 static int calls;
4434 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4435 debugstr_w(description), out_metafile);
4437 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4438 return InvalidParameter;
4440 if(succ)
4441 *succ = FALSE;
4442 *out_metafile = NULL;
4444 if(!(calls++))
4445 FIXME("not implemented\n");
4447 return NotImplemented;
4450 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4451 LPBYTE pData16, INT iMapMode, INT eFlags)
4453 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4454 return NotImplemented;
4457 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4458 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4459 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4460 GpMetafile **metafile)
4462 HDC record_dc;
4463 REAL dpix, dpiy;
4464 REAL framerect_factor_x, framerect_factor_y;
4465 RECT rc, *lprc;
4466 GpStatus stat;
4468 TRACE("%s %p %d %s %d %s %p\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4469 frameUnit, debugstr_w(desc), metafile);
4471 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
4472 return InvalidParameter;
4474 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
4475 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
4477 if (pFrameRect)
4479 switch (frameUnit)
4481 case MetafileFrameUnitPixel:
4482 framerect_factor_x = 2540.0 / dpix;
4483 framerect_factor_y = 2540.0 / dpiy;
4484 break;
4485 case MetafileFrameUnitPoint:
4486 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
4487 break;
4488 case MetafileFrameUnitInch:
4489 framerect_factor_x = framerect_factor_y = 2540.0;
4490 break;
4491 case MetafileFrameUnitDocument:
4492 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
4493 break;
4494 case MetafileFrameUnitMillimeter:
4495 framerect_factor_x = framerect_factor_y = 100.0;
4496 break;
4497 case MetafileFrameUnitGdi:
4498 framerect_factor_x = framerect_factor_y = 1.0;
4499 break;
4500 default:
4501 return InvalidParameter;
4504 rc.left = framerect_factor_x * pFrameRect->X;
4505 rc.top = framerect_factor_y * pFrameRect->Y;
4506 rc.right = rc.left + framerect_factor_x * pFrameRect->Width;
4507 rc.bottom = rc.top + framerect_factor_y * pFrameRect->Height;
4509 lprc = &rc;
4511 else
4512 lprc = NULL;
4514 record_dc = CreateEnhMetaFileW(hdc, fileName, lprc, desc);
4516 if (!record_dc)
4517 return GenericError;
4519 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4520 if(!*metafile)
4522 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4523 return OutOfMemory;
4526 (*metafile)->image.type = ImageTypeMetafile;
4527 (*metafile)->image.flags = ImageFlagsNone;
4528 (*metafile)->image.palette = NULL;
4529 (*metafile)->image.xres = dpix;
4530 (*metafile)->image.yres = dpiy;
4531 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
4532 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
4533 (*metafile)->unit = UnitPixel;
4534 (*metafile)->metafile_type = type;
4535 (*metafile)->record_dc = record_dc;
4536 (*metafile)->comment_data = NULL;
4537 (*metafile)->comment_data_size = 0;
4538 (*metafile)->comment_data_length = 0;
4539 (*metafile)->limit_dpi = 96;
4540 (*metafile)->hemf = NULL;
4541 (*metafile)->printer_display = (GetDeviceCaps(record_dc, TECHNOLOGY) == DT_RASPRINTER);
4542 (*metafile)->logical_dpix = (REAL)GetDeviceCaps(record_dc, LOGPIXELSX);
4543 (*metafile)->logical_dpiy = (REAL)GetDeviceCaps(record_dc, LOGPIXELSY);
4544 list_init(&(*metafile)->containers);
4546 if (!pFrameRect)
4548 (*metafile)->auto_frame = TRUE;
4549 (*metafile)->auto_frame_min.X = 0;
4550 (*metafile)->auto_frame_min.Y = 0;
4551 (*metafile)->auto_frame_max.X = -1;
4552 (*metafile)->auto_frame_max.Y = -1;
4555 stat = METAFILE_WriteHeader(*metafile, hdc);
4557 if (stat != Ok)
4559 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4560 heap_free(*metafile);
4561 *metafile = NULL;
4562 return OutOfMemory;
4565 return stat;
4568 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4569 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4570 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4572 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4573 frameUnit, debugstr_w(desc), metafile);
4575 return NotImplemented;
4578 /*****************************************************************************
4579 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4582 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4583 GpMetafile* metafile, BOOL* conversionSuccess,
4584 const WCHAR* filename, EmfType emfType,
4585 const WCHAR* description, GpMetafile** out_metafile)
4587 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4588 return NotImplemented;
4591 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4593 LARGE_INTEGER zero;
4594 STATSTG statstg;
4595 GpStatus stat;
4596 HRESULT hr;
4598 *size = 0;
4600 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4601 if (FAILED(hr)) return hresult_to_status(hr);
4603 stat = encode_image_png(image, *stream, NULL);
4604 if (stat != Ok)
4606 IStream_Release(*stream);
4607 return stat;
4610 hr = IStream_Stat(*stream, &statstg, 1);
4611 if (FAILED(hr))
4613 IStream_Release(*stream);
4614 return hresult_to_status(hr);
4616 *size = statstg.cbSize.u.LowPart;
4618 zero.QuadPart = 0;
4619 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4620 if (FAILED(hr))
4622 IStream_Release(*stream);
4623 return hresult_to_status(hr);
4626 return Ok;
4629 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4631 HRESULT hr;
4633 record->Width = 0;
4634 record->Height = 0;
4635 record->Stride = 0;
4636 record->PixelFormat = 0;
4637 record->Type = BitmapDataTypeCompressed;
4639 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4640 if (FAILED(hr)) return hresult_to_status(hr);
4641 return Ok;
4644 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4646 EmfPlusObject *object_record;
4647 GpStatus stat;
4648 DWORD size;
4650 *id = -1;
4652 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4653 return Ok;
4655 if (image->type == ImageTypeBitmap)
4657 IStream *stream;
4658 DWORD aligned_size;
4660 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4661 if (stat != Ok) return stat;
4662 aligned_size = (size + 3) & ~3;
4664 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4665 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4666 (void**)&object_record);
4667 if (stat != Ok)
4669 IStream_Release(stream);
4670 return stat;
4672 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4674 *id = METAFILE_AddObjectId(metafile);
4675 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4676 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4677 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4679 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4680 IStream_Release(stream);
4681 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4682 return stat;
4684 else if (image->type == ImageTypeMetafile)
4686 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4687 EmfPlusMetafile *metafile_record;
4689 if (!hemf) return InvalidParameter;
4691 size = GetEnhMetaFileBits(hemf, 0, NULL);
4692 if (!size) return GenericError;
4694 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4695 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4696 (void**)&object_record);
4697 if (stat != Ok) return stat;
4699 *id = METAFILE_AddObjectId(metafile);
4700 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4701 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4702 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4703 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4704 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4705 metafile_record->MetafileDataSize = size;
4706 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4708 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4709 return GenericError;
4711 return Ok;
4713 else
4715 FIXME("not supported image type (%d)\n", image->type);
4716 return NotImplemented;
4720 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4722 EmfPlusObject *object_record;
4723 EmfPlusImageAttributes *attrs_record;
4724 GpStatus stat;
4726 *id = -1;
4728 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4729 return Ok;
4731 if (!attrs)
4732 return Ok;
4734 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4735 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4736 (void**)&object_record);
4737 if (stat != Ok) return stat;
4739 *id = METAFILE_AddObjectId(metafile);
4740 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4741 attrs_record = &object_record->ObjectData.image_attributes;
4742 attrs_record->Version = VERSION_MAGIC2;
4743 attrs_record->Reserved1 = 0;
4744 attrs_record->WrapMode = attrs->wrap;
4745 attrs_record->ClampColor = attrs->outside_color;
4746 attrs_record->ObjectClamp = attrs->clamp;
4747 attrs_record->Reserved2 = 0;
4748 return Ok;
4751 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4752 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4753 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4754 DrawImageAbort callback, VOID *callbackData)
4756 EmfPlusDrawImagePoints *draw_image_record;
4757 DWORD image_id, attributes_id;
4758 GpStatus stat;
4760 if (count != 3) return InvalidParameter;
4762 if (metafile->metafile_type == MetafileTypeEmf)
4764 FIXME("MetafileTypeEmf metafiles not supported\n");
4765 return NotImplemented;
4767 else
4768 FIXME("semi-stub\n");
4770 if (!imageAttributes)
4772 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4774 else if (image->type == ImageTypeBitmap)
4776 INT width = ((GpBitmap*)image)->width;
4777 INT height = ((GpBitmap*)image)->height;
4778 GpGraphics *graphics;
4779 GpBitmap *bitmap;
4781 stat = GdipCreateBitmapFromScan0(width, height,
4782 0, PixelFormat32bppARGB, NULL, &bitmap);
4783 if (stat != Ok) return stat;
4785 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4786 if (stat != Ok)
4788 GdipDisposeImage((GpImage*)bitmap);
4789 return stat;
4792 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4793 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4794 GdipDeleteGraphics(graphics);
4795 if (stat != Ok)
4797 GdipDisposeImage((GpImage*)bitmap);
4798 return stat;
4801 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4802 GdipDisposeImage((GpImage*)bitmap);
4804 else
4806 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4807 return NotImplemented;
4809 if (stat != Ok) return stat;
4811 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4812 if (stat != Ok) return stat;
4814 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawImagePoints,
4815 sizeof(EmfPlusDrawImagePoints), (void **)&draw_image_record);
4816 if (stat != Ok) return stat;
4818 draw_image_record->Header.Flags = image_id;
4819 draw_image_record->ImageAttributesID = attributes_id;
4820 draw_image_record->SrcUnit = UnitPixel;
4821 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres, metafile->printer_display);
4822 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres, metafile->printer_display);
4823 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres, metafile->printer_display);
4824 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres, metafile->printer_display);
4825 draw_image_record->count = 3;
4826 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4827 METAFILE_WriteRecords(metafile);
4828 return Ok;
4831 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4833 EmfPlusRecordHeader *record;
4834 GpStatus stat;
4836 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4837 return Ok;
4839 stat = METAFILE_AllocateRecord(metafile, prop, sizeof(*record), (void**)&record);
4840 if (stat != Ok) return stat;
4842 record->Flags = val;
4844 METAFILE_WriteRecords(metafile);
4845 return Ok;
4848 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4850 EmfPlusObject *object_record;
4851 GpStatus stat;
4852 DWORD size;
4854 *id = -1;
4855 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4856 return Ok;
4858 size = write_path_data(path, NULL);
4859 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4860 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4861 (void**)&object_record);
4862 if (stat != Ok) return stat;
4864 *id = METAFILE_AddObjectId(metafile);
4865 object_record->Header.Flags = *id | ObjectTypePath << 8;
4866 write_path_data(path, &object_record->ObjectData.path);
4867 return Ok;
4870 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4872 DWORD custom_start_cap_size = 0, custom_start_cap_data_size = 0, custom_start_cap_path_size = 0;
4873 DWORD custom_end_cap_size = 0, custom_end_cap_data_size = 0, custom_end_cap_path_size = 0;
4874 DWORD i, data_flags, pen_data_size, brush_size;
4875 EmfPlusObject *object_record;
4876 EmfPlusPenData *pen_data;
4877 GpStatus stat;
4878 BOOL result;
4880 *id = -1;
4881 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4882 return Ok;
4884 data_flags = 0;
4885 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4887 GdipIsMatrixIdentity(&pen->transform, &result);
4888 if (!result)
4890 data_flags |= PenDataTransform;
4891 pen_data_size += sizeof(EmfPlusTransformMatrix);
4893 if (pen->startcap != LineCapFlat)
4895 data_flags |= PenDataStartCap;
4896 pen_data_size += sizeof(DWORD);
4898 if (pen->endcap != LineCapFlat)
4900 data_flags |= PenDataEndCap;
4901 pen_data_size += sizeof(DWORD);
4903 if (pen->join != LineJoinMiter)
4905 data_flags |= PenDataJoin;
4906 pen_data_size += sizeof(DWORD);
4908 if (pen->miterlimit != 10.0)
4910 data_flags |= PenDataMiterLimit;
4911 pen_data_size += sizeof(REAL);
4913 if (pen->style != GP_DEFAULT_PENSTYLE)
4915 data_flags |= PenDataLineStyle;
4916 pen_data_size += sizeof(DWORD);
4918 if (pen->dashcap != DashCapFlat)
4920 data_flags |= PenDataDashedLineCap;
4921 pen_data_size += sizeof(DWORD);
4923 data_flags |= PenDataDashedLineOffset;
4924 pen_data_size += sizeof(REAL);
4925 if (pen->numdashes)
4927 data_flags |= PenDataDashedLine;
4928 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4930 if (pen->align != PenAlignmentCenter)
4932 data_flags |= PenDataNonCenter;
4933 pen_data_size += sizeof(DWORD);
4935 /* TODO: Add support for PenDataCompoundLine */
4936 if (pen->customstart)
4938 data_flags |= PenDataCustomStartCap;
4939 METAFILE_PrepareCustomLineCapData(pen->customstart, &custom_start_cap_size,
4940 &custom_start_cap_data_size, &custom_start_cap_path_size);
4941 pen_data_size += custom_start_cap_size;
4943 if (pen->customend)
4945 data_flags |= PenDataCustomEndCap;
4946 METAFILE_PrepareCustomLineCapData(pen->customend, &custom_end_cap_size,
4947 &custom_end_cap_data_size, &custom_end_cap_path_size);
4948 pen_data_size += custom_end_cap_size;
4951 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4952 if (stat != Ok) return stat;
4954 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4955 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4956 (void**)&object_record);
4957 if (stat != Ok) return stat;
4959 *id = METAFILE_AddObjectId(metafile);
4960 object_record->Header.Flags = *id | ObjectTypePen << 8;
4961 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4962 object_record->ObjectData.pen.Type = 0;
4964 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4965 pen_data->PenDataFlags = data_flags;
4966 pen_data->PenUnit = pen->unit;
4967 pen_data->PenWidth = pen->width;
4969 i = 0;
4970 if (data_flags & PenDataTransform)
4972 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4973 memcpy(m, &pen->transform, sizeof(*m));
4974 i += sizeof(EmfPlusTransformMatrix);
4976 if (data_flags & PenDataStartCap)
4978 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4979 i += sizeof(DWORD);
4981 if (data_flags & PenDataEndCap)
4983 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4984 i += sizeof(DWORD);
4986 if (data_flags & PenDataJoin)
4988 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4989 i += sizeof(DWORD);
4991 if (data_flags & PenDataMiterLimit)
4993 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4994 i += sizeof(REAL);
4996 if (data_flags & PenDataLineStyle)
4998 switch (pen->style & PS_STYLE_MASK)
5000 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
5001 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
5002 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
5003 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
5004 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
5005 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
5007 i += sizeof(DWORD);
5009 if (data_flags & PenDataDashedLineCap)
5011 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
5012 i += sizeof(DWORD);
5014 if (data_flags & PenDataDashedLineOffset)
5016 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
5017 i += sizeof(REAL);
5019 if (data_flags & PenDataDashedLine)
5021 int j;
5023 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
5024 i += sizeof(DWORD);
5026 for (j=0; j<pen->numdashes; j++)
5028 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
5029 i += sizeof(REAL);
5032 if (data_flags & PenDataNonCenter)
5034 *(REAL*)(pen_data->OptionalData + i) = pen->align;
5035 i += sizeof(DWORD);
5037 if (data_flags & PenDataCustomStartCap)
5039 METAFILE_FillCustomLineCapData(pen->customstart, pen_data->OptionalData + i,
5040 pen->miterlimit, custom_start_cap_data_size,
5041 custom_start_cap_path_size);
5042 i += custom_start_cap_size;
5044 if (data_flags & PenDataCustomEndCap)
5046 METAFILE_FillCustomLineCapData(pen->customend, pen_data->OptionalData + i,
5047 pen->miterlimit, custom_end_cap_data_size,
5048 custom_end_cap_path_size);
5049 i += custom_end_cap_size;
5052 METAFILE_FillBrushData(pen->brush,
5053 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
5054 return Ok;
5057 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
5059 EmfPlusDrawPath *draw_path_record;
5060 DWORD path_id;
5061 DWORD pen_id;
5062 GpStatus stat;
5064 if (metafile->metafile_type == MetafileTypeEmf)
5066 FIXME("stub!\n");
5067 return NotImplemented;
5070 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5071 if (stat != Ok) return stat;
5073 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5074 if (stat != Ok) return stat;
5076 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawPath,
5077 sizeof(EmfPlusDrawPath), (void **)&draw_path_record);
5078 if (stat != Ok) return stat;
5079 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
5080 draw_path_record->Header.Flags = path_id;
5081 draw_path_record->PenId = pen_id;
5083 METAFILE_WriteRecords(metafile);
5084 return Ok;
5087 GpStatus METAFILE_DrawEllipse(GpMetafile *metafile, GpPen *pen, GpRectF *rect)
5089 EmfPlusDrawEllipse *record;
5090 GpStatus stat;
5091 DWORD pen_id;
5093 if (metafile->metafile_type == MetafileTypeEmf)
5095 FIXME("stub!\n");
5096 return NotImplemented;
5099 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5100 if (stat != Ok) return stat;
5102 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawEllipse,
5103 sizeof(EmfPlusDrawEllipse), (void **)&record);
5104 if (stat != Ok) return stat;
5105 record->Header.Type = EmfPlusRecordTypeDrawEllipse;
5106 record->Header.Flags = pen_id;
5107 if (is_integer_rect(rect))
5109 record->Header.Flags |= 0x4000;
5110 record->RectData.rect.X = (SHORT)rect->X;
5111 record->RectData.rect.Y = (SHORT)rect->Y;
5112 record->RectData.rect.Width = (SHORT)rect->Width;
5113 record->RectData.rect.Height = (SHORT)rect->Height;
5115 else
5116 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5118 METAFILE_WriteRecords(metafile);
5119 return Ok;
5122 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
5124 EmfPlusFillPath *fill_path_record;
5125 DWORD brush_id = -1, path_id;
5126 BOOL inline_color;
5127 GpStatus stat;
5129 if (metafile->metafile_type == MetafileTypeEmf)
5131 FIXME("stub!\n");
5132 return NotImplemented;
5135 inline_color = brush->bt == BrushTypeSolidColor;
5136 if (!inline_color)
5138 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5139 if (stat != Ok) return stat;
5142 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5143 if (stat != Ok) return stat;
5145 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPath,
5146 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
5147 if (stat != Ok) return stat;
5148 if (inline_color)
5150 fill_path_record->Header.Flags = 0x8000 | path_id;
5151 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
5153 else
5155 fill_path_record->Header.Flags = path_id;
5156 fill_path_record->data.BrushId = brush_id;
5159 METAFILE_WriteRecords(metafile);
5160 return Ok;
5163 GpStatus METAFILE_FillEllipse(GpMetafile *metafile, GpBrush *brush, GpRectF *rect)
5165 EmfPlusFillEllipse *record;
5166 DWORD brush_id = -1;
5167 BOOL inline_color;
5168 GpStatus stat;
5170 if (metafile->metafile_type == MetafileTypeEmf)
5172 FIXME("stub!\n");
5173 return NotImplemented;
5176 inline_color = brush->bt == BrushTypeSolidColor;
5177 if (!inline_color)
5179 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5180 if (stat != Ok) return stat;
5183 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillEllipse, sizeof(EmfPlusFillEllipse), (void **)&record);
5184 if (stat != Ok) return stat;
5185 if (inline_color)
5187 record->Header.Flags = 0x8000;
5188 record->BrushId = ((GpSolidFill *)brush)->color;
5190 else
5191 record->BrushId = brush_id;
5193 if (is_integer_rect(rect))
5195 record->Header.Flags |= 0x4000;
5196 record->RectData.rect.X = (SHORT)rect->X;
5197 record->RectData.rect.Y = (SHORT)rect->Y;
5198 record->RectData.rect.Width = (SHORT)rect->Width;
5199 record->RectData.rect.Height = (SHORT)rect->Height;
5201 else
5202 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5204 METAFILE_WriteRecords(metafile);
5205 return Ok;
5208 GpStatus METAFILE_FillPie(GpMetafile *metafile, GpBrush *brush, const GpRectF *rect,
5209 REAL startAngle, REAL sweepAngle)
5211 BOOL is_int_rect, inline_color;
5212 EmfPlusFillPie *record;
5213 DWORD brush_id = -1;
5214 GpStatus stat;
5216 if (metafile->metafile_type == MetafileTypeEmf)
5218 FIXME("stub!\n");
5219 return NotImplemented;
5222 inline_color = brush->bt == BrushTypeSolidColor;
5223 if (!inline_color)
5225 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5226 if (stat != Ok) return stat;
5229 is_int_rect = is_integer_rect(rect);
5231 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPie,
5232 FIELD_OFFSET(EmfPlusFillPie, RectData) + is_int_rect ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF),
5233 (void **)&record);
5234 if (stat != Ok) return stat;
5235 if (inline_color)
5237 record->Header.Flags = 0x8000;
5238 record->BrushId = ((GpSolidFill *)brush)->color;
5240 else
5241 record->BrushId = brush_id;
5243 record->StartAngle = startAngle;
5244 record->SweepAngle = sweepAngle;
5246 if (is_int_rect)
5248 record->Header.Flags |= 0x4000;
5249 record->RectData.rect.X = (SHORT)rect->X;
5250 record->RectData.rect.Y = (SHORT)rect->Y;
5251 record->RectData.rect.Width = (SHORT)rect->Width;
5252 record->RectData.rect.Height = (SHORT)rect->Height;
5254 else
5255 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5257 METAFILE_WriteRecords(metafile);
5258 return Ok;
5261 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
5263 EmfPlusObject *object_record;
5264 EmfPlusFont *font_record;
5265 GpStatus stat;
5266 INT fn_len;
5267 INT style;
5269 *id = -1;
5271 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5272 metafile->metafile_type != MetafileTypeEmfPlusDual)
5273 return Ok;
5275 /* The following cast is ugly, but GdipGetFontStyle does treat
5276 its first parameter as const. */
5277 stat = GdipGetFontStyle((GpFont*)font, &style);
5278 if (stat != Ok)
5279 return stat;
5281 fn_len = lstrlenW(font->family->FamilyName);
5282 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
5283 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
5284 (void**)&object_record);
5285 if (stat != Ok)
5286 return stat;
5288 *id = METAFILE_AddObjectId(metafile);
5290 object_record->Header.Flags = *id | ObjectTypeFont << 8;
5292 font_record = &object_record->ObjectData.font;
5293 font_record->Version = VERSION_MAGIC2;
5294 font_record->EmSize = font->emSize;
5295 font_record->SizeUnit = font->unit;
5296 font_record->FontStyleFlags = style;
5297 font_record->Reserved = 0;
5298 font_record->Length = fn_len;
5300 memcpy(font_record->FamilyName, font->family->FamilyName,
5301 fn_len * sizeof(*font->family->FamilyName));
5303 return Ok;
5306 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
5307 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
5308 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
5310 DWORD brush_id;
5311 DWORD font_id;
5312 DWORD alloc_size;
5313 GpStatus stat;
5314 EmfPlusDrawDriverString *draw_string_record;
5315 BYTE *cursor;
5316 BOOL inline_color;
5317 BOOL include_matrix = FALSE;
5319 if (length <= 0)
5320 return InvalidParameter;
5322 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5323 metafile->metafile_type != MetafileTypeEmfPlusDual)
5325 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5326 return NotImplemented;
5329 stat = METAFILE_AddFontObject(metafile, font, &font_id);
5330 if (stat != Ok)
5331 return stat;
5333 inline_color = (brush->bt == BrushTypeSolidColor);
5334 if (!inline_color)
5336 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5337 if (stat != Ok)
5338 return stat;
5341 if (matrix)
5343 BOOL identity;
5345 stat = GdipIsMatrixIdentity(matrix, &identity);
5346 if (stat != Ok)
5347 return stat;
5349 include_matrix = !identity;
5352 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
5353 length * (sizeof(*text) + sizeof(*positions));
5355 if (include_matrix)
5356 alloc_size += sizeof(*matrix);
5358 /* Pad record to DWORD alignment. */
5359 alloc_size = (alloc_size + 3) & ~3;
5361 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawDriverString, alloc_size, (void**)&draw_string_record);
5362 if (stat != Ok)
5363 return stat;
5365 draw_string_record->Header.Flags = font_id;
5366 draw_string_record->DriverStringOptionsFlags = flags;
5367 draw_string_record->MatrixPresent = include_matrix;
5368 draw_string_record->GlyphCount = length;
5370 if (inline_color)
5372 draw_string_record->Header.Flags |= 0x8000;
5373 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
5375 else
5376 draw_string_record->brush.BrushId = brush_id;
5378 cursor = &draw_string_record->VariableData[0];
5380 memcpy(cursor, text, length * sizeof(*text));
5381 cursor += length * sizeof(*text);
5383 if (flags & DriverStringOptionsRealizedAdvance)
5385 static BOOL fixme_written = FALSE;
5387 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
5388 in the case of RealizedAdvance, each glyph position is computed
5389 and serialized.
5391 While native GDI+ is capable of playing back metafiles with this
5392 flag set, it is possible that some application might rely on
5393 metafiles produced from GDI+ not setting this flag. Ideally we
5394 would also compute the position of each glyph here, serialize those
5395 values, and not set DriverStringOptionsRealizedAdvance. */
5396 if (!fixme_written)
5398 fixme_written = TRUE;
5399 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
5402 *((PointF*)cursor) = *positions;
5404 else
5405 memcpy(cursor, positions, length * sizeof(*positions));
5407 if (include_matrix)
5409 cursor += length * sizeof(*positions);
5410 memcpy(cursor, matrix, sizeof(*matrix));
5413 METAFILE_WriteRecords(metafile);
5415 return Ok;
5418 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
5420 GpStatus stat;
5421 DWORD brush_id;
5422 DWORD region_id;
5423 EmfPlusFillRegion *fill_region_record;
5424 BOOL inline_color;
5426 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5427 metafile->metafile_type != MetafileTypeEmfPlusDual)
5429 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5430 return NotImplemented;
5433 inline_color = (brush->bt == BrushTypeSolidColor);
5434 if (!inline_color)
5436 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5437 if (stat != Ok)
5438 return stat;
5441 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
5442 if (stat != Ok)
5443 return stat;
5445 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRegion, sizeof(EmfPlusFillRegion),
5446 (void**)&fill_region_record);
5447 if (stat != Ok)
5448 return stat;
5450 fill_region_record->Header.Flags = region_id;
5452 if (inline_color)
5454 fill_region_record->Header.Flags |= 0x8000;
5455 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
5457 else
5458 fill_region_record->data.BrushId = brush_id;
5460 METAFILE_WriteRecords(metafile);
5462 return Ok;
5465 GpStatus METAFILE_DrawRectangles(GpMetafile *metafile, GpPen *pen, const GpRectF *rects, INT count)
5467 EmfPlusDrawRects *record;
5468 GpStatus stat;
5469 BOOL integer_rects = TRUE;
5470 DWORD pen_id;
5471 int i;
5473 if (metafile->metafile_type == MetafileTypeEmf)
5475 FIXME("stub!\n");
5476 return NotImplemented;
5479 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5480 if (stat != Ok) return stat;
5482 for (i = 0; i < count; i++)
5484 if (!is_integer_rect(&rects[i]))
5486 integer_rects = FALSE;
5487 break;
5491 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawRects, FIELD_OFFSET(EmfPlusDrawRects, RectData) +
5492 count * (integer_rects ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF)),
5493 (void **)&record);
5494 if (stat != Ok)
5495 return stat;
5497 record->Header.Flags = pen_id;
5498 if (integer_rects)
5499 record->Header.Flags |= 0x4000;
5500 record->Count = count;
5502 if (integer_rects)
5504 for (i = 0; i < count; i++)
5506 record->RectData.rect[i].X = (SHORT)rects[i].X;
5507 record->RectData.rect[i].Y = (SHORT)rects[i].Y;
5508 record->RectData.rect[i].Width = (SHORT)rects[i].Width;
5509 record->RectData.rect[i].Height = (SHORT)rects[i].Height;
5512 else
5513 memcpy(record->RectData.rectF, rects, sizeof(*rects) * count);
5515 METAFILE_WriteRecords(metafile);
5517 return Ok;
5520 GpStatus METAFILE_DrawArc(GpMetafile *metafile, GpPen *pen, const GpRectF *rect, REAL startAngle, REAL sweepAngle)
5522 EmfPlusDrawArc *record;
5523 GpStatus stat;
5524 BOOL integer_rect;
5525 DWORD pen_id;
5527 if (metafile->metafile_type == MetafileTypeEmf)
5529 FIXME("stub!\n");
5530 return NotImplemented;
5533 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5534 if (stat != Ok) return stat;
5536 integer_rect = is_integer_rect(rect);
5538 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawArc, FIELD_OFFSET(EmfPlusDrawArc, RectData) +
5539 integer_rect ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF),
5540 (void **)&record);
5541 if (stat != Ok)
5542 return stat;
5544 record->Header.Flags = pen_id;
5545 if (integer_rect)
5546 record->Header.Flags |= 0x4000;
5547 record->StartAngle = startAngle;
5548 record->SweepAngle = sweepAngle;
5550 if (integer_rect)
5552 record->RectData.rect.X = (SHORT)rect->X;
5553 record->RectData.rect.Y = (SHORT)rect->Y;
5554 record->RectData.rect.Width = (SHORT)rect->Width;
5555 record->RectData.rect.Height = (SHORT)rect->Height;
5557 else
5558 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5560 METAFILE_WriteRecords(metafile);
5562 return Ok;
5565 GpStatus METAFILE_OffsetClip(GpMetafile *metafile, REAL dx, REAL dy)
5567 EmfPlusOffsetClip *record;
5568 GpStatus stat;
5570 if (metafile->metafile_type == MetafileTypeEmf)
5572 FIXME("stub!\n");
5573 return NotImplemented;
5576 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeOffsetClip,
5577 sizeof(*record), (void **)&record);
5578 if (stat != Ok)
5579 return stat;
5581 record->dx = dx;
5582 record->dy = dy;
5584 METAFILE_WriteRecords(metafile);
5586 return Ok;
5589 GpStatus METAFILE_ResetClip(GpMetafile *metafile)
5591 EmfPlusRecordHeader *record;
5592 GpStatus stat;
5594 if (metafile->metafile_type == MetafileTypeEmf)
5596 FIXME("stub!\n");
5597 return NotImplemented;
5600 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetClip,
5601 sizeof(*record), (void **)&record);
5602 if (stat != Ok)
5603 return stat;
5605 METAFILE_WriteRecords(metafile);
5607 return Ok;
5610 GpStatus METAFILE_SetClipPath(GpMetafile *metafile, GpPath *path, CombineMode mode)
5612 EmfPlusRecordHeader *record;
5613 DWORD path_id;
5614 GpStatus stat;
5616 if (metafile->metafile_type == MetafileTypeEmf)
5618 FIXME("stub!\n");
5619 return NotImplemented;
5622 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5623 if (stat != Ok) return stat;
5625 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipPath,
5626 sizeof(*record), (void **)&record);
5627 if (stat != Ok)
5628 return stat;
5630 record->Flags = ((mode & 0xf) << 8) | path_id;
5632 METAFILE_WriteRecords(metafile);
5634 return Ok;
5637 GpStatus METAFILE_SetRenderingOrigin(GpMetafile *metafile, INT x, INT y)
5639 EmfPlusSetRenderingOrigin *record;
5640 GpStatus stat;
5642 if (metafile->metafile_type == MetafileTypeEmf)
5644 FIXME("stub!\n");
5645 return NotImplemented;
5648 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetRenderingOrigin,
5649 sizeof(*record), (void **)&record);
5650 if (stat != Ok)
5651 return stat;
5653 record->x = x;
5654 record->y = y;
5656 METAFILE_WriteRecords(metafile);
5658 return Ok;