windows.gaming.input: Fake empty IGamepadStatics::Gamepads vector.
[wine/zf.git] / dlls / gdiplus / metafile.c
blob5ff6b113c3d4f6251692a85191279fdf96470ec6
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 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
578 struct emfplus_object *object = &metafile->objtable[id];
580 switch (object->type)
582 case ObjectTypeInvalid:
583 break;
584 case ObjectTypeBrush:
585 GdipDeleteBrush(object->u.brush);
586 break;
587 case ObjectTypePen:
588 GdipDeletePen(object->u.pen);
589 break;
590 case ObjectTypePath:
591 GdipDeletePath(object->u.path);
592 break;
593 case ObjectTypeRegion:
594 GdipDeleteRegion(object->u.region);
595 break;
596 case ObjectTypeImage:
597 GdipDisposeImage(object->u.image);
598 break;
599 case ObjectTypeFont:
600 GdipDeleteFont(object->u.font);
601 break;
602 case ObjectTypeImageAttributes:
603 GdipDisposeImageAttributes(object->u.image_attributes);
604 break;
605 default:
606 FIXME("not implemented for object type %u.\n", object->type);
607 return;
610 object->type = ObjectTypeInvalid;
611 object->u.object = NULL;
614 void METAFILE_Free(GpMetafile *metafile)
616 unsigned int i;
618 heap_free(metafile->comment_data);
619 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
620 if (!metafile->preserve_hemf)
621 DeleteEnhMetaFile(metafile->hemf);
622 if (metafile->record_graphics)
624 WARN("metafile closed while recording\n");
625 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
626 metafile->record_graphics->image = NULL;
627 metafile->record_graphics->busy = TRUE;
630 if (metafile->record_stream)
631 IStream_Release(metafile->record_stream);
633 for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
634 metafile_free_object_table_entry(metafile, i);
637 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
639 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
642 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
644 DWORD size_needed;
645 EmfPlusRecordHeader *record;
647 if (!metafile->comment_data_size)
649 DWORD data_size = max(256, size * 2 + 4);
650 metafile->comment_data = heap_alloc_zero(data_size);
652 if (!metafile->comment_data)
653 return OutOfMemory;
655 memcpy(metafile->comment_data, "EMF+", 4);
657 metafile->comment_data_size = data_size;
658 metafile->comment_data_length = 4;
661 size_needed = size + metafile->comment_data_length;
663 if (size_needed > metafile->comment_data_size)
665 DWORD data_size = size_needed * 2;
666 BYTE *new_data = heap_alloc_zero(data_size);
668 if (!new_data)
669 return OutOfMemory;
671 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
673 metafile->comment_data_size = data_size;
674 heap_free(metafile->comment_data);
675 metafile->comment_data = new_data;
678 *result = metafile->comment_data + metafile->comment_data_length;
679 metafile->comment_data_length += size;
681 record = (EmfPlusRecordHeader*)*result;
682 record->Size = size;
683 record->DataSize = size - sizeof(EmfPlusRecordHeader);
685 return Ok;
688 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
690 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
691 metafile->comment_data_length -= record->Size;
694 static void METAFILE_WriteRecords(GpMetafile *metafile)
696 if (metafile->comment_data_length > 4)
698 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
699 metafile->comment_data_length = 4;
703 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
705 GpStatus stat;
707 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
709 EmfPlusHeader *header;
711 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
712 if (stat != Ok)
713 return stat;
715 header->Header.Type = EmfPlusRecordTypeHeader;
717 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
718 header->Header.Flags = 1;
719 else
720 header->Header.Flags = 0;
722 header->Version = VERSION_MAGIC2;
724 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
725 header->EmfPlusFlags = 1;
726 else
727 header->EmfPlusFlags = 0;
729 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
730 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
732 METAFILE_WriteRecords(metafile);
735 return Ok;
738 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
740 GpStatus stat;
742 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
744 EmfPlusRecordHeader *record;
746 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
747 if (stat != Ok)
748 return stat;
750 record->Type = EmfPlusRecordTypeEndOfFile;
751 record->Flags = 0;
753 METAFILE_WriteRecords(metafile);
756 return Ok;
759 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
760 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
763 TRACE("(%p %d %s %d %p %p)\n", hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
765 return GdipRecordMetafileFileName(NULL, hdc, type, frameRect, frameUnit, desc, metafile);
768 /*****************************************************************************
769 * GdipRecordMetafileI [GDIPLUS.@]
771 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
772 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
774 GpRectF frameRectF, *pFrameRectF;
776 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
778 if (frameRect)
780 frameRectF.X = frameRect->X;
781 frameRectF.Y = frameRect->Y;
782 frameRectF.Width = frameRect->Width;
783 frameRectF.Height = frameRect->Height;
784 pFrameRectF = &frameRectF;
786 else
787 pFrameRectF = NULL;
789 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
792 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
793 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
795 GpRectF frameRectF, *pFrameRectF;
797 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
799 if (frameRect)
801 frameRectF.X = frameRect->X;
802 frameRectF.Y = frameRect->Y;
803 frameRectF.Width = frameRect->Width;
804 frameRectF.Height = frameRect->Height;
805 pFrameRectF = &frameRectF;
807 else
808 pFrameRectF = NULL;
810 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
813 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
814 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
816 GpStatus stat;
818 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
820 if (!stream)
821 return InvalidParameter;
823 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
825 if (stat == Ok)
827 (*metafile)->record_stream = stream;
828 IStream_AddRef(stream);
831 return stat;
834 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
835 UINT num_points)
837 int i;
839 if (!metafile->auto_frame || !num_points)
840 return;
842 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
843 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
845 for (i=0; i<num_points; i++)
847 if (points[i].X < metafile->auto_frame_min.X)
848 metafile->auto_frame_min.X = points[i].X;
849 if (points[i].X > metafile->auto_frame_max.X)
850 metafile->auto_frame_max.X = points[i].X;
851 if (points[i].Y < metafile->auto_frame_min.Y)
852 metafile->auto_frame_min.Y = points[i].Y;
853 if (points[i].Y > metafile->auto_frame_max.Y)
854 metafile->auto_frame_max.Y = points[i].Y;
858 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
860 GpStatus stat;
862 if (!metafile->record_dc || metafile->record_graphics)
863 return InvalidParameter;
865 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
867 if (stat == Ok)
869 *result = metafile->record_graphics;
870 metafile->record_graphics->xres = metafile->logical_dpix;
871 metafile->record_graphics->yres = metafile->logical_dpiy;
872 metafile->record_graphics->printer_display = metafile->printer_display;
875 return stat;
878 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
880 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
882 EmfPlusRecordHeader *record;
883 GpStatus stat;
885 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
886 if (stat != Ok)
887 return stat;
889 record->Type = EmfPlusRecordTypeGetDC;
890 record->Flags = 0;
892 METAFILE_WriteRecords(metafile);
895 *hdc = metafile->record_dc;
897 return Ok;
900 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
902 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
904 EmfPlusClear *record;
905 GpStatus stat;
907 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
908 if (stat != Ok)
909 return stat;
911 record->Header.Type = EmfPlusRecordTypeClear;
912 record->Header.Flags = 0;
913 record->Color = color;
915 METAFILE_WriteRecords(metafile);
918 return Ok;
921 static BOOL is_integer_rect(const GpRectF *rect)
923 SHORT x, y, width, height;
924 x = rect->X;
925 y = rect->Y;
926 width = rect->Width;
927 height = rect->Height;
928 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
929 rect->Width != (REAL)width || rect->Height != (REAL)height)
930 return FALSE;
931 return TRUE;
934 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
936 switch (brush->bt)
938 case BrushTypeSolidColor:
939 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
940 break;
941 case BrushTypeHatchFill:
942 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
943 break;
944 case BrushTypeLinearGradient:
946 BOOL ignore_xform;
947 GpLineGradient *gradient = (GpLineGradient*)brush;
949 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
951 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
952 if (!ignore_xform)
953 *size += sizeof(gradient->transform);
955 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
956 *size += sizeof(DWORD) + gradient->pblendcount *
957 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
958 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
959 *size += sizeof(DWORD) + gradient->blendcount *
960 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
962 break;
964 default:
965 FIXME("unsupported brush type: %d\n", brush->bt);
966 return NotImplemented;
969 return Ok;
972 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
974 data->Version = VERSION_MAGIC2;
975 data->Type = brush->bt;
977 switch (brush->bt)
979 case BrushTypeSolidColor:
981 GpSolidFill *solid = (GpSolidFill *)brush;
982 data->BrushData.solid.SolidColor = solid->color;
983 break;
985 case BrushTypeHatchFill:
987 GpHatch *hatch = (GpHatch *)brush;
988 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
989 data->BrushData.hatch.ForeColor = hatch->forecol;
990 data->BrushData.hatch.BackColor = hatch->backcol;
991 break;
993 case BrushTypeLinearGradient:
995 BYTE *cursor;
996 BOOL ignore_xform;
997 GpLineGradient *gradient = (GpLineGradient*)brush;
999 data->BrushData.lineargradient.BrushDataFlags = 0;
1000 data->BrushData.lineargradient.WrapMode = gradient->wrap;
1001 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
1002 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
1003 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
1004 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
1005 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1006 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1007 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1008 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1010 if (gradient->gamma)
1011 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1013 cursor = &data->BrushData.lineargradient.OptionalData[0];
1015 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1016 if (!ignore_xform)
1018 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1019 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1020 cursor += sizeof(gradient->transform);
1023 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1025 const DWORD count = gradient->pblendcount;
1027 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1029 memcpy(cursor, &count, sizeof(count));
1030 cursor += sizeof(count);
1032 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1033 cursor += count * sizeof(*gradient->pblendpos);
1035 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1037 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1039 const DWORD count = gradient->blendcount;
1041 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1043 memcpy(cursor, &count, sizeof(count));
1044 cursor += sizeof(count);
1046 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1047 cursor += count * sizeof(*gradient->blendpos);
1049 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1052 break;
1054 default:
1055 FIXME("unsupported brush type: %d\n", brush->bt);
1059 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1061 EmfPlusObject *object_record;
1062 GpStatus stat;
1063 DWORD size;
1065 *id = -1;
1066 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1067 return Ok;
1069 stat = METAFILE_PrepareBrushData(brush, &size);
1070 if (stat != Ok) return stat;
1072 stat = METAFILE_AllocateRecord(metafile,
1073 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1074 if (stat != Ok) return stat;
1076 *id = METAFILE_AddObjectId(metafile);
1077 object_record->Header.Type = EmfPlusRecordTypeObject;
1078 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1079 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1080 return Ok;
1083 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1084 GDIPCONST GpRectF* rects, INT count)
1086 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1088 EmfPlusFillRects *record;
1089 GpStatus stat;
1090 BOOL integer_rects = TRUE;
1091 int i;
1092 DWORD brushid;
1093 int flags = 0;
1095 if (brush->bt == BrushTypeSolidColor)
1097 flags |= 0x8000;
1098 brushid = ((GpSolidFill*)brush)->color;
1100 else
1102 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1103 if (stat != Ok)
1104 return stat;
1107 for (i=0; i<count; i++)
1109 if (!is_integer_rect(&rects[i]))
1111 integer_rects = FALSE;
1112 break;
1116 if (integer_rects)
1117 flags |= 0x4000;
1119 stat = METAFILE_AllocateRecord(metafile,
1120 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1121 (void**)&record);
1122 if (stat != Ok)
1123 return stat;
1125 record->Header.Type = EmfPlusRecordTypeFillRects;
1126 record->Header.Flags = flags;
1127 record->BrushID = brushid;
1128 record->Count = count;
1130 if (integer_rects)
1132 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1133 for (i=0; i<count; i++)
1135 record_rects[i].X = (SHORT)rects[i].X;
1136 record_rects[i].Y = (SHORT)rects[i].Y;
1137 record_rects[i].Width = (SHORT)rects[i].Width;
1138 record_rects[i].Height = (SHORT)rects[i].Height;
1141 else
1142 memcpy(record+1, rects, sizeof(GpRectF) * count);
1144 METAFILE_WriteRecords(metafile);
1147 if (metafile->auto_frame)
1149 GpPointF corners[4];
1150 int i;
1152 for (i=0; i<count; i++)
1154 corners[0].X = rects[i].X;
1155 corners[0].Y = rects[i].Y;
1156 corners[1].X = rects[i].X + rects[i].Width;
1157 corners[1].Y = rects[i].Y;
1158 corners[2].X = rects[i].X;
1159 corners[2].Y = rects[i].Y + rects[i].Height;
1160 corners[3].X = rects[i].X + rects[i].Width;
1161 corners[3].Y = rects[i].Y + rects[i].Height;
1163 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1164 CoordinateSpaceWorld, corners, 4);
1166 METAFILE_AdjustFrame(metafile, corners, 4);
1170 return Ok;
1173 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1175 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1177 EmfPlusSetClipRect *record;
1178 GpStatus stat;
1180 stat = METAFILE_AllocateRecord(metafile,
1181 sizeof(EmfPlusSetClipRect),
1182 (void**)&record);
1183 if (stat != Ok)
1184 return stat;
1186 record->Header.Type = EmfPlusRecordTypeSetClipRect;
1187 record->Header.Flags = (mode & 0xf) << 8;
1188 record->ClipRect.X = x;
1189 record->ClipRect.Y = y;
1190 record->ClipRect.Width = width;
1191 record->ClipRect.Height = height;
1193 METAFILE_WriteRecords(metafile);
1196 return Ok;
1199 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1201 EmfPlusObject *object_record;
1202 DWORD size;
1203 GpStatus stat;
1205 *id = -1;
1206 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1207 return Ok;
1209 size = write_region_data(region, NULL);
1210 stat = METAFILE_AllocateRecord(metafile,
1211 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1212 if (stat != Ok) return stat;
1214 *id = METAFILE_AddObjectId(metafile);
1215 object_record->Header.Type = EmfPlusRecordTypeObject;
1216 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1217 write_region_data(region, &object_record->ObjectData.region);
1218 return Ok;
1221 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1223 EmfPlusRecordHeader *record;
1224 DWORD region_id;
1225 GpStatus stat;
1227 if (metafile->metafile_type == MetafileTypeEmf)
1229 FIXME("stub!\n");
1230 return NotImplemented;
1233 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1234 if (stat != Ok) return stat;
1236 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1237 if (stat != Ok) return stat;
1239 record->Type = EmfPlusRecordTypeSetClipRegion;
1240 record->Flags = region_id | mode << 8;
1242 METAFILE_WriteRecords(metafile);
1243 return Ok;
1246 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1248 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1250 EmfPlusSetPageTransform *record;
1251 GpStatus stat;
1253 stat = METAFILE_AllocateRecord(metafile,
1254 sizeof(EmfPlusSetPageTransform),
1255 (void**)&record);
1256 if (stat != Ok)
1257 return stat;
1259 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1260 record->Header.Flags = unit;
1261 record->PageScale = scale;
1263 METAFILE_WriteRecords(metafile);
1266 return Ok;
1269 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1271 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1273 EmfPlusSetWorldTransform *record;
1274 GpStatus stat;
1276 stat = METAFILE_AllocateRecord(metafile,
1277 sizeof(EmfPlusSetWorldTransform),
1278 (void**)&record);
1279 if (stat != Ok)
1280 return stat;
1282 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1283 record->Header.Flags = 0;
1284 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1286 METAFILE_WriteRecords(metafile);
1289 return Ok;
1292 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1294 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1296 EmfPlusScaleWorldTransform *record;
1297 GpStatus stat;
1299 stat = METAFILE_AllocateRecord(metafile,
1300 sizeof(EmfPlusScaleWorldTransform),
1301 (void**)&record);
1302 if (stat != Ok)
1303 return stat;
1305 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1306 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1307 record->Sx = sx;
1308 record->Sy = sy;
1310 METAFILE_WriteRecords(metafile);
1313 return Ok;
1316 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1318 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1320 EmfPlusMultiplyWorldTransform *record;
1321 GpStatus stat;
1323 stat = METAFILE_AllocateRecord(metafile,
1324 sizeof(EmfPlusMultiplyWorldTransform),
1325 (void**)&record);
1326 if (stat != Ok)
1327 return stat;
1329 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1330 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1331 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1333 METAFILE_WriteRecords(metafile);
1336 return Ok;
1339 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1341 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1343 EmfPlusRotateWorldTransform *record;
1344 GpStatus stat;
1346 stat = METAFILE_AllocateRecord(metafile,
1347 sizeof(EmfPlusRotateWorldTransform),
1348 (void**)&record);
1349 if (stat != Ok)
1350 return stat;
1352 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1353 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1354 record->Angle = angle;
1356 METAFILE_WriteRecords(metafile);
1359 return Ok;
1362 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1364 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1366 EmfPlusTranslateWorldTransform *record;
1367 GpStatus stat;
1369 stat = METAFILE_AllocateRecord(metafile,
1370 sizeof(EmfPlusTranslateWorldTransform),
1371 (void**)&record);
1372 if (stat != Ok)
1373 return stat;
1375 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1376 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1377 record->dx = dx;
1378 record->dy = dy;
1380 METAFILE_WriteRecords(metafile);
1383 return Ok;
1386 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1388 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1390 EmfPlusRecordHeader *record;
1391 GpStatus stat;
1393 stat = METAFILE_AllocateRecord(metafile,
1394 sizeof(EmfPlusRecordHeader),
1395 (void**)&record);
1396 if (stat != Ok)
1397 return stat;
1399 record->Type = EmfPlusRecordTypeResetWorldTransform;
1400 record->Flags = 0;
1402 METAFILE_WriteRecords(metafile);
1405 return Ok;
1408 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1409 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1411 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1413 EmfPlusBeginContainer *record;
1414 GpStatus stat;
1416 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1417 if (stat != Ok)
1418 return stat;
1420 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1421 record->Header.Flags = unit & 0xff;
1422 record->DestRect = *dstrect;
1423 record->SrcRect = *srcrect;
1424 record->StackIndex = StackIndex;
1426 METAFILE_WriteRecords(metafile);
1429 return Ok;
1432 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1434 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1436 EmfPlusContainerRecord *record;
1437 GpStatus stat;
1439 stat = METAFILE_AllocateRecord(metafile,
1440 sizeof(EmfPlusContainerRecord),
1441 (void**)&record);
1442 if (stat != Ok)
1443 return stat;
1445 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1446 record->Header.Flags = 0;
1447 record->StackIndex = StackIndex;
1449 METAFILE_WriteRecords(metafile);
1452 return Ok;
1455 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1457 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1459 EmfPlusContainerRecord *record;
1460 GpStatus stat;
1462 stat = METAFILE_AllocateRecord(metafile,
1463 sizeof(EmfPlusContainerRecord),
1464 (void**)&record);
1465 if (stat != Ok)
1466 return stat;
1468 record->Header.Type = EmfPlusRecordTypeEndContainer;
1469 record->Header.Flags = 0;
1470 record->StackIndex = StackIndex;
1472 METAFILE_WriteRecords(metafile);
1475 return Ok;
1478 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1480 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1482 EmfPlusContainerRecord *record;
1483 GpStatus stat;
1485 stat = METAFILE_AllocateRecord(metafile,
1486 sizeof(EmfPlusContainerRecord),
1487 (void**)&record);
1488 if (stat != Ok)
1489 return stat;
1491 record->Header.Type = EmfPlusRecordTypeSave;
1492 record->Header.Flags = 0;
1493 record->StackIndex = StackIndex;
1495 METAFILE_WriteRecords(metafile);
1498 return Ok;
1501 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1503 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1505 EmfPlusContainerRecord *record;
1506 GpStatus stat;
1508 stat = METAFILE_AllocateRecord(metafile,
1509 sizeof(EmfPlusContainerRecord),
1510 (void**)&record);
1511 if (stat != Ok)
1512 return stat;
1514 record->Header.Type = EmfPlusRecordTypeRestore;
1515 record->Header.Flags = 0;
1516 record->StackIndex = StackIndex;
1518 METAFILE_WriteRecords(metafile);
1521 return Ok;
1524 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1526 if (hdc != metafile->record_dc)
1527 return InvalidParameter;
1529 return Ok;
1532 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1534 GpStatus stat;
1536 stat = METAFILE_WriteEndOfFile(metafile);
1537 metafile->record_graphics = NULL;
1539 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1540 metafile->record_dc = NULL;
1542 heap_free(metafile->comment_data);
1543 metafile->comment_data = NULL;
1544 metafile->comment_data_size = 0;
1546 if (stat == Ok)
1548 MetafileHeader header;
1550 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1551 if (stat == Ok && metafile->auto_frame &&
1552 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1554 RECTL bounds_rc, gdi_bounds_rc;
1555 REAL x_scale = 2540.0 / header.DpiX;
1556 REAL y_scale = 2540.0 / header.DpiY;
1557 BYTE* buffer;
1558 UINT buffer_size;
1560 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1561 if (gdi_bounds_rc.right > gdi_bounds_rc.left &&
1562 gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1564 GpPointF *af_min = &metafile->auto_frame_min;
1565 GpPointF *af_max = &metafile->auto_frame_max;
1567 af_min->X = fmin(af_min->X, gdi_bounds_rc.left);
1568 af_min->Y = fmin(af_min->Y, gdi_bounds_rc.top);
1569 af_max->X = fmax(af_max->X, gdi_bounds_rc.right);
1570 af_max->Y = fmax(af_max->Y, gdi_bounds_rc.bottom);
1573 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1574 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1575 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1576 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1578 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1579 buffer = heap_alloc(buffer_size);
1580 if (buffer)
1582 HENHMETAFILE new_hemf;
1584 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1586 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1588 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1590 if (new_hemf)
1592 DeleteEnhMetaFile(metafile->hemf);
1593 metafile->hemf = new_hemf;
1595 else
1596 stat = OutOfMemory;
1598 heap_free(buffer);
1600 else
1601 stat = OutOfMemory;
1603 if (stat == Ok)
1604 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1606 if (stat == Ok)
1608 metafile->bounds.X = header.X;
1609 metafile->bounds.Y = header.Y;
1610 metafile->bounds.Width = header.Width;
1611 metafile->bounds.Height = header.Height;
1615 if (stat == Ok && metafile->record_stream)
1617 BYTE *buffer;
1618 UINT buffer_size;
1620 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1622 buffer = heap_alloc(buffer_size);
1623 if (buffer)
1625 HRESULT hr;
1627 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1629 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1631 if (FAILED(hr))
1632 stat = hresult_to_status(hr);
1634 heap_free(buffer);
1636 else
1637 stat = OutOfMemory;
1640 if (metafile->record_stream)
1642 IStream_Release(metafile->record_stream);
1643 metafile->record_stream = NULL;
1646 return stat;
1649 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1651 TRACE("(%p,%p)\n", metafile, hEmf);
1653 if (!metafile || !hEmf || !metafile->hemf)
1654 return InvalidParameter;
1656 *hEmf = metafile->hemf;
1657 metafile->hemf = NULL;
1659 return Ok;
1662 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1664 GpStatus stat = Ok;
1666 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1668 return stat;
1671 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1673 if (metafile->playback_dc)
1675 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1676 metafile->playback_dc = NULL;
1680 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1682 GpStatus stat;
1683 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1684 if (stat == Ok)
1685 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1686 return stat;
1689 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1691 GpMatrix *real_transform;
1692 GpStatus stat;
1694 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1696 if (stat == Ok)
1698 REAL scale_x = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpix, metafile->printer_display);
1699 REAL scale_y = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpiy, metafile->printer_display);
1701 if (metafile->page_unit != UnitDisplay)
1703 scale_x *= metafile->page_scale;
1704 scale_y *= metafile->page_scale;
1707 stat = GdipScaleMatrix(real_transform, scale_x, scale_y, MatrixOrderPrepend);
1709 if (stat == Ok)
1710 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1712 if (stat == Ok)
1713 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1715 GdipDeleteMatrix(real_transform);
1718 return stat;
1721 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1723 metafile_free_object_table_entry(metafile, id);
1724 metafile->objtable[id].type = type;
1725 metafile->objtable[id].u.object = object;
1728 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1730 EmfPlusImage *data = (EmfPlusImage *)record_data;
1731 GpStatus status;
1733 *image = NULL;
1735 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1736 return InvalidParameter;
1737 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1739 switch (data->Type)
1741 case ImageDataTypeBitmap:
1743 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1745 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1746 return InvalidParameter;
1747 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1749 switch (bitmapdata->Type)
1751 case BitmapDataTypePixel:
1753 ColorPalette *palette;
1754 BYTE *scan0;
1756 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1758 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1759 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1761 if (data_size <= palette_size)
1762 return InvalidParameter;
1763 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1765 if (data_size < palette_size)
1766 return InvalidParameter;
1767 data_size -= palette_size;
1769 palette = (ColorPalette *)bitmapdata->BitmapData;
1770 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1772 else
1774 palette = NULL;
1775 scan0 = bitmapdata->BitmapData;
1778 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1779 return InvalidParameter;
1781 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1782 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1783 if (status == Ok && palette)
1785 status = GdipSetImagePalette(*image, palette);
1786 if (status != Ok)
1788 GdipDisposeImage(*image);
1789 *image = NULL;
1792 break;
1794 case BitmapDataTypeCompressed:
1796 IWICImagingFactory *factory;
1797 IWICStream *stream;
1798 HRESULT hr;
1800 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1801 return GenericError;
1803 hr = IWICImagingFactory_CreateStream(factory, &stream);
1804 IWICImagingFactory_Release(factory);
1805 if (hr != S_OK)
1806 return GenericError;
1808 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1809 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1810 else
1811 status = GenericError;
1813 IWICStream_Release(stream);
1814 break;
1816 default:
1817 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1818 return InvalidParameter;
1820 break;
1822 case ImageDataTypeMetafile:
1824 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1826 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1827 return InvalidParameter;
1828 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1830 switch (metafiledata->Type) {
1831 case MetafileTypeEmf:
1832 case MetafileTypeEmfPlusOnly:
1833 case MetafileTypeEmfPlusDual:
1835 HENHMETAFILE hemf;
1837 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1839 if (!hemf)
1840 return GenericError;
1842 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1844 if (status != Ok)
1845 DeleteEnhMetaFile(hemf);
1847 break;
1849 default:
1850 FIXME("metafile type %d not supported.\n", metafiledata->Type);
1851 return NotImplemented;
1853 break;
1855 default:
1856 FIXME("image type %d not supported.\n", data->Type);
1857 return NotImplemented;
1860 return status;
1863 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1865 EmfPlusPath *data = (EmfPlusPath *)record_data;
1866 GpStatus status;
1867 BYTE *types;
1868 UINT size;
1869 DWORD i;
1871 *path = NULL;
1873 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1874 return InvalidParameter;
1875 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1877 if (data->PathPointFlags & 0x800) /* R */
1879 FIXME("RLE encoded path data is not supported.\n");
1880 return NotImplemented;
1882 else
1884 if (data->PathPointFlags & 0x4000) /* C */
1885 size = sizeof(EmfPlusPoint);
1886 else
1887 size = sizeof(EmfPlusPointF);
1888 size += sizeof(BYTE); /* EmfPlusPathPointType */
1889 size *= data->PathPointCount;
1892 if (data_size < size)
1893 return InvalidParameter;
1895 status = GdipCreatePath(FillModeAlternate, path);
1896 if (status != Ok)
1897 return status;
1899 (*path)->pathdata.Count = data->PathPointCount;
1900 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1901 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1902 (*path)->datalen = (*path)->pathdata.Count;
1904 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1906 GdipDeletePath(*path);
1907 return OutOfMemory;
1910 if (data->PathPointFlags & 0x4000) /* C */
1912 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1913 for (i = 0; i < data->PathPointCount; i++)
1915 (*path)->pathdata.Points[i].X = points[i].X;
1916 (*path)->pathdata.Points[i].Y = points[i].Y;
1918 types = (BYTE *)(points + i);
1920 else
1922 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1923 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1924 types = (BYTE *)(points + data->PathPointCount);
1927 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1929 return Ok;
1932 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1934 const DWORD *type;
1935 GpStatus status;
1937 type = buffer_read(mbuf, sizeof(*type));
1938 if (!type) return Ok;
1940 node->type = *type;
1942 switch (node->type)
1944 case CombineModeReplace:
1945 case CombineModeIntersect:
1946 case CombineModeUnion:
1947 case CombineModeXor:
1948 case CombineModeExclude:
1949 case CombineModeComplement:
1951 region_element *left, *right;
1953 left = heap_alloc_zero(sizeof(*left));
1954 if (!left)
1955 return OutOfMemory;
1957 right = heap_alloc_zero(sizeof(*right));
1958 if (!right)
1960 heap_free(left);
1961 return OutOfMemory;
1964 status = metafile_read_region_node(mbuf, region, left, count);
1965 if (status == Ok)
1967 status = metafile_read_region_node(mbuf, region, right, count);
1968 if (status == Ok)
1970 node->elementdata.combine.left = left;
1971 node->elementdata.combine.right = right;
1972 region->num_children += 2;
1973 return Ok;
1977 heap_free(left);
1978 heap_free(right);
1979 return status;
1981 case RegionDataRect:
1983 const EmfPlusRectF *rect;
1985 rect = buffer_read(mbuf, sizeof(*rect));
1986 if (!rect)
1987 return InvalidParameter;
1989 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1990 *count += 1;
1991 return Ok;
1993 case RegionDataPath:
1995 const BYTE *path_data;
1996 const UINT *data_size;
1997 GpPath *path;
1999 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
2000 if (!data_size)
2001 return InvalidParameter;
2003 path_data = buffer_read(mbuf, *data_size);
2004 if (!path_data)
2005 return InvalidParameter;
2007 status = metafile_deserialize_path(path_data, *data_size, &path);
2008 if (status == Ok)
2010 node->elementdata.path = path;
2011 *count += 1;
2013 return Ok;
2015 case RegionDataEmptyRect:
2016 case RegionDataInfiniteRect:
2017 *count += 1;
2018 return Ok;
2019 default:
2020 FIXME("element type %#x is not supported\n", *type);
2021 break;
2024 return InvalidParameter;
2027 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
2029 struct memory_buffer mbuf;
2030 GpStatus status;
2031 UINT count;
2033 *region = NULL;
2035 init_memory_buffer(&mbuf, record_data, data_size);
2037 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2038 return InvalidParameter;
2040 status = GdipCreateRegion(region);
2041 if (status != Ok)
2042 return status;
2044 count = 0;
2045 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2046 if (status == Ok && !count)
2047 status = InvalidParameter;
2049 if (status != Ok)
2051 GdipDeleteRegion(*region);
2052 *region = NULL;
2055 return status;
2058 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2060 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2061 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2062 EmfPlusTransformMatrix *transform = NULL;
2063 DWORD brushflags;
2064 GpStatus status;
2065 UINT offset;
2067 *brush = NULL;
2069 if (data_size < header_size)
2070 return InvalidParameter;
2072 switch (data->Type)
2074 case BrushTypeSolidColor:
2075 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2076 return InvalidParameter;
2078 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2079 break;
2080 case BrushTypeHatchFill:
2081 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2082 return InvalidParameter;
2084 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2085 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2086 break;
2087 case BrushTypeTextureFill:
2089 GpImage *image;
2091 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2092 if (data_size <= offset)
2093 return InvalidParameter;
2095 brushflags = data->BrushData.texture.BrushDataFlags;
2096 if (brushflags & BrushDataTransform)
2098 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2099 return InvalidParameter;
2100 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2101 offset += sizeof(EmfPlusTransformMatrix);
2104 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2105 if (status != Ok)
2106 return status;
2108 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2109 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2110 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2112 GdipDisposeImage(image);
2113 break;
2115 case BrushTypeLinearGradient:
2117 GpLineGradient *gradient = NULL;
2118 GpRectF rect;
2119 UINT position_count = 0;
2121 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2122 if (data_size < offset)
2123 return InvalidParameter;
2125 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2126 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2127 return InvalidParameter;
2129 if (brushflags & BrushDataTransform)
2131 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2132 return InvalidParameter;
2133 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2134 offset += sizeof(EmfPlusTransformMatrix);
2137 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2139 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2140 return InvalidParameter;
2141 position_count = *(DWORD *)(record_data + offset);
2142 offset += sizeof(DWORD);
2145 if (brushflags & BrushDataPresetColors)
2147 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2148 return InvalidParameter;
2150 else if (brushflags & BrushDataBlendFactorsH)
2152 if (data_size != offset + position_count * 2 * sizeof(float))
2153 return InvalidParameter;
2156 rect.X = data->BrushData.lineargradient.RectF.X;
2157 rect.Y = data->BrushData.lineargradient.RectF.Y;
2158 rect.Width = data->BrushData.lineargradient.RectF.Width;
2159 rect.Height = data->BrushData.lineargradient.RectF.Height;
2161 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2162 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2163 data->BrushData.lineargradient.WrapMode, &gradient);
2164 if (status == Ok)
2166 if (transform)
2167 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2169 if (status == Ok)
2171 if (brushflags & BrushDataPresetColors)
2172 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2173 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2174 else if (brushflags & BrushDataBlendFactorsH)
2175 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2176 (REAL *)(record_data + offset), position_count);
2178 if (brushflags & BrushDataIsGammaCorrected)
2179 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2183 if (status == Ok)
2184 *brush = (GpBrush *)gradient;
2185 else
2186 GdipDeleteBrush((GpBrush *)gradient);
2188 break;
2190 default:
2191 FIXME("brush type %u is not supported.\n", data->Type);
2192 return NotImplemented;
2195 return status;
2198 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2200 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2201 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2203 if (data_size <= offset)
2204 return InvalidParameter;
2206 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2207 if (data_size <= offset)
2208 return InvalidParameter;
2210 if (pendata->PenDataFlags & PenDataTransform)
2211 offset += sizeof(EmfPlusTransformMatrix);
2213 if (pendata->PenDataFlags & PenDataStartCap)
2214 offset += sizeof(DWORD);
2216 if (pendata->PenDataFlags & PenDataEndCap)
2217 offset += sizeof(DWORD);
2219 if (pendata->PenDataFlags & PenDataJoin)
2220 offset += sizeof(DWORD);
2222 if (pendata->PenDataFlags & PenDataMiterLimit)
2223 offset += sizeof(REAL);
2225 if (pendata->PenDataFlags & PenDataLineStyle)
2226 offset += sizeof(DWORD);
2228 if (pendata->PenDataFlags & PenDataDashedLineCap)
2229 offset += sizeof(DWORD);
2231 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2232 offset += sizeof(REAL);
2234 if (pendata->PenDataFlags & PenDataDashedLine)
2236 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2238 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2239 if (data_size <= offset)
2240 return InvalidParameter;
2242 offset += dashedline->DashedLineDataSize * sizeof(float);
2245 if (pendata->PenDataFlags & PenDataNonCenter)
2246 offset += sizeof(DWORD);
2248 if (pendata->PenDataFlags & PenDataCompoundLine)
2250 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2252 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2253 if (data_size <= offset)
2254 return InvalidParameter;
2256 offset += compoundline->CompoundLineDataSize * sizeof(float);
2259 if (pendata->PenDataFlags & PenDataCustomStartCap)
2261 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2263 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2264 if (data_size <= offset)
2265 return InvalidParameter;
2267 offset += startcap->CustomStartCapSize;
2270 if (pendata->PenDataFlags & PenDataCustomEndCap)
2272 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2274 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2275 if (data_size <= offset)
2276 return InvalidParameter;
2278 offset += endcap->CustomEndCapSize;
2281 *ret = offset;
2282 return Ok;
2285 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2287 BYTE type = (flags >> 8) & 0xff;
2288 BYTE id = flags & 0xff;
2289 void *object = NULL;
2290 GpStatus status;
2292 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2293 return InvalidParameter;
2295 switch (type)
2297 case ObjectTypeBrush:
2298 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2299 break;
2300 case ObjectTypePen:
2302 EmfPlusPen *data = (EmfPlusPen *)record_data;
2303 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2304 GpBrush *brush;
2305 DWORD offset;
2306 GpPen *pen;
2308 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2309 if (status != Ok)
2310 return status;
2312 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2313 if (status != Ok)
2314 return status;
2316 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2317 GdipDeleteBrush(brush);
2318 if (status != Ok)
2319 return status;
2321 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2323 if (pendata->PenDataFlags & PenDataTransform)
2325 FIXME("PenDataTransform is not supported.\n");
2326 offset += sizeof(EmfPlusTransformMatrix);
2329 if (pendata->PenDataFlags & PenDataStartCap)
2331 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2332 goto penfailed;
2333 offset += sizeof(DWORD);
2336 if (pendata->PenDataFlags & PenDataEndCap)
2338 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2339 goto penfailed;
2340 offset += sizeof(DWORD);
2343 if (pendata->PenDataFlags & PenDataJoin)
2345 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2346 goto penfailed;
2347 offset += sizeof(DWORD);
2350 if (pendata->PenDataFlags & PenDataMiterLimit)
2352 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2353 goto penfailed;
2354 offset += sizeof(REAL);
2357 if (pendata->PenDataFlags & PenDataLineStyle)
2359 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2360 goto penfailed;
2361 offset += sizeof(DWORD);
2364 if (pendata->PenDataFlags & PenDataDashedLineCap)
2366 FIXME("PenDataDashedLineCap is not supported.\n");
2367 offset += sizeof(DWORD);
2370 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2372 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2373 goto penfailed;
2374 offset += sizeof(REAL);
2377 if (pendata->PenDataFlags & PenDataDashedLine)
2379 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2380 FIXME("PenDataDashedLine is not supported.\n");
2381 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2384 if (pendata->PenDataFlags & PenDataNonCenter)
2386 FIXME("PenDataNonCenter is not supported.\n");
2387 offset += sizeof(DWORD);
2390 if (pendata->PenDataFlags & PenDataCompoundLine)
2392 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2393 FIXME("PenDataCompoundLine is not supported.\n");
2394 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2397 if (pendata->PenDataFlags & PenDataCustomStartCap)
2399 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2400 FIXME("PenDataCustomStartCap is not supported.\n");
2401 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2404 if (pendata->PenDataFlags & PenDataCustomEndCap)
2406 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2407 FIXME("PenDataCustomEndCap is not supported.\n");
2408 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2411 object = pen;
2412 break;
2414 penfailed:
2415 GdipDeletePen(pen);
2416 return status;
2418 case ObjectTypePath:
2419 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2420 break;
2421 case ObjectTypeRegion:
2422 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2423 break;
2424 case ObjectTypeImage:
2425 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2426 break;
2427 case ObjectTypeFont:
2429 EmfPlusFont *data = (EmfPlusFont *)record_data;
2430 GpFontFamily *family;
2431 WCHAR *familyname;
2433 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2434 return InvalidParameter;
2435 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2437 if (data_size < data->Length * sizeof(WCHAR))
2438 return InvalidParameter;
2440 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2441 return OutOfMemory;
2443 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2444 familyname[data->Length] = 0;
2446 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2447 GdipFree(familyname);
2449 /* If a font family cannot be created from family name, native
2450 falls back to a sans serif font. */
2451 if (status != Ok)
2452 status = GdipGetGenericFontFamilySansSerif(&family);
2453 if (status != Ok)
2454 return status;
2456 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2457 GdipDeleteFontFamily(family);
2458 break;
2460 case ObjectTypeImageAttributes:
2462 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2463 GpImageAttributes *attributes = NULL;
2465 if (data_size != sizeof(*data))
2466 return InvalidParameter;
2468 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2469 return status;
2471 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2472 !!data->ObjectClamp);
2473 if (status == Ok)
2474 object = attributes;
2475 else
2476 GdipDisposeImageAttributes(attributes);
2477 break;
2479 default:
2480 FIXME("not implemented for object type %d.\n", type);
2481 return NotImplemented;
2484 if (status == Ok)
2485 metafile_set_object_table_entry(metafile, id, type, object);
2487 return status;
2490 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2492 GpMatrix world_to_device;
2494 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2496 GdipTransformRegion(region, &world_to_device);
2497 GdipCombineRegionRegion(metafile->clip, region, mode);
2499 return METAFILE_PlaybackUpdateClip(metafile);
2502 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2503 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2505 GpStatus stat;
2506 GpMetafile *real_metafile = (GpMetafile*)metafile;
2508 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2510 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2511 return InvalidParameter;
2513 if (recordType >= 1 && recordType <= 0x7a)
2515 /* regular EMF record */
2516 if (metafile->playback_dc)
2518 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2520 if (record)
2522 record->iType = recordType;
2523 record->nSize = dataSize + 8;
2524 memcpy(record->dParm, data, dataSize);
2526 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2527 record, metafile->handle_count) == 0)
2528 ERR("PlayEnhMetaFileRecord failed\n");
2530 heap_free(record);
2532 else
2533 return OutOfMemory;
2536 else
2538 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2540 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2542 switch(recordType)
2544 case EmfPlusRecordTypeHeader:
2545 case EmfPlusRecordTypeEndOfFile:
2546 break;
2547 case EmfPlusRecordTypeGetDC:
2548 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2549 break;
2550 case EmfPlusRecordTypeClear:
2552 EmfPlusClear *record = (EmfPlusClear*)header;
2554 if (dataSize != sizeof(record->Color))
2555 return InvalidParameter;
2557 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2559 case EmfPlusRecordTypeFillRects:
2561 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2562 GpBrush *brush, *temp_brush=NULL;
2563 GpRectF *rects, *temp_rects=NULL;
2565 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2566 return InvalidParameter;
2568 if (flags & 0x4000)
2570 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2571 return InvalidParameter;
2573 else
2575 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2576 return InvalidParameter;
2579 if (flags & 0x8000)
2581 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2582 brush = temp_brush;
2584 else
2586 if (record->BrushID >= EmfPlusObjectTableSize ||
2587 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2588 return InvalidParameter;
2590 brush = real_metafile->objtable[record->BrushID].u.brush;
2591 stat = Ok;
2594 if (stat == Ok)
2596 if (flags & 0x4000)
2598 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2599 int i;
2601 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2602 if (rects)
2604 for (i=0; i<record->Count; i++)
2606 rects[i].X = int_rects[i].X;
2607 rects[i].Y = int_rects[i].Y;
2608 rects[i].Width = int_rects[i].Width;
2609 rects[i].Height = int_rects[i].Height;
2612 else
2613 stat = OutOfMemory;
2615 else
2616 rects = (GpRectF*)(record+1);
2619 if (stat == Ok)
2621 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2624 GdipDeleteBrush(temp_brush);
2625 heap_free(temp_rects);
2627 return stat;
2629 case EmfPlusRecordTypeSetClipRect:
2631 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2632 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2633 GpRegion *region;
2635 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2636 return InvalidParameter;
2638 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2640 if (stat == Ok)
2642 stat = metafile_set_clip_region(real_metafile, region, mode);
2643 GdipDeleteRegion(region);
2646 return stat;
2648 case EmfPlusRecordTypeSetClipRegion:
2650 CombineMode mode = (flags >> 8) & 0xf;
2651 BYTE regionid = flags & 0xff;
2652 GpRegion *region;
2654 if (dataSize != 0)
2655 return InvalidParameter;
2657 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2658 return InvalidParameter;
2660 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2661 if (stat == Ok)
2663 stat = metafile_set_clip_region(real_metafile, region, mode);
2664 GdipDeleteRegion(region);
2667 return stat;
2669 case EmfPlusRecordTypeSetClipPath:
2671 CombineMode mode = (flags >> 8) & 0xf;
2672 BYTE pathid = flags & 0xff;
2673 GpRegion *region;
2675 if (dataSize != 0)
2676 return InvalidParameter;
2678 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2679 return InvalidParameter;
2681 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2682 if (stat == Ok)
2684 stat = metafile_set_clip_region(real_metafile, region, mode);
2685 GdipDeleteRegion(region);
2688 return stat;
2690 case EmfPlusRecordTypeSetPageTransform:
2692 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2693 GpUnit unit = (GpUnit)flags;
2695 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2696 return InvalidParameter;
2698 real_metafile->page_unit = unit;
2699 real_metafile->page_scale = record->PageScale;
2701 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2703 case EmfPlusRecordTypeSetWorldTransform:
2705 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2707 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2708 return InvalidParameter;
2710 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2712 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2714 case EmfPlusRecordTypeScaleWorldTransform:
2716 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2717 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2719 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2720 return InvalidParameter;
2722 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2724 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2726 case EmfPlusRecordTypeMultiplyWorldTransform:
2728 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2729 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2730 GpMatrix matrix;
2732 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2733 return InvalidParameter;
2735 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2737 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2739 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2741 case EmfPlusRecordTypeRotateWorldTransform:
2743 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2744 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2746 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2747 return InvalidParameter;
2749 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2751 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2753 case EmfPlusRecordTypeTranslateWorldTransform:
2755 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2756 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2758 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2759 return InvalidParameter;
2761 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2763 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2765 case EmfPlusRecordTypeResetWorldTransform:
2767 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2769 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2771 case EmfPlusRecordTypeBeginContainer:
2773 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2774 container* cont;
2775 GpUnit unit;
2776 REAL scale_x, scale_y;
2777 GpRectF scaled_srcrect;
2778 GpMatrix transform;
2780 cont = heap_alloc_zero(sizeof(*cont));
2781 if (!cont)
2782 return OutOfMemory;
2784 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2785 if (stat != Ok)
2787 heap_free(cont);
2788 return stat;
2791 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2793 if (stat != Ok)
2795 GdipDeleteRegion(cont->clip);
2796 heap_free(cont);
2797 return stat;
2800 cont->id = record->StackIndex;
2801 cont->type = BEGIN_CONTAINER;
2802 cont->world_transform = *metafile->world_transform;
2803 cont->page_unit = metafile->page_unit;
2804 cont->page_scale = metafile->page_scale;
2805 list_add_head(&real_metafile->containers, &cont->entry);
2807 unit = record->Header.Flags & 0xff;
2809 scale_x = units_to_pixels(1.0, unit, metafile->image.xres, metafile->printer_display);
2810 scale_y = units_to_pixels(1.0, unit, metafile->image.yres, metafile->printer_display);
2812 scaled_srcrect.X = scale_x * record->SrcRect.X;
2813 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2814 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2815 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2817 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2818 transform.matrix[1] = 0.0;
2819 transform.matrix[2] = 0.0;
2820 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2821 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2822 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2824 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2826 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2828 case EmfPlusRecordTypeBeginContainerNoParams:
2829 case EmfPlusRecordTypeSave:
2831 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2832 container* cont;
2834 cont = heap_alloc_zero(sizeof(*cont));
2835 if (!cont)
2836 return OutOfMemory;
2838 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2839 if (stat != Ok)
2841 heap_free(cont);
2842 return stat;
2845 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2846 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2847 else
2848 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2850 if (stat != Ok)
2852 GdipDeleteRegion(cont->clip);
2853 heap_free(cont);
2854 return stat;
2857 cont->id = record->StackIndex;
2858 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2859 cont->type = BEGIN_CONTAINER;
2860 else
2861 cont->type = SAVE_GRAPHICS;
2862 cont->world_transform = *metafile->world_transform;
2863 cont->page_unit = metafile->page_unit;
2864 cont->page_scale = metafile->page_scale;
2865 list_add_head(&real_metafile->containers, &cont->entry);
2867 break;
2869 case EmfPlusRecordTypeEndContainer:
2870 case EmfPlusRecordTypeRestore:
2872 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2873 container* cont;
2874 enum container_type type;
2875 BOOL found=FALSE;
2877 if (recordType == EmfPlusRecordTypeEndContainer)
2878 type = BEGIN_CONTAINER;
2879 else
2880 type = SAVE_GRAPHICS;
2882 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2884 if (cont->id == record->StackIndex && cont->type == type)
2886 found = TRUE;
2887 break;
2891 if (found)
2893 container* cont2;
2895 /* pop any newer items on the stack */
2896 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2898 list_remove(&cont2->entry);
2899 GdipDeleteRegion(cont2->clip);
2900 heap_free(cont2);
2903 if (type == BEGIN_CONTAINER)
2904 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2905 else
2906 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2908 *real_metafile->world_transform = cont->world_transform;
2909 real_metafile->page_unit = cont->page_unit;
2910 real_metafile->page_scale = cont->page_scale;
2911 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2913 list_remove(&cont->entry);
2914 GdipDeleteRegion(cont->clip);
2915 heap_free(cont);
2918 break;
2920 case EmfPlusRecordTypeSetPixelOffsetMode:
2922 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2924 case EmfPlusRecordTypeSetCompositingQuality:
2926 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2928 case EmfPlusRecordTypeSetInterpolationMode:
2930 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2932 case EmfPlusRecordTypeSetTextRenderingHint:
2934 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2936 case EmfPlusRecordTypeSetAntiAliasMode:
2938 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2940 case EmfPlusRecordTypeSetCompositingMode:
2942 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2944 case EmfPlusRecordTypeObject:
2946 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2948 case EmfPlusRecordTypeDrawImage:
2950 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2951 BYTE image = flags & 0xff;
2952 GpPointF points[3];
2954 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2955 return InvalidParameter;
2957 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2958 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2959 return InvalidParameter;
2961 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2962 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2963 return InvalidParameter;
2965 if (flags & 0x4000) /* C */
2967 points[0].X = draw->RectData.rect.X;
2968 points[0].Y = draw->RectData.rect.Y;
2969 points[1].X = points[0].X + draw->RectData.rect.Width;
2970 points[1].Y = points[0].Y;
2971 points[2].X = points[1].X;
2972 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2974 else
2976 points[0].X = draw->RectData.rectF.X;
2977 points[0].Y = draw->RectData.rectF.Y;
2978 points[1].X = points[0].X + draw->RectData.rectF.Width;
2979 points[1].Y = points[0].Y;
2980 points[2].X = points[1].X;
2981 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2984 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2985 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2986 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2988 case EmfPlusRecordTypeDrawImagePoints:
2990 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2991 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2992 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2993 BYTE image = flags & 0xff;
2994 GpPointF points[3];
2995 unsigned int i;
2996 UINT size;
2998 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2999 return InvalidParameter;
3001 if (dataSize <= fixed_part_size)
3002 return InvalidParameter;
3003 dataSize -= fixed_part_size;
3005 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3006 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3007 return InvalidParameter;
3009 if (draw->count != 3)
3010 return InvalidParameter;
3012 if ((flags >> 13) & 1) /* E */
3013 FIXME("image effects are not supported.\n");
3015 if ((flags >> 11) & 1) /* P */
3016 size = sizeof(EmfPlusPointR7) * draw->count;
3017 else if ((flags >> 14) & 1) /* C */
3018 size = sizeof(EmfPlusPoint) * draw->count;
3019 else
3020 size = sizeof(EmfPlusPointF) * draw->count;
3022 if (dataSize != size)
3023 return InvalidParameter;
3025 if ((flags >> 11) & 1) /* P */
3027 points[0].X = draw->PointData.pointsR[0].X;
3028 points[0].Y = draw->PointData.pointsR[0].Y;
3029 for (i = 1; i < 3; i++)
3031 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3032 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3035 else if ((flags >> 14) & 1) /* C */
3037 for (i = 0; i < 3; i++)
3039 points[i].X = draw->PointData.points[i].X;
3040 points[i].Y = draw->PointData.points[i].Y;
3043 else
3044 memcpy(points, draw->PointData.pointsF, sizeof(points));
3046 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3047 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3048 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3050 case EmfPlusRecordTypeFillPath:
3052 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3053 GpSolidFill *solidfill = NULL;
3054 BYTE path = flags & 0xff;
3055 GpBrush *brush;
3057 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3058 return InvalidParameter;
3060 if (dataSize != sizeof(fill->data.BrushId))
3061 return InvalidParameter;
3063 if (flags & 0x8000)
3065 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3066 if (stat != Ok)
3067 return stat;
3068 brush = (GpBrush *)solidfill;
3070 else
3072 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3073 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3074 return InvalidParameter;
3076 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3079 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3080 GdipDeleteBrush((GpBrush *)solidfill);
3081 return stat;
3083 case EmfPlusRecordTypeFillClosedCurve:
3085 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3086 sizeof(EmfPlusRecordHeader);
3087 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3088 GpSolidFill *solidfill = NULL;
3089 GpFillMode mode;
3090 GpBrush *brush;
3091 UINT size, i;
3093 if (dataSize <= fixed_part_size)
3094 return InvalidParameter;
3096 if (fill->Count == 0)
3097 return InvalidParameter;
3099 if (flags & 0x800) /* P */
3100 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3101 else if (flags & 0x4000) /* C */
3102 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3103 else
3104 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3106 if (dataSize != size)
3107 return InvalidParameter;
3109 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3111 if (flags & 0x8000) /* S */
3113 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3114 if (stat != Ok)
3115 return stat;
3116 brush = (GpBrush *)solidfill;
3118 else
3120 if (fill->BrushId >= EmfPlusObjectTableSize ||
3121 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3122 return InvalidParameter;
3124 brush = real_metafile->objtable[fill->BrushId].u.brush;
3127 if (flags & (0x800 | 0x4000))
3129 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3130 if (points)
3132 if (flags & 0x800) /* P */
3134 for (i = 1; i < fill->Count; i++)
3136 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3137 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3140 else
3142 for (i = 0; i < fill->Count; i++)
3144 points[i].X = fill->PointData.points[i].X;
3145 points[i].Y = fill->PointData.points[i].Y;
3149 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3150 points, fill->Count, fill->Tension, mode);
3151 GdipFree(points);
3153 else
3154 stat = OutOfMemory;
3156 else
3157 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3158 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3160 GdipDeleteBrush((GpBrush *)solidfill);
3161 return stat;
3163 case EmfPlusRecordTypeFillEllipse:
3165 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3166 GpSolidFill *solidfill = NULL;
3167 GpBrush *brush;
3169 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3170 return InvalidParameter;
3171 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3173 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3174 return InvalidParameter;
3176 if (flags & 0x8000)
3178 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3179 if (stat != Ok)
3180 return stat;
3181 brush = (GpBrush *)solidfill;
3183 else
3185 if (fill->BrushId >= EmfPlusObjectTableSize ||
3186 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3187 return InvalidParameter;
3189 brush = real_metafile->objtable[fill->BrushId].u.brush;
3192 if (flags & 0x4000)
3193 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3194 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3195 else
3196 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3197 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3199 GdipDeleteBrush((GpBrush *)solidfill);
3200 return stat;
3202 case EmfPlusRecordTypeFillPie:
3204 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3205 GpSolidFill *solidfill = NULL;
3206 GpBrush *brush;
3208 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3209 return InvalidParameter;
3210 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3212 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3213 return InvalidParameter;
3215 if (flags & 0x8000) /* S */
3217 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3218 if (stat != Ok)
3219 return stat;
3220 brush = (GpBrush *)solidfill;
3222 else
3224 if (fill->BrushId >= EmfPlusObjectTableSize ||
3225 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3226 return InvalidParameter;
3228 brush = real_metafile->objtable[fill->BrushId].u.brush;
3231 if (flags & 0x4000) /* C */
3232 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3233 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3234 fill->StartAngle, fill->SweepAngle);
3235 else
3236 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3237 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3238 fill->StartAngle, fill->SweepAngle);
3240 GdipDeleteBrush((GpBrush *)solidfill);
3241 return stat;
3243 case EmfPlusRecordTypeDrawPath:
3245 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3246 BYTE path = flags & 0xff;
3248 if (dataSize != sizeof(draw->PenId))
3249 return InvalidParameter;
3251 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3252 return InvalidParameter;
3254 if (real_metafile->objtable[path].type != ObjectTypePath ||
3255 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3256 return InvalidParameter;
3258 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3259 real_metafile->objtable[path].u.path);
3261 case EmfPlusRecordTypeDrawArc:
3263 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3264 BYTE pen = flags & 0xff;
3266 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3267 return InvalidParameter;
3269 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3270 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3271 return InvalidParameter;
3273 if (flags & 0x4000) /* C */
3274 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3275 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3276 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3277 else
3278 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3279 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3280 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3282 case EmfPlusRecordTypeDrawEllipse:
3284 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3285 BYTE pen = flags & 0xff;
3287 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3288 return InvalidParameter;
3290 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3291 return InvalidParameter;
3293 if (flags & 0x4000) /* C */
3294 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3295 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3296 draw->RectData.rect.Height);
3297 else
3298 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3299 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3300 draw->RectData.rectF.Height);
3302 case EmfPlusRecordTypeDrawPie:
3304 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3305 BYTE pen = flags & 0xff;
3307 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3308 return InvalidParameter;
3310 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3311 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3312 return InvalidParameter;
3314 if (flags & 0x4000) /* C */
3315 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3316 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3317 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3318 else
3319 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3320 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3321 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3323 case EmfPlusRecordTypeDrawRects:
3325 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3326 BYTE pen = flags & 0xff;
3327 GpRectF *rects = NULL;
3329 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3330 return InvalidParameter;
3332 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3333 return InvalidParameter;
3334 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3336 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3337 return InvalidParameter;
3339 if (flags & 0x4000)
3341 DWORD i;
3343 rects = GdipAlloc(draw->Count * sizeof(*rects));
3344 if (!rects)
3345 return OutOfMemory;
3347 for (i = 0; i < draw->Count; i++)
3349 rects[i].X = draw->RectData.rect[i].X;
3350 rects[i].Y = draw->RectData.rect[i].Y;
3351 rects[i].Width = draw->RectData.rect[i].Width;
3352 rects[i].Height = draw->RectData.rect[i].Height;
3356 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3357 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3358 GdipFree(rects);
3359 return stat;
3361 case EmfPlusRecordTypeDrawDriverString:
3363 GpBrush *brush;
3364 DWORD expected_size;
3365 UINT16 *text;
3366 PointF *positions;
3367 GpSolidFill *solidfill = NULL;
3368 void* alignedmem = NULL;
3369 GpMatrix *matrix = NULL;
3370 BYTE font = flags & 0xff;
3371 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3373 if (font >= EmfPlusObjectTableSize ||
3374 real_metafile->objtable[font].type != ObjectTypeFont)
3375 return InvalidParameter;
3377 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3378 sizeof(EmfPlusRecordHeader);
3379 if (dataSize < expected_size || draw->GlyphCount <= 0)
3380 return InvalidParameter;
3382 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3383 if (draw->MatrixPresent)
3384 expected_size += sizeof(*matrix);
3386 /* Pad expected size to DWORD alignment. */
3387 expected_size = (expected_size + 3) & ~3;
3389 if (dataSize != expected_size)
3390 return InvalidParameter;
3392 if (flags & 0x8000)
3394 stat = GdipCreateSolidFill(draw->brush.Color, &solidfill);
3396 if (stat != Ok)
3397 return InvalidParameter;
3399 brush = (GpBrush*)solidfill;
3401 else
3403 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3404 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3405 return InvalidParameter;
3407 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3410 text = (UINT16*)&draw->VariableData[0];
3412 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3413 aligned rather than 4-byte aligned, which may lead to access
3414 issues. Handle this case by making our own copy of positions. */
3415 if (draw->GlyphCount % 2)
3417 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3419 if (draw->MatrixPresent)
3420 alloc_size += sizeof(*matrix);
3422 positions = alignedmem = heap_alloc(alloc_size);
3423 if (!positions)
3425 GdipDeleteBrush((GpBrush*)solidfill);
3426 return OutOfMemory;
3429 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3431 else
3432 positions = (PointF*)&text[draw->GlyphCount];
3434 if (draw->MatrixPresent)
3435 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3437 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3438 real_metafile->objtable[font].u.font, brush, positions,
3439 draw->DriverStringOptionsFlags, matrix);
3441 GdipDeleteBrush((GpBrush*)solidfill);
3442 heap_free(alignedmem);
3444 return stat;
3446 case EmfPlusRecordTypeFillRegion:
3448 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3449 GpSolidFill *solidfill = NULL;
3450 GpBrush *brush;
3451 BYTE region = flags & 0xff;
3453 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3454 return InvalidParameter;
3456 if (region >= EmfPlusObjectTableSize ||
3457 real_metafile->objtable[region].type != ObjectTypeRegion)
3458 return InvalidParameter;
3460 if (flags & 0x8000)
3462 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3463 if (stat != Ok)
3464 return stat;
3465 brush = (GpBrush*)solidfill;
3467 else
3469 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3470 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3471 return InvalidParameter;
3473 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3476 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3477 real_metafile->objtable[region].u.region);
3478 GdipDeleteBrush((GpBrush*)solidfill);
3480 return stat;
3482 default:
3483 FIXME("Not implemented for record type %x\n", recordType);
3484 return NotImplemented;
3488 return Ok;
3491 struct enum_metafile_data
3493 EnumerateMetafileProc callback;
3494 void *callback_data;
3495 GpMetafile *metafile;
3498 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3499 int nObj, LPARAM lpData)
3501 BOOL ret;
3502 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3503 const BYTE* pStr;
3505 data->metafile->handle_table = lpHTable;
3506 data->metafile->handle_count = nObj;
3508 /* First check for an EMF+ record. */
3509 if (lpEMFR->iType == EMR_GDICOMMENT)
3511 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3513 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3515 int offset = 4;
3517 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3519 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3521 if (record->DataSize)
3522 pStr = (const BYTE*)(record+1);
3523 else
3524 pStr = NULL;
3526 ret = data->callback(record->Type, record->Flags, record->DataSize,
3527 pStr, data->callback_data);
3529 if (!ret)
3530 return 0;
3532 offset += record->Size;
3535 return 1;
3539 if (lpEMFR->nSize != 8)
3540 pStr = (const BYTE*)lpEMFR->dParm;
3541 else
3542 pStr = NULL;
3544 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3545 pStr, data->callback_data);
3548 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3549 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3550 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3551 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3553 struct enum_metafile_data data;
3554 GpStatus stat;
3555 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3556 GraphicsContainer state;
3557 GpPath *dst_path;
3558 RECT dst_bounds;
3560 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3561 destPoints, count, srcRect, srcUnit, callback, callbackData,
3562 imageAttributes);
3564 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3565 return InvalidParameter;
3567 if (!metafile->hemf)
3568 return InvalidParameter;
3570 if (metafile->playback_graphics)
3571 return ObjectBusy;
3573 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3574 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3575 debugstr_pointf(&destPoints[2]));
3577 data.callback = callback;
3578 data.callback_data = callbackData;
3579 data.metafile = real_metafile;
3581 real_metafile->playback_graphics = graphics;
3582 real_metafile->playback_dc = NULL;
3583 real_metafile->src_rect = *srcRect;
3585 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3586 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3588 if (stat == Ok)
3589 stat = GdipBeginContainer2(graphics, &state);
3591 if (stat == Ok)
3593 stat = GdipSetPageScale(graphics, 1.0);
3595 if (stat == Ok)
3596 stat = GdipSetPageUnit(graphics, UnitPixel);
3598 if (stat == Ok)
3599 stat = GdipResetWorldTransform(graphics);
3601 if (stat == Ok)
3602 stat = GdipCreateRegion(&real_metafile->base_clip);
3604 if (stat == Ok)
3605 stat = GdipGetClip(graphics, real_metafile->base_clip);
3607 if (stat == Ok)
3608 stat = GdipCreateRegion(&real_metafile->clip);
3610 if (stat == Ok)
3611 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3613 if (stat == Ok)
3615 GpPointF clip_points[4];
3617 clip_points[0] = real_metafile->playback_points[0];
3618 clip_points[1] = real_metafile->playback_points[1];
3619 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3620 - real_metafile->playback_points[0].X;
3621 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3622 - real_metafile->playback_points[0].Y;
3623 clip_points[3] = real_metafile->playback_points[2];
3625 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3627 if (stat == Ok)
3628 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3630 GdipDeletePath(dst_path);
3633 if (stat == Ok)
3634 stat = GdipCreateMatrix(&real_metafile->world_transform);
3636 if (stat == Ok)
3638 real_metafile->page_unit = UnitDisplay;
3639 real_metafile->page_scale = 1.0;
3640 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3643 if (stat == Ok)
3645 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3648 if (stat == Ok)
3650 stat = METAFILE_PlaybackGetDC(real_metafile);
3652 dst_bounds.left = real_metafile->playback_points[0].X;
3653 dst_bounds.right = real_metafile->playback_points[1].X;
3654 dst_bounds.top = real_metafile->playback_points[0].Y;
3655 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3658 if (stat == Ok)
3659 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3660 &data, &dst_bounds);
3662 METAFILE_PlaybackReleaseDC(real_metafile);
3664 GdipDeleteMatrix(real_metafile->world_transform);
3665 real_metafile->world_transform = NULL;
3667 GdipDeleteRegion(real_metafile->base_clip);
3668 real_metafile->base_clip = NULL;
3670 GdipDeleteRegion(real_metafile->clip);
3671 real_metafile->clip = NULL;
3673 while (list_head(&real_metafile->containers))
3675 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3676 list_remove(&cont->entry);
3677 GdipDeleteRegion(cont->clip);
3678 heap_free(cont);
3681 GdipEndContainer(graphics, state);
3684 real_metafile->playback_graphics = NULL;
3686 return stat;
3689 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRect( GpGraphics *graphics,
3690 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3691 GDIPCONST GpRectF *src, Unit srcUnit, EnumerateMetafileProc callback,
3692 VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3694 GpPointF points[3];
3696 if (!graphics || !metafile || !dest) return InvalidParameter;
3698 points[0].X = points[2].X = dest->X;
3699 points[0].Y = points[1].Y = dest->Y;
3700 points[1].X = dest->X + dest->Width;
3701 points[2].Y = dest->Y + dest->Height;
3703 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3704 src, srcUnit, callback, cb_data, attrs);
3707 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3708 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3709 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3711 GpPointF points[3];
3713 if (!graphics || !metafile || !dest) return InvalidParameter;
3715 points[0].X = points[2].X = dest->X;
3716 points[0].Y = points[1].Y = dest->Y;
3717 points[1].X = dest->X + dest->Width;
3718 points[2].Y = dest->Y + dest->Height;
3720 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3721 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3724 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3725 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3726 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3728 GpRectF destf;
3730 if (!graphics || !metafile || !dest) return InvalidParameter;
3732 destf.X = dest->X;
3733 destf.Y = dest->Y;
3734 destf.Width = dest->Width;
3735 destf.Height = dest->Height;
3737 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3740 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3741 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3742 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3744 GpRectF destf;
3746 if (!graphics || !metafile || !dest) return InvalidParameter;
3748 destf.X = dest->X;
3749 destf.Y = dest->Y;
3750 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit,
3751 metafile->image.xres, metafile->printer_display);
3752 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit,
3753 metafile->image.yres, metafile->printer_display);
3755 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3758 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3759 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3760 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3762 GpPointF ptf;
3764 if (!graphics || !metafile || !dest) return InvalidParameter;
3766 ptf.X = dest->X;
3767 ptf.Y = dest->Y;
3769 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3772 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3773 MetafileHeader * header)
3775 GpStatus status;
3777 TRACE("(%p, %p)\n", metafile, header);
3779 if(!metafile || !header)
3780 return InvalidParameter;
3782 if (metafile->hemf)
3784 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3785 if (status != Ok) return status;
3787 else
3789 memset(header, 0, sizeof(*header));
3790 header->Version = VERSION_MAGIC2;
3793 header->Type = metafile->metafile_type;
3794 header->DpiX = metafile->image.xres;
3795 header->DpiY = metafile->image.yres;
3796 header->Width = gdip_round(metafile->bounds.Width);
3797 header->Height = gdip_round(metafile->bounds.Height);
3799 return Ok;
3802 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3803 int nObj, LPARAM lpData)
3805 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3807 if (lpEMFR->iType == EMR_GDICOMMENT)
3809 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3811 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3813 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3815 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3816 header->Type == EmfPlusRecordTypeHeader)
3818 memcpy(dst_header, header, sizeof(*dst_header));
3822 else if (lpEMFR->iType == EMR_HEADER)
3823 return TRUE;
3825 return FALSE;
3828 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3829 MetafileHeader *header)
3831 ENHMETAHEADER3 emfheader;
3832 EmfPlusHeader emfplusheader;
3833 MetafileType metafile_type;
3835 TRACE("(%p,%p)\n", hemf, header);
3837 if(!hemf || !header)
3838 return InvalidParameter;
3840 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3841 return GenericError;
3843 emfplusheader.Header.Type = 0;
3845 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3847 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3849 if ((emfplusheader.Header.Flags & 1) == 1)
3850 metafile_type = MetafileTypeEmfPlusDual;
3851 else
3852 metafile_type = MetafileTypeEmfPlusOnly;
3854 else
3855 metafile_type = MetafileTypeEmf;
3857 header->Type = metafile_type;
3858 header->Size = emfheader.nBytes;
3859 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3860 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3861 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3862 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3863 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3864 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3865 header->u.EmfHeader = emfheader;
3867 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3869 header->Version = emfplusheader.Version;
3870 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3871 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3872 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3873 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3875 else
3877 header->Version = emfheader.nVersion;
3878 header->EmfPlusFlags = 0;
3879 header->EmfPlusHeaderSize = 0;
3880 header->LogicalDpiX = 0;
3881 header->LogicalDpiY = 0;
3884 return Ok;
3887 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3888 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3890 GpStatus status;
3891 GpMetafile *metafile;
3893 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3895 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3896 if (status == Ok)
3898 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3899 GdipDisposeImage(&metafile->image);
3901 return status;
3904 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3905 MetafileHeader *header)
3907 GpStatus status;
3908 GpMetafile *metafile;
3910 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3912 if (!filename || !header)
3913 return InvalidParameter;
3915 status = GdipCreateMetafileFromFile(filename, &metafile);
3916 if (status == Ok)
3918 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3919 GdipDisposeImage(&metafile->image);
3921 return status;
3924 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3925 MetafileHeader *header)
3927 GpStatus status;
3928 GpMetafile *metafile;
3930 TRACE("(%p,%p)\n", stream, header);
3932 if (!stream || !header)
3933 return InvalidParameter;
3935 status = GdipCreateMetafileFromStream(stream, &metafile);
3936 if (status == Ok)
3938 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3939 GdipDisposeImage(&metafile->image);
3941 return status;
3944 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3945 GpMetafile **metafile)
3947 GpStatus stat;
3948 MetafileHeader header;
3950 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3952 if(!hemf || !metafile)
3953 return InvalidParameter;
3955 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3956 if (stat != Ok)
3957 return stat;
3959 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3960 if (!*metafile)
3961 return OutOfMemory;
3963 (*metafile)->image.type = ImageTypeMetafile;
3964 (*metafile)->image.format = ImageFormatEMF;
3965 (*metafile)->image.frame_count = 1;
3966 (*metafile)->image.xres = header.DpiX;
3967 (*metafile)->image.yres = header.DpiY;
3968 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3969 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3970 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3971 / 2540.0 * header.DpiX;
3972 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3973 / 2540.0 * header.DpiY;
3974 (*metafile)->unit = UnitPixel;
3975 (*metafile)->metafile_type = header.Type;
3976 (*metafile)->hemf = hemf;
3977 (*metafile)->preserve_hemf = !delete;
3978 /* If the 31th bit of EmfPlusFlags was set, metafile was recorded with a DC for a video display.
3979 * If clear, metafile was recorded with a DC for a printer */
3980 (*metafile)->printer_display = !(header.EmfPlusFlags & (1u << 31));
3981 (*metafile)->logical_dpix = header.LogicalDpiX;
3982 (*metafile)->logical_dpiy = header.LogicalDpiY;
3983 list_init(&(*metafile)->containers);
3985 TRACE("<-- %p\n", *metafile);
3987 return Ok;
3990 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3991 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3993 UINT read;
3994 BYTE *copy;
3995 HENHMETAFILE hemf;
3996 GpStatus retval = Ok;
3998 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
4000 if(!hwmf || !metafile)
4001 return InvalidParameter;
4003 *metafile = NULL;
4004 read = GetMetaFileBitsEx(hwmf, 0, NULL);
4005 if(!read)
4006 return GenericError;
4007 copy = heap_alloc_zero(read);
4008 GetMetaFileBitsEx(hwmf, read, copy);
4010 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
4011 heap_free(copy);
4013 /* FIXME: We should store and use hwmf instead of converting to hemf */
4014 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4016 if (retval == Ok)
4018 if (placeable)
4020 (*metafile)->image.xres = (REAL)placeable->Inch;
4021 (*metafile)->image.yres = (REAL)placeable->Inch;
4022 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
4023 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
4024 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
4025 placeable->BoundingBox.Left);
4026 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
4027 placeable->BoundingBox.Top);
4028 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
4030 else
4031 (*metafile)->metafile_type = MetafileTypeWmf;
4032 (*metafile)->image.format = ImageFormatWMF;
4034 if (delete) DeleteMetaFile(hwmf);
4036 else
4037 DeleteEnhMetaFile(hemf);
4038 return retval;
4041 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4042 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4044 HMETAFILE hmf;
4045 HENHMETAFILE emf;
4047 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4049 hmf = GetMetaFileW(file);
4050 if(hmf)
4051 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4053 emf = GetEnhMetaFileW(file);
4054 if(emf)
4055 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4057 return GenericError;
4060 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4061 GpMetafile **metafile)
4063 GpStatus status;
4064 IStream *stream;
4066 TRACE("(%p, %p)\n", file, metafile);
4068 if (!file || !metafile) return InvalidParameter;
4070 *metafile = NULL;
4072 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4073 if (status == Ok)
4075 status = GdipCreateMetafileFromStream(stream, metafile);
4076 IStream_Release(stream);
4078 return status;
4081 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4082 GpMetafile **metafile)
4084 GpStatus stat;
4086 TRACE("%p %p\n", stream, metafile);
4088 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4089 if (stat != Ok) return stat;
4091 if ((*metafile)->image.type != ImageTypeMetafile)
4093 GdipDisposeImage(&(*metafile)->image);
4094 *metafile = NULL;
4095 return GenericError;
4098 return Ok;
4101 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4102 UINT *limitDpi)
4104 TRACE("(%p,%p)\n", metafile, limitDpi);
4106 if (!metafile || !limitDpi)
4107 return InvalidParameter;
4109 if (!metafile->record_dc)
4110 return WrongState;
4112 *limitDpi = metafile->limit_dpi;
4114 return Ok;
4117 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4118 UINT limitDpi)
4120 TRACE("(%p,%u)\n", metafile, limitDpi);
4122 if (limitDpi == 0)
4123 limitDpi = 96;
4125 if (!metafile || limitDpi < 10)
4126 return InvalidParameter;
4128 if (!metafile->record_dc)
4129 return WrongState;
4131 metafile->limit_dpi = limitDpi;
4133 return Ok;
4136 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4137 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4138 const WCHAR* description, GpMetafile** out_metafile)
4140 static int calls;
4142 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4143 debugstr_w(description), out_metafile);
4145 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4146 return InvalidParameter;
4148 if(succ)
4149 *succ = FALSE;
4150 *out_metafile = NULL;
4152 if(!(calls++))
4153 FIXME("not implemented\n");
4155 return NotImplemented;
4158 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4159 LPBYTE pData16, INT iMapMode, INT eFlags)
4161 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4162 return NotImplemented;
4165 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4166 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4167 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4168 GpMetafile **metafile)
4170 HDC record_dc;
4171 REAL dpix, dpiy;
4172 REAL framerect_factor_x, framerect_factor_y;
4173 RECT rc, *lprc;
4174 GpStatus stat;
4176 TRACE("%s %p %d %s %d %s %p\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4177 frameUnit, debugstr_w(desc), metafile);
4179 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
4180 return InvalidParameter;
4182 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
4183 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
4185 if (pFrameRect)
4187 switch (frameUnit)
4189 case MetafileFrameUnitPixel:
4190 framerect_factor_x = 2540.0 / dpix;
4191 framerect_factor_y = 2540.0 / dpiy;
4192 break;
4193 case MetafileFrameUnitPoint:
4194 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
4195 break;
4196 case MetafileFrameUnitInch:
4197 framerect_factor_x = framerect_factor_y = 2540.0;
4198 break;
4199 case MetafileFrameUnitDocument:
4200 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
4201 break;
4202 case MetafileFrameUnitMillimeter:
4203 framerect_factor_x = framerect_factor_y = 100.0;
4204 break;
4205 case MetafileFrameUnitGdi:
4206 framerect_factor_x = framerect_factor_y = 1.0;
4207 break;
4208 default:
4209 return InvalidParameter;
4212 rc.left = framerect_factor_x * pFrameRect->X;
4213 rc.top = framerect_factor_y * pFrameRect->Y;
4214 rc.right = rc.left + framerect_factor_x * pFrameRect->Width;
4215 rc.bottom = rc.top + framerect_factor_y * pFrameRect->Height;
4217 lprc = &rc;
4219 else
4220 lprc = NULL;
4222 record_dc = CreateEnhMetaFileW(hdc, fileName, lprc, desc);
4224 if (!record_dc)
4225 return GenericError;
4227 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4228 if(!*metafile)
4230 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4231 return OutOfMemory;
4234 (*metafile)->image.type = ImageTypeMetafile;
4235 (*metafile)->image.flags = ImageFlagsNone;
4236 (*metafile)->image.palette = NULL;
4237 (*metafile)->image.xres = dpix;
4238 (*metafile)->image.yres = dpiy;
4239 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
4240 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
4241 (*metafile)->unit = UnitPixel;
4242 (*metafile)->metafile_type = type;
4243 (*metafile)->record_dc = record_dc;
4244 (*metafile)->comment_data = NULL;
4245 (*metafile)->comment_data_size = 0;
4246 (*metafile)->comment_data_length = 0;
4247 (*metafile)->limit_dpi = 96;
4248 (*metafile)->hemf = NULL;
4249 (*metafile)->printer_display = (GetDeviceCaps(record_dc, TECHNOLOGY) == DT_RASPRINTER);
4250 (*metafile)->logical_dpix = (REAL)GetDeviceCaps(record_dc, LOGPIXELSX);
4251 (*metafile)->logical_dpiy = (REAL)GetDeviceCaps(record_dc, LOGPIXELSY);
4252 list_init(&(*metafile)->containers);
4254 if (!pFrameRect)
4256 (*metafile)->auto_frame = TRUE;
4257 (*metafile)->auto_frame_min.X = 0;
4258 (*metafile)->auto_frame_min.Y = 0;
4259 (*metafile)->auto_frame_max.X = -1;
4260 (*metafile)->auto_frame_max.Y = -1;
4263 stat = METAFILE_WriteHeader(*metafile, hdc);
4265 if (stat != Ok)
4267 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4268 heap_free(*metafile);
4269 *metafile = NULL;
4270 return OutOfMemory;
4273 return stat;
4276 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4277 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4278 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4280 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4281 frameUnit, debugstr_w(desc), metafile);
4283 return NotImplemented;
4286 /*****************************************************************************
4287 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4290 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4291 GpMetafile* metafile, BOOL* conversionSuccess,
4292 const WCHAR* filename, EmfType emfType,
4293 const WCHAR* description, GpMetafile** out_metafile)
4295 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4296 return NotImplemented;
4299 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4301 LARGE_INTEGER zero;
4302 STATSTG statstg;
4303 GpStatus stat;
4304 HRESULT hr;
4306 *size = 0;
4308 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4309 if (FAILED(hr)) return hresult_to_status(hr);
4311 stat = encode_image_png(image, *stream, NULL);
4312 if (stat != Ok)
4314 IStream_Release(*stream);
4315 return stat;
4318 hr = IStream_Stat(*stream, &statstg, 1);
4319 if (FAILED(hr))
4321 IStream_Release(*stream);
4322 return hresult_to_status(hr);
4324 *size = statstg.cbSize.u.LowPart;
4326 zero.QuadPart = 0;
4327 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4328 if (FAILED(hr))
4330 IStream_Release(*stream);
4331 return hresult_to_status(hr);
4334 return Ok;
4337 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4339 HRESULT hr;
4341 record->Width = 0;
4342 record->Height = 0;
4343 record->Stride = 0;
4344 record->PixelFormat = 0;
4345 record->Type = BitmapDataTypeCompressed;
4347 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4348 if (FAILED(hr)) return hresult_to_status(hr);
4349 return Ok;
4352 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4354 EmfPlusObject *object_record;
4355 GpStatus stat;
4356 DWORD size;
4358 *id = -1;
4360 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4361 return Ok;
4363 if (image->type == ImageTypeBitmap)
4365 IStream *stream;
4366 DWORD aligned_size;
4368 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4369 if (stat != Ok) return stat;
4370 aligned_size = (size + 3) & ~3;
4372 stat = METAFILE_AllocateRecord(metafile,
4373 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4374 (void**)&object_record);
4375 if (stat != Ok)
4377 IStream_Release(stream);
4378 return stat;
4380 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4382 *id = METAFILE_AddObjectId(metafile);
4383 object_record->Header.Type = EmfPlusRecordTypeObject;
4384 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4385 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4386 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4388 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4389 IStream_Release(stream);
4390 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4391 return stat;
4393 else if (image->type == ImageTypeMetafile)
4395 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4396 EmfPlusMetafile *metafile_record;
4398 if (!hemf) return InvalidParameter;
4400 size = GetEnhMetaFileBits(hemf, 0, NULL);
4401 if (!size) return GenericError;
4403 stat = METAFILE_AllocateRecord(metafile,
4404 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4405 (void**)&object_record);
4406 if (stat != Ok) return stat;
4408 *id = METAFILE_AddObjectId(metafile);
4409 object_record->Header.Type = EmfPlusRecordTypeObject;
4410 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4411 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4412 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4413 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4414 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4415 metafile_record->MetafileDataSize = size;
4416 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4418 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4419 return GenericError;
4421 return Ok;
4423 else
4425 FIXME("not supported image type (%d)\n", image->type);
4426 return NotImplemented;
4430 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4432 EmfPlusObject *object_record;
4433 EmfPlusImageAttributes *attrs_record;
4434 GpStatus stat;
4436 *id = -1;
4438 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4439 return Ok;
4441 if (!attrs)
4442 return Ok;
4444 stat = METAFILE_AllocateRecord(metafile,
4445 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4446 (void**)&object_record);
4447 if (stat != Ok) return stat;
4449 *id = METAFILE_AddObjectId(metafile);
4450 object_record->Header.Type = EmfPlusRecordTypeObject;
4451 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4452 attrs_record = &object_record->ObjectData.image_attributes;
4453 attrs_record->Version = VERSION_MAGIC2;
4454 attrs_record->Reserved1 = 0;
4455 attrs_record->WrapMode = attrs->wrap;
4456 attrs_record->ClampColor = attrs->outside_color;
4457 attrs_record->ObjectClamp = attrs->clamp;
4458 attrs_record->Reserved2 = 0;
4459 return Ok;
4462 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4463 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4464 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4465 DrawImageAbort callback, VOID *callbackData)
4467 EmfPlusDrawImagePoints *draw_image_record;
4468 DWORD image_id, attributes_id;
4469 GpStatus stat;
4471 if (count != 3) return InvalidParameter;
4473 if (metafile->metafile_type == MetafileTypeEmf)
4475 FIXME("MetafileTypeEmf metafiles not supported\n");
4476 return NotImplemented;
4478 else
4479 FIXME("semi-stub\n");
4481 if (!imageAttributes)
4483 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4485 else if (image->type == ImageTypeBitmap)
4487 INT width = ((GpBitmap*)image)->width;
4488 INT height = ((GpBitmap*)image)->height;
4489 GpGraphics *graphics;
4490 GpBitmap *bitmap;
4492 stat = GdipCreateBitmapFromScan0(width, height,
4493 0, PixelFormat32bppARGB, NULL, &bitmap);
4494 if (stat != Ok) return stat;
4496 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4497 if (stat != Ok)
4499 GdipDisposeImage((GpImage*)bitmap);
4500 return stat;
4503 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4504 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4505 GdipDeleteGraphics(graphics);
4506 if (stat != Ok)
4508 GdipDisposeImage((GpImage*)bitmap);
4509 return stat;
4512 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4513 GdipDisposeImage((GpImage*)bitmap);
4515 else
4517 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4518 return NotImplemented;
4520 if (stat != Ok) return stat;
4522 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4523 if (stat != Ok) return stat;
4525 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
4526 if (stat != Ok) return stat;
4527 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
4528 draw_image_record->Header.Flags = image_id;
4529 draw_image_record->ImageAttributesID = attributes_id;
4530 draw_image_record->SrcUnit = UnitPixel;
4531 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres, metafile->printer_display);
4532 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres, metafile->printer_display);
4533 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres, metafile->printer_display);
4534 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres, metafile->printer_display);
4535 draw_image_record->count = 3;
4536 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4537 METAFILE_WriteRecords(metafile);
4538 return Ok;
4541 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4543 EmfPlusRecordHeader *record;
4544 GpStatus stat;
4546 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4547 return Ok;
4549 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
4550 if (stat != Ok) return stat;
4552 record->Type = prop;
4553 record->Flags = val;
4555 METAFILE_WriteRecords(metafile);
4556 return Ok;
4559 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4561 EmfPlusObject *object_record;
4562 GpStatus stat;
4563 DWORD size;
4565 *id = -1;
4566 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4567 return Ok;
4569 size = write_path_data(path, NULL);
4570 stat = METAFILE_AllocateRecord(metafile,
4571 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4572 (void**)&object_record);
4573 if (stat != Ok) return stat;
4575 *id = METAFILE_AddObjectId(metafile);
4576 object_record->Header.Type = EmfPlusRecordTypeObject;
4577 object_record->Header.Flags = *id | ObjectTypePath << 8;
4578 write_path_data(path, &object_record->ObjectData.path);
4579 return Ok;
4582 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4584 DWORD i, data_flags, pen_data_size, brush_size;
4585 EmfPlusObject *object_record;
4586 EmfPlusPenData *pen_data;
4587 GpStatus stat;
4588 BOOL result;
4590 *id = -1;
4591 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4592 return Ok;
4594 data_flags = 0;
4595 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4597 GdipIsMatrixIdentity(&pen->transform, &result);
4598 if (!result)
4600 data_flags |= PenDataTransform;
4601 pen_data_size += sizeof(EmfPlusTransformMatrix);
4603 if (pen->startcap != LineCapFlat)
4605 data_flags |= PenDataStartCap;
4606 pen_data_size += sizeof(DWORD);
4608 if (pen->endcap != LineCapFlat)
4610 data_flags |= PenDataEndCap;
4611 pen_data_size += sizeof(DWORD);
4613 if (pen->join != LineJoinMiter)
4615 data_flags |= PenDataJoin;
4616 pen_data_size += sizeof(DWORD);
4618 if (pen->miterlimit != 10.0)
4620 data_flags |= PenDataMiterLimit;
4621 pen_data_size += sizeof(REAL);
4623 if (pen->style != GP_DEFAULT_PENSTYLE)
4625 data_flags |= PenDataLineStyle;
4626 pen_data_size += sizeof(DWORD);
4628 if (pen->dashcap != DashCapFlat)
4630 data_flags |= PenDataDashedLineCap;
4631 pen_data_size += sizeof(DWORD);
4633 data_flags |= PenDataDashedLineOffset;
4634 pen_data_size += sizeof(REAL);
4635 if (pen->numdashes)
4637 data_flags |= PenDataDashedLine;
4638 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4640 if (pen->align != PenAlignmentCenter)
4642 data_flags |= PenDataNonCenter;
4643 pen_data_size += sizeof(DWORD);
4645 /* TODO: Add support for PenDataCompoundLine */
4646 if (pen->customstart)
4648 FIXME("ignoring custom start cup\n");
4650 if (pen->customend)
4652 FIXME("ignoring custom end cup\n");
4655 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4656 if (stat != Ok) return stat;
4658 stat = METAFILE_AllocateRecord(metafile,
4659 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4660 (void**)&object_record);
4661 if (stat != Ok) return stat;
4663 *id = METAFILE_AddObjectId(metafile);
4664 object_record->Header.Type = EmfPlusRecordTypeObject;
4665 object_record->Header.Flags = *id | ObjectTypePen << 8;
4666 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4667 object_record->ObjectData.pen.Type = 0;
4669 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4670 pen_data->PenDataFlags = data_flags;
4671 pen_data->PenUnit = pen->unit;
4672 pen_data->PenWidth = pen->width;
4674 i = 0;
4675 if (data_flags & PenDataTransform)
4677 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4678 memcpy(m, &pen->transform, sizeof(*m));
4679 i += sizeof(EmfPlusTransformMatrix);
4681 if (data_flags & PenDataStartCap)
4683 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4684 i += sizeof(DWORD);
4686 if (data_flags & PenDataEndCap)
4688 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4689 i += sizeof(DWORD);
4691 if (data_flags & PenDataJoin)
4693 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4694 i += sizeof(DWORD);
4696 if (data_flags & PenDataMiterLimit)
4698 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4699 i += sizeof(REAL);
4701 if (data_flags & PenDataLineStyle)
4703 switch (pen->style & PS_STYLE_MASK)
4705 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4706 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4707 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4708 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4709 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4710 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4712 i += sizeof(DWORD);
4714 if (data_flags & PenDataDashedLineCap)
4716 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4717 i += sizeof(DWORD);
4719 if (data_flags & PenDataDashedLineOffset)
4721 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4722 i += sizeof(REAL);
4724 if (data_flags & PenDataDashedLine)
4726 int j;
4728 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4729 i += sizeof(DWORD);
4731 for (j=0; j<pen->numdashes; j++)
4733 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4734 i += sizeof(REAL);
4737 if (data_flags & PenDataNonCenter)
4739 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4740 i += sizeof(DWORD);
4743 METAFILE_FillBrushData(pen->brush,
4744 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4745 return Ok;
4748 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4750 EmfPlusDrawPath *draw_path_record;
4751 DWORD path_id;
4752 DWORD pen_id;
4753 GpStatus stat;
4755 if (metafile->metafile_type == MetafileTypeEmf)
4757 FIXME("stub!\n");
4758 return NotImplemented;
4761 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4762 if (stat != Ok) return stat;
4764 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4765 if (stat != Ok) return stat;
4767 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4768 if (stat != Ok) return stat;
4769 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4770 draw_path_record->Header.Flags = path_id;
4771 draw_path_record->PenId = pen_id;
4773 METAFILE_WriteRecords(metafile);
4774 return Ok;
4777 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4779 EmfPlusFillPath *fill_path_record;
4780 DWORD brush_id = -1, path_id;
4781 BOOL inline_color;
4782 GpStatus stat;
4784 if (metafile->metafile_type == MetafileTypeEmf)
4786 FIXME("stub!\n");
4787 return NotImplemented;
4790 inline_color = brush->bt == BrushTypeSolidColor;
4791 if (!inline_color)
4793 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4794 if (stat != Ok) return stat;
4797 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4798 if (stat != Ok) return stat;
4800 stat = METAFILE_AllocateRecord(metafile,
4801 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4802 if (stat != Ok) return stat;
4803 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4804 if (inline_color)
4806 fill_path_record->Header.Flags = 0x8000 | path_id;
4807 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4809 else
4811 fill_path_record->Header.Flags = path_id;
4812 fill_path_record->data.BrushId = brush_id;
4815 METAFILE_WriteRecords(metafile);
4816 return Ok;
4819 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
4821 EmfPlusObject *object_record;
4822 EmfPlusFont *font_record;
4823 GpStatus stat;
4824 INT fn_len;
4825 INT style;
4827 *id = -1;
4829 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4830 metafile->metafile_type != MetafileTypeEmfPlusDual)
4831 return Ok;
4833 /* The following cast is ugly, but GdipGetFontStyle does treat
4834 its first parameter as const. */
4835 stat = GdipGetFontStyle((GpFont*)font, &style);
4836 if (stat != Ok)
4837 return stat;
4839 fn_len = lstrlenW(font->family->FamilyName);
4840 stat = METAFILE_AllocateRecord(metafile,
4841 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
4842 (void**)&object_record);
4843 if (stat != Ok)
4844 return stat;
4846 *id = METAFILE_AddObjectId(metafile);
4848 object_record->Header.Type = EmfPlusRecordTypeObject;
4849 object_record->Header.Flags = *id | ObjectTypeFont << 8;
4851 font_record = &object_record->ObjectData.font;
4852 font_record->Version = VERSION_MAGIC2;
4853 font_record->EmSize = font->emSize;
4854 font_record->SizeUnit = font->unit;
4855 font_record->FontStyleFlags = style;
4856 font_record->Reserved = 0;
4857 font_record->Length = fn_len;
4859 memcpy(font_record->FamilyName, font->family->FamilyName,
4860 fn_len * sizeof(*font->family->FamilyName));
4862 return Ok;
4865 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
4866 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
4867 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
4869 DWORD brush_id;
4870 DWORD font_id;
4871 DWORD alloc_size;
4872 GpStatus stat;
4873 EmfPlusDrawDriverString *draw_string_record;
4874 BYTE *cursor;
4875 BOOL inline_color;
4876 BOOL include_matrix = FALSE;
4878 if (length <= 0)
4879 return InvalidParameter;
4881 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4882 metafile->metafile_type != MetafileTypeEmfPlusDual)
4884 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
4885 return NotImplemented;
4888 stat = METAFILE_AddFontObject(metafile, font, &font_id);
4889 if (stat != Ok)
4890 return stat;
4892 inline_color = (brush->bt == BrushTypeSolidColor);
4893 if (!inline_color)
4895 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4896 if (stat != Ok)
4897 return stat;
4900 if (matrix)
4902 BOOL identity;
4904 stat = GdipIsMatrixIdentity(matrix, &identity);
4905 if (stat != Ok)
4906 return stat;
4908 include_matrix = !identity;
4911 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
4912 length * (sizeof(*text) + sizeof(*positions));
4914 if (include_matrix)
4915 alloc_size += sizeof(*matrix);
4917 /* Pad record to DWORD alignment. */
4918 alloc_size = (alloc_size + 3) & ~3;
4920 stat = METAFILE_AllocateRecord(metafile, alloc_size, (void**)&draw_string_record);
4921 if (stat != Ok)
4922 return stat;
4924 draw_string_record->Header.Type = EmfPlusRecordTypeDrawDriverString;
4925 draw_string_record->Header.Flags = font_id;
4926 draw_string_record->DriverStringOptionsFlags = flags;
4927 draw_string_record->MatrixPresent = include_matrix;
4928 draw_string_record->GlyphCount = length;
4930 if (inline_color)
4932 draw_string_record->Header.Flags |= 0x8000;
4933 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
4935 else
4936 draw_string_record->brush.BrushId = brush_id;
4938 cursor = &draw_string_record->VariableData[0];
4940 memcpy(cursor, text, length * sizeof(*text));
4941 cursor += length * sizeof(*text);
4943 if (flags & DriverStringOptionsRealizedAdvance)
4945 static BOOL fixme_written = FALSE;
4947 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
4948 in the case of RealizedAdvance, each glyph position is computed
4949 and serialized.
4951 While native GDI+ is capable of playing back metafiles with this
4952 flag set, it is possible that some application might rely on
4953 metafiles produced from GDI+ not setting this flag. Ideally we
4954 would also compute the position of each glyph here, serialize those
4955 values, and not set DriverStringOptionsRealizedAdvance. */
4956 if (!fixme_written)
4958 fixme_written = TRUE;
4959 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
4962 *((PointF*)cursor) = *positions;
4964 else
4965 memcpy(cursor, positions, length * sizeof(*positions));
4967 if (include_matrix)
4969 cursor += length * sizeof(*positions);
4970 memcpy(cursor, matrix, sizeof(*matrix));
4973 METAFILE_WriteRecords(metafile);
4975 return Ok;
4978 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
4980 GpStatus stat;
4981 DWORD brush_id;
4982 DWORD region_id;
4983 EmfPlusFillRegion *fill_region_record;
4984 BOOL inline_color;
4986 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4987 metafile->metafile_type != MetafileTypeEmfPlusDual)
4989 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
4990 return NotImplemented;
4993 inline_color = (brush->bt == BrushTypeSolidColor);
4994 if (!inline_color)
4996 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4997 if (stat != Ok)
4998 return stat;
5001 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
5002 if (stat != Ok)
5003 return stat;
5005 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusFillRegion),
5006 (void**)&fill_region_record);
5007 if (stat != Ok)
5008 return stat;
5010 fill_region_record->Header.Type = EmfPlusRecordTypeFillRegion;
5011 fill_region_record->Header.Flags = region_id;
5013 if (inline_color)
5015 fill_region_record->Header.Flags |= 0x8000;
5016 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
5018 else
5019 fill_region_record->data.BrushId = brush_id;
5021 METAFILE_WriteRecords(metafile);
5023 return Ok;