dmsynth: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / gdiplus / metafile.c
blob4880211ad87503df0b7002a8f1bd5ff23534d76a
1 /*
2 * Copyright (C) 2011 Vincent Povirk for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
20 #include <math.h>
21 #include <assert.h>
23 #define NONAMELESSUNION
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
35 #include "winreg.h"
36 #include "shlwapi.h"
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
45 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
47 typedef ARGB EmfPlusARGB;
49 typedef struct EmfPlusPointF
51 float X;
52 float Y;
53 } EmfPlusPointF;
55 typedef struct EmfPlusRecordHeader
57 WORD Type;
58 WORD Flags;
59 DWORD Size;
60 DWORD DataSize;
61 } EmfPlusRecordHeader;
63 typedef struct EmfPlusHeader
65 EmfPlusRecordHeader Header;
66 DWORD Version;
67 DWORD EmfPlusFlags;
68 DWORD LogicalDpiX;
69 DWORD LogicalDpiY;
70 } EmfPlusHeader;
72 typedef struct EmfPlusClear
74 EmfPlusRecordHeader Header;
75 DWORD Color;
76 } EmfPlusClear;
78 typedef struct EmfPlusFillRects
80 EmfPlusRecordHeader Header;
81 DWORD BrushID;
82 DWORD Count;
83 } EmfPlusFillRects;
85 typedef struct EmfPlusSetClipRect
87 EmfPlusRecordHeader Header;
88 GpRectF ClipRect;
89 } EmfPlusSetClipRect;
91 typedef struct EmfPlusSetPageTransform
93 EmfPlusRecordHeader Header;
94 REAL PageScale;
95 } EmfPlusSetPageTransform;
97 typedef struct EmfPlusRect
99 SHORT X;
100 SHORT Y;
101 SHORT Width;
102 SHORT Height;
103 } EmfPlusRect;
105 typedef struct EmfPlusSetWorldTransform
107 EmfPlusRecordHeader Header;
108 REAL MatrixData[6];
109 } EmfPlusSetWorldTransform;
111 typedef struct EmfPlusScaleWorldTransform
113 EmfPlusRecordHeader Header;
114 REAL Sx;
115 REAL Sy;
116 } EmfPlusScaleWorldTransform;
118 typedef struct EmfPlusMultiplyWorldTransform
120 EmfPlusRecordHeader Header;
121 REAL MatrixData[6];
122 } EmfPlusMultiplyWorldTransform;
124 typedef struct EmfPlusRotateWorldTransform
126 EmfPlusRecordHeader Header;
127 REAL Angle;
128 } EmfPlusRotateWorldTransform;
130 typedef struct EmfPlusTranslateWorldTransform
132 EmfPlusRecordHeader Header;
133 REAL dx;
134 REAL dy;
135 } EmfPlusTranslateWorldTransform;
137 typedef struct EmfPlusBeginContainer
139 EmfPlusRecordHeader Header;
140 GpRectF DestRect;
141 GpRectF SrcRect;
142 DWORD StackIndex;
143 } EmfPlusBeginContainer;
145 typedef struct EmfPlusContainerRecord
147 EmfPlusRecordHeader Header;
148 DWORD StackIndex;
149 } EmfPlusContainerRecord;
151 enum container_type
153 BEGIN_CONTAINER,
154 SAVE_GRAPHICS
157 typedef struct container
159 struct list entry;
160 DWORD id;
161 enum container_type type;
162 GraphicsContainer state;
163 GpMatrix world_transform;
164 GpUnit page_unit;
165 REAL page_scale;
166 GpRegion *clip;
167 } container;
169 enum PenDataFlags
171 PenDataTransform = 0x0001,
172 PenDataStartCap = 0x0002,
173 PenDataEndCap = 0x0004,
174 PenDataJoin = 0x0008,
175 PenDataMiterLimit = 0x0010,
176 PenDataLineStyle = 0x0020,
177 PenDataDashedLineCap = 0x0040,
178 PenDataDashedLineOffset = 0x0080,
179 PenDataDashedLine = 0x0100,
180 PenDataNonCenter = 0x0200,
181 PenDataCompoundLine = 0x0400,
182 PenDataCustomStartCap = 0x0800,
183 PenDataCustomEndCap = 0x1000
186 enum CustomLineCapData
188 CustomLineCapDataFillPath = 0x1,
189 CustomLineCapDataLinePath = 0x2,
192 typedef struct EmfPlusTransformMatrix
194 REAL TransformMatrix[6];
195 } EmfPlusTransformMatrix;
197 enum LineStyle
199 LineStyleSolid,
200 LineStyleDash,
201 LineStyleDot,
202 LineStyleDashDot,
203 LineStyleDashDotDot,
204 LineStyleCustom
207 typedef struct EmfPlusDashedLineData
209 DWORD DashedLineDataSize;
210 BYTE data[1];
211 } EmfPlusDashedLineData;
213 typedef struct EmfPlusCompoundLineData
215 DWORD CompoundLineDataSize;
216 BYTE data[1];
217 } EmfPlusCompoundLineData;
219 typedef struct EmfPlusCustomStartCapData
221 DWORD CustomStartCapSize;
222 BYTE data[1];
223 } EmfPlusCustomStartCapData;
225 typedef struct EmfPlusCustomEndCapData
227 DWORD CustomEndCapSize;
228 BYTE data[1];
229 } EmfPlusCustomEndCapData;
231 typedef struct EmfPlusPenData
233 DWORD PenDataFlags;
234 DWORD PenUnit;
235 REAL PenWidth;
236 BYTE OptionalData[1];
237 } EmfPlusPenData;
239 enum BrushDataFlags
241 BrushDataPath = 1 << 0,
242 BrushDataTransform = 1 << 1,
243 BrushDataPresetColors = 1 << 2,
244 BrushDataBlendFactorsH = 1 << 3,
245 BrushDataBlendFactorsV = 1 << 4,
246 BrushDataFocusScales = 1 << 6,
247 BrushDataIsGammaCorrected = 1 << 7,
248 BrushDataDoNotTransform = 1 << 8,
251 typedef struct EmfPlusSolidBrushData
253 EmfPlusARGB SolidColor;
254 } EmfPlusSolidBrushData;
256 typedef struct EmfPlusHatchBrushData
258 DWORD HatchStyle;
259 EmfPlusARGB ForeColor;
260 EmfPlusARGB BackColor;
261 } EmfPlusHatchBrushData;
263 typedef struct EmfPlusTextureBrushData
265 DWORD BrushDataFlags;
266 INT WrapMode;
267 BYTE OptionalData[1];
268 } EmfPlusTextureBrushData;
270 typedef struct EmfPlusRectF
272 float X;
273 float Y;
274 float Width;
275 float Height;
276 } EmfPlusRectF;
278 typedef struct EmfPlusLinearGradientBrushData
280 DWORD BrushDataFlags;
281 INT WrapMode;
282 EmfPlusRectF RectF;
283 EmfPlusARGB StartColor;
284 EmfPlusARGB EndColor;
285 DWORD Reserved1;
286 DWORD Reserved2;
287 BYTE OptionalData[1];
288 } EmfPlusLinearGradientBrushData;
290 typedef struct EmfPlusBrush
292 DWORD Version;
293 DWORD Type;
294 union {
295 EmfPlusSolidBrushData solid;
296 EmfPlusHatchBrushData hatch;
297 EmfPlusTextureBrushData texture;
298 EmfPlusLinearGradientBrushData lineargradient;
299 } BrushData;
300 } EmfPlusBrush;
302 typedef struct EmfPlusCustomLineCapArrowData
304 REAL Width;
305 REAL Height;
306 REAL MiddleInset;
307 BOOL FillState;
308 DWORD LineStartCap;
309 DWORD LineEndCap;
310 DWORD LineJoin;
311 REAL LineMiterLimit;
312 REAL WidthScale;
313 EmfPlusPointF FillHotSpot;
314 EmfPlusPointF LineHotSpot;
315 } EmfPlusCustomLineCapArrowData;
317 typedef struct EmfPlusPath
319 DWORD Version;
320 DWORD PathPointCount;
321 DWORD PathPointFlags;
322 /* PathPoints[] */
323 /* PathPointTypes[] */
324 /* AlignmentPadding */
325 BYTE data[1];
326 } EmfPlusPath;
328 typedef struct EmfPlusCustomLineCapDataFillPath
330 INT FillPathLength;
331 /* EmfPlusPath */
332 BYTE FillPath[1];
333 } EmfPlusCustomLineCapDataFillPath;
335 typedef struct EmfPlusCustomLineCapDataLinePath
337 INT LinePathLength;
338 /* EmfPlusPath */
339 BYTE LinePath[1];
340 } EmfPlusCustomLineCapDataLinePath;
342 typedef struct EmfPlusCustomLineCapData
344 DWORD CustomLineCapDataFlags;
345 DWORD BaseCap;
346 REAL BaseInset;
347 DWORD StrokeStartCap;
348 DWORD StrokeEndCap;
349 DWORD StrokeJoin;
350 REAL StrokeMiterLimit;
351 REAL WidthScale;
352 EmfPlusPointF FillHotSpot;
353 EmfPlusPointF LineHotSpot;
354 /* EmfPlusCustomLineCapDataFillPath */
355 /* EmfPlusCustomLineCapDataLinePath */
356 BYTE OptionalData[1];
357 } EmfPlusCustomLineCapData;
359 typedef struct EmfPlusCustomLineCap
361 DWORD Version;
362 DWORD Type;
363 /* EmfPlusCustomLineCapArrowData */
364 /* EmfPlusCustomLineCapData */
365 BYTE CustomLineCapData[1];
366 } EmfPlusCustomLineCap;
368 typedef struct EmfPlusPen
370 DWORD Version;
371 DWORD Type;
372 /* EmfPlusPenData */
373 /* EmfPlusBrush */
374 BYTE data[1];
375 } EmfPlusPen;
377 typedef struct EmfPlusRegionNodePath
379 DWORD RegionNodePathLength;
380 EmfPlusPath RegionNodePath;
381 } EmfPlusRegionNodePath;
383 typedef struct EmfPlusRegion
385 DWORD Version;
386 DWORD RegionNodeCount;
387 BYTE RegionNode[1];
388 } EmfPlusRegion;
390 typedef struct EmfPlusPalette
392 DWORD PaletteStyleFlags;
393 DWORD PaletteCount;
394 BYTE PaletteEntries[1];
395 } EmfPlusPalette;
397 typedef enum
399 BitmapDataTypePixel,
400 BitmapDataTypeCompressed,
401 } BitmapDataType;
403 typedef struct EmfPlusBitmap
405 DWORD Width;
406 DWORD Height;
407 DWORD Stride;
408 DWORD PixelFormat;
409 DWORD Type;
410 BYTE BitmapData[1];
411 } EmfPlusBitmap;
413 typedef struct EmfPlusMetafile
415 DWORD Type;
416 DWORD MetafileDataSize;
417 BYTE MetafileData[1];
418 } EmfPlusMetafile;
420 typedef enum ImageDataType
422 ImageDataTypeUnknown,
423 ImageDataTypeBitmap,
424 ImageDataTypeMetafile,
425 } ImageDataType;
427 typedef struct EmfPlusImage
429 DWORD Version;
430 ImageDataType Type;
431 union
433 EmfPlusBitmap bitmap;
434 EmfPlusMetafile metafile;
435 } ImageData;
436 } EmfPlusImage;
438 typedef struct EmfPlusImageAttributes
440 DWORD Version;
441 DWORD Reserved1;
442 DWORD WrapMode;
443 EmfPlusARGB ClampColor;
444 DWORD ObjectClamp;
445 DWORD Reserved2;
446 } EmfPlusImageAttributes;
448 typedef struct EmfPlusFont
450 DWORD Version;
451 float EmSize;
452 DWORD SizeUnit;
453 DWORD FontStyleFlags;
454 DWORD Reserved;
455 DWORD Length;
456 WCHAR FamilyName[1];
457 } EmfPlusFont;
459 typedef struct EmfPlusObject
461 EmfPlusRecordHeader Header;
462 union
464 EmfPlusBrush brush;
465 EmfPlusPen pen;
466 EmfPlusPath path;
467 EmfPlusRegion region;
468 EmfPlusImage image;
469 EmfPlusImageAttributes image_attributes;
470 EmfPlusFont font;
471 } ObjectData;
472 } EmfPlusObject;
474 typedef struct EmfPlusPointR7
476 BYTE X;
477 BYTE Y;
478 } EmfPlusPointR7;
480 typedef struct EmfPlusPoint
482 short X;
483 short Y;
484 } EmfPlusPoint;
486 typedef struct EmfPlusDrawImage
488 EmfPlusRecordHeader Header;
489 DWORD ImageAttributesID;
490 DWORD SrcUnit;
491 EmfPlusRectF SrcRect;
492 union
494 EmfPlusRect rect;
495 EmfPlusRectF rectF;
496 } RectData;
497 } EmfPlusDrawImage;
499 typedef struct EmfPlusDrawImagePoints
501 EmfPlusRecordHeader Header;
502 DWORD ImageAttributesID;
503 DWORD SrcUnit;
504 EmfPlusRectF SrcRect;
505 DWORD count;
506 union
508 EmfPlusPointR7 pointsR[3];
509 EmfPlusPoint points[3];
510 EmfPlusPointF pointsF[3];
511 } PointData;
512 } EmfPlusDrawImagePoints;
514 typedef struct EmfPlusDrawPath
516 EmfPlusRecordHeader Header;
517 DWORD PenId;
518 } EmfPlusDrawPath;
520 typedef struct EmfPlusDrawArc
522 EmfPlusRecordHeader Header;
523 float StartAngle;
524 float SweepAngle;
525 union
527 EmfPlusRect rect;
528 EmfPlusRectF rectF;
529 } RectData;
530 } EmfPlusDrawArc;
532 typedef struct EmfPlusDrawEllipse
534 EmfPlusRecordHeader Header;
535 union
537 EmfPlusRect rect;
538 EmfPlusRectF rectF;
539 } RectData;
540 } EmfPlusDrawEllipse;
542 typedef struct EmfPlusDrawPie
544 EmfPlusRecordHeader Header;
545 float StartAngle;
546 float SweepAngle;
547 union
549 EmfPlusRect rect;
550 EmfPlusRectF rectF;
551 } RectData;
552 } EmfPlusDrawPie;
554 typedef struct EmfPlusDrawRects
556 EmfPlusRecordHeader Header;
557 DWORD Count;
558 union
560 EmfPlusRect rect[1];
561 EmfPlusRectF rectF[1];
562 } RectData;
563 } EmfPlusDrawRects;
565 typedef struct EmfPlusFillPath
567 EmfPlusRecordHeader Header;
568 union
570 DWORD BrushId;
571 EmfPlusARGB Color;
572 } data;
573 } EmfPlusFillPath;
575 typedef struct EmfPlusFillClosedCurve
577 EmfPlusRecordHeader Header;
578 DWORD BrushId;
579 float Tension;
580 DWORD Count;
581 union
583 EmfPlusPointR7 pointsR[1];
584 EmfPlusPoint points[1];
585 EmfPlusPointF pointsF[1];
586 } PointData;
587 } EmfPlusFillClosedCurve;
589 typedef struct EmfPlusFillEllipse
591 EmfPlusRecordHeader Header;
592 DWORD BrushId;
593 union
595 EmfPlusRect rect;
596 EmfPlusRectF rectF;
597 } RectData;
598 } EmfPlusFillEllipse;
600 typedef struct EmfPlusFillPie
602 EmfPlusRecordHeader Header;
603 DWORD BrushId;
604 float StartAngle;
605 float SweepAngle;
606 union
608 EmfPlusRect rect;
609 EmfPlusRectF rectF;
610 } RectData;
611 } EmfPlusFillPie;
613 typedef struct EmfPlusDrawDriverString
615 EmfPlusRecordHeader Header;
616 union
618 DWORD BrushId;
619 ARGB Color;
620 } brush;
621 DWORD DriverStringOptionsFlags;
622 DWORD MatrixPresent;
623 DWORD GlyphCount;
624 BYTE VariableData[1];
625 } EmfPlusDrawDriverString;
627 typedef struct EmfPlusFillRegion
629 EmfPlusRecordHeader Header;
630 union
632 DWORD BrushId;
633 EmfPlusARGB Color;
634 } data;
635 } EmfPlusFillRegion;
637 typedef struct EmfPlusOffsetClip
639 EmfPlusRecordHeader Header;
640 float dx;
641 float dy;
642 } EmfPlusOffsetClip;
644 typedef struct EmfPlusSetRenderingOrigin
646 EmfPlusRecordHeader Header;
647 INT x;
648 INT y;
649 } EmfPlusSetRenderingOrigin;
651 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
653 struct emfplus_object *object = &metafile->objtable[id];
655 switch (object->type)
657 case ObjectTypeInvalid:
658 break;
659 case ObjectTypeBrush:
660 GdipDeleteBrush(object->u.brush);
661 break;
662 case ObjectTypePen:
663 GdipDeletePen(object->u.pen);
664 break;
665 case ObjectTypePath:
666 GdipDeletePath(object->u.path);
667 break;
668 case ObjectTypeRegion:
669 GdipDeleteRegion(object->u.region);
670 break;
671 case ObjectTypeImage:
672 GdipDisposeImage(object->u.image);
673 break;
674 case ObjectTypeFont:
675 GdipDeleteFont(object->u.font);
676 break;
677 case ObjectTypeImageAttributes:
678 GdipDisposeImageAttributes(object->u.image_attributes);
679 break;
680 default:
681 FIXME("not implemented for object type %u.\n", object->type);
682 return;
685 object->type = ObjectTypeInvalid;
686 object->u.object = NULL;
689 void METAFILE_Free(GpMetafile *metafile)
691 unsigned int i;
693 heap_free(metafile->comment_data);
694 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
695 if (!metafile->preserve_hemf)
696 DeleteEnhMetaFile(metafile->hemf);
697 if (metafile->record_graphics)
699 WARN("metafile closed while recording\n");
700 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
701 metafile->record_graphics->image = NULL;
702 metafile->record_graphics->busy = TRUE;
705 if (metafile->record_stream)
706 IStream_Release(metafile->record_stream);
708 for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
709 metafile_free_object_table_entry(metafile, i);
712 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
714 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
717 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, EmfPlusRecordType record_type,
718 DWORD size, void **result)
720 DWORD size_needed;
721 EmfPlusRecordHeader *record;
723 if (!metafile->comment_data_size)
725 DWORD data_size = max(256, size * 2 + 4);
726 metafile->comment_data = heap_alloc_zero(data_size);
728 if (!metafile->comment_data)
729 return OutOfMemory;
731 memcpy(metafile->comment_data, "EMF+", 4);
733 metafile->comment_data_size = data_size;
734 metafile->comment_data_length = 4;
737 size_needed = size + metafile->comment_data_length;
739 if (size_needed > metafile->comment_data_size)
741 DWORD data_size = size_needed * 2;
742 BYTE *new_data = heap_alloc_zero(data_size);
744 if (!new_data)
745 return OutOfMemory;
747 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
749 metafile->comment_data_size = data_size;
750 heap_free(metafile->comment_data);
751 metafile->comment_data = new_data;
754 *result = metafile->comment_data + metafile->comment_data_length;
755 metafile->comment_data_length += size;
757 record = (EmfPlusRecordHeader*)*result;
758 record->Type = record_type;
759 record->Flags = 0;
760 record->Size = size;
761 record->DataSize = size - sizeof(EmfPlusRecordHeader);
763 return Ok;
766 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
768 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
769 metafile->comment_data_length -= record->Size;
772 static void METAFILE_WriteRecords(GpMetafile *metafile)
774 if (metafile->comment_data_length > 4)
776 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
777 metafile->comment_data_length = 4;
781 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
783 GpStatus stat;
785 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
787 EmfPlusHeader *header;
789 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeHeader, sizeof(EmfPlusHeader), (void**)&header);
790 if (stat != Ok)
791 return stat;
793 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
794 header->Header.Flags = 1;
796 header->Version = VERSION_MAGIC2;
798 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
799 header->EmfPlusFlags = 1;
800 else
801 header->EmfPlusFlags = 0;
803 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
804 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
806 METAFILE_WriteRecords(metafile);
809 return Ok;
812 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
814 GpStatus stat;
816 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
818 EmfPlusRecordHeader *record;
820 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndOfFile, sizeof(EmfPlusRecordHeader), (void**)&record);
821 if (stat != Ok)
822 return stat;
824 METAFILE_WriteRecords(metafile);
827 return Ok;
830 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
831 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
834 TRACE("(%p %d %s %d %p %p)\n", hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
836 return GdipRecordMetafileFileName(NULL, hdc, type, frameRect, frameUnit, desc, metafile);
839 /*****************************************************************************
840 * GdipRecordMetafileI [GDIPLUS.@]
842 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
843 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
845 GpRectF frameRectF, *pFrameRectF;
847 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
849 if (frameRect)
851 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
852 pFrameRectF = &frameRectF;
854 else
855 pFrameRectF = NULL;
857 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
860 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
861 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
863 GpRectF frameRectF, *pFrameRectF;
865 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
867 if (frameRect)
869 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
870 pFrameRectF = &frameRectF;
872 else
873 pFrameRectF = NULL;
875 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
878 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
879 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
881 GpStatus stat;
883 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
885 if (!stream)
886 return InvalidParameter;
888 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
890 if (stat == Ok)
892 (*metafile)->record_stream = stream;
893 IStream_AddRef(stream);
896 return stat;
899 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
900 UINT num_points)
902 int i;
904 if (!metafile->auto_frame || !num_points)
905 return;
907 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
908 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
910 for (i=0; i<num_points; i++)
912 if (points[i].X < metafile->auto_frame_min.X)
913 metafile->auto_frame_min.X = points[i].X;
914 if (points[i].X > metafile->auto_frame_max.X)
915 metafile->auto_frame_max.X = points[i].X;
916 if (points[i].Y < metafile->auto_frame_min.Y)
917 metafile->auto_frame_min.Y = points[i].Y;
918 if (points[i].Y > metafile->auto_frame_max.Y)
919 metafile->auto_frame_max.Y = points[i].Y;
923 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
925 GpStatus stat;
927 if (!metafile->record_dc || metafile->record_graphics)
928 return InvalidParameter;
930 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
932 if (stat == Ok)
934 *result = metafile->record_graphics;
935 metafile->record_graphics->xres = metafile->logical_dpix;
936 metafile->record_graphics->yres = metafile->logical_dpiy;
937 metafile->record_graphics->printer_display = metafile->printer_display;
940 return stat;
943 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
945 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
947 EmfPlusRecordHeader *record;
948 GpStatus stat;
950 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeGetDC, sizeof(EmfPlusRecordHeader), (void**)&record);
951 if (stat != Ok)
952 return stat;
954 METAFILE_WriteRecords(metafile);
957 *hdc = metafile->record_dc;
959 return Ok;
962 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
964 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
966 EmfPlusClear *record;
967 GpStatus stat;
969 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeClear, sizeof(EmfPlusClear), (void**)&record);
970 if (stat != Ok)
971 return stat;
973 record->Color = color;
975 METAFILE_WriteRecords(metafile);
978 return Ok;
981 static BOOL is_integer_rect(const GpRectF *rect)
983 SHORT x, y, width, height;
984 x = rect->X;
985 y = rect->Y;
986 width = rect->Width;
987 height = rect->Height;
988 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
989 rect->Width != (REAL)width || rect->Height != (REAL)height)
990 return FALSE;
991 return TRUE;
994 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
996 switch (brush->bt)
998 case BrushTypeSolidColor:
999 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
1000 break;
1001 case BrushTypeHatchFill:
1002 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
1003 break;
1004 case BrushTypeLinearGradient:
1006 BOOL ignore_xform;
1007 GpLineGradient *gradient = (GpLineGradient*)brush;
1009 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
1011 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1012 if (!ignore_xform)
1013 *size += sizeof(gradient->transform);
1015 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1016 *size += sizeof(DWORD) + gradient->pblendcount *
1017 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
1018 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1019 *size += sizeof(DWORD) + gradient->blendcount *
1020 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
1022 break;
1024 default:
1025 FIXME("unsupported brush type: %d\n", brush->bt);
1026 return NotImplemented;
1029 return Ok;
1032 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
1034 data->Version = VERSION_MAGIC2;
1035 data->Type = brush->bt;
1037 switch (brush->bt)
1039 case BrushTypeSolidColor:
1041 GpSolidFill *solid = (GpSolidFill *)brush;
1042 data->BrushData.solid.SolidColor = solid->color;
1043 break;
1045 case BrushTypeHatchFill:
1047 GpHatch *hatch = (GpHatch *)brush;
1048 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
1049 data->BrushData.hatch.ForeColor = hatch->forecol;
1050 data->BrushData.hatch.BackColor = hatch->backcol;
1051 break;
1053 case BrushTypeLinearGradient:
1055 BYTE *cursor;
1056 BOOL ignore_xform;
1057 GpLineGradient *gradient = (GpLineGradient*)brush;
1059 data->BrushData.lineargradient.BrushDataFlags = 0;
1060 data->BrushData.lineargradient.WrapMode = gradient->wrap;
1061 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
1062 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
1063 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
1064 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
1065 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1066 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1067 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1068 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1070 if (gradient->gamma)
1071 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1073 cursor = &data->BrushData.lineargradient.OptionalData[0];
1075 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1076 if (!ignore_xform)
1078 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1079 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1080 cursor += sizeof(gradient->transform);
1083 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1085 const DWORD count = gradient->pblendcount;
1087 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1089 memcpy(cursor, &count, sizeof(count));
1090 cursor += sizeof(count);
1092 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1093 cursor += count * sizeof(*gradient->pblendpos);
1095 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1097 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1099 const DWORD count = gradient->blendcount;
1101 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1103 memcpy(cursor, &count, sizeof(count));
1104 cursor += sizeof(count);
1106 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1107 cursor += count * sizeof(*gradient->blendpos);
1109 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1112 break;
1114 default:
1115 FIXME("unsupported brush type: %d\n", brush->bt);
1119 static void METAFILE_PrepareCustomLineCapData(GDIPCONST GpCustomLineCap *cap, DWORD *ret_cap_size,
1120 DWORD *ret_cap_data_size, DWORD *ret_path_size)
1122 DWORD cap_size, path_size = 0;
1124 /* EmfPlusCustomStartCapData */
1125 cap_size = FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1126 /* -> EmfPlusCustomLineCap */
1127 cap_size += FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData);
1128 /* -> EmfPlusCustomLineCapArrowData */
1129 if (cap->type == CustomLineCapTypeAdjustableArrow)
1130 cap_size += sizeof(EmfPlusCustomLineCapArrowData);
1131 /* -> EmfPlusCustomLineCapData */
1132 else
1134 /* -> EmfPlusCustomLineCapOptionalData */
1135 cap_size += FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData);
1136 if (cap->fill)
1137 /* -> EmfPlusCustomLineCapDataFillPath */
1138 cap_size += FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath);
1139 else
1140 /* -> EmfPlusCustomLineCapDataLinePath */
1141 cap_size += FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath);
1143 /* -> EmfPlusPath in EmfPlusCustomLineCapDataFillPath and EmfPlusCustomLineCapDataLinePath */
1144 path_size = FIELD_OFFSET(EmfPlusPath, data);
1145 path_size += sizeof(PointF) * cap->pathdata.Count;
1146 path_size += sizeof(BYTE) * cap->pathdata.Count;
1147 path_size = (path_size + 3) & ~3;
1149 cap_size += path_size;
1152 *ret_cap_size = cap_size;
1153 *ret_cap_data_size = cap_size - FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1154 *ret_path_size = path_size;
1157 static void METAFILE_FillCustomLineCapData(GDIPCONST GpCustomLineCap *cap, BYTE *ptr,
1158 REAL line_miter_limit, DWORD data_size, DWORD path_size)
1160 EmfPlusCustomStartCapData *cap_data;
1161 EmfPlusCustomLineCap *line_cap;
1162 DWORD i;
1164 cap_data = (EmfPlusCustomStartCapData *)ptr;
1165 cap_data->CustomStartCapSize = data_size;
1166 i = FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1168 line_cap = (EmfPlusCustomLineCap *)(ptr + i);
1169 line_cap->Version = VERSION_MAGIC2;
1170 line_cap->Type = cap->type;
1171 i += FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData);
1173 if (cap->type == CustomLineCapTypeAdjustableArrow)
1175 EmfPlusCustomLineCapArrowData *arrow_data;
1176 GpAdjustableArrowCap *arrow_cap;
1178 arrow_data = (EmfPlusCustomLineCapArrowData *)(ptr + i);
1179 arrow_cap = (GpAdjustableArrowCap *)cap;
1180 arrow_data->Width = arrow_cap->width;
1181 arrow_data->Height = arrow_cap->height;
1182 arrow_data->MiddleInset = arrow_cap->middle_inset;
1183 arrow_data->FillState = arrow_cap->cap.fill;
1184 arrow_data->LineStartCap = arrow_cap->cap.strokeStartCap;
1185 arrow_data->LineEndCap = arrow_cap->cap.strokeEndCap;
1186 arrow_data->LineJoin = arrow_cap->cap.join;
1187 arrow_data->LineMiterLimit = line_miter_limit;
1188 arrow_data->WidthScale = arrow_cap->cap.scale;
1189 arrow_data->FillHotSpot.X = 0;
1190 arrow_data->FillHotSpot.Y = 0;
1191 arrow_data->LineHotSpot.X = 0;
1192 arrow_data->LineHotSpot.Y = 0;
1194 else
1196 EmfPlusCustomLineCapData *line_cap_data = (EmfPlusCustomLineCapData *)(ptr + i);
1197 EmfPlusPath *path;
1199 if (cap->fill)
1200 line_cap_data->CustomLineCapDataFlags = CustomLineCapDataFillPath;
1201 else
1202 line_cap_data->CustomLineCapDataFlags = CustomLineCapDataLinePath;
1203 line_cap_data->BaseCap = cap->basecap;
1204 line_cap_data->BaseInset = cap->inset;
1205 line_cap_data->StrokeStartCap = cap->strokeStartCap;
1206 line_cap_data->StrokeEndCap = cap->strokeEndCap;
1207 line_cap_data->StrokeJoin = cap->join;
1208 line_cap_data->StrokeMiterLimit = line_miter_limit;
1209 line_cap_data->WidthScale = cap->scale;
1210 line_cap_data->FillHotSpot.X = 0;
1211 line_cap_data->FillHotSpot.Y = 0;
1212 line_cap_data->LineHotSpot.X = 0;
1213 line_cap_data->LineHotSpot.Y = 0;
1214 i += FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData);
1216 if (cap->fill)
1218 EmfPlusCustomLineCapDataFillPath *fill_path = (EmfPlusCustomLineCapDataFillPath *)(ptr + i);
1219 fill_path->FillPathLength = path_size;
1220 i += FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath);
1222 else
1224 EmfPlusCustomLineCapDataLinePath *line_path = (EmfPlusCustomLineCapDataLinePath *)(ptr + i);
1225 line_path->LinePathLength = path_size;
1226 i += FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath);
1229 path = (EmfPlusPath *)(ptr + i);
1230 path->Version = VERSION_MAGIC2;
1231 path->PathPointCount = cap->pathdata.Count;
1232 path->PathPointFlags = 0;
1233 i += FIELD_OFFSET(EmfPlusPath, data);
1234 memcpy(ptr + i, cap->pathdata.Points, cap->pathdata.Count * sizeof(PointF));
1235 i += cap->pathdata.Count * sizeof(PointF);
1236 memcpy(ptr + i, cap->pathdata.Types, cap->pathdata.Count * sizeof(BYTE));
1240 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1242 EmfPlusObject *object_record;
1243 GpStatus stat;
1244 DWORD size;
1246 *id = -1;
1247 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1248 return Ok;
1250 stat = METAFILE_PrepareBrushData(brush, &size);
1251 if (stat != Ok) return stat;
1253 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1254 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1255 if (stat != Ok) return stat;
1257 *id = METAFILE_AddObjectId(metafile);
1258 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1259 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1260 return Ok;
1263 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1264 GDIPCONST GpRectF* rects, INT count)
1266 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1268 EmfPlusFillRects *record;
1269 GpStatus stat;
1270 BOOL integer_rects = TRUE;
1271 int i;
1272 DWORD brushid;
1273 int flags = 0;
1275 if (brush->bt == BrushTypeSolidColor)
1277 flags |= 0x8000;
1278 brushid = ((GpSolidFill*)brush)->color;
1280 else
1282 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1283 if (stat != Ok)
1284 return stat;
1287 for (i=0; i<count; i++)
1289 if (!is_integer_rect(&rects[i]))
1291 integer_rects = FALSE;
1292 break;
1296 if (integer_rects)
1297 flags |= 0x4000;
1299 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRects,
1300 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1301 (void**)&record);
1302 if (stat != Ok)
1303 return stat;
1305 record->Header.Flags = flags;
1306 record->BrushID = brushid;
1307 record->Count = count;
1309 if (integer_rects)
1311 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1312 for (i=0; i<count; i++)
1314 record_rects[i].X = (SHORT)rects[i].X;
1315 record_rects[i].Y = (SHORT)rects[i].Y;
1316 record_rects[i].Width = (SHORT)rects[i].Width;
1317 record_rects[i].Height = (SHORT)rects[i].Height;
1320 else
1321 memcpy(record+1, rects, sizeof(GpRectF) * count);
1323 METAFILE_WriteRecords(metafile);
1326 if (metafile->auto_frame)
1328 GpPointF corners[4];
1329 int i;
1331 for (i=0; i<count; i++)
1333 corners[0].X = rects[i].X;
1334 corners[0].Y = rects[i].Y;
1335 corners[1].X = rects[i].X + rects[i].Width;
1336 corners[1].Y = rects[i].Y;
1337 corners[2].X = rects[i].X;
1338 corners[2].Y = rects[i].Y + rects[i].Height;
1339 corners[3].X = rects[i].X + rects[i].Width;
1340 corners[3].Y = rects[i].Y + rects[i].Height;
1342 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1343 CoordinateSpaceWorld, corners, 4);
1345 METAFILE_AdjustFrame(metafile, corners, 4);
1349 return Ok;
1352 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1354 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1356 EmfPlusSetClipRect *record;
1357 GpStatus stat;
1359 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRect,
1360 sizeof(EmfPlusSetClipRect), (void **)&record);
1361 if (stat != Ok)
1362 return stat;
1364 record->Header.Flags = (mode & 0xf) << 8;
1365 record->ClipRect.X = x;
1366 record->ClipRect.Y = y;
1367 record->ClipRect.Width = width;
1368 record->ClipRect.Height = height;
1370 METAFILE_WriteRecords(metafile);
1373 return Ok;
1376 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1378 EmfPlusObject *object_record;
1379 DWORD size;
1380 GpStatus stat;
1382 *id = -1;
1383 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1384 return Ok;
1386 size = write_region_data(region, NULL);
1387 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1388 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1389 if (stat != Ok) return stat;
1391 *id = METAFILE_AddObjectId(metafile);
1392 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1393 write_region_data(region, &object_record->ObjectData.region);
1394 return Ok;
1397 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1399 EmfPlusRecordHeader *record;
1400 DWORD region_id;
1401 GpStatus stat;
1403 if (metafile->metafile_type == MetafileTypeEmf)
1405 FIXME("stub!\n");
1406 return NotImplemented;
1409 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1410 if (stat != Ok) return stat;
1412 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRegion, sizeof(*record), (void**)&record);
1413 if (stat != Ok) return stat;
1415 record->Flags = region_id | mode << 8;
1417 METAFILE_WriteRecords(metafile);
1418 return Ok;
1421 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1423 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1425 EmfPlusSetPageTransform *record;
1426 GpStatus stat;
1428 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetPageTransform,
1429 sizeof(EmfPlusSetPageTransform), (void **)&record);
1430 if (stat != Ok)
1431 return stat;
1433 record->Header.Flags = unit;
1434 record->PageScale = scale;
1436 METAFILE_WriteRecords(metafile);
1439 return Ok;
1442 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1444 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1446 EmfPlusSetWorldTransform *record;
1447 GpStatus stat;
1449 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetWorldTransform,
1450 sizeof(EmfPlusSetWorldTransform), (void **)&record);
1451 if (stat != Ok)
1452 return stat;
1454 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1456 METAFILE_WriteRecords(metafile);
1459 return Ok;
1462 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1464 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1466 EmfPlusScaleWorldTransform *record;
1467 GpStatus stat;
1469 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeScaleWorldTransform,
1470 sizeof(EmfPlusScaleWorldTransform), (void **)&record);
1471 if (stat != Ok)
1472 return stat;
1474 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1475 record->Sx = sx;
1476 record->Sy = sy;
1478 METAFILE_WriteRecords(metafile);
1481 return Ok;
1484 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1486 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1488 EmfPlusMultiplyWorldTransform *record;
1489 GpStatus stat;
1491 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeMultiplyWorldTransform,
1492 sizeof(EmfPlusMultiplyWorldTransform), (void **)&record);
1493 if (stat != Ok)
1494 return stat;
1496 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1497 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1499 METAFILE_WriteRecords(metafile);
1502 return Ok;
1505 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1507 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1509 EmfPlusRotateWorldTransform *record;
1510 GpStatus stat;
1512 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRotateWorldTransform,
1513 sizeof(EmfPlusRotateWorldTransform), (void **)&record);
1514 if (stat != Ok)
1515 return stat;
1517 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1518 record->Angle = angle;
1520 METAFILE_WriteRecords(metafile);
1523 return Ok;
1526 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1528 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1530 EmfPlusTranslateWorldTransform *record;
1531 GpStatus stat;
1533 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeTranslateWorldTransform,
1534 sizeof(EmfPlusTranslateWorldTransform), (void **)&record);
1535 if (stat != Ok)
1536 return stat;
1538 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1539 record->dx = dx;
1540 record->dy = dy;
1542 METAFILE_WriteRecords(metafile);
1545 return Ok;
1548 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1550 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1552 EmfPlusRecordHeader *record;
1553 GpStatus stat;
1555 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetWorldTransform,
1556 sizeof(EmfPlusRecordHeader), (void **)&record);
1557 if (stat != Ok)
1558 return stat;
1560 METAFILE_WriteRecords(metafile);
1563 return Ok;
1566 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1567 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1569 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1571 EmfPlusBeginContainer *record;
1572 GpStatus stat;
1574 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainer, sizeof(*record), (void**)&record);
1575 if (stat != Ok)
1576 return stat;
1578 record->Header.Flags = unit & 0xff;
1579 record->DestRect = *dstrect;
1580 record->SrcRect = *srcrect;
1581 record->StackIndex = StackIndex;
1583 METAFILE_WriteRecords(metafile);
1586 return Ok;
1589 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1591 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1593 EmfPlusContainerRecord *record;
1594 GpStatus stat;
1596 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainerNoParams,
1597 sizeof(EmfPlusContainerRecord), (void **)&record);
1598 if (stat != Ok)
1599 return stat;
1601 record->StackIndex = StackIndex;
1603 METAFILE_WriteRecords(metafile);
1606 return Ok;
1609 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1611 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1613 EmfPlusContainerRecord *record;
1614 GpStatus stat;
1616 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndContainer,
1617 sizeof(EmfPlusContainerRecord), (void **)&record);
1618 if (stat != Ok)
1619 return stat;
1621 record->StackIndex = StackIndex;
1623 METAFILE_WriteRecords(metafile);
1626 return Ok;
1629 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1631 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1633 EmfPlusContainerRecord *record;
1634 GpStatus stat;
1636 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSave,
1637 sizeof(EmfPlusContainerRecord), (void **)&record);
1638 if (stat != Ok)
1639 return stat;
1641 record->StackIndex = StackIndex;
1643 METAFILE_WriteRecords(metafile);
1646 return Ok;
1649 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1651 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1653 EmfPlusContainerRecord *record;
1654 GpStatus stat;
1656 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRestore,
1657 sizeof(EmfPlusContainerRecord), (void **)&record);
1658 if (stat != Ok)
1659 return stat;
1661 record->StackIndex = StackIndex;
1663 METAFILE_WriteRecords(metafile);
1666 return Ok;
1669 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1671 if (hdc != metafile->record_dc)
1672 return InvalidParameter;
1674 return Ok;
1677 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1679 GpStatus stat;
1681 stat = METAFILE_WriteEndOfFile(metafile);
1682 metafile->record_graphics = NULL;
1684 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1685 metafile->record_dc = NULL;
1687 heap_free(metafile->comment_data);
1688 metafile->comment_data = NULL;
1689 metafile->comment_data_size = 0;
1691 if (stat == Ok)
1693 MetafileHeader header;
1695 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1696 if (stat == Ok && metafile->auto_frame &&
1697 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1699 RECTL bounds_rc, gdi_bounds_rc;
1700 REAL x_scale = 2540.0 / header.DpiX;
1701 REAL y_scale = 2540.0 / header.DpiY;
1702 BYTE* buffer;
1703 UINT buffer_size;
1705 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1706 if (gdi_bounds_rc.right > gdi_bounds_rc.left &&
1707 gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1709 GpPointF *af_min = &metafile->auto_frame_min;
1710 GpPointF *af_max = &metafile->auto_frame_max;
1712 af_min->X = fmin(af_min->X, gdi_bounds_rc.left);
1713 af_min->Y = fmin(af_min->Y, gdi_bounds_rc.top);
1714 af_max->X = fmax(af_max->X, gdi_bounds_rc.right);
1715 af_max->Y = fmax(af_max->Y, gdi_bounds_rc.bottom);
1718 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1719 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1720 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1721 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1723 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1724 buffer = heap_alloc(buffer_size);
1725 if (buffer)
1727 HENHMETAFILE new_hemf;
1729 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1731 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1733 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1735 if (new_hemf)
1737 DeleteEnhMetaFile(metafile->hemf);
1738 metafile->hemf = new_hemf;
1740 else
1741 stat = OutOfMemory;
1743 heap_free(buffer);
1745 else
1746 stat = OutOfMemory;
1748 if (stat == Ok)
1749 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1751 if (stat == Ok)
1753 metafile->bounds.X = header.X;
1754 metafile->bounds.Y = header.Y;
1755 metafile->bounds.Width = header.Width;
1756 metafile->bounds.Height = header.Height;
1760 if (stat == Ok && metafile->record_stream)
1762 BYTE *buffer;
1763 UINT buffer_size;
1765 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1767 buffer = heap_alloc(buffer_size);
1768 if (buffer)
1770 HRESULT hr;
1772 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1774 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1776 if (FAILED(hr))
1777 stat = hresult_to_status(hr);
1779 heap_free(buffer);
1781 else
1782 stat = OutOfMemory;
1785 if (metafile->record_stream)
1787 IStream_Release(metafile->record_stream);
1788 metafile->record_stream = NULL;
1791 return stat;
1794 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1796 TRACE("(%p,%p)\n", metafile, hEmf);
1798 if (!metafile || !hEmf || !metafile->hemf)
1799 return InvalidParameter;
1801 *hEmf = metafile->hemf;
1802 metafile->hemf = NULL;
1804 return Ok;
1807 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1809 GpStatus stat = Ok;
1811 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1813 return stat;
1816 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1818 if (metafile->playback_dc)
1820 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1821 metafile->playback_dc = NULL;
1825 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1827 GpStatus stat;
1828 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1829 if (stat == Ok)
1830 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1831 return stat;
1834 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1836 GpMatrix *real_transform;
1837 GpStatus stat;
1839 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1841 if (stat == Ok)
1843 REAL scale_x = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpix, metafile->printer_display);
1844 REAL scale_y = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpiy, metafile->printer_display);
1846 if (metafile->page_unit != UnitDisplay)
1848 scale_x *= metafile->page_scale;
1849 scale_y *= metafile->page_scale;
1852 stat = GdipScaleMatrix(real_transform, scale_x, scale_y, MatrixOrderPrepend);
1854 if (stat == Ok)
1855 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1857 if (stat == Ok)
1858 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1860 GdipDeleteMatrix(real_transform);
1863 return stat;
1866 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1868 metafile_free_object_table_entry(metafile, id);
1869 metafile->objtable[id].type = type;
1870 metafile->objtable[id].u.object = object;
1873 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1875 EmfPlusImage *data = (EmfPlusImage *)record_data;
1876 GpStatus status;
1878 *image = NULL;
1880 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1881 return InvalidParameter;
1882 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1884 switch (data->Type)
1886 case ImageDataTypeBitmap:
1888 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1890 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1891 return InvalidParameter;
1892 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1894 switch (bitmapdata->Type)
1896 case BitmapDataTypePixel:
1898 ColorPalette *palette;
1899 BYTE *scan0;
1901 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1903 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1904 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1906 if (data_size <= palette_size)
1907 return InvalidParameter;
1908 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1910 if (data_size < palette_size)
1911 return InvalidParameter;
1912 data_size -= palette_size;
1914 palette = (ColorPalette *)bitmapdata->BitmapData;
1915 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1917 else
1919 palette = NULL;
1920 scan0 = bitmapdata->BitmapData;
1923 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1924 return InvalidParameter;
1926 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1927 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1928 if (status == Ok && palette)
1930 status = GdipSetImagePalette(*image, palette);
1931 if (status != Ok)
1933 GdipDisposeImage(*image);
1934 *image = NULL;
1937 break;
1939 case BitmapDataTypeCompressed:
1941 IWICImagingFactory *factory;
1942 IWICStream *stream;
1943 HRESULT hr;
1945 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1946 return GenericError;
1948 hr = IWICImagingFactory_CreateStream(factory, &stream);
1949 IWICImagingFactory_Release(factory);
1950 if (hr != S_OK)
1951 return GenericError;
1953 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1954 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1955 else
1956 status = GenericError;
1958 IWICStream_Release(stream);
1959 break;
1961 default:
1962 WARN("Invalid bitmap type %ld.\n", bitmapdata->Type);
1963 return InvalidParameter;
1965 break;
1967 case ImageDataTypeMetafile:
1969 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1971 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1972 return InvalidParameter;
1973 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1975 switch (metafiledata->Type) {
1976 case MetafileTypeEmf:
1977 case MetafileTypeEmfPlusOnly:
1978 case MetafileTypeEmfPlusDual:
1980 HENHMETAFILE hemf;
1982 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1984 if (!hemf)
1985 return GenericError;
1987 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1989 if (status != Ok)
1990 DeleteEnhMetaFile(hemf);
1992 break;
1994 default:
1995 FIXME("metafile type %ld not supported.\n", metafiledata->Type);
1996 return NotImplemented;
1998 break;
2000 default:
2001 FIXME("image type %d not supported.\n", data->Type);
2002 return NotImplemented;
2005 return status;
2008 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
2010 EmfPlusPath *data = (EmfPlusPath *)record_data;
2011 GpStatus status;
2012 BYTE *types;
2013 UINT size;
2014 DWORD i;
2016 *path = NULL;
2018 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
2019 return InvalidParameter;
2020 data_size -= FIELD_OFFSET(EmfPlusPath, data);
2022 if (data->PathPointFlags & 0x800) /* R */
2024 FIXME("RLE encoded path data is not supported.\n");
2025 return NotImplemented;
2027 else
2029 if (data->PathPointFlags & 0x4000) /* C */
2030 size = sizeof(EmfPlusPoint);
2031 else
2032 size = sizeof(EmfPlusPointF);
2033 size += sizeof(BYTE); /* EmfPlusPathPointType */
2034 size *= data->PathPointCount;
2037 if (data_size < size)
2038 return InvalidParameter;
2040 status = GdipCreatePath(FillModeAlternate, path);
2041 if (status != Ok)
2042 return status;
2044 (*path)->pathdata.Count = data->PathPointCount;
2045 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
2046 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
2047 (*path)->datalen = (*path)->pathdata.Count;
2049 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
2051 GdipDeletePath(*path);
2052 return OutOfMemory;
2055 if (data->PathPointFlags & 0x4000) /* C */
2057 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
2058 for (i = 0; i < data->PathPointCount; i++)
2060 (*path)->pathdata.Points[i].X = points[i].X;
2061 (*path)->pathdata.Points[i].Y = points[i].Y;
2063 types = (BYTE *)(points + i);
2065 else
2067 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
2068 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
2069 types = (BYTE *)(points + data->PathPointCount);
2072 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
2074 return Ok;
2077 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
2079 const DWORD *type;
2080 GpStatus status;
2082 type = buffer_read(mbuf, sizeof(*type));
2083 if (!type) return Ok;
2085 node->type = *type;
2087 switch (node->type)
2089 case CombineModeReplace:
2090 case CombineModeIntersect:
2091 case CombineModeUnion:
2092 case CombineModeXor:
2093 case CombineModeExclude:
2094 case CombineModeComplement:
2096 region_element *left, *right;
2098 left = heap_alloc_zero(sizeof(*left));
2099 if (!left)
2100 return OutOfMemory;
2102 right = heap_alloc_zero(sizeof(*right));
2103 if (!right)
2105 heap_free(left);
2106 return OutOfMemory;
2109 status = metafile_read_region_node(mbuf, region, left, count);
2110 if (status == Ok)
2112 status = metafile_read_region_node(mbuf, region, right, count);
2113 if (status == Ok)
2115 node->elementdata.combine.left = left;
2116 node->elementdata.combine.right = right;
2117 region->num_children += 2;
2118 return Ok;
2122 heap_free(left);
2123 heap_free(right);
2124 return status;
2126 case RegionDataRect:
2128 const EmfPlusRectF *rect;
2130 rect = buffer_read(mbuf, sizeof(*rect));
2131 if (!rect)
2132 return InvalidParameter;
2134 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
2135 *count += 1;
2136 return Ok;
2138 case RegionDataPath:
2140 const BYTE *path_data;
2141 const UINT *data_size;
2142 GpPath *path;
2144 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
2145 if (!data_size)
2146 return InvalidParameter;
2148 path_data = buffer_read(mbuf, *data_size);
2149 if (!path_data)
2150 return InvalidParameter;
2152 status = metafile_deserialize_path(path_data, *data_size, &path);
2153 if (status == Ok)
2155 node->elementdata.path = path;
2156 *count += 1;
2158 return Ok;
2160 case RegionDataEmptyRect:
2161 case RegionDataInfiniteRect:
2162 *count += 1;
2163 return Ok;
2164 default:
2165 FIXME("element type %#lx is not supported\n", *type);
2166 break;
2169 return InvalidParameter;
2172 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
2174 struct memory_buffer mbuf;
2175 GpStatus status;
2176 UINT count;
2178 *region = NULL;
2180 init_memory_buffer(&mbuf, record_data, data_size);
2182 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2183 return InvalidParameter;
2185 status = GdipCreateRegion(region);
2186 if (status != Ok)
2187 return status;
2189 count = 0;
2190 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2191 if (status == Ok && !count)
2192 status = InvalidParameter;
2194 if (status != Ok)
2196 GdipDeleteRegion(*region);
2197 *region = NULL;
2200 return status;
2203 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2205 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2206 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2207 EmfPlusTransformMatrix *transform = NULL;
2208 DWORD brushflags;
2209 GpStatus status;
2210 UINT offset;
2212 *brush = NULL;
2214 if (data_size < header_size)
2215 return InvalidParameter;
2217 switch (data->Type)
2219 case BrushTypeSolidColor:
2220 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2221 return InvalidParameter;
2223 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2224 break;
2225 case BrushTypeHatchFill:
2226 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2227 return InvalidParameter;
2229 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2230 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2231 break;
2232 case BrushTypeTextureFill:
2234 GpImage *image;
2236 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2237 if (data_size <= offset)
2238 return InvalidParameter;
2240 brushflags = data->BrushData.texture.BrushDataFlags;
2241 if (brushflags & BrushDataTransform)
2243 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2244 return InvalidParameter;
2245 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2246 offset += sizeof(EmfPlusTransformMatrix);
2249 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2250 if (status != Ok)
2251 return status;
2253 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2254 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2255 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2257 GdipDisposeImage(image);
2258 break;
2260 case BrushTypeLinearGradient:
2262 GpLineGradient *gradient = NULL;
2263 GpRectF rect;
2264 UINT position_count = 0;
2266 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2267 if (data_size < offset)
2268 return InvalidParameter;
2270 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2271 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2272 return InvalidParameter;
2274 if (brushflags & BrushDataTransform)
2276 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2277 return InvalidParameter;
2278 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2279 offset += sizeof(EmfPlusTransformMatrix);
2282 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2284 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2285 return InvalidParameter;
2286 position_count = *(DWORD *)(record_data + offset);
2287 offset += sizeof(DWORD);
2290 if (brushflags & BrushDataPresetColors)
2292 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2293 return InvalidParameter;
2295 else if (brushflags & BrushDataBlendFactorsH)
2297 if (data_size != offset + position_count * 2 * sizeof(float))
2298 return InvalidParameter;
2301 rect.X = data->BrushData.lineargradient.RectF.X;
2302 rect.Y = data->BrushData.lineargradient.RectF.Y;
2303 rect.Width = data->BrushData.lineargradient.RectF.Width;
2304 rect.Height = data->BrushData.lineargradient.RectF.Height;
2306 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2307 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2308 data->BrushData.lineargradient.WrapMode, &gradient);
2309 if (status == Ok)
2311 if (transform)
2312 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2314 if (status == Ok)
2316 if (brushflags & BrushDataPresetColors)
2317 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2318 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2319 else if (brushflags & BrushDataBlendFactorsH)
2320 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2321 (REAL *)(record_data + offset), position_count);
2323 if (brushflags & BrushDataIsGammaCorrected)
2324 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2328 if (status == Ok)
2329 *brush = (GpBrush *)gradient;
2330 else
2331 GdipDeleteBrush((GpBrush *)gradient);
2333 break;
2335 default:
2336 FIXME("brush type %lu is not supported.\n", data->Type);
2337 return NotImplemented;
2340 return status;
2343 static GpStatus metafile_deserialize_custom_line_cap(const BYTE *record_data, UINT data_size, GpCustomLineCap **cap)
2345 EmfPlusCustomStartCapData *custom_cap_data = (EmfPlusCustomStartCapData *)record_data;
2346 EmfPlusCustomLineCap *line_cap;
2347 GpStatus status;
2348 UINT offset;
2350 *cap = NULL;
2352 if (data_size < FIELD_OFFSET(EmfPlusCustomStartCapData, data))
2353 return InvalidParameter;
2354 if (data_size < FIELD_OFFSET(EmfPlusCustomStartCapData, data) + custom_cap_data->CustomStartCapSize)
2355 return InvalidParameter;
2356 offset = FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2357 line_cap = (EmfPlusCustomLineCap *)(record_data + offset);
2359 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData))
2360 return InvalidParameter;
2361 offset += FIELD_OFFSET(EmfPlusCustomLineCap, CustomLineCapData);
2363 if (line_cap->Type == CustomLineCapTypeAdjustableArrow)
2365 EmfPlusCustomLineCapArrowData *arrow_data;
2366 GpAdjustableArrowCap *arrow_cap;
2368 arrow_data = (EmfPlusCustomLineCapArrowData *)(record_data + offset);
2370 if (data_size < offset + sizeof(EmfPlusCustomLineCapArrowData))
2371 return InvalidParameter;
2373 if ((status = GdipCreateAdjustableArrowCap(arrow_data->Height, arrow_data->Width,
2374 arrow_data->FillState, &arrow_cap)))
2375 return status;
2377 if ((status = GdipSetAdjustableArrowCapMiddleInset(arrow_cap, arrow_data->MiddleInset)))
2378 goto arrow_cap_failed;
2379 if ((status = GdipSetCustomLineCapStrokeCaps((GpCustomLineCap *)arrow_cap, arrow_data->LineStartCap, arrow_data->LineEndCap)))
2380 goto arrow_cap_failed;
2381 if ((status = GdipSetCustomLineCapStrokeJoin((GpCustomLineCap *)arrow_cap, arrow_data->LineJoin)))
2382 goto arrow_cap_failed;
2383 if ((status = GdipSetCustomLineCapWidthScale((GpCustomLineCap *)arrow_cap, arrow_data->WidthScale)))
2384 goto arrow_cap_failed;
2386 *cap = (GpCustomLineCap *)arrow_cap;
2387 return Ok;
2389 arrow_cap_failed:
2390 GdipDeleteCustomLineCap((GpCustomLineCap *)arrow_cap);
2391 return status;
2393 else
2395 GpPath *path, *fill_path = NULL, *stroke_path = NULL;
2396 EmfPlusCustomLineCapData *line_cap_data;
2397 GpCustomLineCap *line_cap = NULL;
2398 GpStatus status;
2400 line_cap_data = (EmfPlusCustomLineCapData *)(record_data + offset);
2402 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData))
2403 return InvalidParameter;
2404 offset += FIELD_OFFSET(EmfPlusCustomLineCapData, OptionalData);
2406 if (line_cap_data->CustomLineCapDataFlags == CustomLineCapDataFillPath)
2408 EmfPlusCustomLineCapDataFillPath *fill_path = (EmfPlusCustomLineCapDataFillPath *)(record_data + offset);
2410 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath))
2411 return InvalidParameter;
2412 if (data_size < offset + fill_path->FillPathLength)
2413 return InvalidParameter;
2415 offset += FIELD_OFFSET(EmfPlusCustomLineCapDataFillPath, FillPath);
2417 else
2419 EmfPlusCustomLineCapDataLinePath *line_path = (EmfPlusCustomLineCapDataLinePath *)(record_data + offset);
2421 if (data_size < offset + FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath))
2422 return InvalidParameter;
2423 if (data_size < offset + line_path->LinePathLength)
2424 return InvalidParameter;
2426 offset += FIELD_OFFSET(EmfPlusCustomLineCapDataLinePath, LinePath);
2429 if ((status = metafile_deserialize_path(record_data + offset, data_size - offset, &path)))
2430 return status;
2432 if (line_cap_data->CustomLineCapDataFlags == CustomLineCapDataFillPath)
2433 fill_path = path;
2434 else
2435 stroke_path = path;
2437 if ((status = GdipCreateCustomLineCap(fill_path, stroke_path, line_cap_data->BaseCap,
2438 line_cap_data->BaseInset, &line_cap)))
2439 goto default_cap_failed;
2440 if ((status = GdipSetCustomLineCapStrokeCaps(line_cap, line_cap_data->StrokeStartCap, line_cap_data->StrokeEndCap)))
2441 goto default_cap_failed;
2442 if ((status = GdipSetCustomLineCapStrokeJoin(line_cap, line_cap_data->StrokeJoin)))
2443 goto default_cap_failed;
2444 if ((status = GdipSetCustomLineCapWidthScale(line_cap, line_cap_data->WidthScale)))
2445 goto default_cap_failed;
2447 GdipDeletePath(path);
2448 *cap = line_cap;
2449 return Ok;
2451 default_cap_failed:
2452 if (line_cap)
2453 GdipDeleteCustomLineCap(line_cap);
2454 GdipDeletePath(path);
2455 return status;
2459 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2461 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2462 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2464 if (data_size <= offset)
2465 return InvalidParameter;
2467 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2468 if (data_size <= offset)
2469 return InvalidParameter;
2471 if (pendata->PenDataFlags & PenDataTransform)
2472 offset += sizeof(EmfPlusTransformMatrix);
2474 if (pendata->PenDataFlags & PenDataStartCap)
2475 offset += sizeof(DWORD);
2477 if (pendata->PenDataFlags & PenDataEndCap)
2478 offset += sizeof(DWORD);
2480 if (pendata->PenDataFlags & PenDataJoin)
2481 offset += sizeof(DWORD);
2483 if (pendata->PenDataFlags & PenDataMiterLimit)
2484 offset += sizeof(REAL);
2486 if (pendata->PenDataFlags & PenDataLineStyle)
2487 offset += sizeof(DWORD);
2489 if (pendata->PenDataFlags & PenDataDashedLineCap)
2490 offset += sizeof(DWORD);
2492 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2493 offset += sizeof(REAL);
2495 if (pendata->PenDataFlags & PenDataDashedLine)
2497 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2499 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2500 if (data_size <= offset)
2501 return InvalidParameter;
2503 offset += dashedline->DashedLineDataSize * sizeof(float);
2506 if (pendata->PenDataFlags & PenDataNonCenter)
2507 offset += sizeof(DWORD);
2509 if (pendata->PenDataFlags & PenDataCompoundLine)
2511 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2513 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2514 if (data_size <= offset)
2515 return InvalidParameter;
2517 offset += compoundline->CompoundLineDataSize * sizeof(float);
2520 if (pendata->PenDataFlags & PenDataCustomStartCap)
2522 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2524 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2525 if (data_size <= offset)
2526 return InvalidParameter;
2528 offset += startcap->CustomStartCapSize;
2531 if (pendata->PenDataFlags & PenDataCustomEndCap)
2533 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2535 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2536 if (data_size <= offset)
2537 return InvalidParameter;
2539 offset += endcap->CustomEndCapSize;
2542 *ret = offset;
2543 return Ok;
2546 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2548 BYTE type = (flags >> 8) & 0xff;
2549 BYTE id = flags & 0xff;
2550 void *object = NULL;
2551 GpStatus status;
2553 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2554 return InvalidParameter;
2556 switch (type)
2558 case ObjectTypeBrush:
2559 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2560 break;
2561 case ObjectTypePen:
2563 EmfPlusPen *data = (EmfPlusPen *)record_data;
2564 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2565 GpCustomLineCap *custom_line_cap;
2566 GpBrush *brush;
2567 DWORD offset;
2568 GpPen *pen;
2570 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2571 if (status != Ok)
2572 return status;
2574 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2575 if (status != Ok)
2576 return status;
2578 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2579 GdipDeleteBrush(brush);
2580 if (status != Ok)
2581 return status;
2583 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2585 if (pendata->PenDataFlags & PenDataTransform)
2587 FIXME("PenDataTransform is not supported.\n");
2588 offset += sizeof(EmfPlusTransformMatrix);
2591 if (pendata->PenDataFlags & PenDataStartCap)
2593 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2594 goto penfailed;
2595 offset += sizeof(DWORD);
2598 if (pendata->PenDataFlags & PenDataEndCap)
2600 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2601 goto penfailed;
2602 offset += sizeof(DWORD);
2605 if (pendata->PenDataFlags & PenDataJoin)
2607 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2608 goto penfailed;
2609 offset += sizeof(DWORD);
2612 if (pendata->PenDataFlags & PenDataMiterLimit)
2614 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2615 goto penfailed;
2616 offset += sizeof(REAL);
2619 if (pendata->PenDataFlags & PenDataLineStyle)
2621 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2622 goto penfailed;
2623 offset += sizeof(DWORD);
2626 if (pendata->PenDataFlags & PenDataDashedLineCap)
2628 FIXME("PenDataDashedLineCap is not supported.\n");
2629 offset += sizeof(DWORD);
2632 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2634 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2635 goto penfailed;
2636 offset += sizeof(REAL);
2639 if (pendata->PenDataFlags & PenDataDashedLine)
2641 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2642 FIXME("PenDataDashedLine is not supported.\n");
2643 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2646 if (pendata->PenDataFlags & PenDataNonCenter)
2648 FIXME("PenDataNonCenter is not supported.\n");
2649 offset += sizeof(DWORD);
2652 if (pendata->PenDataFlags & PenDataCompoundLine)
2654 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2655 FIXME("PenDataCompoundLine is not supported.\n");
2656 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2659 if (pendata->PenDataFlags & PenDataCustomStartCap)
2661 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2662 if ((status = metafile_deserialize_custom_line_cap((BYTE *)startcap, data_size, &custom_line_cap)) != Ok)
2663 goto penfailed;
2664 status = GdipSetPenCustomStartCap(pen, custom_line_cap);
2665 GdipDeleteCustomLineCap(custom_line_cap);
2666 if (status != Ok)
2667 goto penfailed;
2668 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2671 if (pendata->PenDataFlags & PenDataCustomEndCap)
2673 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2674 if ((status = metafile_deserialize_custom_line_cap((BYTE *)endcap, data_size, &custom_line_cap)) != Ok)
2675 goto penfailed;
2676 status = GdipSetPenCustomEndCap(pen, custom_line_cap);
2677 GdipDeleteCustomLineCap(custom_line_cap);
2678 if (status != Ok)
2679 goto penfailed;
2680 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2683 object = pen;
2684 break;
2686 penfailed:
2687 GdipDeletePen(pen);
2688 return status;
2690 case ObjectTypePath:
2691 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2692 break;
2693 case ObjectTypeRegion:
2694 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2695 break;
2696 case ObjectTypeImage:
2697 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2698 break;
2699 case ObjectTypeFont:
2701 EmfPlusFont *data = (EmfPlusFont *)record_data;
2702 GpFontFamily *family;
2703 WCHAR *familyname;
2705 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2706 return InvalidParameter;
2707 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2709 if (data_size < data->Length * sizeof(WCHAR))
2710 return InvalidParameter;
2712 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2713 return OutOfMemory;
2715 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2716 familyname[data->Length] = 0;
2718 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2719 GdipFree(familyname);
2721 /* If a font family cannot be created from family name, native
2722 falls back to a sans serif font. */
2723 if (status != Ok)
2724 status = GdipGetGenericFontFamilySansSerif(&family);
2725 if (status != Ok)
2726 return status;
2728 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2729 GdipDeleteFontFamily(family);
2730 break;
2732 case ObjectTypeImageAttributes:
2734 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2735 GpImageAttributes *attributes = NULL;
2737 if (data_size != sizeof(*data))
2738 return InvalidParameter;
2740 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2741 return status;
2743 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2744 !!data->ObjectClamp);
2745 if (status == Ok)
2746 object = attributes;
2747 else
2748 GdipDisposeImageAttributes(attributes);
2749 break;
2751 default:
2752 FIXME("not implemented for object type %d.\n", type);
2753 return NotImplemented;
2756 if (status == Ok)
2757 metafile_set_object_table_entry(metafile, id, type, object);
2759 return status;
2762 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2764 GpMatrix world_to_device;
2766 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2768 GdipTransformRegion(region, &world_to_device);
2769 GdipCombineRegionRegion(metafile->clip, region, mode);
2771 return METAFILE_PlaybackUpdateClip(metafile);
2774 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2775 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2777 GpStatus stat;
2778 GpMetafile *real_metafile = (GpMetafile*)metafile;
2780 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2782 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2783 return InvalidParameter;
2785 if (recordType >= 1 && recordType <= 0x7a)
2787 /* regular EMF record */
2788 if (metafile->playback_dc)
2790 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2792 if (record)
2794 record->iType = recordType;
2795 record->nSize = dataSize + 8;
2796 memcpy(record->dParm, data, dataSize);
2798 if (record->iType == EMR_BITBLT || record->iType == EMR_STRETCHBLT)
2799 SetStretchBltMode(metafile->playback_dc, STRETCH_HALFTONE);
2801 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2802 record, metafile->handle_count) == 0)
2803 ERR("PlayEnhMetaFileRecord failed\n");
2805 heap_free(record);
2807 else
2808 return OutOfMemory;
2811 else
2813 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2815 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2817 switch(recordType)
2819 case EmfPlusRecordTypeHeader:
2820 case EmfPlusRecordTypeEndOfFile:
2821 break;
2822 case EmfPlusRecordTypeGetDC:
2823 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2824 break;
2825 case EmfPlusRecordTypeClear:
2827 EmfPlusClear *record = (EmfPlusClear*)header;
2829 if (dataSize != sizeof(record->Color))
2830 return InvalidParameter;
2832 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2834 case EmfPlusRecordTypeFillRects:
2836 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2837 GpBrush *brush, *temp_brush=NULL;
2838 GpRectF *rects, *temp_rects=NULL;
2840 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2841 return InvalidParameter;
2843 if (flags & 0x4000)
2845 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2846 return InvalidParameter;
2848 else
2850 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2851 return InvalidParameter;
2854 if (flags & 0x8000)
2856 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2857 brush = temp_brush;
2859 else
2861 if (record->BrushID >= EmfPlusObjectTableSize ||
2862 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2863 return InvalidParameter;
2865 brush = real_metafile->objtable[record->BrushID].u.brush;
2866 stat = Ok;
2869 if (stat == Ok)
2871 if (flags & 0x4000)
2873 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2874 int i;
2876 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2877 if (rects)
2879 for (i=0; i<record->Count; i++)
2881 rects[i].X = int_rects[i].X;
2882 rects[i].Y = int_rects[i].Y;
2883 rects[i].Width = int_rects[i].Width;
2884 rects[i].Height = int_rects[i].Height;
2887 else
2888 stat = OutOfMemory;
2890 else
2891 rects = (GpRectF*)(record+1);
2894 if (stat == Ok)
2896 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2899 GdipDeleteBrush(temp_brush);
2900 heap_free(temp_rects);
2902 return stat;
2904 case EmfPlusRecordTypeSetClipRect:
2906 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2907 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2908 GpRegion *region;
2910 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2911 return InvalidParameter;
2913 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2915 if (stat == Ok)
2917 stat = metafile_set_clip_region(real_metafile, region, mode);
2918 GdipDeleteRegion(region);
2921 return stat;
2923 case EmfPlusRecordTypeSetClipRegion:
2925 CombineMode mode = (flags >> 8) & 0xf;
2926 BYTE regionid = flags & 0xff;
2927 GpRegion *region;
2929 if (dataSize != 0)
2930 return InvalidParameter;
2932 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2933 return InvalidParameter;
2935 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2936 if (stat == Ok)
2938 stat = metafile_set_clip_region(real_metafile, region, mode);
2939 GdipDeleteRegion(region);
2942 return stat;
2944 case EmfPlusRecordTypeSetClipPath:
2946 CombineMode mode = (flags >> 8) & 0xf;
2947 BYTE pathid = flags & 0xff;
2948 GpRegion *region;
2950 if (dataSize != 0)
2951 return InvalidParameter;
2953 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2954 return InvalidParameter;
2956 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2957 if (stat == Ok)
2959 stat = metafile_set_clip_region(real_metafile, region, mode);
2960 GdipDeleteRegion(region);
2963 return stat;
2965 case EmfPlusRecordTypeSetPageTransform:
2967 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2968 GpUnit unit = (GpUnit)flags;
2970 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2971 return InvalidParameter;
2973 real_metafile->page_unit = unit;
2974 real_metafile->page_scale = record->PageScale;
2976 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2978 case EmfPlusRecordTypeSetWorldTransform:
2980 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2982 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2983 return InvalidParameter;
2985 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2987 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2989 case EmfPlusRecordTypeScaleWorldTransform:
2991 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2992 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2994 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2995 return InvalidParameter;
2997 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2999 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3001 case EmfPlusRecordTypeMultiplyWorldTransform:
3003 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
3004 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
3005 GpMatrix matrix;
3007 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
3008 return InvalidParameter;
3010 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
3012 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
3014 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3016 case EmfPlusRecordTypeRotateWorldTransform:
3018 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
3019 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
3021 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
3022 return InvalidParameter;
3024 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
3026 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3028 case EmfPlusRecordTypeTranslateWorldTransform:
3030 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
3031 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
3033 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
3034 return InvalidParameter;
3036 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
3038 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3040 case EmfPlusRecordTypeResetWorldTransform:
3042 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
3044 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3046 case EmfPlusRecordTypeBeginContainer:
3048 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
3049 container* cont;
3050 GpUnit unit;
3051 REAL scale_x, scale_y;
3052 GpRectF scaled_srcrect;
3053 GpMatrix transform;
3055 cont = heap_alloc_zero(sizeof(*cont));
3056 if (!cont)
3057 return OutOfMemory;
3059 stat = GdipCloneRegion(metafile->clip, &cont->clip);
3060 if (stat != Ok)
3062 heap_free(cont);
3063 return stat;
3066 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
3068 if (stat != Ok)
3070 GdipDeleteRegion(cont->clip);
3071 heap_free(cont);
3072 return stat;
3075 cont->id = record->StackIndex;
3076 cont->type = BEGIN_CONTAINER;
3077 cont->world_transform = *metafile->world_transform;
3078 cont->page_unit = metafile->page_unit;
3079 cont->page_scale = metafile->page_scale;
3080 list_add_head(&real_metafile->containers, &cont->entry);
3082 unit = record->Header.Flags & 0xff;
3084 scale_x = units_to_pixels(1.0, unit, metafile->image.xres, metafile->printer_display);
3085 scale_y = units_to_pixels(1.0, unit, metafile->image.yres, metafile->printer_display);
3087 scaled_srcrect.X = scale_x * record->SrcRect.X;
3088 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
3089 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
3090 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
3092 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
3093 transform.matrix[1] = 0.0;
3094 transform.matrix[2] = 0.0;
3095 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
3096 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
3097 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
3099 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
3101 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3103 case EmfPlusRecordTypeBeginContainerNoParams:
3104 case EmfPlusRecordTypeSave:
3106 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
3107 container* cont;
3109 cont = heap_alloc_zero(sizeof(*cont));
3110 if (!cont)
3111 return OutOfMemory;
3113 stat = GdipCloneRegion(metafile->clip, &cont->clip);
3114 if (stat != Ok)
3116 heap_free(cont);
3117 return stat;
3120 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
3121 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
3122 else
3123 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
3125 if (stat != Ok)
3127 GdipDeleteRegion(cont->clip);
3128 heap_free(cont);
3129 return stat;
3132 cont->id = record->StackIndex;
3133 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
3134 cont->type = BEGIN_CONTAINER;
3135 else
3136 cont->type = SAVE_GRAPHICS;
3137 cont->world_transform = *metafile->world_transform;
3138 cont->page_unit = metafile->page_unit;
3139 cont->page_scale = metafile->page_scale;
3140 list_add_head(&real_metafile->containers, &cont->entry);
3142 break;
3144 case EmfPlusRecordTypeEndContainer:
3145 case EmfPlusRecordTypeRestore:
3147 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
3148 container* cont;
3149 enum container_type type;
3150 BOOL found=FALSE;
3152 if (recordType == EmfPlusRecordTypeEndContainer)
3153 type = BEGIN_CONTAINER;
3154 else
3155 type = SAVE_GRAPHICS;
3157 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
3159 if (cont->id == record->StackIndex && cont->type == type)
3161 found = TRUE;
3162 break;
3166 if (found)
3168 container* cont2;
3170 /* pop any newer items on the stack */
3171 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
3173 list_remove(&cont2->entry);
3174 GdipDeleteRegion(cont2->clip);
3175 heap_free(cont2);
3178 if (type == BEGIN_CONTAINER)
3179 GdipEndContainer(real_metafile->playback_graphics, cont->state);
3180 else
3181 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
3183 *real_metafile->world_transform = cont->world_transform;
3184 real_metafile->page_unit = cont->page_unit;
3185 real_metafile->page_scale = cont->page_scale;
3186 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
3188 list_remove(&cont->entry);
3189 GdipDeleteRegion(cont->clip);
3190 heap_free(cont);
3193 break;
3195 case EmfPlusRecordTypeSetPixelOffsetMode:
3197 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
3199 case EmfPlusRecordTypeSetCompositingQuality:
3201 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
3203 case EmfPlusRecordTypeSetInterpolationMode:
3205 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
3207 case EmfPlusRecordTypeSetTextRenderingHint:
3209 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
3211 case EmfPlusRecordTypeSetAntiAliasMode:
3213 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
3215 case EmfPlusRecordTypeSetCompositingMode:
3217 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
3219 case EmfPlusRecordTypeObject:
3221 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
3223 case EmfPlusRecordTypeDrawImage:
3225 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
3226 BYTE image = flags & 0xff;
3227 GpPointF points[3];
3229 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3230 return InvalidParameter;
3232 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
3233 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3234 return InvalidParameter;
3236 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3237 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3238 return InvalidParameter;
3240 if (flags & 0x4000) /* C */
3242 points[0].X = draw->RectData.rect.X;
3243 points[0].Y = draw->RectData.rect.Y;
3244 points[1].X = points[0].X + draw->RectData.rect.Width;
3245 points[1].Y = points[0].Y;
3246 points[2].X = points[1].X;
3247 points[2].Y = points[1].Y + draw->RectData.rect.Height;
3249 else
3251 points[0].X = draw->RectData.rectF.X;
3252 points[0].Y = draw->RectData.rectF.Y;
3253 points[1].X = points[0].X + draw->RectData.rectF.Width;
3254 points[1].Y = points[0].Y;
3255 points[2].X = points[1].X;
3256 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
3259 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3260 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3261 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3263 case EmfPlusRecordTypeDrawImagePoints:
3265 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
3266 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
3267 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
3268 BYTE image = flags & 0xff;
3269 GpPointF points[3];
3270 unsigned int i;
3271 UINT size;
3273 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3274 return InvalidParameter;
3276 if (dataSize <= fixed_part_size)
3277 return InvalidParameter;
3278 dataSize -= fixed_part_size;
3280 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3281 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3282 return InvalidParameter;
3284 if (draw->count != 3)
3285 return InvalidParameter;
3287 if ((flags >> 13) & 1) /* E */
3288 FIXME("image effects are not supported.\n");
3290 if ((flags >> 11) & 1) /* P */
3291 size = sizeof(EmfPlusPointR7) * draw->count;
3292 else if ((flags >> 14) & 1) /* C */
3293 size = sizeof(EmfPlusPoint) * draw->count;
3294 else
3295 size = sizeof(EmfPlusPointF) * draw->count;
3297 if (dataSize != size)
3298 return InvalidParameter;
3300 if ((flags >> 11) & 1) /* P */
3302 points[0].X = draw->PointData.pointsR[0].X;
3303 points[0].Y = draw->PointData.pointsR[0].Y;
3304 for (i = 1; i < 3; i++)
3306 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3307 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3310 else if ((flags >> 14) & 1) /* C */
3312 for (i = 0; i < 3; i++)
3314 points[i].X = draw->PointData.points[i].X;
3315 points[i].Y = draw->PointData.points[i].Y;
3318 else
3319 memcpy(points, draw->PointData.pointsF, sizeof(points));
3321 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3322 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3323 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3325 case EmfPlusRecordTypeFillPath:
3327 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3328 GpSolidFill *solidfill = NULL;
3329 BYTE path = flags & 0xff;
3330 GpBrush *brush;
3332 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3333 return InvalidParameter;
3335 if (dataSize != sizeof(fill->data.BrushId))
3336 return InvalidParameter;
3338 if (flags & 0x8000)
3340 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3341 if (stat != Ok)
3342 return stat;
3343 brush = (GpBrush *)solidfill;
3345 else
3347 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3348 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3349 return InvalidParameter;
3351 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3354 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3355 GdipDeleteBrush((GpBrush *)solidfill);
3356 return stat;
3358 case EmfPlusRecordTypeFillClosedCurve:
3360 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3361 sizeof(EmfPlusRecordHeader);
3362 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3363 GpSolidFill *solidfill = NULL;
3364 GpFillMode mode;
3365 GpBrush *brush;
3366 UINT size, i;
3368 if (dataSize <= fixed_part_size)
3369 return InvalidParameter;
3371 if (fill->Count == 0)
3372 return InvalidParameter;
3374 if (flags & 0x800) /* P */
3375 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3376 else if (flags & 0x4000) /* C */
3377 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3378 else
3379 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3381 if (dataSize != size)
3382 return InvalidParameter;
3384 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3386 if (flags & 0x8000) /* S */
3388 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3389 if (stat != Ok)
3390 return stat;
3391 brush = (GpBrush *)solidfill;
3393 else
3395 if (fill->BrushId >= EmfPlusObjectTableSize ||
3396 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3397 return InvalidParameter;
3399 brush = real_metafile->objtable[fill->BrushId].u.brush;
3402 if (flags & (0x800 | 0x4000))
3404 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3405 if (points)
3407 if (flags & 0x800) /* P */
3409 for (i = 1; i < fill->Count; i++)
3411 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3412 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3415 else
3417 for (i = 0; i < fill->Count; i++)
3419 points[i].X = fill->PointData.points[i].X;
3420 points[i].Y = fill->PointData.points[i].Y;
3424 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3425 points, fill->Count, fill->Tension, mode);
3426 GdipFree(points);
3428 else
3429 stat = OutOfMemory;
3431 else
3432 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3433 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3435 GdipDeleteBrush((GpBrush *)solidfill);
3436 return stat;
3438 case EmfPlusRecordTypeFillEllipse:
3440 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3441 GpSolidFill *solidfill = NULL;
3442 GpBrush *brush;
3444 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3445 return InvalidParameter;
3446 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3448 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3449 return InvalidParameter;
3451 if (flags & 0x8000)
3453 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3454 if (stat != Ok)
3455 return stat;
3456 brush = (GpBrush *)solidfill;
3458 else
3460 if (fill->BrushId >= EmfPlusObjectTableSize ||
3461 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3462 return InvalidParameter;
3464 brush = real_metafile->objtable[fill->BrushId].u.brush;
3467 if (flags & 0x4000)
3468 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3469 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3470 else
3471 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3472 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3474 GdipDeleteBrush((GpBrush *)solidfill);
3475 return stat;
3477 case EmfPlusRecordTypeFillPie:
3479 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3480 GpSolidFill *solidfill = NULL;
3481 GpBrush *brush;
3483 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3484 return InvalidParameter;
3485 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3487 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3488 return InvalidParameter;
3490 if (flags & 0x8000) /* S */
3492 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3493 if (stat != Ok)
3494 return stat;
3495 brush = (GpBrush *)solidfill;
3497 else
3499 if (fill->BrushId >= EmfPlusObjectTableSize ||
3500 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3501 return InvalidParameter;
3503 brush = real_metafile->objtable[fill->BrushId].u.brush;
3506 if (flags & 0x4000) /* C */
3507 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3508 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3509 fill->StartAngle, fill->SweepAngle);
3510 else
3511 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3512 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3513 fill->StartAngle, fill->SweepAngle);
3515 GdipDeleteBrush((GpBrush *)solidfill);
3516 return stat;
3518 case EmfPlusRecordTypeDrawPath:
3520 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3521 BYTE path = flags & 0xff;
3523 if (dataSize != sizeof(draw->PenId))
3524 return InvalidParameter;
3526 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3527 return InvalidParameter;
3529 if (real_metafile->objtable[path].type != ObjectTypePath ||
3530 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3531 return InvalidParameter;
3533 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3534 real_metafile->objtable[path].u.path);
3536 case EmfPlusRecordTypeDrawArc:
3538 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3539 BYTE pen = flags & 0xff;
3541 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3542 return InvalidParameter;
3544 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3545 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3546 return InvalidParameter;
3548 if (flags & 0x4000) /* C */
3549 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3550 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3551 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3552 else
3553 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3554 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3555 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3557 case EmfPlusRecordTypeDrawEllipse:
3559 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3560 BYTE pen = flags & 0xff;
3562 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3563 return InvalidParameter;
3565 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3566 return InvalidParameter;
3568 if (flags & 0x4000) /* C */
3569 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3570 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3571 draw->RectData.rect.Height);
3572 else
3573 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3574 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3575 draw->RectData.rectF.Height);
3577 case EmfPlusRecordTypeDrawPie:
3579 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3580 BYTE pen = flags & 0xff;
3582 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3583 return InvalidParameter;
3585 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3586 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3587 return InvalidParameter;
3589 if (flags & 0x4000) /* C */
3590 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3591 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3592 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3593 else
3594 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3595 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3596 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3598 case EmfPlusRecordTypeDrawRects:
3600 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3601 BYTE pen = flags & 0xff;
3602 GpRectF *rects = NULL;
3604 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3605 return InvalidParameter;
3607 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3608 return InvalidParameter;
3609 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3611 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3612 return InvalidParameter;
3614 if (flags & 0x4000)
3616 DWORD i;
3618 rects = GdipAlloc(draw->Count * sizeof(*rects));
3619 if (!rects)
3620 return OutOfMemory;
3622 for (i = 0; i < draw->Count; i++)
3624 rects[i].X = draw->RectData.rect[i].X;
3625 rects[i].Y = draw->RectData.rect[i].Y;
3626 rects[i].Width = draw->RectData.rect[i].Width;
3627 rects[i].Height = draw->RectData.rect[i].Height;
3631 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3632 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3633 GdipFree(rects);
3634 return stat;
3636 case EmfPlusRecordTypeDrawDriverString:
3638 GpBrush *brush;
3639 DWORD expected_size;
3640 UINT16 *text;
3641 PointF *positions;
3642 GpSolidFill *solidfill = NULL;
3643 void* alignedmem = NULL;
3644 GpMatrix *matrix = NULL;
3645 BYTE font = flags & 0xff;
3646 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3648 if (font >= EmfPlusObjectTableSize ||
3649 real_metafile->objtable[font].type != ObjectTypeFont)
3650 return InvalidParameter;
3652 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3653 sizeof(EmfPlusRecordHeader);
3654 if (dataSize < expected_size || draw->GlyphCount <= 0)
3655 return InvalidParameter;
3657 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3658 if (draw->MatrixPresent)
3659 expected_size += sizeof(*matrix);
3661 /* Pad expected size to DWORD alignment. */
3662 expected_size = (expected_size + 3) & ~3;
3664 if (dataSize != expected_size)
3665 return InvalidParameter;
3667 if (flags & 0x8000)
3669 stat = GdipCreateSolidFill(draw->brush.Color, &solidfill);
3671 if (stat != Ok)
3672 return InvalidParameter;
3674 brush = (GpBrush*)solidfill;
3676 else
3678 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3679 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3680 return InvalidParameter;
3682 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3685 text = (UINT16*)&draw->VariableData[0];
3687 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3688 aligned rather than 4-byte aligned, which may lead to access
3689 issues. Handle this case by making our own copy of positions. */
3690 if (draw->GlyphCount % 2)
3692 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3694 if (draw->MatrixPresent)
3695 alloc_size += sizeof(*matrix);
3697 positions = alignedmem = heap_alloc(alloc_size);
3698 if (!positions)
3700 GdipDeleteBrush((GpBrush*)solidfill);
3701 return OutOfMemory;
3704 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3706 else
3707 positions = (PointF*)&text[draw->GlyphCount];
3709 if (draw->MatrixPresent)
3710 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3712 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3713 real_metafile->objtable[font].u.font, brush, positions,
3714 draw->DriverStringOptionsFlags, matrix);
3716 GdipDeleteBrush((GpBrush*)solidfill);
3717 heap_free(alignedmem);
3719 return stat;
3721 case EmfPlusRecordTypeFillRegion:
3723 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3724 GpSolidFill *solidfill = NULL;
3725 GpBrush *brush;
3726 BYTE region = flags & 0xff;
3728 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3729 return InvalidParameter;
3731 if (region >= EmfPlusObjectTableSize ||
3732 real_metafile->objtable[region].type != ObjectTypeRegion)
3733 return InvalidParameter;
3735 if (flags & 0x8000)
3737 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3738 if (stat != Ok)
3739 return stat;
3740 brush = (GpBrush*)solidfill;
3742 else
3744 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3745 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3746 return InvalidParameter;
3748 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3751 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3752 real_metafile->objtable[region].u.region);
3753 GdipDeleteBrush((GpBrush*)solidfill);
3755 return stat;
3757 default:
3758 FIXME("Not implemented for record type %x\n", recordType);
3759 return NotImplemented;
3763 return Ok;
3766 struct enum_metafile_data
3768 EnumerateMetafileProc callback;
3769 void *callback_data;
3770 GpMetafile *metafile;
3773 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3774 int nObj, LPARAM lpData)
3776 BOOL ret;
3777 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3778 const BYTE* pStr;
3780 data->metafile->handle_table = lpHTable;
3781 data->metafile->handle_count = nObj;
3783 /* First check for an EMF+ record. */
3784 if (lpEMFR->iType == EMR_GDICOMMENT)
3786 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3788 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3790 int offset = 4;
3792 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3794 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3796 if (record->DataSize)
3797 pStr = (const BYTE*)(record+1);
3798 else
3799 pStr = NULL;
3801 ret = data->callback(record->Type, record->Flags, record->DataSize,
3802 pStr, data->callback_data);
3804 if (!ret)
3805 return 0;
3807 offset += record->Size;
3810 return 1;
3814 if (lpEMFR->nSize != 8)
3815 pStr = (const BYTE*)lpEMFR->dParm;
3816 else
3817 pStr = NULL;
3819 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3820 pStr, data->callback_data);
3823 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3824 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3825 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3826 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3828 struct enum_metafile_data data;
3829 GpStatus stat;
3830 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3831 GraphicsContainer state;
3832 GpPath *dst_path;
3833 RECT dst_bounds;
3835 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3836 destPoints, count, srcRect, srcUnit, callback, callbackData,
3837 imageAttributes);
3839 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3840 return InvalidParameter;
3842 if (!metafile->hemf)
3843 return InvalidParameter;
3845 if (metafile->playback_graphics)
3846 return ObjectBusy;
3848 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3849 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3850 debugstr_pointf(&destPoints[2]));
3852 data.callback = callback;
3853 data.callback_data = callbackData;
3854 data.metafile = real_metafile;
3856 real_metafile->playback_graphics = graphics;
3857 real_metafile->playback_dc = NULL;
3858 real_metafile->src_rect = *srcRect;
3860 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3861 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3863 if (stat == Ok)
3864 stat = GdipBeginContainer2(graphics, &state);
3866 if (stat == Ok)
3868 stat = GdipSetPageScale(graphics, 1.0);
3870 if (stat == Ok)
3871 stat = GdipSetPageUnit(graphics, UnitPixel);
3873 if (stat == Ok)
3874 stat = GdipResetWorldTransform(graphics);
3876 if (stat == Ok)
3877 stat = GdipCreateRegion(&real_metafile->base_clip);
3879 if (stat == Ok)
3880 stat = GdipGetClip(graphics, real_metafile->base_clip);
3882 if (stat == Ok)
3883 stat = GdipCreateRegion(&real_metafile->clip);
3885 if (stat == Ok)
3886 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3888 if (stat == Ok)
3890 GpPointF clip_points[4];
3892 clip_points[0] = real_metafile->playback_points[0];
3893 clip_points[1] = real_metafile->playback_points[1];
3894 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3895 - real_metafile->playback_points[0].X;
3896 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3897 - real_metafile->playback_points[0].Y;
3898 clip_points[3] = real_metafile->playback_points[2];
3900 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3902 if (stat == Ok)
3903 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3905 GdipDeletePath(dst_path);
3908 if (stat == Ok)
3909 stat = GdipCreateMatrix(&real_metafile->world_transform);
3911 if (stat == Ok)
3913 real_metafile->page_unit = UnitDisplay;
3914 real_metafile->page_scale = 1.0;
3915 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3918 if (stat == Ok)
3920 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3923 if (stat == Ok)
3925 stat = METAFILE_PlaybackGetDC(real_metafile);
3927 dst_bounds.left = real_metafile->playback_points[0].X;
3928 dst_bounds.right = real_metafile->playback_points[1].X;
3929 dst_bounds.top = real_metafile->playback_points[0].Y;
3930 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3933 if (stat == Ok)
3934 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3935 &data, &dst_bounds);
3937 METAFILE_PlaybackReleaseDC(real_metafile);
3939 GdipDeleteMatrix(real_metafile->world_transform);
3940 real_metafile->world_transform = NULL;
3942 GdipDeleteRegion(real_metafile->base_clip);
3943 real_metafile->base_clip = NULL;
3945 GdipDeleteRegion(real_metafile->clip);
3946 real_metafile->clip = NULL;
3948 while (list_head(&real_metafile->containers))
3950 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3951 list_remove(&cont->entry);
3952 GdipDeleteRegion(cont->clip);
3953 heap_free(cont);
3956 GdipEndContainer(graphics, state);
3959 real_metafile->playback_graphics = NULL;
3961 return stat;
3964 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRect( GpGraphics *graphics,
3965 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3966 GDIPCONST GpRectF *src, Unit srcUnit, EnumerateMetafileProc callback,
3967 VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3969 GpPointF points[3];
3971 if (!graphics || !metafile || !dest) return InvalidParameter;
3973 points[0].X = points[2].X = dest->X;
3974 points[0].Y = points[1].Y = dest->Y;
3975 points[1].X = dest->X + dest->Width;
3976 points[2].Y = dest->Y + dest->Height;
3978 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3979 src, srcUnit, callback, cb_data, attrs);
3981 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRectI( GpGraphics * graphics,
3982 GDIPCONST GpMetafile *metafile, GDIPCONST Rect *destRect,
3983 GDIPCONST Rect *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3984 VOID *cb_data, GDIPCONST GpImageAttributes *attrs )
3986 GpRectF destRectF, srcRectF;
3988 destRectF.X = destRect->X;
3989 destRectF.Y = destRect->Y;
3990 destRectF.Width = destRect->Width;
3991 destRectF.Height = destRect->Height;
3993 srcRectF.X = srcRect->X;
3994 srcRectF.Y = srcRect->Y;
3995 srcRectF.Width = srcRect->Width;
3996 srcRectF.Height = srcRect->Height;
3998 return GdipEnumerateMetafileSrcRectDestRect( graphics, metafile, &destRectF, &srcRectF, srcUnit, callback, cb_data, attrs);
4001 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
4002 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
4003 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4005 GpPointF points[3];
4007 if (!graphics || !metafile || !dest) return InvalidParameter;
4009 points[0].X = points[2].X = dest->X;
4010 points[0].Y = points[1].Y = dest->Y;
4011 points[1].X = dest->X + dest->Width;
4012 points[2].Y = dest->Y + dest->Height;
4014 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
4015 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
4018 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
4019 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
4020 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4022 GpRectF destf;
4024 if (!graphics || !metafile || !dest) return InvalidParameter;
4026 destf.X = dest->X;
4027 destf.Y = dest->Y;
4028 destf.Width = dest->Width;
4029 destf.Height = dest->Height;
4031 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
4034 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
4035 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
4036 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4038 GpRectF destf;
4040 if (!graphics || !metafile || !dest) return InvalidParameter;
4042 destf.X = dest->X;
4043 destf.Y = dest->Y;
4044 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit,
4045 metafile->image.xres, metafile->printer_display);
4046 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit,
4047 metafile->image.yres, metafile->printer_display);
4049 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
4052 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
4053 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
4054 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
4056 GpPointF ptf;
4058 if (!graphics || !metafile || !dest) return InvalidParameter;
4060 ptf.X = dest->X;
4061 ptf.Y = dest->Y;
4063 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
4066 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
4067 MetafileHeader * header)
4069 GpStatus status;
4071 TRACE("(%p, %p)\n", metafile, header);
4073 if(!metafile || !header)
4074 return InvalidParameter;
4076 if (metafile->hemf)
4078 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
4079 if (status != Ok) return status;
4081 else
4083 memset(header, 0, sizeof(*header));
4084 header->Version = VERSION_MAGIC2;
4087 header->Type = metafile->metafile_type;
4088 header->DpiX = metafile->image.xres;
4089 header->DpiY = metafile->image.yres;
4090 header->Width = gdip_round(metafile->bounds.Width);
4091 header->Height = gdip_round(metafile->bounds.Height);
4093 return Ok;
4096 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
4097 int nObj, LPARAM lpData)
4099 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
4101 if (lpEMFR->iType == EMR_GDICOMMENT)
4103 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
4105 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
4107 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
4109 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
4110 header->Type == EmfPlusRecordTypeHeader)
4112 memcpy(dst_header, header, sizeof(*dst_header));
4116 else if (lpEMFR->iType == EMR_HEADER)
4117 return TRUE;
4119 return FALSE;
4122 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
4123 MetafileHeader *header)
4125 ENHMETAHEADER3 emfheader;
4126 EmfPlusHeader emfplusheader;
4127 MetafileType metafile_type;
4129 TRACE("(%p,%p)\n", hemf, header);
4131 if(!hemf || !header)
4132 return InvalidParameter;
4134 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
4135 return GenericError;
4137 emfplusheader.Header.Type = 0;
4139 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
4141 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
4143 if ((emfplusheader.Header.Flags & 1) == 1)
4144 metafile_type = MetafileTypeEmfPlusDual;
4145 else
4146 metafile_type = MetafileTypeEmfPlusOnly;
4148 else
4149 metafile_type = MetafileTypeEmf;
4151 header->Type = metafile_type;
4152 header->Size = emfheader.nBytes;
4153 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
4154 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
4155 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
4156 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
4157 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
4158 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
4159 header->u.EmfHeader = emfheader;
4161 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
4163 header->Version = emfplusheader.Version;
4164 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
4165 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
4166 header->LogicalDpiX = emfplusheader.LogicalDpiX;
4167 header->LogicalDpiY = emfplusheader.LogicalDpiY;
4169 else
4171 header->Version = emfheader.nVersion;
4172 header->EmfPlusFlags = 0;
4173 header->EmfPlusHeaderSize = 0;
4174 header->LogicalDpiX = 0;
4175 header->LogicalDpiY = 0;
4178 return Ok;
4181 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
4182 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
4184 GpStatus status;
4185 GpMetafile *metafile;
4187 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
4189 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
4190 if (status == Ok)
4192 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4193 GdipDisposeImage(&metafile->image);
4195 return status;
4198 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
4199 MetafileHeader *header)
4201 GpStatus status;
4202 GpMetafile *metafile;
4204 TRACE("(%s,%p)\n", debugstr_w(filename), header);
4206 if (!filename || !header)
4207 return InvalidParameter;
4209 status = GdipCreateMetafileFromFile(filename, &metafile);
4210 if (status == Ok)
4212 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4213 GdipDisposeImage(&metafile->image);
4215 return status;
4218 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
4219 MetafileHeader *header)
4221 GpStatus status;
4222 GpMetafile *metafile;
4224 TRACE("(%p,%p)\n", stream, header);
4226 if (!stream || !header)
4227 return InvalidParameter;
4229 status = GdipCreateMetafileFromStream(stream, &metafile);
4230 if (status == Ok)
4232 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4233 GdipDisposeImage(&metafile->image);
4235 return status;
4238 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
4239 GpMetafile **metafile)
4241 GpStatus stat;
4242 MetafileHeader header;
4244 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
4246 if(!hemf || !metafile)
4247 return InvalidParameter;
4249 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
4250 if (stat != Ok)
4251 return stat;
4253 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4254 if (!*metafile)
4255 return OutOfMemory;
4257 (*metafile)->image.type = ImageTypeMetafile;
4258 (*metafile)->image.format = ImageFormatEMF;
4259 (*metafile)->image.frame_count = 1;
4260 (*metafile)->image.xres = header.DpiX;
4261 (*metafile)->image.yres = header.DpiY;
4262 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
4263 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
4264 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
4265 / 2540.0 * header.DpiX;
4266 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
4267 / 2540.0 * header.DpiY;
4268 (*metafile)->unit = UnitPixel;
4269 (*metafile)->metafile_type = header.Type;
4270 (*metafile)->hemf = hemf;
4271 (*metafile)->preserve_hemf = !delete;
4272 /* If the 31th bit of EmfPlusFlags was set, metafile was recorded with a DC for a video display.
4273 * If clear, metafile was recorded with a DC for a printer */
4274 (*metafile)->printer_display = !(header.EmfPlusFlags & (1u << 31));
4275 (*metafile)->logical_dpix = header.LogicalDpiX;
4276 (*metafile)->logical_dpiy = header.LogicalDpiY;
4277 list_init(&(*metafile)->containers);
4279 TRACE("<-- %p\n", *metafile);
4281 return Ok;
4284 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
4285 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4287 UINT read;
4288 BYTE *copy;
4289 HENHMETAFILE hemf;
4290 GpStatus retval = Ok;
4292 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
4294 if(!hwmf || !metafile)
4295 return InvalidParameter;
4297 *metafile = NULL;
4298 read = GetMetaFileBitsEx(hwmf, 0, NULL);
4299 if(!read)
4300 return GenericError;
4301 copy = heap_alloc_zero(read);
4302 GetMetaFileBitsEx(hwmf, read, copy);
4304 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
4305 heap_free(copy);
4307 /* FIXME: We should store and use hwmf instead of converting to hemf */
4308 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4310 if (retval == Ok)
4312 if (placeable)
4314 (*metafile)->image.xres = (REAL)placeable->Inch;
4315 (*metafile)->image.yres = (REAL)placeable->Inch;
4316 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
4317 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
4318 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
4319 placeable->BoundingBox.Left);
4320 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
4321 placeable->BoundingBox.Top);
4322 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
4324 else
4325 (*metafile)->metafile_type = MetafileTypeWmf;
4326 (*metafile)->image.format = ImageFormatWMF;
4328 if (delete) DeleteMetaFile(hwmf);
4330 else
4331 DeleteEnhMetaFile(hemf);
4332 return retval;
4335 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4336 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4338 HMETAFILE hmf;
4339 HENHMETAFILE emf;
4341 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4343 hmf = GetMetaFileW(file);
4344 if(hmf)
4345 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4347 emf = GetEnhMetaFileW(file);
4348 if(emf)
4349 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4351 return GenericError;
4354 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4355 GpMetafile **metafile)
4357 GpStatus status;
4358 IStream *stream;
4360 TRACE("(%p, %p)\n", file, metafile);
4362 if (!file || !metafile) return InvalidParameter;
4364 *metafile = NULL;
4366 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4367 if (status == Ok)
4369 status = GdipCreateMetafileFromStream(stream, metafile);
4370 IStream_Release(stream);
4372 return status;
4375 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4376 GpMetafile **metafile)
4378 GpStatus stat;
4380 TRACE("%p %p\n", stream, metafile);
4382 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4383 if (stat != Ok) return stat;
4385 if ((*metafile)->image.type != ImageTypeMetafile)
4387 GdipDisposeImage(&(*metafile)->image);
4388 *metafile = NULL;
4389 return GenericError;
4392 return Ok;
4395 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4396 UINT *limitDpi)
4398 TRACE("(%p,%p)\n", metafile, limitDpi);
4400 if (!metafile || !limitDpi)
4401 return InvalidParameter;
4403 if (!metafile->record_dc)
4404 return WrongState;
4406 *limitDpi = metafile->limit_dpi;
4408 return Ok;
4411 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4412 UINT limitDpi)
4414 TRACE("(%p,%u)\n", metafile, limitDpi);
4416 if (limitDpi == 0)
4417 limitDpi = 96;
4419 if (!metafile || limitDpi < 10)
4420 return InvalidParameter;
4422 if (!metafile->record_dc)
4423 return WrongState;
4425 metafile->limit_dpi = limitDpi;
4427 return Ok;
4430 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4431 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4432 const WCHAR* description, GpMetafile** out_metafile)
4434 static int calls;
4436 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4437 debugstr_w(description), out_metafile);
4439 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4440 return InvalidParameter;
4442 if(succ)
4443 *succ = FALSE;
4444 *out_metafile = NULL;
4446 if(!(calls++))
4447 FIXME("not implemented\n");
4449 return NotImplemented;
4452 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4453 LPBYTE pData16, INT iMapMode, INT eFlags)
4455 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4456 return NotImplemented;
4459 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4460 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4461 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4462 GpMetafile **metafile)
4464 HDC record_dc;
4465 REAL dpix, dpiy;
4466 REAL framerect_factor_x, framerect_factor_y;
4467 RECT rc, *lprc;
4468 GpStatus stat;
4470 TRACE("%s %p %d %s %d %s %p\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4471 frameUnit, debugstr_w(desc), metafile);
4473 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
4474 return InvalidParameter;
4476 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
4477 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
4479 if (pFrameRect)
4481 switch (frameUnit)
4483 case MetafileFrameUnitPixel:
4484 framerect_factor_x = 2540.0 / dpix;
4485 framerect_factor_y = 2540.0 / dpiy;
4486 break;
4487 case MetafileFrameUnitPoint:
4488 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
4489 break;
4490 case MetafileFrameUnitInch:
4491 framerect_factor_x = framerect_factor_y = 2540.0;
4492 break;
4493 case MetafileFrameUnitDocument:
4494 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
4495 break;
4496 case MetafileFrameUnitMillimeter:
4497 framerect_factor_x = framerect_factor_y = 100.0;
4498 break;
4499 case MetafileFrameUnitGdi:
4500 framerect_factor_x = framerect_factor_y = 1.0;
4501 break;
4502 default:
4503 return InvalidParameter;
4506 rc.left = framerect_factor_x * pFrameRect->X;
4507 rc.top = framerect_factor_y * pFrameRect->Y;
4508 rc.right = rc.left + framerect_factor_x * pFrameRect->Width;
4509 rc.bottom = rc.top + framerect_factor_y * pFrameRect->Height;
4511 lprc = &rc;
4513 else
4514 lprc = NULL;
4516 record_dc = CreateEnhMetaFileW(hdc, fileName, lprc, desc);
4518 if (!record_dc)
4519 return GenericError;
4521 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4522 if(!*metafile)
4524 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4525 return OutOfMemory;
4528 (*metafile)->image.type = ImageTypeMetafile;
4529 (*metafile)->image.flags = ImageFlagsNone;
4530 (*metafile)->image.palette = NULL;
4531 (*metafile)->image.xres = dpix;
4532 (*metafile)->image.yres = dpiy;
4533 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
4534 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
4535 (*metafile)->unit = UnitPixel;
4536 (*metafile)->metafile_type = type;
4537 (*metafile)->record_dc = record_dc;
4538 (*metafile)->comment_data = NULL;
4539 (*metafile)->comment_data_size = 0;
4540 (*metafile)->comment_data_length = 0;
4541 (*metafile)->limit_dpi = 96;
4542 (*metafile)->hemf = NULL;
4543 (*metafile)->printer_display = (GetDeviceCaps(record_dc, TECHNOLOGY) == DT_RASPRINTER);
4544 (*metafile)->logical_dpix = (REAL)GetDeviceCaps(record_dc, LOGPIXELSX);
4545 (*metafile)->logical_dpiy = (REAL)GetDeviceCaps(record_dc, LOGPIXELSY);
4546 list_init(&(*metafile)->containers);
4548 if (!pFrameRect)
4550 (*metafile)->auto_frame = TRUE;
4551 (*metafile)->auto_frame_min.X = 0;
4552 (*metafile)->auto_frame_min.Y = 0;
4553 (*metafile)->auto_frame_max.X = -1;
4554 (*metafile)->auto_frame_max.Y = -1;
4557 stat = METAFILE_WriteHeader(*metafile, hdc);
4559 if (stat != Ok)
4561 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4562 heap_free(*metafile);
4563 *metafile = NULL;
4564 return OutOfMemory;
4567 return stat;
4570 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4571 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4572 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4574 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4575 frameUnit, debugstr_w(desc), metafile);
4577 return NotImplemented;
4580 /*****************************************************************************
4581 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4584 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4585 GpMetafile* metafile, BOOL* conversionSuccess,
4586 const WCHAR* filename, EmfType emfType,
4587 const WCHAR* description, GpMetafile** out_metafile)
4589 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4590 return NotImplemented;
4593 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4595 LARGE_INTEGER zero;
4596 STATSTG statstg;
4597 GpStatus stat;
4598 HRESULT hr;
4600 *size = 0;
4602 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4603 if (FAILED(hr)) return hresult_to_status(hr);
4605 stat = encode_image_png(image, *stream, NULL);
4606 if (stat != Ok)
4608 IStream_Release(*stream);
4609 return stat;
4612 hr = IStream_Stat(*stream, &statstg, 1);
4613 if (FAILED(hr))
4615 IStream_Release(*stream);
4616 return hresult_to_status(hr);
4618 *size = statstg.cbSize.u.LowPart;
4620 zero.QuadPart = 0;
4621 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4622 if (FAILED(hr))
4624 IStream_Release(*stream);
4625 return hresult_to_status(hr);
4628 return Ok;
4631 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4633 HRESULT hr;
4635 record->Width = 0;
4636 record->Height = 0;
4637 record->Stride = 0;
4638 record->PixelFormat = 0;
4639 record->Type = BitmapDataTypeCompressed;
4641 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4642 if (FAILED(hr)) return hresult_to_status(hr);
4643 return Ok;
4646 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4648 EmfPlusObject *object_record;
4649 GpStatus stat;
4650 DWORD size;
4652 *id = -1;
4654 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4655 return Ok;
4657 if (image->type == ImageTypeBitmap)
4659 IStream *stream;
4660 DWORD aligned_size;
4662 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4663 if (stat != Ok) return stat;
4664 aligned_size = (size + 3) & ~3;
4666 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4667 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4668 (void**)&object_record);
4669 if (stat != Ok)
4671 IStream_Release(stream);
4672 return stat;
4674 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4676 *id = METAFILE_AddObjectId(metafile);
4677 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4678 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4679 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4681 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4682 IStream_Release(stream);
4683 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4684 return stat;
4686 else if (image->type == ImageTypeMetafile)
4688 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4689 EmfPlusMetafile *metafile_record;
4691 if (!hemf) return InvalidParameter;
4693 size = GetEnhMetaFileBits(hemf, 0, NULL);
4694 if (!size) return GenericError;
4696 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4697 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4698 (void**)&object_record);
4699 if (stat != Ok) return stat;
4701 *id = METAFILE_AddObjectId(metafile);
4702 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4703 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4704 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4705 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4706 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4707 metafile_record->MetafileDataSize = size;
4708 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4710 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4711 return GenericError;
4713 return Ok;
4715 else
4717 FIXME("not supported image type (%d)\n", image->type);
4718 return NotImplemented;
4722 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4724 EmfPlusObject *object_record;
4725 EmfPlusImageAttributes *attrs_record;
4726 GpStatus stat;
4728 *id = -1;
4730 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4731 return Ok;
4733 if (!attrs)
4734 return Ok;
4736 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4737 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4738 (void**)&object_record);
4739 if (stat != Ok) return stat;
4741 *id = METAFILE_AddObjectId(metafile);
4742 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4743 attrs_record = &object_record->ObjectData.image_attributes;
4744 attrs_record->Version = VERSION_MAGIC2;
4745 attrs_record->Reserved1 = 0;
4746 attrs_record->WrapMode = attrs->wrap;
4747 attrs_record->ClampColor = attrs->outside_color;
4748 attrs_record->ObjectClamp = attrs->clamp;
4749 attrs_record->Reserved2 = 0;
4750 return Ok;
4753 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4754 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4755 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4756 DrawImageAbort callback, VOID *callbackData)
4758 EmfPlusDrawImagePoints *draw_image_record;
4759 DWORD image_id, attributes_id;
4760 GpStatus stat;
4762 if (count != 3) return InvalidParameter;
4764 if (metafile->metafile_type == MetafileTypeEmf)
4766 FIXME("MetafileTypeEmf metafiles not supported\n");
4767 return NotImplemented;
4769 else
4770 FIXME("semi-stub\n");
4772 if (!imageAttributes)
4774 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4776 else if (image->type == ImageTypeBitmap)
4778 INT width = ((GpBitmap*)image)->width;
4779 INT height = ((GpBitmap*)image)->height;
4780 GpGraphics *graphics;
4781 GpBitmap *bitmap;
4783 stat = GdipCreateBitmapFromScan0(width, height,
4784 0, PixelFormat32bppARGB, NULL, &bitmap);
4785 if (stat != Ok) return stat;
4787 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4788 if (stat != Ok)
4790 GdipDisposeImage((GpImage*)bitmap);
4791 return stat;
4794 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4795 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4796 GdipDeleteGraphics(graphics);
4797 if (stat != Ok)
4799 GdipDisposeImage((GpImage*)bitmap);
4800 return stat;
4803 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4804 GdipDisposeImage((GpImage*)bitmap);
4806 else
4808 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4809 return NotImplemented;
4811 if (stat != Ok) return stat;
4813 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4814 if (stat != Ok) return stat;
4816 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawImagePoints,
4817 sizeof(EmfPlusDrawImagePoints), (void **)&draw_image_record);
4818 if (stat != Ok) return stat;
4820 draw_image_record->Header.Flags = image_id;
4821 draw_image_record->ImageAttributesID = attributes_id;
4822 draw_image_record->SrcUnit = UnitPixel;
4823 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres, metafile->printer_display);
4824 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres, metafile->printer_display);
4825 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres, metafile->printer_display);
4826 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres, metafile->printer_display);
4827 draw_image_record->count = 3;
4828 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4829 METAFILE_WriteRecords(metafile);
4830 return Ok;
4833 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4835 EmfPlusRecordHeader *record;
4836 GpStatus stat;
4838 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4839 return Ok;
4841 stat = METAFILE_AllocateRecord(metafile, prop, sizeof(*record), (void**)&record);
4842 if (stat != Ok) return stat;
4844 record->Flags = val;
4846 METAFILE_WriteRecords(metafile);
4847 return Ok;
4850 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4852 EmfPlusObject *object_record;
4853 GpStatus stat;
4854 DWORD size;
4856 *id = -1;
4857 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4858 return Ok;
4860 size = write_path_data(path, NULL);
4861 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4862 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4863 (void**)&object_record);
4864 if (stat != Ok) return stat;
4866 *id = METAFILE_AddObjectId(metafile);
4867 object_record->Header.Flags = *id | ObjectTypePath << 8;
4868 write_path_data(path, &object_record->ObjectData.path);
4869 return Ok;
4872 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4874 DWORD custom_start_cap_size = 0, custom_start_cap_data_size = 0, custom_start_cap_path_size = 0;
4875 DWORD custom_end_cap_size = 0, custom_end_cap_data_size = 0, custom_end_cap_path_size = 0;
4876 DWORD i, data_flags, pen_data_size, brush_size;
4877 EmfPlusObject *object_record;
4878 EmfPlusPenData *pen_data;
4879 GpStatus stat;
4880 BOOL result;
4882 *id = -1;
4883 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4884 return Ok;
4886 data_flags = 0;
4887 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4889 GdipIsMatrixIdentity(&pen->transform, &result);
4890 if (!result)
4892 data_flags |= PenDataTransform;
4893 pen_data_size += sizeof(EmfPlusTransformMatrix);
4895 if (pen->startcap != LineCapFlat)
4897 data_flags |= PenDataStartCap;
4898 pen_data_size += sizeof(DWORD);
4900 if (pen->endcap != LineCapFlat)
4902 data_flags |= PenDataEndCap;
4903 pen_data_size += sizeof(DWORD);
4905 if (pen->join != LineJoinMiter)
4907 data_flags |= PenDataJoin;
4908 pen_data_size += sizeof(DWORD);
4910 if (pen->miterlimit != 10.0)
4912 data_flags |= PenDataMiterLimit;
4913 pen_data_size += sizeof(REAL);
4915 if (pen->style != GP_DEFAULT_PENSTYLE)
4917 data_flags |= PenDataLineStyle;
4918 pen_data_size += sizeof(DWORD);
4920 if (pen->dashcap != DashCapFlat)
4922 data_flags |= PenDataDashedLineCap;
4923 pen_data_size += sizeof(DWORD);
4925 data_flags |= PenDataDashedLineOffset;
4926 pen_data_size += sizeof(REAL);
4927 if (pen->numdashes)
4929 data_flags |= PenDataDashedLine;
4930 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4932 if (pen->align != PenAlignmentCenter)
4934 data_flags |= PenDataNonCenter;
4935 pen_data_size += sizeof(DWORD);
4937 /* TODO: Add support for PenDataCompoundLine */
4938 if (pen->customstart)
4940 data_flags |= PenDataCustomStartCap;
4941 METAFILE_PrepareCustomLineCapData(pen->customstart, &custom_start_cap_size,
4942 &custom_start_cap_data_size, &custom_start_cap_path_size);
4943 pen_data_size += custom_start_cap_size;
4945 if (pen->customend)
4947 data_flags |= PenDataCustomEndCap;
4948 METAFILE_PrepareCustomLineCapData(pen->customend, &custom_end_cap_size,
4949 &custom_end_cap_data_size, &custom_end_cap_path_size);
4950 pen_data_size += custom_end_cap_size;
4953 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4954 if (stat != Ok) return stat;
4956 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4957 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4958 (void**)&object_record);
4959 if (stat != Ok) return stat;
4961 *id = METAFILE_AddObjectId(metafile);
4962 object_record->Header.Flags = *id | ObjectTypePen << 8;
4963 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4964 object_record->ObjectData.pen.Type = 0;
4966 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4967 pen_data->PenDataFlags = data_flags;
4968 pen_data->PenUnit = pen->unit;
4969 pen_data->PenWidth = pen->width;
4971 i = 0;
4972 if (data_flags & PenDataTransform)
4974 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4975 memcpy(m, &pen->transform, sizeof(*m));
4976 i += sizeof(EmfPlusTransformMatrix);
4978 if (data_flags & PenDataStartCap)
4980 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4981 i += sizeof(DWORD);
4983 if (data_flags & PenDataEndCap)
4985 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4986 i += sizeof(DWORD);
4988 if (data_flags & PenDataJoin)
4990 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4991 i += sizeof(DWORD);
4993 if (data_flags & PenDataMiterLimit)
4995 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4996 i += sizeof(REAL);
4998 if (data_flags & PenDataLineStyle)
5000 switch (pen->style & PS_STYLE_MASK)
5002 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
5003 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
5004 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
5005 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
5006 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
5007 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
5009 i += sizeof(DWORD);
5011 if (data_flags & PenDataDashedLineCap)
5013 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
5014 i += sizeof(DWORD);
5016 if (data_flags & PenDataDashedLineOffset)
5018 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
5019 i += sizeof(REAL);
5021 if (data_flags & PenDataDashedLine)
5023 int j;
5025 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
5026 i += sizeof(DWORD);
5028 for (j=0; j<pen->numdashes; j++)
5030 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
5031 i += sizeof(REAL);
5034 if (data_flags & PenDataNonCenter)
5036 *(REAL*)(pen_data->OptionalData + i) = pen->align;
5037 i += sizeof(DWORD);
5039 if (data_flags & PenDataCustomStartCap)
5041 METAFILE_FillCustomLineCapData(pen->customstart, pen_data->OptionalData + i,
5042 pen->miterlimit, custom_start_cap_data_size,
5043 custom_start_cap_path_size);
5044 i += custom_start_cap_size;
5046 if (data_flags & PenDataCustomEndCap)
5048 METAFILE_FillCustomLineCapData(pen->customend, pen_data->OptionalData + i,
5049 pen->miterlimit, custom_end_cap_data_size,
5050 custom_end_cap_path_size);
5051 i += custom_end_cap_size;
5054 METAFILE_FillBrushData(pen->brush,
5055 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
5056 return Ok;
5059 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
5061 EmfPlusDrawPath *draw_path_record;
5062 DWORD path_id;
5063 DWORD pen_id;
5064 GpStatus stat;
5066 if (metafile->metafile_type == MetafileTypeEmf)
5068 FIXME("stub!\n");
5069 return NotImplemented;
5072 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5073 if (stat != Ok) return stat;
5075 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5076 if (stat != Ok) return stat;
5078 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawPath,
5079 sizeof(EmfPlusDrawPath), (void **)&draw_path_record);
5080 if (stat != Ok) return stat;
5081 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
5082 draw_path_record->Header.Flags = path_id;
5083 draw_path_record->PenId = pen_id;
5085 METAFILE_WriteRecords(metafile);
5086 return Ok;
5089 GpStatus METAFILE_DrawEllipse(GpMetafile *metafile, GpPen *pen, GpRectF *rect)
5091 EmfPlusDrawEllipse *record;
5092 GpStatus stat;
5093 DWORD pen_id;
5095 if (metafile->metafile_type == MetafileTypeEmf)
5097 FIXME("stub!\n");
5098 return NotImplemented;
5101 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5102 if (stat != Ok) return stat;
5104 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawEllipse,
5105 sizeof(EmfPlusDrawEllipse), (void **)&record);
5106 if (stat != Ok) return stat;
5107 record->Header.Type = EmfPlusRecordTypeDrawEllipse;
5108 record->Header.Flags = pen_id;
5109 if (is_integer_rect(rect))
5111 record->Header.Flags |= 0x4000;
5112 record->RectData.rect.X = (SHORT)rect->X;
5113 record->RectData.rect.Y = (SHORT)rect->Y;
5114 record->RectData.rect.Width = (SHORT)rect->Width;
5115 record->RectData.rect.Height = (SHORT)rect->Height;
5117 else
5118 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5120 METAFILE_WriteRecords(metafile);
5121 return Ok;
5124 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
5126 EmfPlusFillPath *fill_path_record;
5127 DWORD brush_id = -1, path_id;
5128 BOOL inline_color;
5129 GpStatus stat;
5131 if (metafile->metafile_type == MetafileTypeEmf)
5133 FIXME("stub!\n");
5134 return NotImplemented;
5137 inline_color = brush->bt == BrushTypeSolidColor;
5138 if (!inline_color)
5140 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5141 if (stat != Ok) return stat;
5144 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5145 if (stat != Ok) return stat;
5147 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPath,
5148 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
5149 if (stat != Ok) return stat;
5150 if (inline_color)
5152 fill_path_record->Header.Flags = 0x8000 | path_id;
5153 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
5155 else
5157 fill_path_record->Header.Flags = path_id;
5158 fill_path_record->data.BrushId = brush_id;
5161 METAFILE_WriteRecords(metafile);
5162 return Ok;
5165 GpStatus METAFILE_FillEllipse(GpMetafile *metafile, GpBrush *brush, GpRectF *rect)
5167 EmfPlusFillEllipse *record;
5168 DWORD brush_id = -1;
5169 BOOL inline_color;
5170 GpStatus stat;
5172 if (metafile->metafile_type == MetafileTypeEmf)
5174 FIXME("stub!\n");
5175 return NotImplemented;
5178 inline_color = brush->bt == BrushTypeSolidColor;
5179 if (!inline_color)
5181 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5182 if (stat != Ok) return stat;
5185 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillEllipse, sizeof(EmfPlusFillEllipse), (void **)&record);
5186 if (stat != Ok) return stat;
5187 if (inline_color)
5189 record->Header.Flags = 0x8000;
5190 record->BrushId = ((GpSolidFill *)brush)->color;
5192 else
5193 record->BrushId = brush_id;
5195 if (is_integer_rect(rect))
5197 record->Header.Flags |= 0x4000;
5198 record->RectData.rect.X = (SHORT)rect->X;
5199 record->RectData.rect.Y = (SHORT)rect->Y;
5200 record->RectData.rect.Width = (SHORT)rect->Width;
5201 record->RectData.rect.Height = (SHORT)rect->Height;
5203 else
5204 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5206 METAFILE_WriteRecords(metafile);
5207 return Ok;
5210 GpStatus METAFILE_FillPie(GpMetafile *metafile, GpBrush *brush, const GpRectF *rect,
5211 REAL startAngle, REAL sweepAngle)
5213 BOOL is_int_rect, inline_color;
5214 EmfPlusFillPie *record;
5215 DWORD brush_id = -1;
5216 GpStatus stat;
5218 if (metafile->metafile_type == MetafileTypeEmf)
5220 FIXME("stub!\n");
5221 return NotImplemented;
5224 inline_color = brush->bt == BrushTypeSolidColor;
5225 if (!inline_color)
5227 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5228 if (stat != Ok) return stat;
5231 is_int_rect = is_integer_rect(rect);
5233 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPie,
5234 FIELD_OFFSET(EmfPlusFillPie, RectData) + is_int_rect ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF),
5235 (void **)&record);
5236 if (stat != Ok) return stat;
5237 if (inline_color)
5239 record->Header.Flags = 0x8000;
5240 record->BrushId = ((GpSolidFill *)brush)->color;
5242 else
5243 record->BrushId = brush_id;
5245 record->StartAngle = startAngle;
5246 record->SweepAngle = sweepAngle;
5248 if (is_int_rect)
5250 record->Header.Flags |= 0x4000;
5251 record->RectData.rect.X = (SHORT)rect->X;
5252 record->RectData.rect.Y = (SHORT)rect->Y;
5253 record->RectData.rect.Width = (SHORT)rect->Width;
5254 record->RectData.rect.Height = (SHORT)rect->Height;
5256 else
5257 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5259 METAFILE_WriteRecords(metafile);
5260 return Ok;
5263 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
5265 EmfPlusObject *object_record;
5266 EmfPlusFont *font_record;
5267 GpStatus stat;
5268 INT fn_len;
5269 INT style;
5271 *id = -1;
5273 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5274 metafile->metafile_type != MetafileTypeEmfPlusDual)
5275 return Ok;
5277 /* The following cast is ugly, but GdipGetFontStyle does treat
5278 its first parameter as const. */
5279 stat = GdipGetFontStyle((GpFont*)font, &style);
5280 if (stat != Ok)
5281 return stat;
5283 fn_len = lstrlenW(font->family->FamilyName);
5284 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
5285 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
5286 (void**)&object_record);
5287 if (stat != Ok)
5288 return stat;
5290 *id = METAFILE_AddObjectId(metafile);
5292 object_record->Header.Flags = *id | ObjectTypeFont << 8;
5294 font_record = &object_record->ObjectData.font;
5295 font_record->Version = VERSION_MAGIC2;
5296 font_record->EmSize = font->emSize;
5297 font_record->SizeUnit = font->unit;
5298 font_record->FontStyleFlags = style;
5299 font_record->Reserved = 0;
5300 font_record->Length = fn_len;
5302 memcpy(font_record->FamilyName, font->family->FamilyName,
5303 fn_len * sizeof(*font->family->FamilyName));
5305 return Ok;
5308 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
5309 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
5310 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
5312 DWORD brush_id;
5313 DWORD font_id;
5314 DWORD alloc_size;
5315 GpStatus stat;
5316 EmfPlusDrawDriverString *draw_string_record;
5317 BYTE *cursor;
5318 BOOL inline_color;
5319 BOOL include_matrix = FALSE;
5321 if (length <= 0)
5322 return InvalidParameter;
5324 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5325 metafile->metafile_type != MetafileTypeEmfPlusDual)
5327 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5328 return NotImplemented;
5331 stat = METAFILE_AddFontObject(metafile, font, &font_id);
5332 if (stat != Ok)
5333 return stat;
5335 inline_color = (brush->bt == BrushTypeSolidColor);
5336 if (!inline_color)
5338 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5339 if (stat != Ok)
5340 return stat;
5343 if (matrix)
5345 BOOL identity;
5347 stat = GdipIsMatrixIdentity(matrix, &identity);
5348 if (stat != Ok)
5349 return stat;
5351 include_matrix = !identity;
5354 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
5355 length * (sizeof(*text) + sizeof(*positions));
5357 if (include_matrix)
5358 alloc_size += sizeof(*matrix);
5360 /* Pad record to DWORD alignment. */
5361 alloc_size = (alloc_size + 3) & ~3;
5363 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawDriverString, alloc_size, (void**)&draw_string_record);
5364 if (stat != Ok)
5365 return stat;
5367 draw_string_record->Header.Flags = font_id;
5368 draw_string_record->DriverStringOptionsFlags = flags;
5369 draw_string_record->MatrixPresent = include_matrix;
5370 draw_string_record->GlyphCount = length;
5372 if (inline_color)
5374 draw_string_record->Header.Flags |= 0x8000;
5375 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
5377 else
5378 draw_string_record->brush.BrushId = brush_id;
5380 cursor = &draw_string_record->VariableData[0];
5382 memcpy(cursor, text, length * sizeof(*text));
5383 cursor += length * sizeof(*text);
5385 if (flags & DriverStringOptionsRealizedAdvance)
5387 static BOOL fixme_written = FALSE;
5389 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
5390 in the case of RealizedAdvance, each glyph position is computed
5391 and serialized.
5393 While native GDI+ is capable of playing back metafiles with this
5394 flag set, it is possible that some application might rely on
5395 metafiles produced from GDI+ not setting this flag. Ideally we
5396 would also compute the position of each glyph here, serialize those
5397 values, and not set DriverStringOptionsRealizedAdvance. */
5398 if (!fixme_written)
5400 fixme_written = TRUE;
5401 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
5404 *((PointF*)cursor) = *positions;
5406 else
5407 memcpy(cursor, positions, length * sizeof(*positions));
5409 if (include_matrix)
5411 cursor += length * sizeof(*positions);
5412 memcpy(cursor, matrix, sizeof(*matrix));
5415 METAFILE_WriteRecords(metafile);
5417 return Ok;
5420 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
5422 GpStatus stat;
5423 DWORD brush_id;
5424 DWORD region_id;
5425 EmfPlusFillRegion *fill_region_record;
5426 BOOL inline_color;
5428 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5429 metafile->metafile_type != MetafileTypeEmfPlusDual)
5431 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5432 return NotImplemented;
5435 inline_color = (brush->bt == BrushTypeSolidColor);
5436 if (!inline_color)
5438 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5439 if (stat != Ok)
5440 return stat;
5443 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
5444 if (stat != Ok)
5445 return stat;
5447 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRegion, sizeof(EmfPlusFillRegion),
5448 (void**)&fill_region_record);
5449 if (stat != Ok)
5450 return stat;
5452 fill_region_record->Header.Flags = region_id;
5454 if (inline_color)
5456 fill_region_record->Header.Flags |= 0x8000;
5457 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
5459 else
5460 fill_region_record->data.BrushId = brush_id;
5462 METAFILE_WriteRecords(metafile);
5464 return Ok;
5467 GpStatus METAFILE_DrawRectangles(GpMetafile *metafile, GpPen *pen, const GpRectF *rects, INT count)
5469 EmfPlusDrawRects *record;
5470 GpStatus stat;
5471 BOOL integer_rects = TRUE;
5472 DWORD pen_id;
5473 int i;
5475 if (metafile->metafile_type == MetafileTypeEmf)
5477 FIXME("stub!\n");
5478 return NotImplemented;
5481 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5482 if (stat != Ok) return stat;
5484 for (i = 0; i < count; i++)
5486 if (!is_integer_rect(&rects[i]))
5488 integer_rects = FALSE;
5489 break;
5493 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawRects, FIELD_OFFSET(EmfPlusDrawRects, RectData) +
5494 count * (integer_rects ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF)),
5495 (void **)&record);
5496 if (stat != Ok)
5497 return stat;
5499 record->Header.Flags = pen_id;
5500 if (integer_rects)
5501 record->Header.Flags |= 0x4000;
5502 record->Count = count;
5504 if (integer_rects)
5506 for (i = 0; i < count; i++)
5508 record->RectData.rect[i].X = (SHORT)rects[i].X;
5509 record->RectData.rect[i].Y = (SHORT)rects[i].Y;
5510 record->RectData.rect[i].Width = (SHORT)rects[i].Width;
5511 record->RectData.rect[i].Height = (SHORT)rects[i].Height;
5514 else
5515 memcpy(record->RectData.rectF, rects, sizeof(*rects) * count);
5517 METAFILE_WriteRecords(metafile);
5519 return Ok;
5522 GpStatus METAFILE_DrawArc(GpMetafile *metafile, GpPen *pen, const GpRectF *rect, REAL startAngle, REAL sweepAngle)
5524 EmfPlusDrawArc *record;
5525 GpStatus stat;
5526 BOOL integer_rect;
5527 DWORD pen_id;
5529 if (metafile->metafile_type == MetafileTypeEmf)
5531 FIXME("stub!\n");
5532 return NotImplemented;
5535 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5536 if (stat != Ok) return stat;
5538 integer_rect = is_integer_rect(rect);
5540 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawArc, FIELD_OFFSET(EmfPlusDrawArc, RectData) +
5541 integer_rect ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF),
5542 (void **)&record);
5543 if (stat != Ok)
5544 return stat;
5546 record->Header.Flags = pen_id;
5547 if (integer_rect)
5548 record->Header.Flags |= 0x4000;
5549 record->StartAngle = startAngle;
5550 record->SweepAngle = sweepAngle;
5552 if (integer_rect)
5554 record->RectData.rect.X = (SHORT)rect->X;
5555 record->RectData.rect.Y = (SHORT)rect->Y;
5556 record->RectData.rect.Width = (SHORT)rect->Width;
5557 record->RectData.rect.Height = (SHORT)rect->Height;
5559 else
5560 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5562 METAFILE_WriteRecords(metafile);
5564 return Ok;
5567 GpStatus METAFILE_OffsetClip(GpMetafile *metafile, REAL dx, REAL dy)
5569 EmfPlusOffsetClip *record;
5570 GpStatus stat;
5572 if (metafile->metafile_type == MetafileTypeEmf)
5574 FIXME("stub!\n");
5575 return NotImplemented;
5578 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeOffsetClip,
5579 sizeof(*record), (void **)&record);
5580 if (stat != Ok)
5581 return stat;
5583 record->dx = dx;
5584 record->dy = dy;
5586 METAFILE_WriteRecords(metafile);
5588 return Ok;
5591 GpStatus METAFILE_ResetClip(GpMetafile *metafile)
5593 EmfPlusRecordHeader *record;
5594 GpStatus stat;
5596 if (metafile->metafile_type == MetafileTypeEmf)
5598 FIXME("stub!\n");
5599 return NotImplemented;
5602 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetClip,
5603 sizeof(*record), (void **)&record);
5604 if (stat != Ok)
5605 return stat;
5607 METAFILE_WriteRecords(metafile);
5609 return Ok;
5612 GpStatus METAFILE_SetClipPath(GpMetafile *metafile, GpPath *path, CombineMode mode)
5614 EmfPlusRecordHeader *record;
5615 DWORD path_id;
5616 GpStatus stat;
5618 if (metafile->metafile_type == MetafileTypeEmf)
5620 FIXME("stub!\n");
5621 return NotImplemented;
5624 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5625 if (stat != Ok) return stat;
5627 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipPath,
5628 sizeof(*record), (void **)&record);
5629 if (stat != Ok)
5630 return stat;
5632 record->Flags = ((mode & 0xf) << 8) | path_id;
5634 METAFILE_WriteRecords(metafile);
5636 return Ok;
5639 GpStatus METAFILE_SetRenderingOrigin(GpMetafile *metafile, INT x, INT y)
5641 EmfPlusSetRenderingOrigin *record;
5642 GpStatus stat;
5644 if (metafile->metafile_type == MetafileTypeEmf)
5646 FIXME("stub!\n");
5647 return NotImplemented;
5650 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetRenderingOrigin,
5651 sizeof(*record), (void **)&record);
5652 if (stat != Ok)
5653 return stat;
5655 record->x = x;
5656 record->y = y;
5658 METAFILE_WriteRecords(metafile);
5660 return Ok;