dwrite/layout: Constify some internal helpers arguments.
[wine.git] / dlls / gdiplus / metafile.c
blob90ef39e34d66a11b6c90eca0518aac7df206f9fc
1 /*
2 * Copyright (C) 2011 Vincent Povirk for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
20 #include <math.h>
21 #include <assert.h>
23 #define NONAMELESSUNION
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
35 #include "winreg.h"
36 #include "shlwapi.h"
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
45 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
47 typedef ARGB EmfPlusARGB;
49 typedef struct EmfPlusRecordHeader
51 WORD Type;
52 WORD Flags;
53 DWORD Size;
54 DWORD DataSize;
55 } EmfPlusRecordHeader;
57 typedef struct EmfPlusHeader
59 EmfPlusRecordHeader Header;
60 DWORD Version;
61 DWORD EmfPlusFlags;
62 DWORD LogicalDpiX;
63 DWORD LogicalDpiY;
64 } EmfPlusHeader;
66 typedef struct EmfPlusClear
68 EmfPlusRecordHeader Header;
69 DWORD Color;
70 } EmfPlusClear;
72 typedef struct EmfPlusFillRects
74 EmfPlusRecordHeader Header;
75 DWORD BrushID;
76 DWORD Count;
77 } EmfPlusFillRects;
79 typedef struct EmfPlusSetClipRect
81 EmfPlusRecordHeader Header;
82 GpRectF ClipRect;
83 } EmfPlusSetClipRect;
85 typedef struct EmfPlusSetPageTransform
87 EmfPlusRecordHeader Header;
88 REAL PageScale;
89 } EmfPlusSetPageTransform;
91 typedef struct EmfPlusRect
93 SHORT X;
94 SHORT Y;
95 SHORT Width;
96 SHORT Height;
97 } EmfPlusRect;
99 typedef struct EmfPlusSetWorldTransform
101 EmfPlusRecordHeader Header;
102 REAL MatrixData[6];
103 } EmfPlusSetWorldTransform;
105 typedef struct EmfPlusScaleWorldTransform
107 EmfPlusRecordHeader Header;
108 REAL Sx;
109 REAL Sy;
110 } EmfPlusScaleWorldTransform;
112 typedef struct EmfPlusMultiplyWorldTransform
114 EmfPlusRecordHeader Header;
115 REAL MatrixData[6];
116 } EmfPlusMultiplyWorldTransform;
118 typedef struct EmfPlusRotateWorldTransform
120 EmfPlusRecordHeader Header;
121 REAL Angle;
122 } EmfPlusRotateWorldTransform;
124 typedef struct EmfPlusTranslateWorldTransform
126 EmfPlusRecordHeader Header;
127 REAL dx;
128 REAL dy;
129 } EmfPlusTranslateWorldTransform;
131 typedef struct EmfPlusBeginContainer
133 EmfPlusRecordHeader Header;
134 GpRectF DestRect;
135 GpRectF SrcRect;
136 DWORD StackIndex;
137 } EmfPlusBeginContainer;
139 typedef struct EmfPlusContainerRecord
141 EmfPlusRecordHeader Header;
142 DWORD StackIndex;
143 } EmfPlusContainerRecord;
145 enum container_type
147 BEGIN_CONTAINER,
148 SAVE_GRAPHICS
151 typedef struct container
153 struct list entry;
154 DWORD id;
155 enum container_type type;
156 GraphicsContainer state;
157 GpMatrix world_transform;
158 GpUnit page_unit;
159 REAL page_scale;
160 GpRegion *clip;
161 } container;
163 enum PenDataFlags
165 PenDataTransform = 0x0001,
166 PenDataStartCap = 0x0002,
167 PenDataEndCap = 0x0004,
168 PenDataJoin = 0x0008,
169 PenDataMiterLimit = 0x0010,
170 PenDataLineStyle = 0x0020,
171 PenDataDashedLineCap = 0x0040,
172 PenDataDashedLineOffset = 0x0080,
173 PenDataDashedLine = 0x0100,
174 PenDataNonCenter = 0x0200,
175 PenDataCompoundLine = 0x0400,
176 PenDataCustomStartCap = 0x0800,
177 PenDataCustomEndCap = 0x1000
180 typedef struct EmfPlusTransformMatrix
182 REAL TransformMatrix[6];
183 } EmfPlusTransformMatrix;
185 enum LineStyle
187 LineStyleSolid,
188 LineStyleDash,
189 LineStyleDot,
190 LineStyleDashDot,
191 LineStyleDashDotDot,
192 LineStyleCustom
195 typedef struct EmfPlusDashedLineData
197 DWORD DashedLineDataSize;
198 BYTE data[1];
199 } EmfPlusDashedLineData;
201 typedef struct EmfPlusCompoundLineData
203 DWORD CompoundLineDataSize;
204 BYTE data[1];
205 } EmfPlusCompoundLineData;
207 typedef struct EmfPlusCustomStartCapData
209 DWORD CustomStartCapSize;
210 BYTE data[1];
211 } EmfPlusCustomStartCapData;
213 typedef struct EmfPlusCustomEndCapData
215 DWORD CustomEndCapSize;
216 BYTE data[1];
217 } EmfPlusCustomEndCapData;
219 typedef struct EmfPlusPenData
221 DWORD PenDataFlags;
222 DWORD PenUnit;
223 REAL PenWidth;
224 BYTE OptionalData[1];
225 } EmfPlusPenData;
227 enum BrushDataFlags
229 BrushDataPath = 1 << 0,
230 BrushDataTransform = 1 << 1,
231 BrushDataPresetColors = 1 << 2,
232 BrushDataBlendFactorsH = 1 << 3,
233 BrushDataBlendFactorsV = 1 << 4,
234 BrushDataFocusScales = 1 << 6,
235 BrushDataIsGammaCorrected = 1 << 7,
236 BrushDataDoNotTransform = 1 << 8,
239 typedef struct EmfPlusSolidBrushData
241 EmfPlusARGB SolidColor;
242 } EmfPlusSolidBrushData;
244 typedef struct EmfPlusHatchBrushData
246 DWORD HatchStyle;
247 EmfPlusARGB ForeColor;
248 EmfPlusARGB BackColor;
249 } EmfPlusHatchBrushData;
251 typedef struct EmfPlusTextureBrushData
253 DWORD BrushDataFlags;
254 INT WrapMode;
255 BYTE OptionalData[1];
256 } EmfPlusTextureBrushData;
258 typedef struct EmfPlusRectF
260 float X;
261 float Y;
262 float Width;
263 float Height;
264 } EmfPlusRectF;
266 typedef struct EmfPlusLinearGradientBrushData
268 DWORD BrushDataFlags;
269 INT WrapMode;
270 EmfPlusRectF RectF;
271 EmfPlusARGB StartColor;
272 EmfPlusARGB EndColor;
273 DWORD Reserved1;
274 DWORD Reserved2;
275 BYTE OptionalData[1];
276 } EmfPlusLinearGradientBrushData;
278 typedef struct EmfPlusBrush
280 DWORD Version;
281 DWORD Type;
282 union {
283 EmfPlusSolidBrushData solid;
284 EmfPlusHatchBrushData hatch;
285 EmfPlusTextureBrushData texture;
286 EmfPlusLinearGradientBrushData lineargradient;
287 } BrushData;
288 } EmfPlusBrush;
290 typedef struct EmfPlusPen
292 DWORD Version;
293 DWORD Type;
294 /* EmfPlusPenData */
295 /* EmfPlusBrush */
296 BYTE data[1];
297 } EmfPlusPen;
299 typedef struct EmfPlusPath
301 DWORD Version;
302 DWORD PathPointCount;
303 DWORD PathPointFlags;
304 /* PathPoints[] */
305 /* PathPointTypes[] */
306 /* AlignmentPadding */
307 BYTE data[1];
308 } EmfPlusPath;
310 typedef struct EmfPlusRegionNodePath
312 DWORD RegionNodePathLength;
313 EmfPlusPath RegionNodePath;
314 } EmfPlusRegionNodePath;
316 typedef struct EmfPlusRegion
318 DWORD Version;
319 DWORD RegionNodeCount;
320 BYTE RegionNode[1];
321 } EmfPlusRegion;
323 typedef struct EmfPlusPalette
325 DWORD PaletteStyleFlags;
326 DWORD PaletteCount;
327 BYTE PaletteEntries[1];
328 } EmfPlusPalette;
330 typedef enum
332 BitmapDataTypePixel,
333 BitmapDataTypeCompressed,
334 } BitmapDataType;
336 typedef struct EmfPlusBitmap
338 DWORD Width;
339 DWORD Height;
340 DWORD Stride;
341 DWORD PixelFormat;
342 DWORD Type;
343 BYTE BitmapData[1];
344 } EmfPlusBitmap;
346 typedef struct EmfPlusMetafile
348 DWORD Type;
349 DWORD MetafileDataSize;
350 BYTE MetafileData[1];
351 } EmfPlusMetafile;
353 typedef enum ImageDataType
355 ImageDataTypeUnknown,
356 ImageDataTypeBitmap,
357 ImageDataTypeMetafile,
358 } ImageDataType;
360 typedef struct EmfPlusImage
362 DWORD Version;
363 ImageDataType Type;
364 union
366 EmfPlusBitmap bitmap;
367 EmfPlusMetafile metafile;
368 } ImageData;
369 } EmfPlusImage;
371 typedef struct EmfPlusImageAttributes
373 DWORD Version;
374 DWORD Reserved1;
375 DWORD WrapMode;
376 EmfPlusARGB ClampColor;
377 DWORD ObjectClamp;
378 DWORD Reserved2;
379 } EmfPlusImageAttributes;
381 typedef struct EmfPlusFont
383 DWORD Version;
384 float EmSize;
385 DWORD SizeUnit;
386 DWORD FontStyleFlags;
387 DWORD Reserved;
388 DWORD Length;
389 WCHAR FamilyName[1];
390 } EmfPlusFont;
392 typedef struct EmfPlusObject
394 EmfPlusRecordHeader Header;
395 union
397 EmfPlusBrush brush;
398 EmfPlusPen pen;
399 EmfPlusPath path;
400 EmfPlusRegion region;
401 EmfPlusImage image;
402 EmfPlusImageAttributes image_attributes;
403 EmfPlusFont font;
404 } ObjectData;
405 } EmfPlusObject;
407 typedef struct EmfPlusPointR7
409 BYTE X;
410 BYTE Y;
411 } EmfPlusPointR7;
413 typedef struct EmfPlusPoint
415 short X;
416 short Y;
417 } EmfPlusPoint;
419 typedef struct EmfPlusPointF
421 float X;
422 float Y;
423 } EmfPlusPointF;
425 typedef struct EmfPlusDrawImage
427 EmfPlusRecordHeader Header;
428 DWORD ImageAttributesID;
429 DWORD SrcUnit;
430 EmfPlusRectF SrcRect;
431 union
433 EmfPlusRect rect;
434 EmfPlusRectF rectF;
435 } RectData;
436 } EmfPlusDrawImage;
438 typedef struct EmfPlusDrawImagePoints
440 EmfPlusRecordHeader Header;
441 DWORD ImageAttributesID;
442 DWORD SrcUnit;
443 EmfPlusRectF SrcRect;
444 DWORD count;
445 union
447 EmfPlusPointR7 pointsR[3];
448 EmfPlusPoint points[3];
449 EmfPlusPointF pointsF[3];
450 } PointData;
451 } EmfPlusDrawImagePoints;
453 typedef struct EmfPlusDrawPath
455 EmfPlusRecordHeader Header;
456 DWORD PenId;
457 } EmfPlusDrawPath;
459 typedef struct EmfPlusDrawArc
461 EmfPlusRecordHeader Header;
462 float StartAngle;
463 float SweepAngle;
464 union
466 EmfPlusRect rect;
467 EmfPlusRectF rectF;
468 } RectData;
469 } EmfPlusDrawArc;
471 typedef struct EmfPlusDrawEllipse
473 EmfPlusRecordHeader Header;
474 union
476 EmfPlusRect rect;
477 EmfPlusRectF rectF;
478 } RectData;
479 } EmfPlusDrawEllipse;
481 typedef struct EmfPlusDrawPie
483 EmfPlusRecordHeader Header;
484 float StartAngle;
485 float SweepAngle;
486 union
488 EmfPlusRect rect;
489 EmfPlusRectF rectF;
490 } RectData;
491 } EmfPlusDrawPie;
493 typedef struct EmfPlusDrawRects
495 EmfPlusRecordHeader Header;
496 DWORD Count;
497 union
499 EmfPlusRect rect[1];
500 EmfPlusRectF rectF[1];
501 } RectData;
502 } EmfPlusDrawRects;
504 typedef struct EmfPlusFillPath
506 EmfPlusRecordHeader Header;
507 union
509 DWORD BrushId;
510 EmfPlusARGB Color;
511 } data;
512 } EmfPlusFillPath;
514 typedef struct EmfPlusFillClosedCurve
516 EmfPlusRecordHeader Header;
517 DWORD BrushId;
518 float Tension;
519 DWORD Count;
520 union
522 EmfPlusPointR7 pointsR[1];
523 EmfPlusPoint points[1];
524 EmfPlusPointF pointsF[1];
525 } PointData;
526 } EmfPlusFillClosedCurve;
528 typedef struct EmfPlusFillEllipse
530 EmfPlusRecordHeader Header;
531 DWORD BrushId;
532 union
534 EmfPlusRect rect;
535 EmfPlusRectF rectF;
536 } RectData;
537 } EmfPlusFillEllipse;
539 typedef struct EmfPlusFillPie
541 EmfPlusRecordHeader Header;
542 DWORD BrushId;
543 float StartAngle;
544 float SweepAngle;
545 union
547 EmfPlusRect rect;
548 EmfPlusRectF rectF;
549 } RectData;
550 } EmfPlusFillPie;
552 typedef struct EmfPlusDrawDriverString
554 EmfPlusRecordHeader Header;
555 union
557 DWORD BrushId;
558 ARGB Color;
559 } brush;
560 DWORD DriverStringOptionsFlags;
561 DWORD MatrixPresent;
562 DWORD GlyphCount;
563 BYTE VariableData[1];
564 } EmfPlusDrawDriverString;
566 typedef struct EmfPlusFillRegion
568 EmfPlusRecordHeader Header;
569 union
571 DWORD BrushId;
572 EmfPlusARGB Color;
573 } data;
574 } EmfPlusFillRegion;
576 typedef struct EmfPlusOffsetClip
578 EmfPlusRecordHeader Header;
579 float dx;
580 float dy;
581 } EmfPlusOffsetClip;
583 typedef struct EmfPlusSetRenderingOrigin
585 EmfPlusRecordHeader Header;
586 INT x;
587 INT y;
588 } EmfPlusSetRenderingOrigin;
590 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
592 struct emfplus_object *object = &metafile->objtable[id];
594 switch (object->type)
596 case ObjectTypeInvalid:
597 break;
598 case ObjectTypeBrush:
599 GdipDeleteBrush(object->u.brush);
600 break;
601 case ObjectTypePen:
602 GdipDeletePen(object->u.pen);
603 break;
604 case ObjectTypePath:
605 GdipDeletePath(object->u.path);
606 break;
607 case ObjectTypeRegion:
608 GdipDeleteRegion(object->u.region);
609 break;
610 case ObjectTypeImage:
611 GdipDisposeImage(object->u.image);
612 break;
613 case ObjectTypeFont:
614 GdipDeleteFont(object->u.font);
615 break;
616 case ObjectTypeImageAttributes:
617 GdipDisposeImageAttributes(object->u.image_attributes);
618 break;
619 default:
620 FIXME("not implemented for object type %u.\n", object->type);
621 return;
624 object->type = ObjectTypeInvalid;
625 object->u.object = NULL;
628 void METAFILE_Free(GpMetafile *metafile)
630 unsigned int i;
632 heap_free(metafile->comment_data);
633 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
634 if (!metafile->preserve_hemf)
635 DeleteEnhMetaFile(metafile->hemf);
636 if (metafile->record_graphics)
638 WARN("metafile closed while recording\n");
639 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
640 metafile->record_graphics->image = NULL;
641 metafile->record_graphics->busy = TRUE;
644 if (metafile->record_stream)
645 IStream_Release(metafile->record_stream);
647 for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
648 metafile_free_object_table_entry(metafile, i);
651 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
653 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
656 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, EmfPlusRecordType record_type,
657 DWORD size, void **result)
659 DWORD size_needed;
660 EmfPlusRecordHeader *record;
662 if (!metafile->comment_data_size)
664 DWORD data_size = max(256, size * 2 + 4);
665 metafile->comment_data = heap_alloc_zero(data_size);
667 if (!metafile->comment_data)
668 return OutOfMemory;
670 memcpy(metafile->comment_data, "EMF+", 4);
672 metafile->comment_data_size = data_size;
673 metafile->comment_data_length = 4;
676 size_needed = size + metafile->comment_data_length;
678 if (size_needed > metafile->comment_data_size)
680 DWORD data_size = size_needed * 2;
681 BYTE *new_data = heap_alloc_zero(data_size);
683 if (!new_data)
684 return OutOfMemory;
686 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
688 metafile->comment_data_size = data_size;
689 heap_free(metafile->comment_data);
690 metafile->comment_data = new_data;
693 *result = metafile->comment_data + metafile->comment_data_length;
694 metafile->comment_data_length += size;
696 record = (EmfPlusRecordHeader*)*result;
697 record->Type = record_type;
698 record->Flags = 0;
699 record->Size = size;
700 record->DataSize = size - sizeof(EmfPlusRecordHeader);
702 return Ok;
705 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
707 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
708 metafile->comment_data_length -= record->Size;
711 static void METAFILE_WriteRecords(GpMetafile *metafile)
713 if (metafile->comment_data_length > 4)
715 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
716 metafile->comment_data_length = 4;
720 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
722 GpStatus stat;
724 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
726 EmfPlusHeader *header;
728 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeHeader, sizeof(EmfPlusHeader), (void**)&header);
729 if (stat != Ok)
730 return stat;
732 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
733 header->Header.Flags = 1;
735 header->Version = VERSION_MAGIC2;
737 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
738 header->EmfPlusFlags = 1;
739 else
740 header->EmfPlusFlags = 0;
742 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
743 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
745 METAFILE_WriteRecords(metafile);
748 return Ok;
751 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
753 GpStatus stat;
755 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
757 EmfPlusRecordHeader *record;
759 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndOfFile, sizeof(EmfPlusRecordHeader), (void**)&record);
760 if (stat != Ok)
761 return stat;
763 METAFILE_WriteRecords(metafile);
766 return Ok;
769 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
770 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
773 TRACE("(%p %d %s %d %p %p)\n", hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
775 return GdipRecordMetafileFileName(NULL, hdc, type, frameRect, frameUnit, desc, metafile);
778 /*****************************************************************************
779 * GdipRecordMetafileI [GDIPLUS.@]
781 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
782 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
784 GpRectF frameRectF, *pFrameRectF;
786 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
788 if (frameRect)
790 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
791 pFrameRectF = &frameRectF;
793 else
794 pFrameRectF = NULL;
796 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
799 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
800 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
802 GpRectF frameRectF, *pFrameRectF;
804 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
806 if (frameRect)
808 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
809 pFrameRectF = &frameRectF;
811 else
812 pFrameRectF = NULL;
814 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
817 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
818 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
820 GpStatus stat;
822 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
824 if (!stream)
825 return InvalidParameter;
827 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
829 if (stat == Ok)
831 (*metafile)->record_stream = stream;
832 IStream_AddRef(stream);
835 return stat;
838 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
839 UINT num_points)
841 int i;
843 if (!metafile->auto_frame || !num_points)
844 return;
846 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
847 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
849 for (i=0; i<num_points; i++)
851 if (points[i].X < metafile->auto_frame_min.X)
852 metafile->auto_frame_min.X = points[i].X;
853 if (points[i].X > metafile->auto_frame_max.X)
854 metafile->auto_frame_max.X = points[i].X;
855 if (points[i].Y < metafile->auto_frame_min.Y)
856 metafile->auto_frame_min.Y = points[i].Y;
857 if (points[i].Y > metafile->auto_frame_max.Y)
858 metafile->auto_frame_max.Y = points[i].Y;
862 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
864 GpStatus stat;
866 if (!metafile->record_dc || metafile->record_graphics)
867 return InvalidParameter;
869 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
871 if (stat == Ok)
873 *result = metafile->record_graphics;
874 metafile->record_graphics->xres = metafile->logical_dpix;
875 metafile->record_graphics->yres = metafile->logical_dpiy;
876 metafile->record_graphics->printer_display = metafile->printer_display;
879 return stat;
882 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
884 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
886 EmfPlusRecordHeader *record;
887 GpStatus stat;
889 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeGetDC, sizeof(EmfPlusRecordHeader), (void**)&record);
890 if (stat != Ok)
891 return stat;
893 METAFILE_WriteRecords(metafile);
896 *hdc = metafile->record_dc;
898 return Ok;
901 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
903 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
905 EmfPlusClear *record;
906 GpStatus stat;
908 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeClear, sizeof(EmfPlusClear), (void**)&record);
909 if (stat != Ok)
910 return stat;
912 record->Color = color;
914 METAFILE_WriteRecords(metafile);
917 return Ok;
920 static BOOL is_integer_rect(const GpRectF *rect)
922 SHORT x, y, width, height;
923 x = rect->X;
924 y = rect->Y;
925 width = rect->Width;
926 height = rect->Height;
927 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
928 rect->Width != (REAL)width || rect->Height != (REAL)height)
929 return FALSE;
930 return TRUE;
933 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
935 switch (brush->bt)
937 case BrushTypeSolidColor:
938 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
939 break;
940 case BrushTypeHatchFill:
941 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
942 break;
943 case BrushTypeLinearGradient:
945 BOOL ignore_xform;
946 GpLineGradient *gradient = (GpLineGradient*)brush;
948 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
950 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
951 if (!ignore_xform)
952 *size += sizeof(gradient->transform);
954 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
955 *size += sizeof(DWORD) + gradient->pblendcount *
956 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
957 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
958 *size += sizeof(DWORD) + gradient->blendcount *
959 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
961 break;
963 default:
964 FIXME("unsupported brush type: %d\n", brush->bt);
965 return NotImplemented;
968 return Ok;
971 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
973 data->Version = VERSION_MAGIC2;
974 data->Type = brush->bt;
976 switch (brush->bt)
978 case BrushTypeSolidColor:
980 GpSolidFill *solid = (GpSolidFill *)brush;
981 data->BrushData.solid.SolidColor = solid->color;
982 break;
984 case BrushTypeHatchFill:
986 GpHatch *hatch = (GpHatch *)brush;
987 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
988 data->BrushData.hatch.ForeColor = hatch->forecol;
989 data->BrushData.hatch.BackColor = hatch->backcol;
990 break;
992 case BrushTypeLinearGradient:
994 BYTE *cursor;
995 BOOL ignore_xform;
996 GpLineGradient *gradient = (GpLineGradient*)brush;
998 data->BrushData.lineargradient.BrushDataFlags = 0;
999 data->BrushData.lineargradient.WrapMode = gradient->wrap;
1000 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
1001 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
1002 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
1003 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
1004 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1005 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1006 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1007 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1009 if (gradient->gamma)
1010 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1012 cursor = &data->BrushData.lineargradient.OptionalData[0];
1014 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1015 if (!ignore_xform)
1017 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1018 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1019 cursor += sizeof(gradient->transform);
1022 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1024 const DWORD count = gradient->pblendcount;
1026 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1028 memcpy(cursor, &count, sizeof(count));
1029 cursor += sizeof(count);
1031 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1032 cursor += count * sizeof(*gradient->pblendpos);
1034 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1036 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1038 const DWORD count = gradient->blendcount;
1040 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1042 memcpy(cursor, &count, sizeof(count));
1043 cursor += sizeof(count);
1045 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1046 cursor += count * sizeof(*gradient->blendpos);
1048 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1051 break;
1053 default:
1054 FIXME("unsupported brush type: %d\n", brush->bt);
1058 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1060 EmfPlusObject *object_record;
1061 GpStatus stat;
1062 DWORD size;
1064 *id = -1;
1065 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1066 return Ok;
1068 stat = METAFILE_PrepareBrushData(brush, &size);
1069 if (stat != Ok) return stat;
1071 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1072 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1073 if (stat != Ok) return stat;
1075 *id = METAFILE_AddObjectId(metafile);
1076 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1077 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1078 return Ok;
1081 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1082 GDIPCONST GpRectF* rects, INT count)
1084 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1086 EmfPlusFillRects *record;
1087 GpStatus stat;
1088 BOOL integer_rects = TRUE;
1089 int i;
1090 DWORD brushid;
1091 int flags = 0;
1093 if (brush->bt == BrushTypeSolidColor)
1095 flags |= 0x8000;
1096 brushid = ((GpSolidFill*)brush)->color;
1098 else
1100 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1101 if (stat != Ok)
1102 return stat;
1105 for (i=0; i<count; i++)
1107 if (!is_integer_rect(&rects[i]))
1109 integer_rects = FALSE;
1110 break;
1114 if (integer_rects)
1115 flags |= 0x4000;
1117 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRects,
1118 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1119 (void**)&record);
1120 if (stat != Ok)
1121 return stat;
1123 record->Header.Flags = flags;
1124 record->BrushID = brushid;
1125 record->Count = count;
1127 if (integer_rects)
1129 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1130 for (i=0; i<count; i++)
1132 record_rects[i].X = (SHORT)rects[i].X;
1133 record_rects[i].Y = (SHORT)rects[i].Y;
1134 record_rects[i].Width = (SHORT)rects[i].Width;
1135 record_rects[i].Height = (SHORT)rects[i].Height;
1138 else
1139 memcpy(record+1, rects, sizeof(GpRectF) * count);
1141 METAFILE_WriteRecords(metafile);
1144 if (metafile->auto_frame)
1146 GpPointF corners[4];
1147 int i;
1149 for (i=0; i<count; i++)
1151 corners[0].X = rects[i].X;
1152 corners[0].Y = rects[i].Y;
1153 corners[1].X = rects[i].X + rects[i].Width;
1154 corners[1].Y = rects[i].Y;
1155 corners[2].X = rects[i].X;
1156 corners[2].Y = rects[i].Y + rects[i].Height;
1157 corners[3].X = rects[i].X + rects[i].Width;
1158 corners[3].Y = rects[i].Y + rects[i].Height;
1160 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1161 CoordinateSpaceWorld, corners, 4);
1163 METAFILE_AdjustFrame(metafile, corners, 4);
1167 return Ok;
1170 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1172 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1174 EmfPlusSetClipRect *record;
1175 GpStatus stat;
1177 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRect,
1178 sizeof(EmfPlusSetClipRect), (void **)&record);
1179 if (stat != Ok)
1180 return stat;
1182 record->Header.Flags = (mode & 0xf) << 8;
1183 record->ClipRect.X = x;
1184 record->ClipRect.Y = y;
1185 record->ClipRect.Width = width;
1186 record->ClipRect.Height = height;
1188 METAFILE_WriteRecords(metafile);
1191 return Ok;
1194 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1196 EmfPlusObject *object_record;
1197 DWORD size;
1198 GpStatus stat;
1200 *id = -1;
1201 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1202 return Ok;
1204 size = write_region_data(region, NULL);
1205 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
1206 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1207 if (stat != Ok) return stat;
1209 *id = METAFILE_AddObjectId(metafile);
1210 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1211 write_region_data(region, &object_record->ObjectData.region);
1212 return Ok;
1215 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1217 EmfPlusRecordHeader *record;
1218 DWORD region_id;
1219 GpStatus stat;
1221 if (metafile->metafile_type == MetafileTypeEmf)
1223 FIXME("stub!\n");
1224 return NotImplemented;
1227 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1228 if (stat != Ok) return stat;
1230 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipRegion, sizeof(*record), (void**)&record);
1231 if (stat != Ok) return stat;
1233 record->Flags = region_id | mode << 8;
1235 METAFILE_WriteRecords(metafile);
1236 return Ok;
1239 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1241 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1243 EmfPlusSetPageTransform *record;
1244 GpStatus stat;
1246 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetPageTransform,
1247 sizeof(EmfPlusSetPageTransform), (void **)&record);
1248 if (stat != Ok)
1249 return stat;
1251 record->Header.Flags = unit;
1252 record->PageScale = scale;
1254 METAFILE_WriteRecords(metafile);
1257 return Ok;
1260 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1262 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1264 EmfPlusSetWorldTransform *record;
1265 GpStatus stat;
1267 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetWorldTransform,
1268 sizeof(EmfPlusSetWorldTransform), (void **)&record);
1269 if (stat != Ok)
1270 return stat;
1272 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1274 METAFILE_WriteRecords(metafile);
1277 return Ok;
1280 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1282 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1284 EmfPlusScaleWorldTransform *record;
1285 GpStatus stat;
1287 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeScaleWorldTransform,
1288 sizeof(EmfPlusScaleWorldTransform), (void **)&record);
1289 if (stat != Ok)
1290 return stat;
1292 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1293 record->Sx = sx;
1294 record->Sy = sy;
1296 METAFILE_WriteRecords(metafile);
1299 return Ok;
1302 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1304 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1306 EmfPlusMultiplyWorldTransform *record;
1307 GpStatus stat;
1309 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeMultiplyWorldTransform,
1310 sizeof(EmfPlusMultiplyWorldTransform), (void **)&record);
1311 if (stat != Ok)
1312 return stat;
1314 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1315 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1317 METAFILE_WriteRecords(metafile);
1320 return Ok;
1323 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1325 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1327 EmfPlusRotateWorldTransform *record;
1328 GpStatus stat;
1330 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRotateWorldTransform,
1331 sizeof(EmfPlusRotateWorldTransform), (void **)&record);
1332 if (stat != Ok)
1333 return stat;
1335 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1336 record->Angle = angle;
1338 METAFILE_WriteRecords(metafile);
1341 return Ok;
1344 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1346 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1348 EmfPlusTranslateWorldTransform *record;
1349 GpStatus stat;
1351 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeTranslateWorldTransform,
1352 sizeof(EmfPlusTranslateWorldTransform), (void **)&record);
1353 if (stat != Ok)
1354 return stat;
1356 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1357 record->dx = dx;
1358 record->dy = dy;
1360 METAFILE_WriteRecords(metafile);
1363 return Ok;
1366 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1368 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1370 EmfPlusRecordHeader *record;
1371 GpStatus stat;
1373 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetWorldTransform,
1374 sizeof(EmfPlusRecordHeader), (void **)&record);
1375 if (stat != Ok)
1376 return stat;
1378 METAFILE_WriteRecords(metafile);
1381 return Ok;
1384 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1385 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1387 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1389 EmfPlusBeginContainer *record;
1390 GpStatus stat;
1392 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainer, sizeof(*record), (void**)&record);
1393 if (stat != Ok)
1394 return stat;
1396 record->Header.Flags = unit & 0xff;
1397 record->DestRect = *dstrect;
1398 record->SrcRect = *srcrect;
1399 record->StackIndex = StackIndex;
1401 METAFILE_WriteRecords(metafile);
1404 return Ok;
1407 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1409 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1411 EmfPlusContainerRecord *record;
1412 GpStatus stat;
1414 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeBeginContainerNoParams,
1415 sizeof(EmfPlusContainerRecord), (void **)&record);
1416 if (stat != Ok)
1417 return stat;
1419 record->StackIndex = StackIndex;
1421 METAFILE_WriteRecords(metafile);
1424 return Ok;
1427 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1429 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1431 EmfPlusContainerRecord *record;
1432 GpStatus stat;
1434 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeEndContainer,
1435 sizeof(EmfPlusContainerRecord), (void **)&record);
1436 if (stat != Ok)
1437 return stat;
1439 record->StackIndex = StackIndex;
1441 METAFILE_WriteRecords(metafile);
1444 return Ok;
1447 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1449 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1451 EmfPlusContainerRecord *record;
1452 GpStatus stat;
1454 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSave,
1455 sizeof(EmfPlusContainerRecord), (void **)&record);
1456 if (stat != Ok)
1457 return stat;
1459 record->StackIndex = StackIndex;
1461 METAFILE_WriteRecords(metafile);
1464 return Ok;
1467 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1469 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1471 EmfPlusContainerRecord *record;
1472 GpStatus stat;
1474 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeRestore,
1475 sizeof(EmfPlusContainerRecord), (void **)&record);
1476 if (stat != Ok)
1477 return stat;
1479 record->StackIndex = StackIndex;
1481 METAFILE_WriteRecords(metafile);
1484 return Ok;
1487 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1489 if (hdc != metafile->record_dc)
1490 return InvalidParameter;
1492 return Ok;
1495 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1497 GpStatus stat;
1499 stat = METAFILE_WriteEndOfFile(metafile);
1500 metafile->record_graphics = NULL;
1502 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1503 metafile->record_dc = NULL;
1505 heap_free(metafile->comment_data);
1506 metafile->comment_data = NULL;
1507 metafile->comment_data_size = 0;
1509 if (stat == Ok)
1511 MetafileHeader header;
1513 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1514 if (stat == Ok && metafile->auto_frame &&
1515 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1517 RECTL bounds_rc, gdi_bounds_rc;
1518 REAL x_scale = 2540.0 / header.DpiX;
1519 REAL y_scale = 2540.0 / header.DpiY;
1520 BYTE* buffer;
1521 UINT buffer_size;
1523 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1524 if (gdi_bounds_rc.right > gdi_bounds_rc.left &&
1525 gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1527 GpPointF *af_min = &metafile->auto_frame_min;
1528 GpPointF *af_max = &metafile->auto_frame_max;
1530 af_min->X = fmin(af_min->X, gdi_bounds_rc.left);
1531 af_min->Y = fmin(af_min->Y, gdi_bounds_rc.top);
1532 af_max->X = fmax(af_max->X, gdi_bounds_rc.right);
1533 af_max->Y = fmax(af_max->Y, gdi_bounds_rc.bottom);
1536 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1537 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1538 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1539 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1541 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1542 buffer = heap_alloc(buffer_size);
1543 if (buffer)
1545 HENHMETAFILE new_hemf;
1547 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1549 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1551 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1553 if (new_hemf)
1555 DeleteEnhMetaFile(metafile->hemf);
1556 metafile->hemf = new_hemf;
1558 else
1559 stat = OutOfMemory;
1561 heap_free(buffer);
1563 else
1564 stat = OutOfMemory;
1566 if (stat == Ok)
1567 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1569 if (stat == Ok)
1571 metafile->bounds.X = header.X;
1572 metafile->bounds.Y = header.Y;
1573 metafile->bounds.Width = header.Width;
1574 metafile->bounds.Height = header.Height;
1578 if (stat == Ok && metafile->record_stream)
1580 BYTE *buffer;
1581 UINT buffer_size;
1583 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1585 buffer = heap_alloc(buffer_size);
1586 if (buffer)
1588 HRESULT hr;
1590 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1592 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1594 if (FAILED(hr))
1595 stat = hresult_to_status(hr);
1597 heap_free(buffer);
1599 else
1600 stat = OutOfMemory;
1603 if (metafile->record_stream)
1605 IStream_Release(metafile->record_stream);
1606 metafile->record_stream = NULL;
1609 return stat;
1612 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1614 TRACE("(%p,%p)\n", metafile, hEmf);
1616 if (!metafile || !hEmf || !metafile->hemf)
1617 return InvalidParameter;
1619 *hEmf = metafile->hemf;
1620 metafile->hemf = NULL;
1622 return Ok;
1625 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1627 GpStatus stat = Ok;
1629 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1631 return stat;
1634 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1636 if (metafile->playback_dc)
1638 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1639 metafile->playback_dc = NULL;
1643 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1645 GpStatus stat;
1646 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1647 if (stat == Ok)
1648 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1649 return stat;
1652 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1654 GpMatrix *real_transform;
1655 GpStatus stat;
1657 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1659 if (stat == Ok)
1661 REAL scale_x = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpix, metafile->printer_display);
1662 REAL scale_y = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpiy, metafile->printer_display);
1664 if (metafile->page_unit != UnitDisplay)
1666 scale_x *= metafile->page_scale;
1667 scale_y *= metafile->page_scale;
1670 stat = GdipScaleMatrix(real_transform, scale_x, scale_y, MatrixOrderPrepend);
1672 if (stat == Ok)
1673 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1675 if (stat == Ok)
1676 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1678 GdipDeleteMatrix(real_transform);
1681 return stat;
1684 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1686 metafile_free_object_table_entry(metafile, id);
1687 metafile->objtable[id].type = type;
1688 metafile->objtable[id].u.object = object;
1691 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1693 EmfPlusImage *data = (EmfPlusImage *)record_data;
1694 GpStatus status;
1696 *image = NULL;
1698 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1699 return InvalidParameter;
1700 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1702 switch (data->Type)
1704 case ImageDataTypeBitmap:
1706 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1708 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1709 return InvalidParameter;
1710 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1712 switch (bitmapdata->Type)
1714 case BitmapDataTypePixel:
1716 ColorPalette *palette;
1717 BYTE *scan0;
1719 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1721 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1722 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1724 if (data_size <= palette_size)
1725 return InvalidParameter;
1726 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1728 if (data_size < palette_size)
1729 return InvalidParameter;
1730 data_size -= palette_size;
1732 palette = (ColorPalette *)bitmapdata->BitmapData;
1733 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1735 else
1737 palette = NULL;
1738 scan0 = bitmapdata->BitmapData;
1741 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1742 return InvalidParameter;
1744 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1745 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1746 if (status == Ok && palette)
1748 status = GdipSetImagePalette(*image, palette);
1749 if (status != Ok)
1751 GdipDisposeImage(*image);
1752 *image = NULL;
1755 break;
1757 case BitmapDataTypeCompressed:
1759 IWICImagingFactory *factory;
1760 IWICStream *stream;
1761 HRESULT hr;
1763 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1764 return GenericError;
1766 hr = IWICImagingFactory_CreateStream(factory, &stream);
1767 IWICImagingFactory_Release(factory);
1768 if (hr != S_OK)
1769 return GenericError;
1771 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1772 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1773 else
1774 status = GenericError;
1776 IWICStream_Release(stream);
1777 break;
1779 default:
1780 WARN("Invalid bitmap type %ld.\n", bitmapdata->Type);
1781 return InvalidParameter;
1783 break;
1785 case ImageDataTypeMetafile:
1787 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1789 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1790 return InvalidParameter;
1791 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1793 switch (metafiledata->Type) {
1794 case MetafileTypeEmf:
1795 case MetafileTypeEmfPlusOnly:
1796 case MetafileTypeEmfPlusDual:
1798 HENHMETAFILE hemf;
1800 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1802 if (!hemf)
1803 return GenericError;
1805 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1807 if (status != Ok)
1808 DeleteEnhMetaFile(hemf);
1810 break;
1812 default:
1813 FIXME("metafile type %ld not supported.\n", metafiledata->Type);
1814 return NotImplemented;
1816 break;
1818 default:
1819 FIXME("image type %d not supported.\n", data->Type);
1820 return NotImplemented;
1823 return status;
1826 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1828 EmfPlusPath *data = (EmfPlusPath *)record_data;
1829 GpStatus status;
1830 BYTE *types;
1831 UINT size;
1832 DWORD i;
1834 *path = NULL;
1836 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1837 return InvalidParameter;
1838 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1840 if (data->PathPointFlags & 0x800) /* R */
1842 FIXME("RLE encoded path data is not supported.\n");
1843 return NotImplemented;
1845 else
1847 if (data->PathPointFlags & 0x4000) /* C */
1848 size = sizeof(EmfPlusPoint);
1849 else
1850 size = sizeof(EmfPlusPointF);
1851 size += sizeof(BYTE); /* EmfPlusPathPointType */
1852 size *= data->PathPointCount;
1855 if (data_size < size)
1856 return InvalidParameter;
1858 status = GdipCreatePath(FillModeAlternate, path);
1859 if (status != Ok)
1860 return status;
1862 (*path)->pathdata.Count = data->PathPointCount;
1863 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1864 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1865 (*path)->datalen = (*path)->pathdata.Count;
1867 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1869 GdipDeletePath(*path);
1870 return OutOfMemory;
1873 if (data->PathPointFlags & 0x4000) /* C */
1875 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1876 for (i = 0; i < data->PathPointCount; i++)
1878 (*path)->pathdata.Points[i].X = points[i].X;
1879 (*path)->pathdata.Points[i].Y = points[i].Y;
1881 types = (BYTE *)(points + i);
1883 else
1885 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1886 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1887 types = (BYTE *)(points + data->PathPointCount);
1890 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1892 return Ok;
1895 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1897 const DWORD *type;
1898 GpStatus status;
1900 type = buffer_read(mbuf, sizeof(*type));
1901 if (!type) return Ok;
1903 node->type = *type;
1905 switch (node->type)
1907 case CombineModeReplace:
1908 case CombineModeIntersect:
1909 case CombineModeUnion:
1910 case CombineModeXor:
1911 case CombineModeExclude:
1912 case CombineModeComplement:
1914 region_element *left, *right;
1916 left = heap_alloc_zero(sizeof(*left));
1917 if (!left)
1918 return OutOfMemory;
1920 right = heap_alloc_zero(sizeof(*right));
1921 if (!right)
1923 heap_free(left);
1924 return OutOfMemory;
1927 status = metafile_read_region_node(mbuf, region, left, count);
1928 if (status == Ok)
1930 status = metafile_read_region_node(mbuf, region, right, count);
1931 if (status == Ok)
1933 node->elementdata.combine.left = left;
1934 node->elementdata.combine.right = right;
1935 region->num_children += 2;
1936 return Ok;
1940 heap_free(left);
1941 heap_free(right);
1942 return status;
1944 case RegionDataRect:
1946 const EmfPlusRectF *rect;
1948 rect = buffer_read(mbuf, sizeof(*rect));
1949 if (!rect)
1950 return InvalidParameter;
1952 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1953 *count += 1;
1954 return Ok;
1956 case RegionDataPath:
1958 const BYTE *path_data;
1959 const UINT *data_size;
1960 GpPath *path;
1962 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1963 if (!data_size)
1964 return InvalidParameter;
1966 path_data = buffer_read(mbuf, *data_size);
1967 if (!path_data)
1968 return InvalidParameter;
1970 status = metafile_deserialize_path(path_data, *data_size, &path);
1971 if (status == Ok)
1973 node->elementdata.path = path;
1974 *count += 1;
1976 return Ok;
1978 case RegionDataEmptyRect:
1979 case RegionDataInfiniteRect:
1980 *count += 1;
1981 return Ok;
1982 default:
1983 FIXME("element type %#lx is not supported\n", *type);
1984 break;
1987 return InvalidParameter;
1990 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1992 struct memory_buffer mbuf;
1993 GpStatus status;
1994 UINT count;
1996 *region = NULL;
1998 init_memory_buffer(&mbuf, record_data, data_size);
2000 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2001 return InvalidParameter;
2003 status = GdipCreateRegion(region);
2004 if (status != Ok)
2005 return status;
2007 count = 0;
2008 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2009 if (status == Ok && !count)
2010 status = InvalidParameter;
2012 if (status != Ok)
2014 GdipDeleteRegion(*region);
2015 *region = NULL;
2018 return status;
2021 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2023 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2024 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2025 EmfPlusTransformMatrix *transform = NULL;
2026 DWORD brushflags;
2027 GpStatus status;
2028 UINT offset;
2030 *brush = NULL;
2032 if (data_size < header_size)
2033 return InvalidParameter;
2035 switch (data->Type)
2037 case BrushTypeSolidColor:
2038 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2039 return InvalidParameter;
2041 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2042 break;
2043 case BrushTypeHatchFill:
2044 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2045 return InvalidParameter;
2047 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2048 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2049 break;
2050 case BrushTypeTextureFill:
2052 GpImage *image;
2054 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2055 if (data_size <= offset)
2056 return InvalidParameter;
2058 brushflags = data->BrushData.texture.BrushDataFlags;
2059 if (brushflags & BrushDataTransform)
2061 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2062 return InvalidParameter;
2063 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2064 offset += sizeof(EmfPlusTransformMatrix);
2067 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2068 if (status != Ok)
2069 return status;
2071 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2072 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2073 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2075 GdipDisposeImage(image);
2076 break;
2078 case BrushTypeLinearGradient:
2080 GpLineGradient *gradient = NULL;
2081 GpRectF rect;
2082 UINT position_count = 0;
2084 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2085 if (data_size < offset)
2086 return InvalidParameter;
2088 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2089 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2090 return InvalidParameter;
2092 if (brushflags & BrushDataTransform)
2094 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2095 return InvalidParameter;
2096 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2097 offset += sizeof(EmfPlusTransformMatrix);
2100 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2102 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2103 return InvalidParameter;
2104 position_count = *(DWORD *)(record_data + offset);
2105 offset += sizeof(DWORD);
2108 if (brushflags & BrushDataPresetColors)
2110 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2111 return InvalidParameter;
2113 else if (brushflags & BrushDataBlendFactorsH)
2115 if (data_size != offset + position_count * 2 * sizeof(float))
2116 return InvalidParameter;
2119 rect.X = data->BrushData.lineargradient.RectF.X;
2120 rect.Y = data->BrushData.lineargradient.RectF.Y;
2121 rect.Width = data->BrushData.lineargradient.RectF.Width;
2122 rect.Height = data->BrushData.lineargradient.RectF.Height;
2124 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2125 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2126 data->BrushData.lineargradient.WrapMode, &gradient);
2127 if (status == Ok)
2129 if (transform)
2130 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2132 if (status == Ok)
2134 if (brushflags & BrushDataPresetColors)
2135 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2136 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2137 else if (brushflags & BrushDataBlendFactorsH)
2138 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2139 (REAL *)(record_data + offset), position_count);
2141 if (brushflags & BrushDataIsGammaCorrected)
2142 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2146 if (status == Ok)
2147 *brush = (GpBrush *)gradient;
2148 else
2149 GdipDeleteBrush((GpBrush *)gradient);
2151 break;
2153 default:
2154 FIXME("brush type %lu is not supported.\n", data->Type);
2155 return NotImplemented;
2158 return status;
2161 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2163 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2164 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2166 if (data_size <= offset)
2167 return InvalidParameter;
2169 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2170 if (data_size <= offset)
2171 return InvalidParameter;
2173 if (pendata->PenDataFlags & PenDataTransform)
2174 offset += sizeof(EmfPlusTransformMatrix);
2176 if (pendata->PenDataFlags & PenDataStartCap)
2177 offset += sizeof(DWORD);
2179 if (pendata->PenDataFlags & PenDataEndCap)
2180 offset += sizeof(DWORD);
2182 if (pendata->PenDataFlags & PenDataJoin)
2183 offset += sizeof(DWORD);
2185 if (pendata->PenDataFlags & PenDataMiterLimit)
2186 offset += sizeof(REAL);
2188 if (pendata->PenDataFlags & PenDataLineStyle)
2189 offset += sizeof(DWORD);
2191 if (pendata->PenDataFlags & PenDataDashedLineCap)
2192 offset += sizeof(DWORD);
2194 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2195 offset += sizeof(REAL);
2197 if (pendata->PenDataFlags & PenDataDashedLine)
2199 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2201 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2202 if (data_size <= offset)
2203 return InvalidParameter;
2205 offset += dashedline->DashedLineDataSize * sizeof(float);
2208 if (pendata->PenDataFlags & PenDataNonCenter)
2209 offset += sizeof(DWORD);
2211 if (pendata->PenDataFlags & PenDataCompoundLine)
2213 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2215 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2216 if (data_size <= offset)
2217 return InvalidParameter;
2219 offset += compoundline->CompoundLineDataSize * sizeof(float);
2222 if (pendata->PenDataFlags & PenDataCustomStartCap)
2224 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2226 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2227 if (data_size <= offset)
2228 return InvalidParameter;
2230 offset += startcap->CustomStartCapSize;
2233 if (pendata->PenDataFlags & PenDataCustomEndCap)
2235 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2237 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2238 if (data_size <= offset)
2239 return InvalidParameter;
2241 offset += endcap->CustomEndCapSize;
2244 *ret = offset;
2245 return Ok;
2248 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2250 BYTE type = (flags >> 8) & 0xff;
2251 BYTE id = flags & 0xff;
2252 void *object = NULL;
2253 GpStatus status;
2255 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2256 return InvalidParameter;
2258 switch (type)
2260 case ObjectTypeBrush:
2261 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2262 break;
2263 case ObjectTypePen:
2265 EmfPlusPen *data = (EmfPlusPen *)record_data;
2266 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2267 GpBrush *brush;
2268 DWORD offset;
2269 GpPen *pen;
2271 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2272 if (status != Ok)
2273 return status;
2275 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2276 if (status != Ok)
2277 return status;
2279 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2280 GdipDeleteBrush(brush);
2281 if (status != Ok)
2282 return status;
2284 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2286 if (pendata->PenDataFlags & PenDataTransform)
2288 FIXME("PenDataTransform is not supported.\n");
2289 offset += sizeof(EmfPlusTransformMatrix);
2292 if (pendata->PenDataFlags & PenDataStartCap)
2294 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2295 goto penfailed;
2296 offset += sizeof(DWORD);
2299 if (pendata->PenDataFlags & PenDataEndCap)
2301 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2302 goto penfailed;
2303 offset += sizeof(DWORD);
2306 if (pendata->PenDataFlags & PenDataJoin)
2308 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2309 goto penfailed;
2310 offset += sizeof(DWORD);
2313 if (pendata->PenDataFlags & PenDataMiterLimit)
2315 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2316 goto penfailed;
2317 offset += sizeof(REAL);
2320 if (pendata->PenDataFlags & PenDataLineStyle)
2322 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2323 goto penfailed;
2324 offset += sizeof(DWORD);
2327 if (pendata->PenDataFlags & PenDataDashedLineCap)
2329 FIXME("PenDataDashedLineCap is not supported.\n");
2330 offset += sizeof(DWORD);
2333 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2335 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2336 goto penfailed;
2337 offset += sizeof(REAL);
2340 if (pendata->PenDataFlags & PenDataDashedLine)
2342 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2343 FIXME("PenDataDashedLine is not supported.\n");
2344 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2347 if (pendata->PenDataFlags & PenDataNonCenter)
2349 FIXME("PenDataNonCenter is not supported.\n");
2350 offset += sizeof(DWORD);
2353 if (pendata->PenDataFlags & PenDataCompoundLine)
2355 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2356 FIXME("PenDataCompoundLine is not supported.\n");
2357 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2360 if (pendata->PenDataFlags & PenDataCustomStartCap)
2362 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2363 FIXME("PenDataCustomStartCap is not supported.\n");
2364 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2367 if (pendata->PenDataFlags & PenDataCustomEndCap)
2369 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2370 FIXME("PenDataCustomEndCap is not supported.\n");
2371 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2374 object = pen;
2375 break;
2377 penfailed:
2378 GdipDeletePen(pen);
2379 return status;
2381 case ObjectTypePath:
2382 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2383 break;
2384 case ObjectTypeRegion:
2385 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2386 break;
2387 case ObjectTypeImage:
2388 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2389 break;
2390 case ObjectTypeFont:
2392 EmfPlusFont *data = (EmfPlusFont *)record_data;
2393 GpFontFamily *family;
2394 WCHAR *familyname;
2396 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2397 return InvalidParameter;
2398 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2400 if (data_size < data->Length * sizeof(WCHAR))
2401 return InvalidParameter;
2403 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2404 return OutOfMemory;
2406 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2407 familyname[data->Length] = 0;
2409 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2410 GdipFree(familyname);
2412 /* If a font family cannot be created from family name, native
2413 falls back to a sans serif font. */
2414 if (status != Ok)
2415 status = GdipGetGenericFontFamilySansSerif(&family);
2416 if (status != Ok)
2417 return status;
2419 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2420 GdipDeleteFontFamily(family);
2421 break;
2423 case ObjectTypeImageAttributes:
2425 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2426 GpImageAttributes *attributes = NULL;
2428 if (data_size != sizeof(*data))
2429 return InvalidParameter;
2431 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2432 return status;
2434 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2435 !!data->ObjectClamp);
2436 if (status == Ok)
2437 object = attributes;
2438 else
2439 GdipDisposeImageAttributes(attributes);
2440 break;
2442 default:
2443 FIXME("not implemented for object type %d.\n", type);
2444 return NotImplemented;
2447 if (status == Ok)
2448 metafile_set_object_table_entry(metafile, id, type, object);
2450 return status;
2453 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2455 GpMatrix world_to_device;
2457 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2459 GdipTransformRegion(region, &world_to_device);
2460 GdipCombineRegionRegion(metafile->clip, region, mode);
2462 return METAFILE_PlaybackUpdateClip(metafile);
2465 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2466 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2468 GpStatus stat;
2469 GpMetafile *real_metafile = (GpMetafile*)metafile;
2471 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2473 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2474 return InvalidParameter;
2476 if (recordType >= 1 && recordType <= 0x7a)
2478 /* regular EMF record */
2479 if (metafile->playback_dc)
2481 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2483 if (record)
2485 record->iType = recordType;
2486 record->nSize = dataSize + 8;
2487 memcpy(record->dParm, data, dataSize);
2489 if (record->iType == EMR_BITBLT || record->iType == EMR_STRETCHBLT)
2490 SetStretchBltMode(metafile->playback_dc, STRETCH_HALFTONE);
2492 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2493 record, metafile->handle_count) == 0)
2494 ERR("PlayEnhMetaFileRecord failed\n");
2496 heap_free(record);
2498 else
2499 return OutOfMemory;
2502 else
2504 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2506 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2508 switch(recordType)
2510 case EmfPlusRecordTypeHeader:
2511 case EmfPlusRecordTypeEndOfFile:
2512 break;
2513 case EmfPlusRecordTypeGetDC:
2514 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2515 break;
2516 case EmfPlusRecordTypeClear:
2518 EmfPlusClear *record = (EmfPlusClear*)header;
2520 if (dataSize != sizeof(record->Color))
2521 return InvalidParameter;
2523 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2525 case EmfPlusRecordTypeFillRects:
2527 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2528 GpBrush *brush, *temp_brush=NULL;
2529 GpRectF *rects, *temp_rects=NULL;
2531 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2532 return InvalidParameter;
2534 if (flags & 0x4000)
2536 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2537 return InvalidParameter;
2539 else
2541 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2542 return InvalidParameter;
2545 if (flags & 0x8000)
2547 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2548 brush = temp_brush;
2550 else
2552 if (record->BrushID >= EmfPlusObjectTableSize ||
2553 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2554 return InvalidParameter;
2556 brush = real_metafile->objtable[record->BrushID].u.brush;
2557 stat = Ok;
2560 if (stat == Ok)
2562 if (flags & 0x4000)
2564 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2565 int i;
2567 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2568 if (rects)
2570 for (i=0; i<record->Count; i++)
2572 rects[i].X = int_rects[i].X;
2573 rects[i].Y = int_rects[i].Y;
2574 rects[i].Width = int_rects[i].Width;
2575 rects[i].Height = int_rects[i].Height;
2578 else
2579 stat = OutOfMemory;
2581 else
2582 rects = (GpRectF*)(record+1);
2585 if (stat == Ok)
2587 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2590 GdipDeleteBrush(temp_brush);
2591 heap_free(temp_rects);
2593 return stat;
2595 case EmfPlusRecordTypeSetClipRect:
2597 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2598 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2599 GpRegion *region;
2601 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2602 return InvalidParameter;
2604 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2606 if (stat == Ok)
2608 stat = metafile_set_clip_region(real_metafile, region, mode);
2609 GdipDeleteRegion(region);
2612 return stat;
2614 case EmfPlusRecordTypeSetClipRegion:
2616 CombineMode mode = (flags >> 8) & 0xf;
2617 BYTE regionid = flags & 0xff;
2618 GpRegion *region;
2620 if (dataSize != 0)
2621 return InvalidParameter;
2623 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2624 return InvalidParameter;
2626 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2627 if (stat == Ok)
2629 stat = metafile_set_clip_region(real_metafile, region, mode);
2630 GdipDeleteRegion(region);
2633 return stat;
2635 case EmfPlusRecordTypeSetClipPath:
2637 CombineMode mode = (flags >> 8) & 0xf;
2638 BYTE pathid = flags & 0xff;
2639 GpRegion *region;
2641 if (dataSize != 0)
2642 return InvalidParameter;
2644 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2645 return InvalidParameter;
2647 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2648 if (stat == Ok)
2650 stat = metafile_set_clip_region(real_metafile, region, mode);
2651 GdipDeleteRegion(region);
2654 return stat;
2656 case EmfPlusRecordTypeSetPageTransform:
2658 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2659 GpUnit unit = (GpUnit)flags;
2661 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2662 return InvalidParameter;
2664 real_metafile->page_unit = unit;
2665 real_metafile->page_scale = record->PageScale;
2667 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2669 case EmfPlusRecordTypeSetWorldTransform:
2671 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2673 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2674 return InvalidParameter;
2676 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2678 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2680 case EmfPlusRecordTypeScaleWorldTransform:
2682 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2683 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2685 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2686 return InvalidParameter;
2688 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2690 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2692 case EmfPlusRecordTypeMultiplyWorldTransform:
2694 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2695 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2696 GpMatrix matrix;
2698 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2699 return InvalidParameter;
2701 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2703 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2705 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2707 case EmfPlusRecordTypeRotateWorldTransform:
2709 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2710 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2712 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2713 return InvalidParameter;
2715 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2717 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2719 case EmfPlusRecordTypeTranslateWorldTransform:
2721 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2722 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2724 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2725 return InvalidParameter;
2727 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2729 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2731 case EmfPlusRecordTypeResetWorldTransform:
2733 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2735 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2737 case EmfPlusRecordTypeBeginContainer:
2739 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2740 container* cont;
2741 GpUnit unit;
2742 REAL scale_x, scale_y;
2743 GpRectF scaled_srcrect;
2744 GpMatrix transform;
2746 cont = heap_alloc_zero(sizeof(*cont));
2747 if (!cont)
2748 return OutOfMemory;
2750 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2751 if (stat != Ok)
2753 heap_free(cont);
2754 return stat;
2757 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2759 if (stat != Ok)
2761 GdipDeleteRegion(cont->clip);
2762 heap_free(cont);
2763 return stat;
2766 cont->id = record->StackIndex;
2767 cont->type = BEGIN_CONTAINER;
2768 cont->world_transform = *metafile->world_transform;
2769 cont->page_unit = metafile->page_unit;
2770 cont->page_scale = metafile->page_scale;
2771 list_add_head(&real_metafile->containers, &cont->entry);
2773 unit = record->Header.Flags & 0xff;
2775 scale_x = units_to_pixels(1.0, unit, metafile->image.xres, metafile->printer_display);
2776 scale_y = units_to_pixels(1.0, unit, metafile->image.yres, metafile->printer_display);
2778 scaled_srcrect.X = scale_x * record->SrcRect.X;
2779 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2780 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2781 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2783 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2784 transform.matrix[1] = 0.0;
2785 transform.matrix[2] = 0.0;
2786 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2787 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2788 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2790 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2792 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2794 case EmfPlusRecordTypeBeginContainerNoParams:
2795 case EmfPlusRecordTypeSave:
2797 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2798 container* cont;
2800 cont = heap_alloc_zero(sizeof(*cont));
2801 if (!cont)
2802 return OutOfMemory;
2804 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2805 if (stat != Ok)
2807 heap_free(cont);
2808 return stat;
2811 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2812 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2813 else
2814 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2816 if (stat != Ok)
2818 GdipDeleteRegion(cont->clip);
2819 heap_free(cont);
2820 return stat;
2823 cont->id = record->StackIndex;
2824 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2825 cont->type = BEGIN_CONTAINER;
2826 else
2827 cont->type = SAVE_GRAPHICS;
2828 cont->world_transform = *metafile->world_transform;
2829 cont->page_unit = metafile->page_unit;
2830 cont->page_scale = metafile->page_scale;
2831 list_add_head(&real_metafile->containers, &cont->entry);
2833 break;
2835 case EmfPlusRecordTypeEndContainer:
2836 case EmfPlusRecordTypeRestore:
2838 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2839 container* cont;
2840 enum container_type type;
2841 BOOL found=FALSE;
2843 if (recordType == EmfPlusRecordTypeEndContainer)
2844 type = BEGIN_CONTAINER;
2845 else
2846 type = SAVE_GRAPHICS;
2848 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2850 if (cont->id == record->StackIndex && cont->type == type)
2852 found = TRUE;
2853 break;
2857 if (found)
2859 container* cont2;
2861 /* pop any newer items on the stack */
2862 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2864 list_remove(&cont2->entry);
2865 GdipDeleteRegion(cont2->clip);
2866 heap_free(cont2);
2869 if (type == BEGIN_CONTAINER)
2870 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2871 else
2872 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2874 *real_metafile->world_transform = cont->world_transform;
2875 real_metafile->page_unit = cont->page_unit;
2876 real_metafile->page_scale = cont->page_scale;
2877 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2879 list_remove(&cont->entry);
2880 GdipDeleteRegion(cont->clip);
2881 heap_free(cont);
2884 break;
2886 case EmfPlusRecordTypeSetPixelOffsetMode:
2888 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2890 case EmfPlusRecordTypeSetCompositingQuality:
2892 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2894 case EmfPlusRecordTypeSetInterpolationMode:
2896 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2898 case EmfPlusRecordTypeSetTextRenderingHint:
2900 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2902 case EmfPlusRecordTypeSetAntiAliasMode:
2904 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2906 case EmfPlusRecordTypeSetCompositingMode:
2908 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2910 case EmfPlusRecordTypeObject:
2912 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2914 case EmfPlusRecordTypeDrawImage:
2916 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2917 BYTE image = flags & 0xff;
2918 GpPointF points[3];
2920 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2921 return InvalidParameter;
2923 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2924 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2925 return InvalidParameter;
2927 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2928 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2929 return InvalidParameter;
2931 if (flags & 0x4000) /* C */
2933 points[0].X = draw->RectData.rect.X;
2934 points[0].Y = draw->RectData.rect.Y;
2935 points[1].X = points[0].X + draw->RectData.rect.Width;
2936 points[1].Y = points[0].Y;
2937 points[2].X = points[1].X;
2938 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2940 else
2942 points[0].X = draw->RectData.rectF.X;
2943 points[0].Y = draw->RectData.rectF.Y;
2944 points[1].X = points[0].X + draw->RectData.rectF.Width;
2945 points[1].Y = points[0].Y;
2946 points[2].X = points[1].X;
2947 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2950 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2951 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2952 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2954 case EmfPlusRecordTypeDrawImagePoints:
2956 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2957 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2958 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2959 BYTE image = flags & 0xff;
2960 GpPointF points[3];
2961 unsigned int i;
2962 UINT size;
2964 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2965 return InvalidParameter;
2967 if (dataSize <= fixed_part_size)
2968 return InvalidParameter;
2969 dataSize -= fixed_part_size;
2971 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2972 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2973 return InvalidParameter;
2975 if (draw->count != 3)
2976 return InvalidParameter;
2978 if ((flags >> 13) & 1) /* E */
2979 FIXME("image effects are not supported.\n");
2981 if ((flags >> 11) & 1) /* P */
2982 size = sizeof(EmfPlusPointR7) * draw->count;
2983 else if ((flags >> 14) & 1) /* C */
2984 size = sizeof(EmfPlusPoint) * draw->count;
2985 else
2986 size = sizeof(EmfPlusPointF) * draw->count;
2988 if (dataSize != size)
2989 return InvalidParameter;
2991 if ((flags >> 11) & 1) /* P */
2993 points[0].X = draw->PointData.pointsR[0].X;
2994 points[0].Y = draw->PointData.pointsR[0].Y;
2995 for (i = 1; i < 3; i++)
2997 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
2998 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3001 else if ((flags >> 14) & 1) /* C */
3003 for (i = 0; i < 3; i++)
3005 points[i].X = draw->PointData.points[i].X;
3006 points[i].Y = draw->PointData.points[i].Y;
3009 else
3010 memcpy(points, draw->PointData.pointsF, sizeof(points));
3012 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3013 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3014 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3016 case EmfPlusRecordTypeFillPath:
3018 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3019 GpSolidFill *solidfill = NULL;
3020 BYTE path = flags & 0xff;
3021 GpBrush *brush;
3023 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3024 return InvalidParameter;
3026 if (dataSize != sizeof(fill->data.BrushId))
3027 return InvalidParameter;
3029 if (flags & 0x8000)
3031 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3032 if (stat != Ok)
3033 return stat;
3034 brush = (GpBrush *)solidfill;
3036 else
3038 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3039 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3040 return InvalidParameter;
3042 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3045 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3046 GdipDeleteBrush((GpBrush *)solidfill);
3047 return stat;
3049 case EmfPlusRecordTypeFillClosedCurve:
3051 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3052 sizeof(EmfPlusRecordHeader);
3053 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3054 GpSolidFill *solidfill = NULL;
3055 GpFillMode mode;
3056 GpBrush *brush;
3057 UINT size, i;
3059 if (dataSize <= fixed_part_size)
3060 return InvalidParameter;
3062 if (fill->Count == 0)
3063 return InvalidParameter;
3065 if (flags & 0x800) /* P */
3066 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3067 else if (flags & 0x4000) /* C */
3068 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3069 else
3070 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3072 if (dataSize != size)
3073 return InvalidParameter;
3075 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3077 if (flags & 0x8000) /* S */
3079 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3080 if (stat != Ok)
3081 return stat;
3082 brush = (GpBrush *)solidfill;
3084 else
3086 if (fill->BrushId >= EmfPlusObjectTableSize ||
3087 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3088 return InvalidParameter;
3090 brush = real_metafile->objtable[fill->BrushId].u.brush;
3093 if (flags & (0x800 | 0x4000))
3095 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3096 if (points)
3098 if (flags & 0x800) /* P */
3100 for (i = 1; i < fill->Count; i++)
3102 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3103 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3106 else
3108 for (i = 0; i < fill->Count; i++)
3110 points[i].X = fill->PointData.points[i].X;
3111 points[i].Y = fill->PointData.points[i].Y;
3115 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3116 points, fill->Count, fill->Tension, mode);
3117 GdipFree(points);
3119 else
3120 stat = OutOfMemory;
3122 else
3123 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3124 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3126 GdipDeleteBrush((GpBrush *)solidfill);
3127 return stat;
3129 case EmfPlusRecordTypeFillEllipse:
3131 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3132 GpSolidFill *solidfill = NULL;
3133 GpBrush *brush;
3135 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3136 return InvalidParameter;
3137 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3139 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3140 return InvalidParameter;
3142 if (flags & 0x8000)
3144 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3145 if (stat != Ok)
3146 return stat;
3147 brush = (GpBrush *)solidfill;
3149 else
3151 if (fill->BrushId >= EmfPlusObjectTableSize ||
3152 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3153 return InvalidParameter;
3155 brush = real_metafile->objtable[fill->BrushId].u.brush;
3158 if (flags & 0x4000)
3159 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3160 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3161 else
3162 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3163 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3165 GdipDeleteBrush((GpBrush *)solidfill);
3166 return stat;
3168 case EmfPlusRecordTypeFillPie:
3170 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3171 GpSolidFill *solidfill = NULL;
3172 GpBrush *brush;
3174 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3175 return InvalidParameter;
3176 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3178 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3179 return InvalidParameter;
3181 if (flags & 0x8000) /* S */
3183 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3184 if (stat != Ok)
3185 return stat;
3186 brush = (GpBrush *)solidfill;
3188 else
3190 if (fill->BrushId >= EmfPlusObjectTableSize ||
3191 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3192 return InvalidParameter;
3194 brush = real_metafile->objtable[fill->BrushId].u.brush;
3197 if (flags & 0x4000) /* C */
3198 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3199 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3200 fill->StartAngle, fill->SweepAngle);
3201 else
3202 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3203 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3204 fill->StartAngle, fill->SweepAngle);
3206 GdipDeleteBrush((GpBrush *)solidfill);
3207 return stat;
3209 case EmfPlusRecordTypeDrawPath:
3211 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3212 BYTE path = flags & 0xff;
3214 if (dataSize != sizeof(draw->PenId))
3215 return InvalidParameter;
3217 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3218 return InvalidParameter;
3220 if (real_metafile->objtable[path].type != ObjectTypePath ||
3221 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3222 return InvalidParameter;
3224 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3225 real_metafile->objtable[path].u.path);
3227 case EmfPlusRecordTypeDrawArc:
3229 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3230 BYTE pen = flags & 0xff;
3232 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3233 return InvalidParameter;
3235 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3236 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3237 return InvalidParameter;
3239 if (flags & 0x4000) /* C */
3240 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3241 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3242 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3243 else
3244 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3245 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3246 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3248 case EmfPlusRecordTypeDrawEllipse:
3250 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3251 BYTE pen = flags & 0xff;
3253 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3254 return InvalidParameter;
3256 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3257 return InvalidParameter;
3259 if (flags & 0x4000) /* C */
3260 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3261 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3262 draw->RectData.rect.Height);
3263 else
3264 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3265 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3266 draw->RectData.rectF.Height);
3268 case EmfPlusRecordTypeDrawPie:
3270 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3271 BYTE pen = flags & 0xff;
3273 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3274 return InvalidParameter;
3276 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3277 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3278 return InvalidParameter;
3280 if (flags & 0x4000) /* C */
3281 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3282 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3283 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3284 else
3285 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3286 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3287 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3289 case EmfPlusRecordTypeDrawRects:
3291 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3292 BYTE pen = flags & 0xff;
3293 GpRectF *rects = NULL;
3295 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3296 return InvalidParameter;
3298 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3299 return InvalidParameter;
3300 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3302 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3303 return InvalidParameter;
3305 if (flags & 0x4000)
3307 DWORD i;
3309 rects = GdipAlloc(draw->Count * sizeof(*rects));
3310 if (!rects)
3311 return OutOfMemory;
3313 for (i = 0; i < draw->Count; i++)
3315 rects[i].X = draw->RectData.rect[i].X;
3316 rects[i].Y = draw->RectData.rect[i].Y;
3317 rects[i].Width = draw->RectData.rect[i].Width;
3318 rects[i].Height = draw->RectData.rect[i].Height;
3322 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3323 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3324 GdipFree(rects);
3325 return stat;
3327 case EmfPlusRecordTypeDrawDriverString:
3329 GpBrush *brush;
3330 DWORD expected_size;
3331 UINT16 *text;
3332 PointF *positions;
3333 GpSolidFill *solidfill = NULL;
3334 void* alignedmem = NULL;
3335 GpMatrix *matrix = NULL;
3336 BYTE font = flags & 0xff;
3337 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3339 if (font >= EmfPlusObjectTableSize ||
3340 real_metafile->objtable[font].type != ObjectTypeFont)
3341 return InvalidParameter;
3343 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3344 sizeof(EmfPlusRecordHeader);
3345 if (dataSize < expected_size || draw->GlyphCount <= 0)
3346 return InvalidParameter;
3348 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3349 if (draw->MatrixPresent)
3350 expected_size += sizeof(*matrix);
3352 /* Pad expected size to DWORD alignment. */
3353 expected_size = (expected_size + 3) & ~3;
3355 if (dataSize != expected_size)
3356 return InvalidParameter;
3358 if (flags & 0x8000)
3360 stat = GdipCreateSolidFill(draw->brush.Color, &solidfill);
3362 if (stat != Ok)
3363 return InvalidParameter;
3365 brush = (GpBrush*)solidfill;
3367 else
3369 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3370 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3371 return InvalidParameter;
3373 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3376 text = (UINT16*)&draw->VariableData[0];
3378 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3379 aligned rather than 4-byte aligned, which may lead to access
3380 issues. Handle this case by making our own copy of positions. */
3381 if (draw->GlyphCount % 2)
3383 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3385 if (draw->MatrixPresent)
3386 alloc_size += sizeof(*matrix);
3388 positions = alignedmem = heap_alloc(alloc_size);
3389 if (!positions)
3391 GdipDeleteBrush((GpBrush*)solidfill);
3392 return OutOfMemory;
3395 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3397 else
3398 positions = (PointF*)&text[draw->GlyphCount];
3400 if (draw->MatrixPresent)
3401 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3403 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3404 real_metafile->objtable[font].u.font, brush, positions,
3405 draw->DriverStringOptionsFlags, matrix);
3407 GdipDeleteBrush((GpBrush*)solidfill);
3408 heap_free(alignedmem);
3410 return stat;
3412 case EmfPlusRecordTypeFillRegion:
3414 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3415 GpSolidFill *solidfill = NULL;
3416 GpBrush *brush;
3417 BYTE region = flags & 0xff;
3419 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3420 return InvalidParameter;
3422 if (region >= EmfPlusObjectTableSize ||
3423 real_metafile->objtable[region].type != ObjectTypeRegion)
3424 return InvalidParameter;
3426 if (flags & 0x8000)
3428 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3429 if (stat != Ok)
3430 return stat;
3431 brush = (GpBrush*)solidfill;
3433 else
3435 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3436 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3437 return InvalidParameter;
3439 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3442 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3443 real_metafile->objtable[region].u.region);
3444 GdipDeleteBrush((GpBrush*)solidfill);
3446 return stat;
3448 default:
3449 FIXME("Not implemented for record type %x\n", recordType);
3450 return NotImplemented;
3454 return Ok;
3457 struct enum_metafile_data
3459 EnumerateMetafileProc callback;
3460 void *callback_data;
3461 GpMetafile *metafile;
3464 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3465 int nObj, LPARAM lpData)
3467 BOOL ret;
3468 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3469 const BYTE* pStr;
3471 data->metafile->handle_table = lpHTable;
3472 data->metafile->handle_count = nObj;
3474 /* First check for an EMF+ record. */
3475 if (lpEMFR->iType == EMR_GDICOMMENT)
3477 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3479 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3481 int offset = 4;
3483 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3485 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3487 if (record->DataSize)
3488 pStr = (const BYTE*)(record+1);
3489 else
3490 pStr = NULL;
3492 ret = data->callback(record->Type, record->Flags, record->DataSize,
3493 pStr, data->callback_data);
3495 if (!ret)
3496 return 0;
3498 offset += record->Size;
3501 return 1;
3505 if (lpEMFR->nSize != 8)
3506 pStr = (const BYTE*)lpEMFR->dParm;
3507 else
3508 pStr = NULL;
3510 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3511 pStr, data->callback_data);
3514 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3515 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3516 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3517 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3519 struct enum_metafile_data data;
3520 GpStatus stat;
3521 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3522 GraphicsContainer state;
3523 GpPath *dst_path;
3524 RECT dst_bounds;
3526 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3527 destPoints, count, srcRect, srcUnit, callback, callbackData,
3528 imageAttributes);
3530 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3531 return InvalidParameter;
3533 if (!metafile->hemf)
3534 return InvalidParameter;
3536 if (metafile->playback_graphics)
3537 return ObjectBusy;
3539 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3540 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3541 debugstr_pointf(&destPoints[2]));
3543 data.callback = callback;
3544 data.callback_data = callbackData;
3545 data.metafile = real_metafile;
3547 real_metafile->playback_graphics = graphics;
3548 real_metafile->playback_dc = NULL;
3549 real_metafile->src_rect = *srcRect;
3551 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3552 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3554 if (stat == Ok)
3555 stat = GdipBeginContainer2(graphics, &state);
3557 if (stat == Ok)
3559 stat = GdipSetPageScale(graphics, 1.0);
3561 if (stat == Ok)
3562 stat = GdipSetPageUnit(graphics, UnitPixel);
3564 if (stat == Ok)
3565 stat = GdipResetWorldTransform(graphics);
3567 if (stat == Ok)
3568 stat = GdipCreateRegion(&real_metafile->base_clip);
3570 if (stat == Ok)
3571 stat = GdipGetClip(graphics, real_metafile->base_clip);
3573 if (stat == Ok)
3574 stat = GdipCreateRegion(&real_metafile->clip);
3576 if (stat == Ok)
3577 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3579 if (stat == Ok)
3581 GpPointF clip_points[4];
3583 clip_points[0] = real_metafile->playback_points[0];
3584 clip_points[1] = real_metafile->playback_points[1];
3585 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3586 - real_metafile->playback_points[0].X;
3587 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3588 - real_metafile->playback_points[0].Y;
3589 clip_points[3] = real_metafile->playback_points[2];
3591 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3593 if (stat == Ok)
3594 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3596 GdipDeletePath(dst_path);
3599 if (stat == Ok)
3600 stat = GdipCreateMatrix(&real_metafile->world_transform);
3602 if (stat == Ok)
3604 real_metafile->page_unit = UnitDisplay;
3605 real_metafile->page_scale = 1.0;
3606 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3609 if (stat == Ok)
3611 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3614 if (stat == Ok)
3616 stat = METAFILE_PlaybackGetDC(real_metafile);
3618 dst_bounds.left = real_metafile->playback_points[0].X;
3619 dst_bounds.right = real_metafile->playback_points[1].X;
3620 dst_bounds.top = real_metafile->playback_points[0].Y;
3621 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3624 if (stat == Ok)
3625 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3626 &data, &dst_bounds);
3628 METAFILE_PlaybackReleaseDC(real_metafile);
3630 GdipDeleteMatrix(real_metafile->world_transform);
3631 real_metafile->world_transform = NULL;
3633 GdipDeleteRegion(real_metafile->base_clip);
3634 real_metafile->base_clip = NULL;
3636 GdipDeleteRegion(real_metafile->clip);
3637 real_metafile->clip = NULL;
3639 while (list_head(&real_metafile->containers))
3641 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3642 list_remove(&cont->entry);
3643 GdipDeleteRegion(cont->clip);
3644 heap_free(cont);
3647 GdipEndContainer(graphics, state);
3650 real_metafile->playback_graphics = NULL;
3652 return stat;
3655 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRect( GpGraphics *graphics,
3656 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3657 GDIPCONST GpRectF *src, Unit srcUnit, EnumerateMetafileProc callback,
3658 VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3660 GpPointF points[3];
3662 if (!graphics || !metafile || !dest) return InvalidParameter;
3664 points[0].X = points[2].X = dest->X;
3665 points[0].Y = points[1].Y = dest->Y;
3666 points[1].X = dest->X + dest->Width;
3667 points[2].Y = dest->Y + dest->Height;
3669 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3670 src, srcUnit, callback, cb_data, attrs);
3672 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRectI( GpGraphics * graphics,
3673 GDIPCONST GpMetafile *metafile, GDIPCONST Rect *destRect,
3674 GDIPCONST Rect *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3675 VOID *cb_data, GDIPCONST GpImageAttributes *attrs )
3677 GpRectF destRectF, srcRectF;
3679 destRectF.X = destRect->X;
3680 destRectF.Y = destRect->Y;
3681 destRectF.Width = destRect->Width;
3682 destRectF.Height = destRect->Height;
3684 srcRectF.X = srcRect->X;
3685 srcRectF.Y = srcRect->Y;
3686 srcRectF.Width = srcRect->Width;
3687 srcRectF.Height = srcRect->Height;
3689 return GdipEnumerateMetafileSrcRectDestRect( graphics, metafile, &destRectF, &srcRectF, srcUnit, callback, cb_data, attrs);
3692 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3693 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3694 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3696 GpPointF points[3];
3698 if (!graphics || !metafile || !dest) return InvalidParameter;
3700 points[0].X = points[2].X = dest->X;
3701 points[0].Y = points[1].Y = dest->Y;
3702 points[1].X = dest->X + dest->Width;
3703 points[2].Y = dest->Y + dest->Height;
3705 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3706 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3709 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3710 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3711 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3713 GpRectF destf;
3715 if (!graphics || !metafile || !dest) return InvalidParameter;
3717 destf.X = dest->X;
3718 destf.Y = dest->Y;
3719 destf.Width = dest->Width;
3720 destf.Height = dest->Height;
3722 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3725 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3726 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3727 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3729 GpRectF destf;
3731 if (!graphics || !metafile || !dest) return InvalidParameter;
3733 destf.X = dest->X;
3734 destf.Y = dest->Y;
3735 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit,
3736 metafile->image.xres, metafile->printer_display);
3737 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit,
3738 metafile->image.yres, metafile->printer_display);
3740 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3743 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3744 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3745 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3747 GpPointF ptf;
3749 if (!graphics || !metafile || !dest) return InvalidParameter;
3751 ptf.X = dest->X;
3752 ptf.Y = dest->Y;
3754 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3757 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3758 MetafileHeader * header)
3760 GpStatus status;
3762 TRACE("(%p, %p)\n", metafile, header);
3764 if(!metafile || !header)
3765 return InvalidParameter;
3767 if (metafile->hemf)
3769 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3770 if (status != Ok) return status;
3772 else
3774 memset(header, 0, sizeof(*header));
3775 header->Version = VERSION_MAGIC2;
3778 header->Type = metafile->metafile_type;
3779 header->DpiX = metafile->image.xres;
3780 header->DpiY = metafile->image.yres;
3781 header->Width = gdip_round(metafile->bounds.Width);
3782 header->Height = gdip_round(metafile->bounds.Height);
3784 return Ok;
3787 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3788 int nObj, LPARAM lpData)
3790 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3792 if (lpEMFR->iType == EMR_GDICOMMENT)
3794 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3796 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3798 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3800 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3801 header->Type == EmfPlusRecordTypeHeader)
3803 memcpy(dst_header, header, sizeof(*dst_header));
3807 else if (lpEMFR->iType == EMR_HEADER)
3808 return TRUE;
3810 return FALSE;
3813 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3814 MetafileHeader *header)
3816 ENHMETAHEADER3 emfheader;
3817 EmfPlusHeader emfplusheader;
3818 MetafileType metafile_type;
3820 TRACE("(%p,%p)\n", hemf, header);
3822 if(!hemf || !header)
3823 return InvalidParameter;
3825 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3826 return GenericError;
3828 emfplusheader.Header.Type = 0;
3830 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3832 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3834 if ((emfplusheader.Header.Flags & 1) == 1)
3835 metafile_type = MetafileTypeEmfPlusDual;
3836 else
3837 metafile_type = MetafileTypeEmfPlusOnly;
3839 else
3840 metafile_type = MetafileTypeEmf;
3842 header->Type = metafile_type;
3843 header->Size = emfheader.nBytes;
3844 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3845 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3846 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3847 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3848 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3849 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3850 header->u.EmfHeader = emfheader;
3852 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3854 header->Version = emfplusheader.Version;
3855 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3856 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3857 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3858 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3860 else
3862 header->Version = emfheader.nVersion;
3863 header->EmfPlusFlags = 0;
3864 header->EmfPlusHeaderSize = 0;
3865 header->LogicalDpiX = 0;
3866 header->LogicalDpiY = 0;
3869 return Ok;
3872 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3873 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3875 GpStatus status;
3876 GpMetafile *metafile;
3878 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3880 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3881 if (status == Ok)
3883 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3884 GdipDisposeImage(&metafile->image);
3886 return status;
3889 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3890 MetafileHeader *header)
3892 GpStatus status;
3893 GpMetafile *metafile;
3895 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3897 if (!filename || !header)
3898 return InvalidParameter;
3900 status = GdipCreateMetafileFromFile(filename, &metafile);
3901 if (status == Ok)
3903 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3904 GdipDisposeImage(&metafile->image);
3906 return status;
3909 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3910 MetafileHeader *header)
3912 GpStatus status;
3913 GpMetafile *metafile;
3915 TRACE("(%p,%p)\n", stream, header);
3917 if (!stream || !header)
3918 return InvalidParameter;
3920 status = GdipCreateMetafileFromStream(stream, &metafile);
3921 if (status == Ok)
3923 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3924 GdipDisposeImage(&metafile->image);
3926 return status;
3929 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3930 GpMetafile **metafile)
3932 GpStatus stat;
3933 MetafileHeader header;
3935 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3937 if(!hemf || !metafile)
3938 return InvalidParameter;
3940 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3941 if (stat != Ok)
3942 return stat;
3944 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3945 if (!*metafile)
3946 return OutOfMemory;
3948 (*metafile)->image.type = ImageTypeMetafile;
3949 (*metafile)->image.format = ImageFormatEMF;
3950 (*metafile)->image.frame_count = 1;
3951 (*metafile)->image.xres = header.DpiX;
3952 (*metafile)->image.yres = header.DpiY;
3953 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3954 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3955 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3956 / 2540.0 * header.DpiX;
3957 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3958 / 2540.0 * header.DpiY;
3959 (*metafile)->unit = UnitPixel;
3960 (*metafile)->metafile_type = header.Type;
3961 (*metafile)->hemf = hemf;
3962 (*metafile)->preserve_hemf = !delete;
3963 /* If the 31th bit of EmfPlusFlags was set, metafile was recorded with a DC for a video display.
3964 * If clear, metafile was recorded with a DC for a printer */
3965 (*metafile)->printer_display = !(header.EmfPlusFlags & (1u << 31));
3966 (*metafile)->logical_dpix = header.LogicalDpiX;
3967 (*metafile)->logical_dpiy = header.LogicalDpiY;
3968 list_init(&(*metafile)->containers);
3970 TRACE("<-- %p\n", *metafile);
3972 return Ok;
3975 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3976 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3978 UINT read;
3979 BYTE *copy;
3980 HENHMETAFILE hemf;
3981 GpStatus retval = Ok;
3983 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3985 if(!hwmf || !metafile)
3986 return InvalidParameter;
3988 *metafile = NULL;
3989 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3990 if(!read)
3991 return GenericError;
3992 copy = heap_alloc_zero(read);
3993 GetMetaFileBitsEx(hwmf, read, copy);
3995 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3996 heap_free(copy);
3998 /* FIXME: We should store and use hwmf instead of converting to hemf */
3999 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4001 if (retval == Ok)
4003 if (placeable)
4005 (*metafile)->image.xres = (REAL)placeable->Inch;
4006 (*metafile)->image.yres = (REAL)placeable->Inch;
4007 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
4008 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
4009 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
4010 placeable->BoundingBox.Left);
4011 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
4012 placeable->BoundingBox.Top);
4013 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
4015 else
4016 (*metafile)->metafile_type = MetafileTypeWmf;
4017 (*metafile)->image.format = ImageFormatWMF;
4019 if (delete) DeleteMetaFile(hwmf);
4021 else
4022 DeleteEnhMetaFile(hemf);
4023 return retval;
4026 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4027 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4029 HMETAFILE hmf;
4030 HENHMETAFILE emf;
4032 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4034 hmf = GetMetaFileW(file);
4035 if(hmf)
4036 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4038 emf = GetEnhMetaFileW(file);
4039 if(emf)
4040 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4042 return GenericError;
4045 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4046 GpMetafile **metafile)
4048 GpStatus status;
4049 IStream *stream;
4051 TRACE("(%p, %p)\n", file, metafile);
4053 if (!file || !metafile) return InvalidParameter;
4055 *metafile = NULL;
4057 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4058 if (status == Ok)
4060 status = GdipCreateMetafileFromStream(stream, metafile);
4061 IStream_Release(stream);
4063 return status;
4066 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4067 GpMetafile **metafile)
4069 GpStatus stat;
4071 TRACE("%p %p\n", stream, metafile);
4073 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4074 if (stat != Ok) return stat;
4076 if ((*metafile)->image.type != ImageTypeMetafile)
4078 GdipDisposeImage(&(*metafile)->image);
4079 *metafile = NULL;
4080 return GenericError;
4083 return Ok;
4086 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4087 UINT *limitDpi)
4089 TRACE("(%p,%p)\n", metafile, limitDpi);
4091 if (!metafile || !limitDpi)
4092 return InvalidParameter;
4094 if (!metafile->record_dc)
4095 return WrongState;
4097 *limitDpi = metafile->limit_dpi;
4099 return Ok;
4102 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4103 UINT limitDpi)
4105 TRACE("(%p,%u)\n", metafile, limitDpi);
4107 if (limitDpi == 0)
4108 limitDpi = 96;
4110 if (!metafile || limitDpi < 10)
4111 return InvalidParameter;
4113 if (!metafile->record_dc)
4114 return WrongState;
4116 metafile->limit_dpi = limitDpi;
4118 return Ok;
4121 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4122 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4123 const WCHAR* description, GpMetafile** out_metafile)
4125 static int calls;
4127 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4128 debugstr_w(description), out_metafile);
4130 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4131 return InvalidParameter;
4133 if(succ)
4134 *succ = FALSE;
4135 *out_metafile = NULL;
4137 if(!(calls++))
4138 FIXME("not implemented\n");
4140 return NotImplemented;
4143 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4144 LPBYTE pData16, INT iMapMode, INT eFlags)
4146 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4147 return NotImplemented;
4150 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4151 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4152 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4153 GpMetafile **metafile)
4155 HDC record_dc;
4156 REAL dpix, dpiy;
4157 REAL framerect_factor_x, framerect_factor_y;
4158 RECT rc, *lprc;
4159 GpStatus stat;
4161 TRACE("%s %p %d %s %d %s %p\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4162 frameUnit, debugstr_w(desc), metafile);
4164 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
4165 return InvalidParameter;
4167 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
4168 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
4170 if (pFrameRect)
4172 switch (frameUnit)
4174 case MetafileFrameUnitPixel:
4175 framerect_factor_x = 2540.0 / dpix;
4176 framerect_factor_y = 2540.0 / dpiy;
4177 break;
4178 case MetafileFrameUnitPoint:
4179 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
4180 break;
4181 case MetafileFrameUnitInch:
4182 framerect_factor_x = framerect_factor_y = 2540.0;
4183 break;
4184 case MetafileFrameUnitDocument:
4185 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
4186 break;
4187 case MetafileFrameUnitMillimeter:
4188 framerect_factor_x = framerect_factor_y = 100.0;
4189 break;
4190 case MetafileFrameUnitGdi:
4191 framerect_factor_x = framerect_factor_y = 1.0;
4192 break;
4193 default:
4194 return InvalidParameter;
4197 rc.left = framerect_factor_x * pFrameRect->X;
4198 rc.top = framerect_factor_y * pFrameRect->Y;
4199 rc.right = rc.left + framerect_factor_x * pFrameRect->Width;
4200 rc.bottom = rc.top + framerect_factor_y * pFrameRect->Height;
4202 lprc = &rc;
4204 else
4205 lprc = NULL;
4207 record_dc = CreateEnhMetaFileW(hdc, fileName, lprc, desc);
4209 if (!record_dc)
4210 return GenericError;
4212 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4213 if(!*metafile)
4215 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4216 return OutOfMemory;
4219 (*metafile)->image.type = ImageTypeMetafile;
4220 (*metafile)->image.flags = ImageFlagsNone;
4221 (*metafile)->image.palette = NULL;
4222 (*metafile)->image.xres = dpix;
4223 (*metafile)->image.yres = dpiy;
4224 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
4225 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
4226 (*metafile)->unit = UnitPixel;
4227 (*metafile)->metafile_type = type;
4228 (*metafile)->record_dc = record_dc;
4229 (*metafile)->comment_data = NULL;
4230 (*metafile)->comment_data_size = 0;
4231 (*metafile)->comment_data_length = 0;
4232 (*metafile)->limit_dpi = 96;
4233 (*metafile)->hemf = NULL;
4234 (*metafile)->printer_display = (GetDeviceCaps(record_dc, TECHNOLOGY) == DT_RASPRINTER);
4235 (*metafile)->logical_dpix = (REAL)GetDeviceCaps(record_dc, LOGPIXELSX);
4236 (*metafile)->logical_dpiy = (REAL)GetDeviceCaps(record_dc, LOGPIXELSY);
4237 list_init(&(*metafile)->containers);
4239 if (!pFrameRect)
4241 (*metafile)->auto_frame = TRUE;
4242 (*metafile)->auto_frame_min.X = 0;
4243 (*metafile)->auto_frame_min.Y = 0;
4244 (*metafile)->auto_frame_max.X = -1;
4245 (*metafile)->auto_frame_max.Y = -1;
4248 stat = METAFILE_WriteHeader(*metafile, hdc);
4250 if (stat != Ok)
4252 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4253 heap_free(*metafile);
4254 *metafile = NULL;
4255 return OutOfMemory;
4258 return stat;
4261 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4262 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4263 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4265 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4266 frameUnit, debugstr_w(desc), metafile);
4268 return NotImplemented;
4271 /*****************************************************************************
4272 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4275 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4276 GpMetafile* metafile, BOOL* conversionSuccess,
4277 const WCHAR* filename, EmfType emfType,
4278 const WCHAR* description, GpMetafile** out_metafile)
4280 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4281 return NotImplemented;
4284 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4286 LARGE_INTEGER zero;
4287 STATSTG statstg;
4288 GpStatus stat;
4289 HRESULT hr;
4291 *size = 0;
4293 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4294 if (FAILED(hr)) return hresult_to_status(hr);
4296 stat = encode_image_png(image, *stream, NULL);
4297 if (stat != Ok)
4299 IStream_Release(*stream);
4300 return stat;
4303 hr = IStream_Stat(*stream, &statstg, 1);
4304 if (FAILED(hr))
4306 IStream_Release(*stream);
4307 return hresult_to_status(hr);
4309 *size = statstg.cbSize.u.LowPart;
4311 zero.QuadPart = 0;
4312 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4313 if (FAILED(hr))
4315 IStream_Release(*stream);
4316 return hresult_to_status(hr);
4319 return Ok;
4322 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4324 HRESULT hr;
4326 record->Width = 0;
4327 record->Height = 0;
4328 record->Stride = 0;
4329 record->PixelFormat = 0;
4330 record->Type = BitmapDataTypeCompressed;
4332 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4333 if (FAILED(hr)) return hresult_to_status(hr);
4334 return Ok;
4337 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4339 EmfPlusObject *object_record;
4340 GpStatus stat;
4341 DWORD size;
4343 *id = -1;
4345 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4346 return Ok;
4348 if (image->type == ImageTypeBitmap)
4350 IStream *stream;
4351 DWORD aligned_size;
4353 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4354 if (stat != Ok) return stat;
4355 aligned_size = (size + 3) & ~3;
4357 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4358 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4359 (void**)&object_record);
4360 if (stat != Ok)
4362 IStream_Release(stream);
4363 return stat;
4365 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4367 *id = METAFILE_AddObjectId(metafile);
4368 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4369 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4370 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4372 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4373 IStream_Release(stream);
4374 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4375 return stat;
4377 else if (image->type == ImageTypeMetafile)
4379 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4380 EmfPlusMetafile *metafile_record;
4382 if (!hemf) return InvalidParameter;
4384 size = GetEnhMetaFileBits(hemf, 0, NULL);
4385 if (!size) return GenericError;
4387 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4388 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4389 (void**)&object_record);
4390 if (stat != Ok) return stat;
4392 *id = METAFILE_AddObjectId(metafile);
4393 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4394 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4395 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4396 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4397 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4398 metafile_record->MetafileDataSize = size;
4399 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4401 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4402 return GenericError;
4404 return Ok;
4406 else
4408 FIXME("not supported image type (%d)\n", image->type);
4409 return NotImplemented;
4413 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4415 EmfPlusObject *object_record;
4416 EmfPlusImageAttributes *attrs_record;
4417 GpStatus stat;
4419 *id = -1;
4421 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4422 return Ok;
4424 if (!attrs)
4425 return Ok;
4427 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4428 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4429 (void**)&object_record);
4430 if (stat != Ok) return stat;
4432 *id = METAFILE_AddObjectId(metafile);
4433 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4434 attrs_record = &object_record->ObjectData.image_attributes;
4435 attrs_record->Version = VERSION_MAGIC2;
4436 attrs_record->Reserved1 = 0;
4437 attrs_record->WrapMode = attrs->wrap;
4438 attrs_record->ClampColor = attrs->outside_color;
4439 attrs_record->ObjectClamp = attrs->clamp;
4440 attrs_record->Reserved2 = 0;
4441 return Ok;
4444 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4445 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4446 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4447 DrawImageAbort callback, VOID *callbackData)
4449 EmfPlusDrawImagePoints *draw_image_record;
4450 DWORD image_id, attributes_id;
4451 GpStatus stat;
4453 if (count != 3) return InvalidParameter;
4455 if (metafile->metafile_type == MetafileTypeEmf)
4457 FIXME("MetafileTypeEmf metafiles not supported\n");
4458 return NotImplemented;
4460 else
4461 FIXME("semi-stub\n");
4463 if (!imageAttributes)
4465 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4467 else if (image->type == ImageTypeBitmap)
4469 INT width = ((GpBitmap*)image)->width;
4470 INT height = ((GpBitmap*)image)->height;
4471 GpGraphics *graphics;
4472 GpBitmap *bitmap;
4474 stat = GdipCreateBitmapFromScan0(width, height,
4475 0, PixelFormat32bppARGB, NULL, &bitmap);
4476 if (stat != Ok) return stat;
4478 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4479 if (stat != Ok)
4481 GdipDisposeImage((GpImage*)bitmap);
4482 return stat;
4485 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4486 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4487 GdipDeleteGraphics(graphics);
4488 if (stat != Ok)
4490 GdipDisposeImage((GpImage*)bitmap);
4491 return stat;
4494 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4495 GdipDisposeImage((GpImage*)bitmap);
4497 else
4499 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4500 return NotImplemented;
4502 if (stat != Ok) return stat;
4504 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4505 if (stat != Ok) return stat;
4507 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawImagePoints,
4508 sizeof(EmfPlusDrawImagePoints), (void **)&draw_image_record);
4509 if (stat != Ok) return stat;
4511 draw_image_record->Header.Flags = image_id;
4512 draw_image_record->ImageAttributesID = attributes_id;
4513 draw_image_record->SrcUnit = UnitPixel;
4514 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres, metafile->printer_display);
4515 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres, metafile->printer_display);
4516 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres, metafile->printer_display);
4517 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres, metafile->printer_display);
4518 draw_image_record->count = 3;
4519 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4520 METAFILE_WriteRecords(metafile);
4521 return Ok;
4524 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4526 EmfPlusRecordHeader *record;
4527 GpStatus stat;
4529 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4530 return Ok;
4532 stat = METAFILE_AllocateRecord(metafile, prop, sizeof(*record), (void**)&record);
4533 if (stat != Ok) return stat;
4535 record->Flags = val;
4537 METAFILE_WriteRecords(metafile);
4538 return Ok;
4541 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4543 EmfPlusObject *object_record;
4544 GpStatus stat;
4545 DWORD size;
4547 *id = -1;
4548 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4549 return Ok;
4551 size = write_path_data(path, NULL);
4552 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4553 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4554 (void**)&object_record);
4555 if (stat != Ok) return stat;
4557 *id = METAFILE_AddObjectId(metafile);
4558 object_record->Header.Flags = *id | ObjectTypePath << 8;
4559 write_path_data(path, &object_record->ObjectData.path);
4560 return Ok;
4563 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4565 DWORD i, data_flags, pen_data_size, brush_size;
4566 EmfPlusObject *object_record;
4567 EmfPlusPenData *pen_data;
4568 GpStatus stat;
4569 BOOL result;
4571 *id = -1;
4572 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4573 return Ok;
4575 data_flags = 0;
4576 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4578 GdipIsMatrixIdentity(&pen->transform, &result);
4579 if (!result)
4581 data_flags |= PenDataTransform;
4582 pen_data_size += sizeof(EmfPlusTransformMatrix);
4584 if (pen->startcap != LineCapFlat)
4586 data_flags |= PenDataStartCap;
4587 pen_data_size += sizeof(DWORD);
4589 if (pen->endcap != LineCapFlat)
4591 data_flags |= PenDataEndCap;
4592 pen_data_size += sizeof(DWORD);
4594 if (pen->join != LineJoinMiter)
4596 data_flags |= PenDataJoin;
4597 pen_data_size += sizeof(DWORD);
4599 if (pen->miterlimit != 10.0)
4601 data_flags |= PenDataMiterLimit;
4602 pen_data_size += sizeof(REAL);
4604 if (pen->style != GP_DEFAULT_PENSTYLE)
4606 data_flags |= PenDataLineStyle;
4607 pen_data_size += sizeof(DWORD);
4609 if (pen->dashcap != DashCapFlat)
4611 data_flags |= PenDataDashedLineCap;
4612 pen_data_size += sizeof(DWORD);
4614 data_flags |= PenDataDashedLineOffset;
4615 pen_data_size += sizeof(REAL);
4616 if (pen->numdashes)
4618 data_flags |= PenDataDashedLine;
4619 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4621 if (pen->align != PenAlignmentCenter)
4623 data_flags |= PenDataNonCenter;
4624 pen_data_size += sizeof(DWORD);
4626 /* TODO: Add support for PenDataCompoundLine */
4627 if (pen->customstart)
4629 FIXME("ignoring custom start cup\n");
4631 if (pen->customend)
4633 FIXME("ignoring custom end cup\n");
4636 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4637 if (stat != Ok) return stat;
4639 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4640 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4641 (void**)&object_record);
4642 if (stat != Ok) return stat;
4644 *id = METAFILE_AddObjectId(metafile);
4645 object_record->Header.Flags = *id | ObjectTypePen << 8;
4646 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4647 object_record->ObjectData.pen.Type = 0;
4649 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4650 pen_data->PenDataFlags = data_flags;
4651 pen_data->PenUnit = pen->unit;
4652 pen_data->PenWidth = pen->width;
4654 i = 0;
4655 if (data_flags & PenDataTransform)
4657 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4658 memcpy(m, &pen->transform, sizeof(*m));
4659 i += sizeof(EmfPlusTransformMatrix);
4661 if (data_flags & PenDataStartCap)
4663 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4664 i += sizeof(DWORD);
4666 if (data_flags & PenDataEndCap)
4668 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4669 i += sizeof(DWORD);
4671 if (data_flags & PenDataJoin)
4673 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4674 i += sizeof(DWORD);
4676 if (data_flags & PenDataMiterLimit)
4678 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4679 i += sizeof(REAL);
4681 if (data_flags & PenDataLineStyle)
4683 switch (pen->style & PS_STYLE_MASK)
4685 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4686 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4687 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4688 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4689 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4690 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4692 i += sizeof(DWORD);
4694 if (data_flags & PenDataDashedLineCap)
4696 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4697 i += sizeof(DWORD);
4699 if (data_flags & PenDataDashedLineOffset)
4701 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4702 i += sizeof(REAL);
4704 if (data_flags & PenDataDashedLine)
4706 int j;
4708 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4709 i += sizeof(DWORD);
4711 for (j=0; j<pen->numdashes; j++)
4713 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4714 i += sizeof(REAL);
4717 if (data_flags & PenDataNonCenter)
4719 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4720 i += sizeof(DWORD);
4723 METAFILE_FillBrushData(pen->brush,
4724 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4725 return Ok;
4728 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4730 EmfPlusDrawPath *draw_path_record;
4731 DWORD path_id;
4732 DWORD pen_id;
4733 GpStatus stat;
4735 if (metafile->metafile_type == MetafileTypeEmf)
4737 FIXME("stub!\n");
4738 return NotImplemented;
4741 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4742 if (stat != Ok) return stat;
4744 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4745 if (stat != Ok) return stat;
4747 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawPath,
4748 sizeof(EmfPlusDrawPath), (void **)&draw_path_record);
4749 if (stat != Ok) return stat;
4750 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4751 draw_path_record->Header.Flags = path_id;
4752 draw_path_record->PenId = pen_id;
4754 METAFILE_WriteRecords(metafile);
4755 return Ok;
4758 GpStatus METAFILE_DrawEllipse(GpMetafile *metafile, GpPen *pen, GpRectF *rect)
4760 EmfPlusDrawEllipse *record;
4761 GpStatus stat;
4762 DWORD pen_id;
4764 if (metafile->metafile_type == MetafileTypeEmf)
4766 FIXME("stub!\n");
4767 return NotImplemented;
4770 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4771 if (stat != Ok) return stat;
4773 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawEllipse,
4774 sizeof(EmfPlusDrawEllipse), (void **)&record);
4775 if (stat != Ok) return stat;
4776 record->Header.Type = EmfPlusRecordTypeDrawEllipse;
4777 record->Header.Flags = pen_id;
4778 if (is_integer_rect(rect))
4780 record->Header.Flags |= 0x4000;
4781 record->RectData.rect.X = (SHORT)rect->X;
4782 record->RectData.rect.Y = (SHORT)rect->Y;
4783 record->RectData.rect.Width = (SHORT)rect->Width;
4784 record->RectData.rect.Height = (SHORT)rect->Height;
4786 else
4787 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4789 METAFILE_WriteRecords(metafile);
4790 return Ok;
4793 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4795 EmfPlusFillPath *fill_path_record;
4796 DWORD brush_id = -1, path_id;
4797 BOOL inline_color;
4798 GpStatus stat;
4800 if (metafile->metafile_type == MetafileTypeEmf)
4802 FIXME("stub!\n");
4803 return NotImplemented;
4806 inline_color = brush->bt == BrushTypeSolidColor;
4807 if (!inline_color)
4809 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4810 if (stat != Ok) return stat;
4813 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4814 if (stat != Ok) return stat;
4816 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPath,
4817 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4818 if (stat != Ok) return stat;
4819 if (inline_color)
4821 fill_path_record->Header.Flags = 0x8000 | path_id;
4822 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4824 else
4826 fill_path_record->Header.Flags = path_id;
4827 fill_path_record->data.BrushId = brush_id;
4830 METAFILE_WriteRecords(metafile);
4831 return Ok;
4834 GpStatus METAFILE_FillEllipse(GpMetafile *metafile, GpBrush *brush, GpRectF *rect)
4836 EmfPlusFillEllipse *record;
4837 DWORD brush_id = -1;
4838 BOOL inline_color;
4839 GpStatus stat;
4841 if (metafile->metafile_type == MetafileTypeEmf)
4843 FIXME("stub!\n");
4844 return NotImplemented;
4847 inline_color = brush->bt == BrushTypeSolidColor;
4848 if (!inline_color)
4850 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4851 if (stat != Ok) return stat;
4854 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillEllipse, sizeof(EmfPlusFillEllipse), (void **)&record);
4855 if (stat != Ok) return stat;
4856 if (inline_color)
4858 record->Header.Flags = 0x8000;
4859 record->BrushId = ((GpSolidFill *)brush)->color;
4861 else
4862 record->BrushId = brush_id;
4864 if (is_integer_rect(rect))
4866 record->Header.Flags |= 0x4000;
4867 record->RectData.rect.X = (SHORT)rect->X;
4868 record->RectData.rect.Y = (SHORT)rect->Y;
4869 record->RectData.rect.Width = (SHORT)rect->Width;
4870 record->RectData.rect.Height = (SHORT)rect->Height;
4872 else
4873 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4875 METAFILE_WriteRecords(metafile);
4876 return Ok;
4879 GpStatus METAFILE_FillPie(GpMetafile *metafile, GpBrush *brush, const GpRectF *rect,
4880 REAL startAngle, REAL sweepAngle)
4882 BOOL is_int_rect, inline_color;
4883 EmfPlusFillPie *record;
4884 DWORD brush_id = -1;
4885 GpStatus stat;
4887 if (metafile->metafile_type == MetafileTypeEmf)
4889 FIXME("stub!\n");
4890 return NotImplemented;
4893 inline_color = brush->bt == BrushTypeSolidColor;
4894 if (!inline_color)
4896 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4897 if (stat != Ok) return stat;
4900 is_int_rect = is_integer_rect(rect);
4902 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillPie,
4903 FIELD_OFFSET(EmfPlusFillPie, RectData) + is_int_rect ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF),
4904 (void **)&record);
4905 if (stat != Ok) return stat;
4906 if (inline_color)
4908 record->Header.Flags = 0x8000;
4909 record->BrushId = ((GpSolidFill *)brush)->color;
4911 else
4912 record->BrushId = brush_id;
4914 record->StartAngle = startAngle;
4915 record->SweepAngle = sweepAngle;
4917 if (is_int_rect)
4919 record->Header.Flags |= 0x4000;
4920 record->RectData.rect.X = (SHORT)rect->X;
4921 record->RectData.rect.Y = (SHORT)rect->Y;
4922 record->RectData.rect.Width = (SHORT)rect->Width;
4923 record->RectData.rect.Height = (SHORT)rect->Height;
4925 else
4926 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4928 METAFILE_WriteRecords(metafile);
4929 return Ok;
4932 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
4934 EmfPlusObject *object_record;
4935 EmfPlusFont *font_record;
4936 GpStatus stat;
4937 INT fn_len;
4938 INT style;
4940 *id = -1;
4942 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4943 metafile->metafile_type != MetafileTypeEmfPlusDual)
4944 return Ok;
4946 /* The following cast is ugly, but GdipGetFontStyle does treat
4947 its first parameter as const. */
4948 stat = GdipGetFontStyle((GpFont*)font, &style);
4949 if (stat != Ok)
4950 return stat;
4952 fn_len = lstrlenW(font->family->FamilyName);
4953 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeObject,
4954 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
4955 (void**)&object_record);
4956 if (stat != Ok)
4957 return stat;
4959 *id = METAFILE_AddObjectId(metafile);
4961 object_record->Header.Flags = *id | ObjectTypeFont << 8;
4963 font_record = &object_record->ObjectData.font;
4964 font_record->Version = VERSION_MAGIC2;
4965 font_record->EmSize = font->emSize;
4966 font_record->SizeUnit = font->unit;
4967 font_record->FontStyleFlags = style;
4968 font_record->Reserved = 0;
4969 font_record->Length = fn_len;
4971 memcpy(font_record->FamilyName, font->family->FamilyName,
4972 fn_len * sizeof(*font->family->FamilyName));
4974 return Ok;
4977 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
4978 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
4979 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
4981 DWORD brush_id;
4982 DWORD font_id;
4983 DWORD alloc_size;
4984 GpStatus stat;
4985 EmfPlusDrawDriverString *draw_string_record;
4986 BYTE *cursor;
4987 BOOL inline_color;
4988 BOOL include_matrix = FALSE;
4990 if (length <= 0)
4991 return InvalidParameter;
4993 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4994 metafile->metafile_type != MetafileTypeEmfPlusDual)
4996 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
4997 return NotImplemented;
5000 stat = METAFILE_AddFontObject(metafile, font, &font_id);
5001 if (stat != Ok)
5002 return stat;
5004 inline_color = (brush->bt == BrushTypeSolidColor);
5005 if (!inline_color)
5007 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5008 if (stat != Ok)
5009 return stat;
5012 if (matrix)
5014 BOOL identity;
5016 stat = GdipIsMatrixIdentity(matrix, &identity);
5017 if (stat != Ok)
5018 return stat;
5020 include_matrix = !identity;
5023 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
5024 length * (sizeof(*text) + sizeof(*positions));
5026 if (include_matrix)
5027 alloc_size += sizeof(*matrix);
5029 /* Pad record to DWORD alignment. */
5030 alloc_size = (alloc_size + 3) & ~3;
5032 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawDriverString, alloc_size, (void**)&draw_string_record);
5033 if (stat != Ok)
5034 return stat;
5036 draw_string_record->Header.Flags = font_id;
5037 draw_string_record->DriverStringOptionsFlags = flags;
5038 draw_string_record->MatrixPresent = include_matrix;
5039 draw_string_record->GlyphCount = length;
5041 if (inline_color)
5043 draw_string_record->Header.Flags |= 0x8000;
5044 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
5046 else
5047 draw_string_record->brush.BrushId = brush_id;
5049 cursor = &draw_string_record->VariableData[0];
5051 memcpy(cursor, text, length * sizeof(*text));
5052 cursor += length * sizeof(*text);
5054 if (flags & DriverStringOptionsRealizedAdvance)
5056 static BOOL fixme_written = FALSE;
5058 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
5059 in the case of RealizedAdvance, each glyph position is computed
5060 and serialized.
5062 While native GDI+ is capable of playing back metafiles with this
5063 flag set, it is possible that some application might rely on
5064 metafiles produced from GDI+ not setting this flag. Ideally we
5065 would also compute the position of each glyph here, serialize those
5066 values, and not set DriverStringOptionsRealizedAdvance. */
5067 if (!fixme_written)
5069 fixme_written = TRUE;
5070 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
5073 *((PointF*)cursor) = *positions;
5075 else
5076 memcpy(cursor, positions, length * sizeof(*positions));
5078 if (include_matrix)
5080 cursor += length * sizeof(*positions);
5081 memcpy(cursor, matrix, sizeof(*matrix));
5084 METAFILE_WriteRecords(metafile);
5086 return Ok;
5089 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
5091 GpStatus stat;
5092 DWORD brush_id;
5093 DWORD region_id;
5094 EmfPlusFillRegion *fill_region_record;
5095 BOOL inline_color;
5097 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5098 metafile->metafile_type != MetafileTypeEmfPlusDual)
5100 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5101 return NotImplemented;
5104 inline_color = (brush->bt == BrushTypeSolidColor);
5105 if (!inline_color)
5107 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5108 if (stat != Ok)
5109 return stat;
5112 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
5113 if (stat != Ok)
5114 return stat;
5116 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeFillRegion, sizeof(EmfPlusFillRegion),
5117 (void**)&fill_region_record);
5118 if (stat != Ok)
5119 return stat;
5121 fill_region_record->Header.Flags = region_id;
5123 if (inline_color)
5125 fill_region_record->Header.Flags |= 0x8000;
5126 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
5128 else
5129 fill_region_record->data.BrushId = brush_id;
5131 METAFILE_WriteRecords(metafile);
5133 return Ok;
5136 GpStatus METAFILE_DrawRectangles(GpMetafile *metafile, GpPen *pen, const GpRectF *rects, INT count)
5138 EmfPlusDrawRects *record;
5139 GpStatus stat;
5140 BOOL integer_rects = TRUE;
5141 DWORD pen_id;
5142 int i;
5144 if (metafile->metafile_type == MetafileTypeEmf)
5146 FIXME("stub!\n");
5147 return NotImplemented;
5150 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5151 if (stat != Ok) return stat;
5153 for (i = 0; i < count; i++)
5155 if (!is_integer_rect(&rects[i]))
5157 integer_rects = FALSE;
5158 break;
5162 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawRects, FIELD_OFFSET(EmfPlusDrawRects, RectData) +
5163 count * (integer_rects ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF)),
5164 (void **)&record);
5165 if (stat != Ok)
5166 return stat;
5168 record->Header.Flags = pen_id;
5169 if (integer_rects)
5170 record->Header.Flags |= 0x4000;
5171 record->Count = count;
5173 if (integer_rects)
5175 for (i = 0; i < count; i++)
5177 record->RectData.rect[i].X = (SHORT)rects[i].X;
5178 record->RectData.rect[i].Y = (SHORT)rects[i].Y;
5179 record->RectData.rect[i].Width = (SHORT)rects[i].Width;
5180 record->RectData.rect[i].Height = (SHORT)rects[i].Height;
5183 else
5184 memcpy(record->RectData.rectF, rects, sizeof(*rects) * count);
5186 METAFILE_WriteRecords(metafile);
5188 return Ok;
5191 GpStatus METAFILE_DrawArc(GpMetafile *metafile, GpPen *pen, const GpRectF *rect, REAL startAngle, REAL sweepAngle)
5193 EmfPlusDrawArc *record;
5194 GpStatus stat;
5195 BOOL integer_rect;
5196 DWORD pen_id;
5198 if (metafile->metafile_type == MetafileTypeEmf)
5200 FIXME("stub!\n");
5201 return NotImplemented;
5204 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5205 if (stat != Ok) return stat;
5207 integer_rect = is_integer_rect(rect);
5209 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeDrawArc, FIELD_OFFSET(EmfPlusDrawArc, RectData) +
5210 integer_rect ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF),
5211 (void **)&record);
5212 if (stat != Ok)
5213 return stat;
5215 record->Header.Flags = pen_id;
5216 if (integer_rect)
5217 record->Header.Flags |= 0x4000;
5218 record->StartAngle = startAngle;
5219 record->SweepAngle = sweepAngle;
5221 if (integer_rect)
5223 record->RectData.rect.X = (SHORT)rect->X;
5224 record->RectData.rect.Y = (SHORT)rect->Y;
5225 record->RectData.rect.Width = (SHORT)rect->Width;
5226 record->RectData.rect.Height = (SHORT)rect->Height;
5228 else
5229 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5231 METAFILE_WriteRecords(metafile);
5233 return Ok;
5236 GpStatus METAFILE_OffsetClip(GpMetafile *metafile, REAL dx, REAL dy)
5238 EmfPlusOffsetClip *record;
5239 GpStatus stat;
5241 if (metafile->metafile_type == MetafileTypeEmf)
5243 FIXME("stub!\n");
5244 return NotImplemented;
5247 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeOffsetClip,
5248 sizeof(*record), (void **)&record);
5249 if (stat != Ok)
5250 return stat;
5252 record->dx = dx;
5253 record->dy = dy;
5255 METAFILE_WriteRecords(metafile);
5257 return Ok;
5260 GpStatus METAFILE_ResetClip(GpMetafile *metafile)
5262 EmfPlusRecordHeader *record;
5263 GpStatus stat;
5265 if (metafile->metafile_type == MetafileTypeEmf)
5267 FIXME("stub!\n");
5268 return NotImplemented;
5271 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeResetClip,
5272 sizeof(*record), (void **)&record);
5273 if (stat != Ok)
5274 return stat;
5276 METAFILE_WriteRecords(metafile);
5278 return Ok;
5281 GpStatus METAFILE_SetClipPath(GpMetafile *metafile, GpPath *path, CombineMode mode)
5283 EmfPlusRecordHeader *record;
5284 DWORD path_id;
5285 GpStatus stat;
5287 if (metafile->metafile_type == MetafileTypeEmf)
5289 FIXME("stub!\n");
5290 return NotImplemented;
5293 stat = METAFILE_AddPathObject(metafile, path, &path_id);
5294 if (stat != Ok) return stat;
5296 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetClipPath,
5297 sizeof(*record), (void **)&record);
5298 if (stat != Ok)
5299 return stat;
5301 record->Flags = ((mode & 0xf) << 8) | path_id;
5303 METAFILE_WriteRecords(metafile);
5305 return Ok;
5308 GpStatus METAFILE_SetRenderingOrigin(GpMetafile *metafile, INT x, INT y)
5310 EmfPlusSetRenderingOrigin *record;
5311 GpStatus stat;
5313 if (metafile->metafile_type == MetafileTypeEmf)
5315 FIXME("stub!\n");
5316 return NotImplemented;
5319 stat = METAFILE_AllocateRecord(metafile, EmfPlusRecordTypeSetRenderingOrigin,
5320 sizeof(*record), (void **)&record);
5321 if (stat != Ok)
5322 return stat;
5324 record->x = x;
5325 record->y = y;
5327 METAFILE_WriteRecords(metafile);
5329 return Ok;