mfplat: Implement GetScanline0AndPitch() for d3d11 buffers.
[wine/zf.git] / dlls / gdiplus / metafile.c
blob41950eb31c7cb59b810c3952b9b5c8e5793ccda0
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 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
781 pFrameRectF = &frameRectF;
783 else
784 pFrameRectF = NULL;
786 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
789 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
790 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
792 GpRectF frameRectF, *pFrameRectF;
794 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
796 if (frameRect)
798 set_rect(&frameRectF, frameRect->X, frameRect->Y, frameRect->Width, frameRect->Height);
799 pFrameRectF = &frameRectF;
801 else
802 pFrameRectF = NULL;
804 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
807 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
808 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
810 GpStatus stat;
812 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
814 if (!stream)
815 return InvalidParameter;
817 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
819 if (stat == Ok)
821 (*metafile)->record_stream = stream;
822 IStream_AddRef(stream);
825 return stat;
828 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
829 UINT num_points)
831 int i;
833 if (!metafile->auto_frame || !num_points)
834 return;
836 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
837 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
839 for (i=0; i<num_points; i++)
841 if (points[i].X < metafile->auto_frame_min.X)
842 metafile->auto_frame_min.X = points[i].X;
843 if (points[i].X > metafile->auto_frame_max.X)
844 metafile->auto_frame_max.X = points[i].X;
845 if (points[i].Y < metafile->auto_frame_min.Y)
846 metafile->auto_frame_min.Y = points[i].Y;
847 if (points[i].Y > metafile->auto_frame_max.Y)
848 metafile->auto_frame_max.Y = points[i].Y;
852 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
854 GpStatus stat;
856 if (!metafile->record_dc || metafile->record_graphics)
857 return InvalidParameter;
859 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
861 if (stat == Ok)
863 *result = metafile->record_graphics;
864 metafile->record_graphics->xres = metafile->logical_dpix;
865 metafile->record_graphics->yres = metafile->logical_dpiy;
866 metafile->record_graphics->printer_display = metafile->printer_display;
869 return stat;
872 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
874 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
876 EmfPlusRecordHeader *record;
877 GpStatus stat;
879 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
880 if (stat != Ok)
881 return stat;
883 record->Type = EmfPlusRecordTypeGetDC;
884 record->Flags = 0;
886 METAFILE_WriteRecords(metafile);
889 *hdc = metafile->record_dc;
891 return Ok;
894 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
896 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
898 EmfPlusClear *record;
899 GpStatus stat;
901 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
902 if (stat != Ok)
903 return stat;
905 record->Header.Type = EmfPlusRecordTypeClear;
906 record->Header.Flags = 0;
907 record->Color = color;
909 METAFILE_WriteRecords(metafile);
912 return Ok;
915 static BOOL is_integer_rect(const GpRectF *rect)
917 SHORT x, y, width, height;
918 x = rect->X;
919 y = rect->Y;
920 width = rect->Width;
921 height = rect->Height;
922 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
923 rect->Width != (REAL)width || rect->Height != (REAL)height)
924 return FALSE;
925 return TRUE;
928 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
930 switch (brush->bt)
932 case BrushTypeSolidColor:
933 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
934 break;
935 case BrushTypeHatchFill:
936 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
937 break;
938 case BrushTypeLinearGradient:
940 BOOL ignore_xform;
941 GpLineGradient *gradient = (GpLineGradient*)brush;
943 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
945 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
946 if (!ignore_xform)
947 *size += sizeof(gradient->transform);
949 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
950 *size += sizeof(DWORD) + gradient->pblendcount *
951 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
952 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
953 *size += sizeof(DWORD) + gradient->blendcount *
954 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
956 break;
958 default:
959 FIXME("unsupported brush type: %d\n", brush->bt);
960 return NotImplemented;
963 return Ok;
966 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
968 data->Version = VERSION_MAGIC2;
969 data->Type = brush->bt;
971 switch (brush->bt)
973 case BrushTypeSolidColor:
975 GpSolidFill *solid = (GpSolidFill *)brush;
976 data->BrushData.solid.SolidColor = solid->color;
977 break;
979 case BrushTypeHatchFill:
981 GpHatch *hatch = (GpHatch *)brush;
982 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
983 data->BrushData.hatch.ForeColor = hatch->forecol;
984 data->BrushData.hatch.BackColor = hatch->backcol;
985 break;
987 case BrushTypeLinearGradient:
989 BYTE *cursor;
990 BOOL ignore_xform;
991 GpLineGradient *gradient = (GpLineGradient*)brush;
993 data->BrushData.lineargradient.BrushDataFlags = 0;
994 data->BrushData.lineargradient.WrapMode = gradient->wrap;
995 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
996 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
997 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
998 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
999 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1000 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1001 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1002 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1004 if (gradient->gamma)
1005 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1007 cursor = &data->BrushData.lineargradient.OptionalData[0];
1009 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1010 if (!ignore_xform)
1012 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1013 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1014 cursor += sizeof(gradient->transform);
1017 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1019 const DWORD count = gradient->pblendcount;
1021 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1023 memcpy(cursor, &count, sizeof(count));
1024 cursor += sizeof(count);
1026 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1027 cursor += count * sizeof(*gradient->pblendpos);
1029 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1031 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1033 const DWORD count = gradient->blendcount;
1035 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1037 memcpy(cursor, &count, sizeof(count));
1038 cursor += sizeof(count);
1040 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1041 cursor += count * sizeof(*gradient->blendpos);
1043 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1046 break;
1048 default:
1049 FIXME("unsupported brush type: %d\n", brush->bt);
1053 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1055 EmfPlusObject *object_record;
1056 GpStatus stat;
1057 DWORD size;
1059 *id = -1;
1060 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1061 return Ok;
1063 stat = METAFILE_PrepareBrushData(brush, &size);
1064 if (stat != Ok) return stat;
1066 stat = METAFILE_AllocateRecord(metafile,
1067 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1068 if (stat != Ok) return stat;
1070 *id = METAFILE_AddObjectId(metafile);
1071 object_record->Header.Type = EmfPlusRecordTypeObject;
1072 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1073 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1074 return Ok;
1077 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1078 GDIPCONST GpRectF* rects, INT count)
1080 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1082 EmfPlusFillRects *record;
1083 GpStatus stat;
1084 BOOL integer_rects = TRUE;
1085 int i;
1086 DWORD brushid;
1087 int flags = 0;
1089 if (brush->bt == BrushTypeSolidColor)
1091 flags |= 0x8000;
1092 brushid = ((GpSolidFill*)brush)->color;
1094 else
1096 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1097 if (stat != Ok)
1098 return stat;
1101 for (i=0; i<count; i++)
1103 if (!is_integer_rect(&rects[i]))
1105 integer_rects = FALSE;
1106 break;
1110 if (integer_rects)
1111 flags |= 0x4000;
1113 stat = METAFILE_AllocateRecord(metafile,
1114 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1115 (void**)&record);
1116 if (stat != Ok)
1117 return stat;
1119 record->Header.Type = EmfPlusRecordTypeFillRects;
1120 record->Header.Flags = flags;
1121 record->BrushID = brushid;
1122 record->Count = count;
1124 if (integer_rects)
1126 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1127 for (i=0; i<count; i++)
1129 record_rects[i].X = (SHORT)rects[i].X;
1130 record_rects[i].Y = (SHORT)rects[i].Y;
1131 record_rects[i].Width = (SHORT)rects[i].Width;
1132 record_rects[i].Height = (SHORT)rects[i].Height;
1135 else
1136 memcpy(record+1, rects, sizeof(GpRectF) * count);
1138 METAFILE_WriteRecords(metafile);
1141 if (metafile->auto_frame)
1143 GpPointF corners[4];
1144 int i;
1146 for (i=0; i<count; i++)
1148 corners[0].X = rects[i].X;
1149 corners[0].Y = rects[i].Y;
1150 corners[1].X = rects[i].X + rects[i].Width;
1151 corners[1].Y = rects[i].Y;
1152 corners[2].X = rects[i].X;
1153 corners[2].Y = rects[i].Y + rects[i].Height;
1154 corners[3].X = rects[i].X + rects[i].Width;
1155 corners[3].Y = rects[i].Y + rects[i].Height;
1157 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1158 CoordinateSpaceWorld, corners, 4);
1160 METAFILE_AdjustFrame(metafile, corners, 4);
1164 return Ok;
1167 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1169 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1171 EmfPlusSetClipRect *record;
1172 GpStatus stat;
1174 stat = METAFILE_AllocateRecord(metafile,
1175 sizeof(EmfPlusSetClipRect),
1176 (void**)&record);
1177 if (stat != Ok)
1178 return stat;
1180 record->Header.Type = EmfPlusRecordTypeSetClipRect;
1181 record->Header.Flags = (mode & 0xf) << 8;
1182 record->ClipRect.X = x;
1183 record->ClipRect.Y = y;
1184 record->ClipRect.Width = width;
1185 record->ClipRect.Height = height;
1187 METAFILE_WriteRecords(metafile);
1190 return Ok;
1193 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1195 EmfPlusObject *object_record;
1196 DWORD size;
1197 GpStatus stat;
1199 *id = -1;
1200 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1201 return Ok;
1203 size = write_region_data(region, NULL);
1204 stat = METAFILE_AllocateRecord(metafile,
1205 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1206 if (stat != Ok) return stat;
1208 *id = METAFILE_AddObjectId(metafile);
1209 object_record->Header.Type = EmfPlusRecordTypeObject;
1210 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1211 write_region_data(region, &object_record->ObjectData.region);
1212 return Ok;
1215 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1217 EmfPlusRecordHeader *record;
1218 DWORD region_id;
1219 GpStatus stat;
1221 if (metafile->metafile_type == MetafileTypeEmf)
1223 FIXME("stub!\n");
1224 return NotImplemented;
1227 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1228 if (stat != Ok) return stat;
1230 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1231 if (stat != Ok) return stat;
1233 record->Type = EmfPlusRecordTypeSetClipRegion;
1234 record->Flags = region_id | mode << 8;
1236 METAFILE_WriteRecords(metafile);
1237 return Ok;
1240 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1242 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1244 EmfPlusSetPageTransform *record;
1245 GpStatus stat;
1247 stat = METAFILE_AllocateRecord(metafile,
1248 sizeof(EmfPlusSetPageTransform),
1249 (void**)&record);
1250 if (stat != Ok)
1251 return stat;
1253 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1254 record->Header.Flags = unit;
1255 record->PageScale = scale;
1257 METAFILE_WriteRecords(metafile);
1260 return Ok;
1263 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1265 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1267 EmfPlusSetWorldTransform *record;
1268 GpStatus stat;
1270 stat = METAFILE_AllocateRecord(metafile,
1271 sizeof(EmfPlusSetWorldTransform),
1272 (void**)&record);
1273 if (stat != Ok)
1274 return stat;
1276 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1277 record->Header.Flags = 0;
1278 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1280 METAFILE_WriteRecords(metafile);
1283 return Ok;
1286 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1288 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1290 EmfPlusScaleWorldTransform *record;
1291 GpStatus stat;
1293 stat = METAFILE_AllocateRecord(metafile,
1294 sizeof(EmfPlusScaleWorldTransform),
1295 (void**)&record);
1296 if (stat != Ok)
1297 return stat;
1299 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1300 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1301 record->Sx = sx;
1302 record->Sy = sy;
1304 METAFILE_WriteRecords(metafile);
1307 return Ok;
1310 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1312 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1314 EmfPlusMultiplyWorldTransform *record;
1315 GpStatus stat;
1317 stat = METAFILE_AllocateRecord(metafile,
1318 sizeof(EmfPlusMultiplyWorldTransform),
1319 (void**)&record);
1320 if (stat != Ok)
1321 return stat;
1323 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1324 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1325 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1327 METAFILE_WriteRecords(metafile);
1330 return Ok;
1333 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1335 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1337 EmfPlusRotateWorldTransform *record;
1338 GpStatus stat;
1340 stat = METAFILE_AllocateRecord(metafile,
1341 sizeof(EmfPlusRotateWorldTransform),
1342 (void**)&record);
1343 if (stat != Ok)
1344 return stat;
1346 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1347 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1348 record->Angle = angle;
1350 METAFILE_WriteRecords(metafile);
1353 return Ok;
1356 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1358 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1360 EmfPlusTranslateWorldTransform *record;
1361 GpStatus stat;
1363 stat = METAFILE_AllocateRecord(metafile,
1364 sizeof(EmfPlusTranslateWorldTransform),
1365 (void**)&record);
1366 if (stat != Ok)
1367 return stat;
1369 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1370 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1371 record->dx = dx;
1372 record->dy = dy;
1374 METAFILE_WriteRecords(metafile);
1377 return Ok;
1380 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1382 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1384 EmfPlusRecordHeader *record;
1385 GpStatus stat;
1387 stat = METAFILE_AllocateRecord(metafile,
1388 sizeof(EmfPlusRecordHeader),
1389 (void**)&record);
1390 if (stat != Ok)
1391 return stat;
1393 record->Type = EmfPlusRecordTypeResetWorldTransform;
1394 record->Flags = 0;
1396 METAFILE_WriteRecords(metafile);
1399 return Ok;
1402 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1403 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1405 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1407 EmfPlusBeginContainer *record;
1408 GpStatus stat;
1410 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1411 if (stat != Ok)
1412 return stat;
1414 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1415 record->Header.Flags = unit & 0xff;
1416 record->DestRect = *dstrect;
1417 record->SrcRect = *srcrect;
1418 record->StackIndex = StackIndex;
1420 METAFILE_WriteRecords(metafile);
1423 return Ok;
1426 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1428 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1430 EmfPlusContainerRecord *record;
1431 GpStatus stat;
1433 stat = METAFILE_AllocateRecord(metafile,
1434 sizeof(EmfPlusContainerRecord),
1435 (void**)&record);
1436 if (stat != Ok)
1437 return stat;
1439 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1440 record->Header.Flags = 0;
1441 record->StackIndex = StackIndex;
1443 METAFILE_WriteRecords(metafile);
1446 return Ok;
1449 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1451 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1453 EmfPlusContainerRecord *record;
1454 GpStatus stat;
1456 stat = METAFILE_AllocateRecord(metafile,
1457 sizeof(EmfPlusContainerRecord),
1458 (void**)&record);
1459 if (stat != Ok)
1460 return stat;
1462 record->Header.Type = EmfPlusRecordTypeEndContainer;
1463 record->Header.Flags = 0;
1464 record->StackIndex = StackIndex;
1466 METAFILE_WriteRecords(metafile);
1469 return Ok;
1472 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1474 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1476 EmfPlusContainerRecord *record;
1477 GpStatus stat;
1479 stat = METAFILE_AllocateRecord(metafile,
1480 sizeof(EmfPlusContainerRecord),
1481 (void**)&record);
1482 if (stat != Ok)
1483 return stat;
1485 record->Header.Type = EmfPlusRecordTypeSave;
1486 record->Header.Flags = 0;
1487 record->StackIndex = StackIndex;
1489 METAFILE_WriteRecords(metafile);
1492 return Ok;
1495 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1497 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1499 EmfPlusContainerRecord *record;
1500 GpStatus stat;
1502 stat = METAFILE_AllocateRecord(metafile,
1503 sizeof(EmfPlusContainerRecord),
1504 (void**)&record);
1505 if (stat != Ok)
1506 return stat;
1508 record->Header.Type = EmfPlusRecordTypeRestore;
1509 record->Header.Flags = 0;
1510 record->StackIndex = StackIndex;
1512 METAFILE_WriteRecords(metafile);
1515 return Ok;
1518 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1520 if (hdc != metafile->record_dc)
1521 return InvalidParameter;
1523 return Ok;
1526 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1528 GpStatus stat;
1530 stat = METAFILE_WriteEndOfFile(metafile);
1531 metafile->record_graphics = NULL;
1533 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1534 metafile->record_dc = NULL;
1536 heap_free(metafile->comment_data);
1537 metafile->comment_data = NULL;
1538 metafile->comment_data_size = 0;
1540 if (stat == Ok)
1542 MetafileHeader header;
1544 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1545 if (stat == Ok && metafile->auto_frame &&
1546 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1548 RECTL bounds_rc, gdi_bounds_rc;
1549 REAL x_scale = 2540.0 / header.DpiX;
1550 REAL y_scale = 2540.0 / header.DpiY;
1551 BYTE* buffer;
1552 UINT buffer_size;
1554 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1555 if (gdi_bounds_rc.right > gdi_bounds_rc.left &&
1556 gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1558 GpPointF *af_min = &metafile->auto_frame_min;
1559 GpPointF *af_max = &metafile->auto_frame_max;
1561 af_min->X = fmin(af_min->X, gdi_bounds_rc.left);
1562 af_min->Y = fmin(af_min->Y, gdi_bounds_rc.top);
1563 af_max->X = fmax(af_max->X, gdi_bounds_rc.right);
1564 af_max->Y = fmax(af_max->Y, gdi_bounds_rc.bottom);
1567 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1568 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1569 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1570 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1572 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1573 buffer = heap_alloc(buffer_size);
1574 if (buffer)
1576 HENHMETAFILE new_hemf;
1578 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1580 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1582 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1584 if (new_hemf)
1586 DeleteEnhMetaFile(metafile->hemf);
1587 metafile->hemf = new_hemf;
1589 else
1590 stat = OutOfMemory;
1592 heap_free(buffer);
1594 else
1595 stat = OutOfMemory;
1597 if (stat == Ok)
1598 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1600 if (stat == Ok)
1602 metafile->bounds.X = header.X;
1603 metafile->bounds.Y = header.Y;
1604 metafile->bounds.Width = header.Width;
1605 metafile->bounds.Height = header.Height;
1609 if (stat == Ok && metafile->record_stream)
1611 BYTE *buffer;
1612 UINT buffer_size;
1614 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1616 buffer = heap_alloc(buffer_size);
1617 if (buffer)
1619 HRESULT hr;
1621 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1623 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1625 if (FAILED(hr))
1626 stat = hresult_to_status(hr);
1628 heap_free(buffer);
1630 else
1631 stat = OutOfMemory;
1634 if (metafile->record_stream)
1636 IStream_Release(metafile->record_stream);
1637 metafile->record_stream = NULL;
1640 return stat;
1643 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1645 TRACE("(%p,%p)\n", metafile, hEmf);
1647 if (!metafile || !hEmf || !metafile->hemf)
1648 return InvalidParameter;
1650 *hEmf = metafile->hemf;
1651 metafile->hemf = NULL;
1653 return Ok;
1656 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1658 GpStatus stat = Ok;
1660 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1662 return stat;
1665 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1667 if (metafile->playback_dc)
1669 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1670 metafile->playback_dc = NULL;
1674 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1676 GpStatus stat;
1677 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1678 if (stat == Ok)
1679 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1680 return stat;
1683 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1685 GpMatrix *real_transform;
1686 GpStatus stat;
1688 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1690 if (stat == Ok)
1692 REAL scale_x = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpix, metafile->printer_display);
1693 REAL scale_y = units_to_pixels(1.0, metafile->page_unit, metafile->logical_dpiy, metafile->printer_display);
1695 if (metafile->page_unit != UnitDisplay)
1697 scale_x *= metafile->page_scale;
1698 scale_y *= metafile->page_scale;
1701 stat = GdipScaleMatrix(real_transform, scale_x, scale_y, MatrixOrderPrepend);
1703 if (stat == Ok)
1704 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1706 if (stat == Ok)
1707 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1709 GdipDeleteMatrix(real_transform);
1712 return stat;
1715 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1717 metafile_free_object_table_entry(metafile, id);
1718 metafile->objtable[id].type = type;
1719 metafile->objtable[id].u.object = object;
1722 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1724 EmfPlusImage *data = (EmfPlusImage *)record_data;
1725 GpStatus status;
1727 *image = NULL;
1729 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1730 return InvalidParameter;
1731 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1733 switch (data->Type)
1735 case ImageDataTypeBitmap:
1737 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1739 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1740 return InvalidParameter;
1741 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1743 switch (bitmapdata->Type)
1745 case BitmapDataTypePixel:
1747 ColorPalette *palette;
1748 BYTE *scan0;
1750 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1752 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1753 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1755 if (data_size <= palette_size)
1756 return InvalidParameter;
1757 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1759 if (data_size < palette_size)
1760 return InvalidParameter;
1761 data_size -= palette_size;
1763 palette = (ColorPalette *)bitmapdata->BitmapData;
1764 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1766 else
1768 palette = NULL;
1769 scan0 = bitmapdata->BitmapData;
1772 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1773 return InvalidParameter;
1775 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1776 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1777 if (status == Ok && palette)
1779 status = GdipSetImagePalette(*image, palette);
1780 if (status != Ok)
1782 GdipDisposeImage(*image);
1783 *image = NULL;
1786 break;
1788 case BitmapDataTypeCompressed:
1790 IWICImagingFactory *factory;
1791 IWICStream *stream;
1792 HRESULT hr;
1794 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1795 return GenericError;
1797 hr = IWICImagingFactory_CreateStream(factory, &stream);
1798 IWICImagingFactory_Release(factory);
1799 if (hr != S_OK)
1800 return GenericError;
1802 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1803 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1804 else
1805 status = GenericError;
1807 IWICStream_Release(stream);
1808 break;
1810 default:
1811 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1812 return InvalidParameter;
1814 break;
1816 case ImageDataTypeMetafile:
1818 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1820 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1821 return InvalidParameter;
1822 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1824 switch (metafiledata->Type) {
1825 case MetafileTypeEmf:
1826 case MetafileTypeEmfPlusOnly:
1827 case MetafileTypeEmfPlusDual:
1829 HENHMETAFILE hemf;
1831 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1833 if (!hemf)
1834 return GenericError;
1836 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1838 if (status != Ok)
1839 DeleteEnhMetaFile(hemf);
1841 break;
1843 default:
1844 FIXME("metafile type %d not supported.\n", metafiledata->Type);
1845 return NotImplemented;
1847 break;
1849 default:
1850 FIXME("image type %d not supported.\n", data->Type);
1851 return NotImplemented;
1854 return status;
1857 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1859 EmfPlusPath *data = (EmfPlusPath *)record_data;
1860 GpStatus status;
1861 BYTE *types;
1862 UINT size;
1863 DWORD i;
1865 *path = NULL;
1867 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1868 return InvalidParameter;
1869 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1871 if (data->PathPointFlags & 0x800) /* R */
1873 FIXME("RLE encoded path data is not supported.\n");
1874 return NotImplemented;
1876 else
1878 if (data->PathPointFlags & 0x4000) /* C */
1879 size = sizeof(EmfPlusPoint);
1880 else
1881 size = sizeof(EmfPlusPointF);
1882 size += sizeof(BYTE); /* EmfPlusPathPointType */
1883 size *= data->PathPointCount;
1886 if (data_size < size)
1887 return InvalidParameter;
1889 status = GdipCreatePath(FillModeAlternate, path);
1890 if (status != Ok)
1891 return status;
1893 (*path)->pathdata.Count = data->PathPointCount;
1894 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1895 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1896 (*path)->datalen = (*path)->pathdata.Count;
1898 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1900 GdipDeletePath(*path);
1901 return OutOfMemory;
1904 if (data->PathPointFlags & 0x4000) /* C */
1906 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1907 for (i = 0; i < data->PathPointCount; i++)
1909 (*path)->pathdata.Points[i].X = points[i].X;
1910 (*path)->pathdata.Points[i].Y = points[i].Y;
1912 types = (BYTE *)(points + i);
1914 else
1916 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1917 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1918 types = (BYTE *)(points + data->PathPointCount);
1921 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1923 return Ok;
1926 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1928 const DWORD *type;
1929 GpStatus status;
1931 type = buffer_read(mbuf, sizeof(*type));
1932 if (!type) return Ok;
1934 node->type = *type;
1936 switch (node->type)
1938 case CombineModeReplace:
1939 case CombineModeIntersect:
1940 case CombineModeUnion:
1941 case CombineModeXor:
1942 case CombineModeExclude:
1943 case CombineModeComplement:
1945 region_element *left, *right;
1947 left = heap_alloc_zero(sizeof(*left));
1948 if (!left)
1949 return OutOfMemory;
1951 right = heap_alloc_zero(sizeof(*right));
1952 if (!right)
1954 heap_free(left);
1955 return OutOfMemory;
1958 status = metafile_read_region_node(mbuf, region, left, count);
1959 if (status == Ok)
1961 status = metafile_read_region_node(mbuf, region, right, count);
1962 if (status == Ok)
1964 node->elementdata.combine.left = left;
1965 node->elementdata.combine.right = right;
1966 region->num_children += 2;
1967 return Ok;
1971 heap_free(left);
1972 heap_free(right);
1973 return status;
1975 case RegionDataRect:
1977 const EmfPlusRectF *rect;
1979 rect = buffer_read(mbuf, sizeof(*rect));
1980 if (!rect)
1981 return InvalidParameter;
1983 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1984 *count += 1;
1985 return Ok;
1987 case RegionDataPath:
1989 const BYTE *path_data;
1990 const UINT *data_size;
1991 GpPath *path;
1993 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1994 if (!data_size)
1995 return InvalidParameter;
1997 path_data = buffer_read(mbuf, *data_size);
1998 if (!path_data)
1999 return InvalidParameter;
2001 status = metafile_deserialize_path(path_data, *data_size, &path);
2002 if (status == Ok)
2004 node->elementdata.path = path;
2005 *count += 1;
2007 return Ok;
2009 case RegionDataEmptyRect:
2010 case RegionDataInfiniteRect:
2011 *count += 1;
2012 return Ok;
2013 default:
2014 FIXME("element type %#x is not supported\n", *type);
2015 break;
2018 return InvalidParameter;
2021 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
2023 struct memory_buffer mbuf;
2024 GpStatus status;
2025 UINT count;
2027 *region = NULL;
2029 init_memory_buffer(&mbuf, record_data, data_size);
2031 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2032 return InvalidParameter;
2034 status = GdipCreateRegion(region);
2035 if (status != Ok)
2036 return status;
2038 count = 0;
2039 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2040 if (status == Ok && !count)
2041 status = InvalidParameter;
2043 if (status != Ok)
2045 GdipDeleteRegion(*region);
2046 *region = NULL;
2049 return status;
2052 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2054 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2055 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2056 EmfPlusTransformMatrix *transform = NULL;
2057 DWORD brushflags;
2058 GpStatus status;
2059 UINT offset;
2061 *brush = NULL;
2063 if (data_size < header_size)
2064 return InvalidParameter;
2066 switch (data->Type)
2068 case BrushTypeSolidColor:
2069 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2070 return InvalidParameter;
2072 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2073 break;
2074 case BrushTypeHatchFill:
2075 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2076 return InvalidParameter;
2078 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2079 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2080 break;
2081 case BrushTypeTextureFill:
2083 GpImage *image;
2085 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2086 if (data_size <= offset)
2087 return InvalidParameter;
2089 brushflags = data->BrushData.texture.BrushDataFlags;
2090 if (brushflags & BrushDataTransform)
2092 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2093 return InvalidParameter;
2094 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2095 offset += sizeof(EmfPlusTransformMatrix);
2098 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2099 if (status != Ok)
2100 return status;
2102 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2103 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2104 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2106 GdipDisposeImage(image);
2107 break;
2109 case BrushTypeLinearGradient:
2111 GpLineGradient *gradient = NULL;
2112 GpRectF rect;
2113 UINT position_count = 0;
2115 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2116 if (data_size < offset)
2117 return InvalidParameter;
2119 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2120 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2121 return InvalidParameter;
2123 if (brushflags & BrushDataTransform)
2125 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2126 return InvalidParameter;
2127 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2128 offset += sizeof(EmfPlusTransformMatrix);
2131 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2133 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2134 return InvalidParameter;
2135 position_count = *(DWORD *)(record_data + offset);
2136 offset += sizeof(DWORD);
2139 if (brushflags & BrushDataPresetColors)
2141 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2142 return InvalidParameter;
2144 else if (brushflags & BrushDataBlendFactorsH)
2146 if (data_size != offset + position_count * 2 * sizeof(float))
2147 return InvalidParameter;
2150 rect.X = data->BrushData.lineargradient.RectF.X;
2151 rect.Y = data->BrushData.lineargradient.RectF.Y;
2152 rect.Width = data->BrushData.lineargradient.RectF.Width;
2153 rect.Height = data->BrushData.lineargradient.RectF.Height;
2155 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2156 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2157 data->BrushData.lineargradient.WrapMode, &gradient);
2158 if (status == Ok)
2160 if (transform)
2161 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2163 if (status == Ok)
2165 if (brushflags & BrushDataPresetColors)
2166 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2167 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2168 else if (brushflags & BrushDataBlendFactorsH)
2169 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2170 (REAL *)(record_data + offset), position_count);
2172 if (brushflags & BrushDataIsGammaCorrected)
2173 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2177 if (status == Ok)
2178 *brush = (GpBrush *)gradient;
2179 else
2180 GdipDeleteBrush((GpBrush *)gradient);
2182 break;
2184 default:
2185 FIXME("brush type %u is not supported.\n", data->Type);
2186 return NotImplemented;
2189 return status;
2192 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2194 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2195 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2197 if (data_size <= offset)
2198 return InvalidParameter;
2200 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2201 if (data_size <= offset)
2202 return InvalidParameter;
2204 if (pendata->PenDataFlags & PenDataTransform)
2205 offset += sizeof(EmfPlusTransformMatrix);
2207 if (pendata->PenDataFlags & PenDataStartCap)
2208 offset += sizeof(DWORD);
2210 if (pendata->PenDataFlags & PenDataEndCap)
2211 offset += sizeof(DWORD);
2213 if (pendata->PenDataFlags & PenDataJoin)
2214 offset += sizeof(DWORD);
2216 if (pendata->PenDataFlags & PenDataMiterLimit)
2217 offset += sizeof(REAL);
2219 if (pendata->PenDataFlags & PenDataLineStyle)
2220 offset += sizeof(DWORD);
2222 if (pendata->PenDataFlags & PenDataDashedLineCap)
2223 offset += sizeof(DWORD);
2225 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2226 offset += sizeof(REAL);
2228 if (pendata->PenDataFlags & PenDataDashedLine)
2230 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2232 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2233 if (data_size <= offset)
2234 return InvalidParameter;
2236 offset += dashedline->DashedLineDataSize * sizeof(float);
2239 if (pendata->PenDataFlags & PenDataNonCenter)
2240 offset += sizeof(DWORD);
2242 if (pendata->PenDataFlags & PenDataCompoundLine)
2244 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2246 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2247 if (data_size <= offset)
2248 return InvalidParameter;
2250 offset += compoundline->CompoundLineDataSize * sizeof(float);
2253 if (pendata->PenDataFlags & PenDataCustomStartCap)
2255 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2257 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2258 if (data_size <= offset)
2259 return InvalidParameter;
2261 offset += startcap->CustomStartCapSize;
2264 if (pendata->PenDataFlags & PenDataCustomEndCap)
2266 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2268 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2269 if (data_size <= offset)
2270 return InvalidParameter;
2272 offset += endcap->CustomEndCapSize;
2275 *ret = offset;
2276 return Ok;
2279 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2281 BYTE type = (flags >> 8) & 0xff;
2282 BYTE id = flags & 0xff;
2283 void *object = NULL;
2284 GpStatus status;
2286 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2287 return InvalidParameter;
2289 switch (type)
2291 case ObjectTypeBrush:
2292 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2293 break;
2294 case ObjectTypePen:
2296 EmfPlusPen *data = (EmfPlusPen *)record_data;
2297 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2298 GpBrush *brush;
2299 DWORD offset;
2300 GpPen *pen;
2302 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2303 if (status != Ok)
2304 return status;
2306 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2307 if (status != Ok)
2308 return status;
2310 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2311 GdipDeleteBrush(brush);
2312 if (status != Ok)
2313 return status;
2315 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2317 if (pendata->PenDataFlags & PenDataTransform)
2319 FIXME("PenDataTransform is not supported.\n");
2320 offset += sizeof(EmfPlusTransformMatrix);
2323 if (pendata->PenDataFlags & PenDataStartCap)
2325 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2326 goto penfailed;
2327 offset += sizeof(DWORD);
2330 if (pendata->PenDataFlags & PenDataEndCap)
2332 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2333 goto penfailed;
2334 offset += sizeof(DWORD);
2337 if (pendata->PenDataFlags & PenDataJoin)
2339 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2340 goto penfailed;
2341 offset += sizeof(DWORD);
2344 if (pendata->PenDataFlags & PenDataMiterLimit)
2346 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2347 goto penfailed;
2348 offset += sizeof(REAL);
2351 if (pendata->PenDataFlags & PenDataLineStyle)
2353 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2354 goto penfailed;
2355 offset += sizeof(DWORD);
2358 if (pendata->PenDataFlags & PenDataDashedLineCap)
2360 FIXME("PenDataDashedLineCap is not supported.\n");
2361 offset += sizeof(DWORD);
2364 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2366 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2367 goto penfailed;
2368 offset += sizeof(REAL);
2371 if (pendata->PenDataFlags & PenDataDashedLine)
2373 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2374 FIXME("PenDataDashedLine is not supported.\n");
2375 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2378 if (pendata->PenDataFlags & PenDataNonCenter)
2380 FIXME("PenDataNonCenter is not supported.\n");
2381 offset += sizeof(DWORD);
2384 if (pendata->PenDataFlags & PenDataCompoundLine)
2386 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2387 FIXME("PenDataCompoundLine is not supported.\n");
2388 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2391 if (pendata->PenDataFlags & PenDataCustomStartCap)
2393 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2394 FIXME("PenDataCustomStartCap is not supported.\n");
2395 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2398 if (pendata->PenDataFlags & PenDataCustomEndCap)
2400 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2401 FIXME("PenDataCustomEndCap is not supported.\n");
2402 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2405 object = pen;
2406 break;
2408 penfailed:
2409 GdipDeletePen(pen);
2410 return status;
2412 case ObjectTypePath:
2413 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2414 break;
2415 case ObjectTypeRegion:
2416 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2417 break;
2418 case ObjectTypeImage:
2419 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2420 break;
2421 case ObjectTypeFont:
2423 EmfPlusFont *data = (EmfPlusFont *)record_data;
2424 GpFontFamily *family;
2425 WCHAR *familyname;
2427 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2428 return InvalidParameter;
2429 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2431 if (data_size < data->Length * sizeof(WCHAR))
2432 return InvalidParameter;
2434 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2435 return OutOfMemory;
2437 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2438 familyname[data->Length] = 0;
2440 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2441 GdipFree(familyname);
2443 /* If a font family cannot be created from family name, native
2444 falls back to a sans serif font. */
2445 if (status != Ok)
2446 status = GdipGetGenericFontFamilySansSerif(&family);
2447 if (status != Ok)
2448 return status;
2450 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2451 GdipDeleteFontFamily(family);
2452 break;
2454 case ObjectTypeImageAttributes:
2456 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2457 GpImageAttributes *attributes = NULL;
2459 if (data_size != sizeof(*data))
2460 return InvalidParameter;
2462 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2463 return status;
2465 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2466 !!data->ObjectClamp);
2467 if (status == Ok)
2468 object = attributes;
2469 else
2470 GdipDisposeImageAttributes(attributes);
2471 break;
2473 default:
2474 FIXME("not implemented for object type %d.\n", type);
2475 return NotImplemented;
2478 if (status == Ok)
2479 metafile_set_object_table_entry(metafile, id, type, object);
2481 return status;
2484 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2486 GpMatrix world_to_device;
2488 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2490 GdipTransformRegion(region, &world_to_device);
2491 GdipCombineRegionRegion(metafile->clip, region, mode);
2493 return METAFILE_PlaybackUpdateClip(metafile);
2496 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2497 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2499 GpStatus stat;
2500 GpMetafile *real_metafile = (GpMetafile*)metafile;
2502 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2504 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2505 return InvalidParameter;
2507 if (recordType >= 1 && recordType <= 0x7a)
2509 /* regular EMF record */
2510 if (metafile->playback_dc)
2512 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2514 if (record)
2516 record->iType = recordType;
2517 record->nSize = dataSize + 8;
2518 memcpy(record->dParm, data, dataSize);
2520 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2521 record, metafile->handle_count) == 0)
2522 ERR("PlayEnhMetaFileRecord failed\n");
2524 heap_free(record);
2526 else
2527 return OutOfMemory;
2530 else
2532 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2534 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2536 switch(recordType)
2538 case EmfPlusRecordTypeHeader:
2539 case EmfPlusRecordTypeEndOfFile:
2540 break;
2541 case EmfPlusRecordTypeGetDC:
2542 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2543 break;
2544 case EmfPlusRecordTypeClear:
2546 EmfPlusClear *record = (EmfPlusClear*)header;
2548 if (dataSize != sizeof(record->Color))
2549 return InvalidParameter;
2551 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2553 case EmfPlusRecordTypeFillRects:
2555 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2556 GpBrush *brush, *temp_brush=NULL;
2557 GpRectF *rects, *temp_rects=NULL;
2559 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2560 return InvalidParameter;
2562 if (flags & 0x4000)
2564 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2565 return InvalidParameter;
2567 else
2569 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2570 return InvalidParameter;
2573 if (flags & 0x8000)
2575 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2576 brush = temp_brush;
2578 else
2580 if (record->BrushID >= EmfPlusObjectTableSize ||
2581 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2582 return InvalidParameter;
2584 brush = real_metafile->objtable[record->BrushID].u.brush;
2585 stat = Ok;
2588 if (stat == Ok)
2590 if (flags & 0x4000)
2592 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2593 int i;
2595 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2596 if (rects)
2598 for (i=0; i<record->Count; i++)
2600 rects[i].X = int_rects[i].X;
2601 rects[i].Y = int_rects[i].Y;
2602 rects[i].Width = int_rects[i].Width;
2603 rects[i].Height = int_rects[i].Height;
2606 else
2607 stat = OutOfMemory;
2609 else
2610 rects = (GpRectF*)(record+1);
2613 if (stat == Ok)
2615 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2618 GdipDeleteBrush(temp_brush);
2619 heap_free(temp_rects);
2621 return stat;
2623 case EmfPlusRecordTypeSetClipRect:
2625 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2626 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2627 GpRegion *region;
2629 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2630 return InvalidParameter;
2632 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2634 if (stat == Ok)
2636 stat = metafile_set_clip_region(real_metafile, region, mode);
2637 GdipDeleteRegion(region);
2640 return stat;
2642 case EmfPlusRecordTypeSetClipRegion:
2644 CombineMode mode = (flags >> 8) & 0xf;
2645 BYTE regionid = flags & 0xff;
2646 GpRegion *region;
2648 if (dataSize != 0)
2649 return InvalidParameter;
2651 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2652 return InvalidParameter;
2654 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2655 if (stat == Ok)
2657 stat = metafile_set_clip_region(real_metafile, region, mode);
2658 GdipDeleteRegion(region);
2661 return stat;
2663 case EmfPlusRecordTypeSetClipPath:
2665 CombineMode mode = (flags >> 8) & 0xf;
2666 BYTE pathid = flags & 0xff;
2667 GpRegion *region;
2669 if (dataSize != 0)
2670 return InvalidParameter;
2672 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2673 return InvalidParameter;
2675 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2676 if (stat == Ok)
2678 stat = metafile_set_clip_region(real_metafile, region, mode);
2679 GdipDeleteRegion(region);
2682 return stat;
2684 case EmfPlusRecordTypeSetPageTransform:
2686 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2687 GpUnit unit = (GpUnit)flags;
2689 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2690 return InvalidParameter;
2692 real_metafile->page_unit = unit;
2693 real_metafile->page_scale = record->PageScale;
2695 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2697 case EmfPlusRecordTypeSetWorldTransform:
2699 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2701 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2702 return InvalidParameter;
2704 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2706 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2708 case EmfPlusRecordTypeScaleWorldTransform:
2710 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2711 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2713 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2714 return InvalidParameter;
2716 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2718 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2720 case EmfPlusRecordTypeMultiplyWorldTransform:
2722 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2723 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2724 GpMatrix matrix;
2726 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2727 return InvalidParameter;
2729 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2731 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2733 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2735 case EmfPlusRecordTypeRotateWorldTransform:
2737 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2738 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2740 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2741 return InvalidParameter;
2743 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2745 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2747 case EmfPlusRecordTypeTranslateWorldTransform:
2749 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2750 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2752 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2753 return InvalidParameter;
2755 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2757 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2759 case EmfPlusRecordTypeResetWorldTransform:
2761 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2763 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2765 case EmfPlusRecordTypeBeginContainer:
2767 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2768 container* cont;
2769 GpUnit unit;
2770 REAL scale_x, scale_y;
2771 GpRectF scaled_srcrect;
2772 GpMatrix transform;
2774 cont = heap_alloc_zero(sizeof(*cont));
2775 if (!cont)
2776 return OutOfMemory;
2778 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2779 if (stat != Ok)
2781 heap_free(cont);
2782 return stat;
2785 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2787 if (stat != Ok)
2789 GdipDeleteRegion(cont->clip);
2790 heap_free(cont);
2791 return stat;
2794 cont->id = record->StackIndex;
2795 cont->type = BEGIN_CONTAINER;
2796 cont->world_transform = *metafile->world_transform;
2797 cont->page_unit = metafile->page_unit;
2798 cont->page_scale = metafile->page_scale;
2799 list_add_head(&real_metafile->containers, &cont->entry);
2801 unit = record->Header.Flags & 0xff;
2803 scale_x = units_to_pixels(1.0, unit, metafile->image.xres, metafile->printer_display);
2804 scale_y = units_to_pixels(1.0, unit, metafile->image.yres, metafile->printer_display);
2806 scaled_srcrect.X = scale_x * record->SrcRect.X;
2807 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2808 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2809 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2811 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2812 transform.matrix[1] = 0.0;
2813 transform.matrix[2] = 0.0;
2814 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2815 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2816 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2818 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2820 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2822 case EmfPlusRecordTypeBeginContainerNoParams:
2823 case EmfPlusRecordTypeSave:
2825 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2826 container* cont;
2828 cont = heap_alloc_zero(sizeof(*cont));
2829 if (!cont)
2830 return OutOfMemory;
2832 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2833 if (stat != Ok)
2835 heap_free(cont);
2836 return stat;
2839 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2840 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2841 else
2842 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2844 if (stat != Ok)
2846 GdipDeleteRegion(cont->clip);
2847 heap_free(cont);
2848 return stat;
2851 cont->id = record->StackIndex;
2852 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2853 cont->type = BEGIN_CONTAINER;
2854 else
2855 cont->type = SAVE_GRAPHICS;
2856 cont->world_transform = *metafile->world_transform;
2857 cont->page_unit = metafile->page_unit;
2858 cont->page_scale = metafile->page_scale;
2859 list_add_head(&real_metafile->containers, &cont->entry);
2861 break;
2863 case EmfPlusRecordTypeEndContainer:
2864 case EmfPlusRecordTypeRestore:
2866 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2867 container* cont;
2868 enum container_type type;
2869 BOOL found=FALSE;
2871 if (recordType == EmfPlusRecordTypeEndContainer)
2872 type = BEGIN_CONTAINER;
2873 else
2874 type = SAVE_GRAPHICS;
2876 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2878 if (cont->id == record->StackIndex && cont->type == type)
2880 found = TRUE;
2881 break;
2885 if (found)
2887 container* cont2;
2889 /* pop any newer items on the stack */
2890 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2892 list_remove(&cont2->entry);
2893 GdipDeleteRegion(cont2->clip);
2894 heap_free(cont2);
2897 if (type == BEGIN_CONTAINER)
2898 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2899 else
2900 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2902 *real_metafile->world_transform = cont->world_transform;
2903 real_metafile->page_unit = cont->page_unit;
2904 real_metafile->page_scale = cont->page_scale;
2905 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2907 list_remove(&cont->entry);
2908 GdipDeleteRegion(cont->clip);
2909 heap_free(cont);
2912 break;
2914 case EmfPlusRecordTypeSetPixelOffsetMode:
2916 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2918 case EmfPlusRecordTypeSetCompositingQuality:
2920 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2922 case EmfPlusRecordTypeSetInterpolationMode:
2924 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2926 case EmfPlusRecordTypeSetTextRenderingHint:
2928 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2930 case EmfPlusRecordTypeSetAntiAliasMode:
2932 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2934 case EmfPlusRecordTypeSetCompositingMode:
2936 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2938 case EmfPlusRecordTypeObject:
2940 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2942 case EmfPlusRecordTypeDrawImage:
2944 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2945 BYTE image = flags & 0xff;
2946 GpPointF points[3];
2948 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2949 return InvalidParameter;
2951 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2952 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2953 return InvalidParameter;
2955 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2956 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2957 return InvalidParameter;
2959 if (flags & 0x4000) /* C */
2961 points[0].X = draw->RectData.rect.X;
2962 points[0].Y = draw->RectData.rect.Y;
2963 points[1].X = points[0].X + draw->RectData.rect.Width;
2964 points[1].Y = points[0].Y;
2965 points[2].X = points[1].X;
2966 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2968 else
2970 points[0].X = draw->RectData.rectF.X;
2971 points[0].Y = draw->RectData.rectF.Y;
2972 points[1].X = points[0].X + draw->RectData.rectF.Width;
2973 points[1].Y = points[0].Y;
2974 points[2].X = points[1].X;
2975 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2978 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2979 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2980 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2982 case EmfPlusRecordTypeDrawImagePoints:
2984 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2985 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2986 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2987 BYTE image = flags & 0xff;
2988 GpPointF points[3];
2989 unsigned int i;
2990 UINT size;
2992 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2993 return InvalidParameter;
2995 if (dataSize <= fixed_part_size)
2996 return InvalidParameter;
2997 dataSize -= fixed_part_size;
2999 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3000 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3001 return InvalidParameter;
3003 if (draw->count != 3)
3004 return InvalidParameter;
3006 if ((flags >> 13) & 1) /* E */
3007 FIXME("image effects are not supported.\n");
3009 if ((flags >> 11) & 1) /* P */
3010 size = sizeof(EmfPlusPointR7) * draw->count;
3011 else if ((flags >> 14) & 1) /* C */
3012 size = sizeof(EmfPlusPoint) * draw->count;
3013 else
3014 size = sizeof(EmfPlusPointF) * draw->count;
3016 if (dataSize != size)
3017 return InvalidParameter;
3019 if ((flags >> 11) & 1) /* P */
3021 points[0].X = draw->PointData.pointsR[0].X;
3022 points[0].Y = draw->PointData.pointsR[0].Y;
3023 for (i = 1; i < 3; i++)
3025 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3026 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3029 else if ((flags >> 14) & 1) /* C */
3031 for (i = 0; i < 3; i++)
3033 points[i].X = draw->PointData.points[i].X;
3034 points[i].Y = draw->PointData.points[i].Y;
3037 else
3038 memcpy(points, draw->PointData.pointsF, sizeof(points));
3040 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3041 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3042 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3044 case EmfPlusRecordTypeFillPath:
3046 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3047 GpSolidFill *solidfill = NULL;
3048 BYTE path = flags & 0xff;
3049 GpBrush *brush;
3051 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3052 return InvalidParameter;
3054 if (dataSize != sizeof(fill->data.BrushId))
3055 return InvalidParameter;
3057 if (flags & 0x8000)
3059 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3060 if (stat != Ok)
3061 return stat;
3062 brush = (GpBrush *)solidfill;
3064 else
3066 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3067 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3068 return InvalidParameter;
3070 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3073 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3074 GdipDeleteBrush((GpBrush *)solidfill);
3075 return stat;
3077 case EmfPlusRecordTypeFillClosedCurve:
3079 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3080 sizeof(EmfPlusRecordHeader);
3081 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3082 GpSolidFill *solidfill = NULL;
3083 GpFillMode mode;
3084 GpBrush *brush;
3085 UINT size, i;
3087 if (dataSize <= fixed_part_size)
3088 return InvalidParameter;
3090 if (fill->Count == 0)
3091 return InvalidParameter;
3093 if (flags & 0x800) /* P */
3094 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3095 else if (flags & 0x4000) /* C */
3096 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3097 else
3098 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3100 if (dataSize != size)
3101 return InvalidParameter;
3103 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3105 if (flags & 0x8000) /* S */
3107 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3108 if (stat != Ok)
3109 return stat;
3110 brush = (GpBrush *)solidfill;
3112 else
3114 if (fill->BrushId >= EmfPlusObjectTableSize ||
3115 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3116 return InvalidParameter;
3118 brush = real_metafile->objtable[fill->BrushId].u.brush;
3121 if (flags & (0x800 | 0x4000))
3123 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3124 if (points)
3126 if (flags & 0x800) /* P */
3128 for (i = 1; i < fill->Count; i++)
3130 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3131 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3134 else
3136 for (i = 0; i < fill->Count; i++)
3138 points[i].X = fill->PointData.points[i].X;
3139 points[i].Y = fill->PointData.points[i].Y;
3143 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3144 points, fill->Count, fill->Tension, mode);
3145 GdipFree(points);
3147 else
3148 stat = OutOfMemory;
3150 else
3151 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3152 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3154 GdipDeleteBrush((GpBrush *)solidfill);
3155 return stat;
3157 case EmfPlusRecordTypeFillEllipse:
3159 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3160 GpSolidFill *solidfill = NULL;
3161 GpBrush *brush;
3163 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3164 return InvalidParameter;
3165 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3167 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3168 return InvalidParameter;
3170 if (flags & 0x8000)
3172 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3173 if (stat != Ok)
3174 return stat;
3175 brush = (GpBrush *)solidfill;
3177 else
3179 if (fill->BrushId >= EmfPlusObjectTableSize ||
3180 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3181 return InvalidParameter;
3183 brush = real_metafile->objtable[fill->BrushId].u.brush;
3186 if (flags & 0x4000)
3187 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3188 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3189 else
3190 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3191 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3193 GdipDeleteBrush((GpBrush *)solidfill);
3194 return stat;
3196 case EmfPlusRecordTypeFillPie:
3198 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3199 GpSolidFill *solidfill = NULL;
3200 GpBrush *brush;
3202 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3203 return InvalidParameter;
3204 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3206 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3207 return InvalidParameter;
3209 if (flags & 0x8000) /* S */
3211 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3212 if (stat != Ok)
3213 return stat;
3214 brush = (GpBrush *)solidfill;
3216 else
3218 if (fill->BrushId >= EmfPlusObjectTableSize ||
3219 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3220 return InvalidParameter;
3222 brush = real_metafile->objtable[fill->BrushId].u.brush;
3225 if (flags & 0x4000) /* C */
3226 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3227 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3228 fill->StartAngle, fill->SweepAngle);
3229 else
3230 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3231 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3232 fill->StartAngle, fill->SweepAngle);
3234 GdipDeleteBrush((GpBrush *)solidfill);
3235 return stat;
3237 case EmfPlusRecordTypeDrawPath:
3239 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3240 BYTE path = flags & 0xff;
3242 if (dataSize != sizeof(draw->PenId))
3243 return InvalidParameter;
3245 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3246 return InvalidParameter;
3248 if (real_metafile->objtable[path].type != ObjectTypePath ||
3249 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3250 return InvalidParameter;
3252 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3253 real_metafile->objtable[path].u.path);
3255 case EmfPlusRecordTypeDrawArc:
3257 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3258 BYTE pen = flags & 0xff;
3260 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3261 return InvalidParameter;
3263 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3264 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3265 return InvalidParameter;
3267 if (flags & 0x4000) /* C */
3268 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3269 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3270 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3271 else
3272 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3273 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3274 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3276 case EmfPlusRecordTypeDrawEllipse:
3278 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3279 BYTE pen = flags & 0xff;
3281 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3282 return InvalidParameter;
3284 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3285 return InvalidParameter;
3287 if (flags & 0x4000) /* C */
3288 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3289 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3290 draw->RectData.rect.Height);
3291 else
3292 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3293 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3294 draw->RectData.rectF.Height);
3296 case EmfPlusRecordTypeDrawPie:
3298 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3299 BYTE pen = flags & 0xff;
3301 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3302 return InvalidParameter;
3304 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3305 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3306 return InvalidParameter;
3308 if (flags & 0x4000) /* C */
3309 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3310 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3311 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3312 else
3313 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3314 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3315 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3317 case EmfPlusRecordTypeDrawRects:
3319 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3320 BYTE pen = flags & 0xff;
3321 GpRectF *rects = NULL;
3323 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3324 return InvalidParameter;
3326 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3327 return InvalidParameter;
3328 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3330 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3331 return InvalidParameter;
3333 if (flags & 0x4000)
3335 DWORD i;
3337 rects = GdipAlloc(draw->Count * sizeof(*rects));
3338 if (!rects)
3339 return OutOfMemory;
3341 for (i = 0; i < draw->Count; i++)
3343 rects[i].X = draw->RectData.rect[i].X;
3344 rects[i].Y = draw->RectData.rect[i].Y;
3345 rects[i].Width = draw->RectData.rect[i].Width;
3346 rects[i].Height = draw->RectData.rect[i].Height;
3350 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3351 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3352 GdipFree(rects);
3353 return stat;
3355 case EmfPlusRecordTypeDrawDriverString:
3357 GpBrush *brush;
3358 DWORD expected_size;
3359 UINT16 *text;
3360 PointF *positions;
3361 GpSolidFill *solidfill = NULL;
3362 void* alignedmem = NULL;
3363 GpMatrix *matrix = NULL;
3364 BYTE font = flags & 0xff;
3365 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3367 if (font >= EmfPlusObjectTableSize ||
3368 real_metafile->objtable[font].type != ObjectTypeFont)
3369 return InvalidParameter;
3371 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3372 sizeof(EmfPlusRecordHeader);
3373 if (dataSize < expected_size || draw->GlyphCount <= 0)
3374 return InvalidParameter;
3376 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3377 if (draw->MatrixPresent)
3378 expected_size += sizeof(*matrix);
3380 /* Pad expected size to DWORD alignment. */
3381 expected_size = (expected_size + 3) & ~3;
3383 if (dataSize != expected_size)
3384 return InvalidParameter;
3386 if (flags & 0x8000)
3388 stat = GdipCreateSolidFill(draw->brush.Color, &solidfill);
3390 if (stat != Ok)
3391 return InvalidParameter;
3393 brush = (GpBrush*)solidfill;
3395 else
3397 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3398 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3399 return InvalidParameter;
3401 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3404 text = (UINT16*)&draw->VariableData[0];
3406 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3407 aligned rather than 4-byte aligned, which may lead to access
3408 issues. Handle this case by making our own copy of positions. */
3409 if (draw->GlyphCount % 2)
3411 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3413 if (draw->MatrixPresent)
3414 alloc_size += sizeof(*matrix);
3416 positions = alignedmem = heap_alloc(alloc_size);
3417 if (!positions)
3419 GdipDeleteBrush((GpBrush*)solidfill);
3420 return OutOfMemory;
3423 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3425 else
3426 positions = (PointF*)&text[draw->GlyphCount];
3428 if (draw->MatrixPresent)
3429 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3431 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3432 real_metafile->objtable[font].u.font, brush, positions,
3433 draw->DriverStringOptionsFlags, matrix);
3435 GdipDeleteBrush((GpBrush*)solidfill);
3436 heap_free(alignedmem);
3438 return stat;
3440 case EmfPlusRecordTypeFillRegion:
3442 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3443 GpSolidFill *solidfill = NULL;
3444 GpBrush *brush;
3445 BYTE region = flags & 0xff;
3447 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3448 return InvalidParameter;
3450 if (region >= EmfPlusObjectTableSize ||
3451 real_metafile->objtable[region].type != ObjectTypeRegion)
3452 return InvalidParameter;
3454 if (flags & 0x8000)
3456 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3457 if (stat != Ok)
3458 return stat;
3459 brush = (GpBrush*)solidfill;
3461 else
3463 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3464 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3465 return InvalidParameter;
3467 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3470 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3471 real_metafile->objtable[region].u.region);
3472 GdipDeleteBrush((GpBrush*)solidfill);
3474 return stat;
3476 default:
3477 FIXME("Not implemented for record type %x\n", recordType);
3478 return NotImplemented;
3482 return Ok;
3485 struct enum_metafile_data
3487 EnumerateMetafileProc callback;
3488 void *callback_data;
3489 GpMetafile *metafile;
3492 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3493 int nObj, LPARAM lpData)
3495 BOOL ret;
3496 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3497 const BYTE* pStr;
3499 data->metafile->handle_table = lpHTable;
3500 data->metafile->handle_count = nObj;
3502 /* First check for an EMF+ record. */
3503 if (lpEMFR->iType == EMR_GDICOMMENT)
3505 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3507 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3509 int offset = 4;
3511 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3513 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3515 if (record->DataSize)
3516 pStr = (const BYTE*)(record+1);
3517 else
3518 pStr = NULL;
3520 ret = data->callback(record->Type, record->Flags, record->DataSize,
3521 pStr, data->callback_data);
3523 if (!ret)
3524 return 0;
3526 offset += record->Size;
3529 return 1;
3533 if (lpEMFR->nSize != 8)
3534 pStr = (const BYTE*)lpEMFR->dParm;
3535 else
3536 pStr = NULL;
3538 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3539 pStr, data->callback_data);
3542 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3543 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3544 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3545 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3547 struct enum_metafile_data data;
3548 GpStatus stat;
3549 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3550 GraphicsContainer state;
3551 GpPath *dst_path;
3552 RECT dst_bounds;
3554 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3555 destPoints, count, srcRect, srcUnit, callback, callbackData,
3556 imageAttributes);
3558 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3559 return InvalidParameter;
3561 if (!metafile->hemf)
3562 return InvalidParameter;
3564 if (metafile->playback_graphics)
3565 return ObjectBusy;
3567 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3568 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3569 debugstr_pointf(&destPoints[2]));
3571 data.callback = callback;
3572 data.callback_data = callbackData;
3573 data.metafile = real_metafile;
3575 real_metafile->playback_graphics = graphics;
3576 real_metafile->playback_dc = NULL;
3577 real_metafile->src_rect = *srcRect;
3579 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3580 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3582 if (stat == Ok)
3583 stat = GdipBeginContainer2(graphics, &state);
3585 if (stat == Ok)
3587 stat = GdipSetPageScale(graphics, 1.0);
3589 if (stat == Ok)
3590 stat = GdipSetPageUnit(graphics, UnitPixel);
3592 if (stat == Ok)
3593 stat = GdipResetWorldTransform(graphics);
3595 if (stat == Ok)
3596 stat = GdipCreateRegion(&real_metafile->base_clip);
3598 if (stat == Ok)
3599 stat = GdipGetClip(graphics, real_metafile->base_clip);
3601 if (stat == Ok)
3602 stat = GdipCreateRegion(&real_metafile->clip);
3604 if (stat == Ok)
3605 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3607 if (stat == Ok)
3609 GpPointF clip_points[4];
3611 clip_points[0] = real_metafile->playback_points[0];
3612 clip_points[1] = real_metafile->playback_points[1];
3613 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3614 - real_metafile->playback_points[0].X;
3615 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3616 - real_metafile->playback_points[0].Y;
3617 clip_points[3] = real_metafile->playback_points[2];
3619 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3621 if (stat == Ok)
3622 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3624 GdipDeletePath(dst_path);
3627 if (stat == Ok)
3628 stat = GdipCreateMatrix(&real_metafile->world_transform);
3630 if (stat == Ok)
3632 real_metafile->page_unit = UnitDisplay;
3633 real_metafile->page_scale = 1.0;
3634 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3637 if (stat == Ok)
3639 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3642 if (stat == Ok)
3644 stat = METAFILE_PlaybackGetDC(real_metafile);
3646 dst_bounds.left = real_metafile->playback_points[0].X;
3647 dst_bounds.right = real_metafile->playback_points[1].X;
3648 dst_bounds.top = real_metafile->playback_points[0].Y;
3649 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3652 if (stat == Ok)
3653 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3654 &data, &dst_bounds);
3656 METAFILE_PlaybackReleaseDC(real_metafile);
3658 GdipDeleteMatrix(real_metafile->world_transform);
3659 real_metafile->world_transform = NULL;
3661 GdipDeleteRegion(real_metafile->base_clip);
3662 real_metafile->base_clip = NULL;
3664 GdipDeleteRegion(real_metafile->clip);
3665 real_metafile->clip = NULL;
3667 while (list_head(&real_metafile->containers))
3669 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3670 list_remove(&cont->entry);
3671 GdipDeleteRegion(cont->clip);
3672 heap_free(cont);
3675 GdipEndContainer(graphics, state);
3678 real_metafile->playback_graphics = NULL;
3680 return stat;
3683 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestRect( GpGraphics *graphics,
3684 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3685 GDIPCONST GpRectF *src, Unit srcUnit, EnumerateMetafileProc callback,
3686 VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3688 GpPointF points[3];
3690 if (!graphics || !metafile || !dest) return InvalidParameter;
3692 points[0].X = points[2].X = dest->X;
3693 points[0].Y = points[1].Y = dest->Y;
3694 points[1].X = dest->X + dest->Width;
3695 points[2].Y = dest->Y + dest->Height;
3697 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3698 src, srcUnit, callback, cb_data, attrs);
3701 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3702 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3703 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3705 GpPointF points[3];
3707 if (!graphics || !metafile || !dest) return InvalidParameter;
3709 points[0].X = points[2].X = dest->X;
3710 points[0].Y = points[1].Y = dest->Y;
3711 points[1].X = dest->X + dest->Width;
3712 points[2].Y = dest->Y + dest->Height;
3714 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3715 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3718 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3719 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3720 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3722 GpRectF destf;
3724 if (!graphics || !metafile || !dest) return InvalidParameter;
3726 destf.X = dest->X;
3727 destf.Y = dest->Y;
3728 destf.Width = dest->Width;
3729 destf.Height = dest->Height;
3731 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3734 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3735 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3736 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3738 GpRectF destf;
3740 if (!graphics || !metafile || !dest) return InvalidParameter;
3742 destf.X = dest->X;
3743 destf.Y = dest->Y;
3744 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit,
3745 metafile->image.xres, metafile->printer_display);
3746 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit,
3747 metafile->image.yres, metafile->printer_display);
3749 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3752 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3753 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3754 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3756 GpPointF ptf;
3758 if (!graphics || !metafile || !dest) return InvalidParameter;
3760 ptf.X = dest->X;
3761 ptf.Y = dest->Y;
3763 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3766 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3767 MetafileHeader * header)
3769 GpStatus status;
3771 TRACE("(%p, %p)\n", metafile, header);
3773 if(!metafile || !header)
3774 return InvalidParameter;
3776 if (metafile->hemf)
3778 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3779 if (status != Ok) return status;
3781 else
3783 memset(header, 0, sizeof(*header));
3784 header->Version = VERSION_MAGIC2;
3787 header->Type = metafile->metafile_type;
3788 header->DpiX = metafile->image.xres;
3789 header->DpiY = metafile->image.yres;
3790 header->Width = gdip_round(metafile->bounds.Width);
3791 header->Height = gdip_round(metafile->bounds.Height);
3793 return Ok;
3796 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3797 int nObj, LPARAM lpData)
3799 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3801 if (lpEMFR->iType == EMR_GDICOMMENT)
3803 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3805 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3807 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3809 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3810 header->Type == EmfPlusRecordTypeHeader)
3812 memcpy(dst_header, header, sizeof(*dst_header));
3816 else if (lpEMFR->iType == EMR_HEADER)
3817 return TRUE;
3819 return FALSE;
3822 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3823 MetafileHeader *header)
3825 ENHMETAHEADER3 emfheader;
3826 EmfPlusHeader emfplusheader;
3827 MetafileType metafile_type;
3829 TRACE("(%p,%p)\n", hemf, header);
3831 if(!hemf || !header)
3832 return InvalidParameter;
3834 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3835 return GenericError;
3837 emfplusheader.Header.Type = 0;
3839 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3841 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3843 if ((emfplusheader.Header.Flags & 1) == 1)
3844 metafile_type = MetafileTypeEmfPlusDual;
3845 else
3846 metafile_type = MetafileTypeEmfPlusOnly;
3848 else
3849 metafile_type = MetafileTypeEmf;
3851 header->Type = metafile_type;
3852 header->Size = emfheader.nBytes;
3853 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3854 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3855 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3856 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3857 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3858 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3859 header->u.EmfHeader = emfheader;
3861 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3863 header->Version = emfplusheader.Version;
3864 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3865 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3866 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3867 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3869 else
3871 header->Version = emfheader.nVersion;
3872 header->EmfPlusFlags = 0;
3873 header->EmfPlusHeaderSize = 0;
3874 header->LogicalDpiX = 0;
3875 header->LogicalDpiY = 0;
3878 return Ok;
3881 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3882 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3884 GpStatus status;
3885 GpMetafile *metafile;
3887 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3889 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3890 if (status == Ok)
3892 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3893 GdipDisposeImage(&metafile->image);
3895 return status;
3898 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3899 MetafileHeader *header)
3901 GpStatus status;
3902 GpMetafile *metafile;
3904 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3906 if (!filename || !header)
3907 return InvalidParameter;
3909 status = GdipCreateMetafileFromFile(filename, &metafile);
3910 if (status == Ok)
3912 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3913 GdipDisposeImage(&metafile->image);
3915 return status;
3918 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3919 MetafileHeader *header)
3921 GpStatus status;
3922 GpMetafile *metafile;
3924 TRACE("(%p,%p)\n", stream, header);
3926 if (!stream || !header)
3927 return InvalidParameter;
3929 status = GdipCreateMetafileFromStream(stream, &metafile);
3930 if (status == Ok)
3932 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3933 GdipDisposeImage(&metafile->image);
3935 return status;
3938 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3939 GpMetafile **metafile)
3941 GpStatus stat;
3942 MetafileHeader header;
3944 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3946 if(!hemf || !metafile)
3947 return InvalidParameter;
3949 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3950 if (stat != Ok)
3951 return stat;
3953 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3954 if (!*metafile)
3955 return OutOfMemory;
3957 (*metafile)->image.type = ImageTypeMetafile;
3958 (*metafile)->image.format = ImageFormatEMF;
3959 (*metafile)->image.frame_count = 1;
3960 (*metafile)->image.xres = header.DpiX;
3961 (*metafile)->image.yres = header.DpiY;
3962 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3963 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3964 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3965 / 2540.0 * header.DpiX;
3966 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3967 / 2540.0 * header.DpiY;
3968 (*metafile)->unit = UnitPixel;
3969 (*metafile)->metafile_type = header.Type;
3970 (*metafile)->hemf = hemf;
3971 (*metafile)->preserve_hemf = !delete;
3972 /* If the 31th bit of EmfPlusFlags was set, metafile was recorded with a DC for a video display.
3973 * If clear, metafile was recorded with a DC for a printer */
3974 (*metafile)->printer_display = !(header.EmfPlusFlags & (1u << 31));
3975 (*metafile)->logical_dpix = header.LogicalDpiX;
3976 (*metafile)->logical_dpiy = header.LogicalDpiY;
3977 list_init(&(*metafile)->containers);
3979 TRACE("<-- %p\n", *metafile);
3981 return Ok;
3984 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3985 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3987 UINT read;
3988 BYTE *copy;
3989 HENHMETAFILE hemf;
3990 GpStatus retval = Ok;
3992 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3994 if(!hwmf || !metafile)
3995 return InvalidParameter;
3997 *metafile = NULL;
3998 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3999 if(!read)
4000 return GenericError;
4001 copy = heap_alloc_zero(read);
4002 GetMetaFileBitsEx(hwmf, read, copy);
4004 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
4005 heap_free(copy);
4007 /* FIXME: We should store and use hwmf instead of converting to hemf */
4008 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4010 if (retval == Ok)
4012 if (placeable)
4014 (*metafile)->image.xres = (REAL)placeable->Inch;
4015 (*metafile)->image.yres = (REAL)placeable->Inch;
4016 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
4017 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
4018 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
4019 placeable->BoundingBox.Left);
4020 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
4021 placeable->BoundingBox.Top);
4022 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
4024 else
4025 (*metafile)->metafile_type = MetafileTypeWmf;
4026 (*metafile)->image.format = ImageFormatWMF;
4028 if (delete) DeleteMetaFile(hwmf);
4030 else
4031 DeleteEnhMetaFile(hemf);
4032 return retval;
4035 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4036 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4038 HMETAFILE hmf;
4039 HENHMETAFILE emf;
4041 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4043 hmf = GetMetaFileW(file);
4044 if(hmf)
4045 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4047 emf = GetEnhMetaFileW(file);
4048 if(emf)
4049 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4051 return GenericError;
4054 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4055 GpMetafile **metafile)
4057 GpStatus status;
4058 IStream *stream;
4060 TRACE("(%p, %p)\n", file, metafile);
4062 if (!file || !metafile) return InvalidParameter;
4064 *metafile = NULL;
4066 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4067 if (status == Ok)
4069 status = GdipCreateMetafileFromStream(stream, metafile);
4070 IStream_Release(stream);
4072 return status;
4075 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4076 GpMetafile **metafile)
4078 GpStatus stat;
4080 TRACE("%p %p\n", stream, metafile);
4082 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4083 if (stat != Ok) return stat;
4085 if ((*metafile)->image.type != ImageTypeMetafile)
4087 GdipDisposeImage(&(*metafile)->image);
4088 *metafile = NULL;
4089 return GenericError;
4092 return Ok;
4095 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4096 UINT *limitDpi)
4098 TRACE("(%p,%p)\n", metafile, limitDpi);
4100 if (!metafile || !limitDpi)
4101 return InvalidParameter;
4103 if (!metafile->record_dc)
4104 return WrongState;
4106 *limitDpi = metafile->limit_dpi;
4108 return Ok;
4111 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4112 UINT limitDpi)
4114 TRACE("(%p,%u)\n", metafile, limitDpi);
4116 if (limitDpi == 0)
4117 limitDpi = 96;
4119 if (!metafile || limitDpi < 10)
4120 return InvalidParameter;
4122 if (!metafile->record_dc)
4123 return WrongState;
4125 metafile->limit_dpi = limitDpi;
4127 return Ok;
4130 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4131 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4132 const WCHAR* description, GpMetafile** out_metafile)
4134 static int calls;
4136 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4137 debugstr_w(description), out_metafile);
4139 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4140 return InvalidParameter;
4142 if(succ)
4143 *succ = FALSE;
4144 *out_metafile = NULL;
4146 if(!(calls++))
4147 FIXME("not implemented\n");
4149 return NotImplemented;
4152 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4153 LPBYTE pData16, INT iMapMode, INT eFlags)
4155 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4156 return NotImplemented;
4159 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4160 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4161 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4162 GpMetafile **metafile)
4164 HDC record_dc;
4165 REAL dpix, dpiy;
4166 REAL framerect_factor_x, framerect_factor_y;
4167 RECT rc, *lprc;
4168 GpStatus stat;
4170 TRACE("%s %p %d %s %d %s %p\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4171 frameUnit, debugstr_w(desc), metafile);
4173 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
4174 return InvalidParameter;
4176 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
4177 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
4179 if (pFrameRect)
4181 switch (frameUnit)
4183 case MetafileFrameUnitPixel:
4184 framerect_factor_x = 2540.0 / dpix;
4185 framerect_factor_y = 2540.0 / dpiy;
4186 break;
4187 case MetafileFrameUnitPoint:
4188 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
4189 break;
4190 case MetafileFrameUnitInch:
4191 framerect_factor_x = framerect_factor_y = 2540.0;
4192 break;
4193 case MetafileFrameUnitDocument:
4194 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
4195 break;
4196 case MetafileFrameUnitMillimeter:
4197 framerect_factor_x = framerect_factor_y = 100.0;
4198 break;
4199 case MetafileFrameUnitGdi:
4200 framerect_factor_x = framerect_factor_y = 1.0;
4201 break;
4202 default:
4203 return InvalidParameter;
4206 rc.left = framerect_factor_x * pFrameRect->X;
4207 rc.top = framerect_factor_y * pFrameRect->Y;
4208 rc.right = rc.left + framerect_factor_x * pFrameRect->Width;
4209 rc.bottom = rc.top + framerect_factor_y * pFrameRect->Height;
4211 lprc = &rc;
4213 else
4214 lprc = NULL;
4216 record_dc = CreateEnhMetaFileW(hdc, fileName, lprc, desc);
4218 if (!record_dc)
4219 return GenericError;
4221 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4222 if(!*metafile)
4224 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4225 return OutOfMemory;
4228 (*metafile)->image.type = ImageTypeMetafile;
4229 (*metafile)->image.flags = ImageFlagsNone;
4230 (*metafile)->image.palette = NULL;
4231 (*metafile)->image.xres = dpix;
4232 (*metafile)->image.yres = dpiy;
4233 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
4234 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
4235 (*metafile)->unit = UnitPixel;
4236 (*metafile)->metafile_type = type;
4237 (*metafile)->record_dc = record_dc;
4238 (*metafile)->comment_data = NULL;
4239 (*metafile)->comment_data_size = 0;
4240 (*metafile)->comment_data_length = 0;
4241 (*metafile)->limit_dpi = 96;
4242 (*metafile)->hemf = NULL;
4243 (*metafile)->printer_display = (GetDeviceCaps(record_dc, TECHNOLOGY) == DT_RASPRINTER);
4244 (*metafile)->logical_dpix = (REAL)GetDeviceCaps(record_dc, LOGPIXELSX);
4245 (*metafile)->logical_dpiy = (REAL)GetDeviceCaps(record_dc, LOGPIXELSY);
4246 list_init(&(*metafile)->containers);
4248 if (!pFrameRect)
4250 (*metafile)->auto_frame = TRUE;
4251 (*metafile)->auto_frame_min.X = 0;
4252 (*metafile)->auto_frame_min.Y = 0;
4253 (*metafile)->auto_frame_max.X = -1;
4254 (*metafile)->auto_frame_max.Y = -1;
4257 stat = METAFILE_WriteHeader(*metafile, hdc);
4259 if (stat != Ok)
4261 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
4262 heap_free(*metafile);
4263 *metafile = NULL;
4264 return OutOfMemory;
4267 return stat;
4270 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4271 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4272 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4274 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4275 frameUnit, debugstr_w(desc), metafile);
4277 return NotImplemented;
4280 /*****************************************************************************
4281 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4284 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4285 GpMetafile* metafile, BOOL* conversionSuccess,
4286 const WCHAR* filename, EmfType emfType,
4287 const WCHAR* description, GpMetafile** out_metafile)
4289 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4290 return NotImplemented;
4293 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4295 LARGE_INTEGER zero;
4296 STATSTG statstg;
4297 GpStatus stat;
4298 HRESULT hr;
4300 *size = 0;
4302 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4303 if (FAILED(hr)) return hresult_to_status(hr);
4305 stat = encode_image_png(image, *stream, NULL);
4306 if (stat != Ok)
4308 IStream_Release(*stream);
4309 return stat;
4312 hr = IStream_Stat(*stream, &statstg, 1);
4313 if (FAILED(hr))
4315 IStream_Release(*stream);
4316 return hresult_to_status(hr);
4318 *size = statstg.cbSize.u.LowPart;
4320 zero.QuadPart = 0;
4321 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4322 if (FAILED(hr))
4324 IStream_Release(*stream);
4325 return hresult_to_status(hr);
4328 return Ok;
4331 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4333 HRESULT hr;
4335 record->Width = 0;
4336 record->Height = 0;
4337 record->Stride = 0;
4338 record->PixelFormat = 0;
4339 record->Type = BitmapDataTypeCompressed;
4341 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4342 if (FAILED(hr)) return hresult_to_status(hr);
4343 return Ok;
4346 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4348 EmfPlusObject *object_record;
4349 GpStatus stat;
4350 DWORD size;
4352 *id = -1;
4354 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4355 return Ok;
4357 if (image->type == ImageTypeBitmap)
4359 IStream *stream;
4360 DWORD aligned_size;
4362 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4363 if (stat != Ok) return stat;
4364 aligned_size = (size + 3) & ~3;
4366 stat = METAFILE_AllocateRecord(metafile,
4367 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4368 (void**)&object_record);
4369 if (stat != Ok)
4371 IStream_Release(stream);
4372 return stat;
4374 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4376 *id = METAFILE_AddObjectId(metafile);
4377 object_record->Header.Type = EmfPlusRecordTypeObject;
4378 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4379 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4380 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4382 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4383 IStream_Release(stream);
4384 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4385 return stat;
4387 else if (image->type == ImageTypeMetafile)
4389 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4390 EmfPlusMetafile *metafile_record;
4392 if (!hemf) return InvalidParameter;
4394 size = GetEnhMetaFileBits(hemf, 0, NULL);
4395 if (!size) return GenericError;
4397 stat = METAFILE_AllocateRecord(metafile,
4398 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4399 (void**)&object_record);
4400 if (stat != Ok) return stat;
4402 *id = METAFILE_AddObjectId(metafile);
4403 object_record->Header.Type = EmfPlusRecordTypeObject;
4404 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4405 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4406 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4407 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4408 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4409 metafile_record->MetafileDataSize = size;
4410 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4412 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4413 return GenericError;
4415 return Ok;
4417 else
4419 FIXME("not supported image type (%d)\n", image->type);
4420 return NotImplemented;
4424 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4426 EmfPlusObject *object_record;
4427 EmfPlusImageAttributes *attrs_record;
4428 GpStatus stat;
4430 *id = -1;
4432 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4433 return Ok;
4435 if (!attrs)
4436 return Ok;
4438 stat = METAFILE_AllocateRecord(metafile,
4439 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4440 (void**)&object_record);
4441 if (stat != Ok) return stat;
4443 *id = METAFILE_AddObjectId(metafile);
4444 object_record->Header.Type = EmfPlusRecordTypeObject;
4445 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4446 attrs_record = &object_record->ObjectData.image_attributes;
4447 attrs_record->Version = VERSION_MAGIC2;
4448 attrs_record->Reserved1 = 0;
4449 attrs_record->WrapMode = attrs->wrap;
4450 attrs_record->ClampColor = attrs->outside_color;
4451 attrs_record->ObjectClamp = attrs->clamp;
4452 attrs_record->Reserved2 = 0;
4453 return Ok;
4456 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4457 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4458 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4459 DrawImageAbort callback, VOID *callbackData)
4461 EmfPlusDrawImagePoints *draw_image_record;
4462 DWORD image_id, attributes_id;
4463 GpStatus stat;
4465 if (count != 3) return InvalidParameter;
4467 if (metafile->metafile_type == MetafileTypeEmf)
4469 FIXME("MetafileTypeEmf metafiles not supported\n");
4470 return NotImplemented;
4472 else
4473 FIXME("semi-stub\n");
4475 if (!imageAttributes)
4477 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4479 else if (image->type == ImageTypeBitmap)
4481 INT width = ((GpBitmap*)image)->width;
4482 INT height = ((GpBitmap*)image)->height;
4483 GpGraphics *graphics;
4484 GpBitmap *bitmap;
4486 stat = GdipCreateBitmapFromScan0(width, height,
4487 0, PixelFormat32bppARGB, NULL, &bitmap);
4488 if (stat != Ok) return stat;
4490 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4491 if (stat != Ok)
4493 GdipDisposeImage((GpImage*)bitmap);
4494 return stat;
4497 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4498 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4499 GdipDeleteGraphics(graphics);
4500 if (stat != Ok)
4502 GdipDisposeImage((GpImage*)bitmap);
4503 return stat;
4506 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4507 GdipDisposeImage((GpImage*)bitmap);
4509 else
4511 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4512 return NotImplemented;
4514 if (stat != Ok) return stat;
4516 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4517 if (stat != Ok) return stat;
4519 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
4520 if (stat != Ok) return stat;
4521 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
4522 draw_image_record->Header.Flags = image_id;
4523 draw_image_record->ImageAttributesID = attributes_id;
4524 draw_image_record->SrcUnit = UnitPixel;
4525 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres, metafile->printer_display);
4526 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres, metafile->printer_display);
4527 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres, metafile->printer_display);
4528 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres, metafile->printer_display);
4529 draw_image_record->count = 3;
4530 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4531 METAFILE_WriteRecords(metafile);
4532 return Ok;
4535 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4537 EmfPlusRecordHeader *record;
4538 GpStatus stat;
4540 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4541 return Ok;
4543 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
4544 if (stat != Ok) return stat;
4546 record->Type = prop;
4547 record->Flags = val;
4549 METAFILE_WriteRecords(metafile);
4550 return Ok;
4553 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4555 EmfPlusObject *object_record;
4556 GpStatus stat;
4557 DWORD size;
4559 *id = -1;
4560 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4561 return Ok;
4563 size = write_path_data(path, NULL);
4564 stat = METAFILE_AllocateRecord(metafile,
4565 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4566 (void**)&object_record);
4567 if (stat != Ok) return stat;
4569 *id = METAFILE_AddObjectId(metafile);
4570 object_record->Header.Type = EmfPlusRecordTypeObject;
4571 object_record->Header.Flags = *id | ObjectTypePath << 8;
4572 write_path_data(path, &object_record->ObjectData.path);
4573 return Ok;
4576 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4578 DWORD i, data_flags, pen_data_size, brush_size;
4579 EmfPlusObject *object_record;
4580 EmfPlusPenData *pen_data;
4581 GpStatus stat;
4582 BOOL result;
4584 *id = -1;
4585 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4586 return Ok;
4588 data_flags = 0;
4589 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4591 GdipIsMatrixIdentity(&pen->transform, &result);
4592 if (!result)
4594 data_flags |= PenDataTransform;
4595 pen_data_size += sizeof(EmfPlusTransformMatrix);
4597 if (pen->startcap != LineCapFlat)
4599 data_flags |= PenDataStartCap;
4600 pen_data_size += sizeof(DWORD);
4602 if (pen->endcap != LineCapFlat)
4604 data_flags |= PenDataEndCap;
4605 pen_data_size += sizeof(DWORD);
4607 if (pen->join != LineJoinMiter)
4609 data_flags |= PenDataJoin;
4610 pen_data_size += sizeof(DWORD);
4612 if (pen->miterlimit != 10.0)
4614 data_flags |= PenDataMiterLimit;
4615 pen_data_size += sizeof(REAL);
4617 if (pen->style != GP_DEFAULT_PENSTYLE)
4619 data_flags |= PenDataLineStyle;
4620 pen_data_size += sizeof(DWORD);
4622 if (pen->dashcap != DashCapFlat)
4624 data_flags |= PenDataDashedLineCap;
4625 pen_data_size += sizeof(DWORD);
4627 data_flags |= PenDataDashedLineOffset;
4628 pen_data_size += sizeof(REAL);
4629 if (pen->numdashes)
4631 data_flags |= PenDataDashedLine;
4632 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4634 if (pen->align != PenAlignmentCenter)
4636 data_flags |= PenDataNonCenter;
4637 pen_data_size += sizeof(DWORD);
4639 /* TODO: Add support for PenDataCompoundLine */
4640 if (pen->customstart)
4642 FIXME("ignoring custom start cup\n");
4644 if (pen->customend)
4646 FIXME("ignoring custom end cup\n");
4649 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4650 if (stat != Ok) return stat;
4652 stat = METAFILE_AllocateRecord(metafile,
4653 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4654 (void**)&object_record);
4655 if (stat != Ok) return stat;
4657 *id = METAFILE_AddObjectId(metafile);
4658 object_record->Header.Type = EmfPlusRecordTypeObject;
4659 object_record->Header.Flags = *id | ObjectTypePen << 8;
4660 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4661 object_record->ObjectData.pen.Type = 0;
4663 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4664 pen_data->PenDataFlags = data_flags;
4665 pen_data->PenUnit = pen->unit;
4666 pen_data->PenWidth = pen->width;
4668 i = 0;
4669 if (data_flags & PenDataTransform)
4671 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4672 memcpy(m, &pen->transform, sizeof(*m));
4673 i += sizeof(EmfPlusTransformMatrix);
4675 if (data_flags & PenDataStartCap)
4677 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4678 i += sizeof(DWORD);
4680 if (data_flags & PenDataEndCap)
4682 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4683 i += sizeof(DWORD);
4685 if (data_flags & PenDataJoin)
4687 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4688 i += sizeof(DWORD);
4690 if (data_flags & PenDataMiterLimit)
4692 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4693 i += sizeof(REAL);
4695 if (data_flags & PenDataLineStyle)
4697 switch (pen->style & PS_STYLE_MASK)
4699 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4700 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4701 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4702 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4703 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4704 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4706 i += sizeof(DWORD);
4708 if (data_flags & PenDataDashedLineCap)
4710 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4711 i += sizeof(DWORD);
4713 if (data_flags & PenDataDashedLineOffset)
4715 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4716 i += sizeof(REAL);
4718 if (data_flags & PenDataDashedLine)
4720 int j;
4722 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4723 i += sizeof(DWORD);
4725 for (j=0; j<pen->numdashes; j++)
4727 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4728 i += sizeof(REAL);
4731 if (data_flags & PenDataNonCenter)
4733 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4734 i += sizeof(DWORD);
4737 METAFILE_FillBrushData(pen->brush,
4738 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4739 return Ok;
4742 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4744 EmfPlusDrawPath *draw_path_record;
4745 DWORD path_id;
4746 DWORD pen_id;
4747 GpStatus stat;
4749 if (metafile->metafile_type == MetafileTypeEmf)
4751 FIXME("stub!\n");
4752 return NotImplemented;
4755 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4756 if (stat != Ok) return stat;
4758 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4759 if (stat != Ok) return stat;
4761 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4762 if (stat != Ok) return stat;
4763 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4764 draw_path_record->Header.Flags = path_id;
4765 draw_path_record->PenId = pen_id;
4767 METAFILE_WriteRecords(metafile);
4768 return Ok;
4771 GpStatus METAFILE_DrawEllipse(GpMetafile *metafile, GpPen *pen, GpRectF *rect)
4773 EmfPlusDrawEllipse *record;
4774 GpStatus stat;
4775 DWORD pen_id;
4777 if (metafile->metafile_type == MetafileTypeEmf)
4779 FIXME("stub!\n");
4780 return NotImplemented;
4783 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4784 if (stat != Ok) return stat;
4786 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawEllipse), (void **)&record);
4787 if (stat != Ok) return stat;
4788 record->Header.Type = EmfPlusRecordTypeDrawEllipse;
4789 record->Header.Flags = pen_id;
4790 if (is_integer_rect(rect))
4792 record->Header.Flags |= 0x4000;
4793 record->RectData.rect.X = (SHORT)rect->X;
4794 record->RectData.rect.Y = (SHORT)rect->Y;
4795 record->RectData.rect.Width = (SHORT)rect->Width;
4796 record->RectData.rect.Height = (SHORT)rect->Height;
4798 else
4799 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4801 METAFILE_WriteRecords(metafile);
4802 return Ok;
4805 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4807 EmfPlusFillPath *fill_path_record;
4808 DWORD brush_id = -1, path_id;
4809 BOOL inline_color;
4810 GpStatus stat;
4812 if (metafile->metafile_type == MetafileTypeEmf)
4814 FIXME("stub!\n");
4815 return NotImplemented;
4818 inline_color = brush->bt == BrushTypeSolidColor;
4819 if (!inline_color)
4821 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4822 if (stat != Ok) return stat;
4825 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4826 if (stat != Ok) return stat;
4828 stat = METAFILE_AllocateRecord(metafile,
4829 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4830 if (stat != Ok) return stat;
4831 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4832 if (inline_color)
4834 fill_path_record->Header.Flags = 0x8000 | path_id;
4835 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4837 else
4839 fill_path_record->Header.Flags = path_id;
4840 fill_path_record->data.BrushId = brush_id;
4843 METAFILE_WriteRecords(metafile);
4844 return Ok;
4847 GpStatus METAFILE_FillEllipse(GpMetafile *metafile, GpBrush *brush, GpRectF *rect)
4849 EmfPlusFillEllipse *record;
4850 DWORD brush_id = -1;
4851 BOOL inline_color;
4852 GpStatus stat;
4854 if (metafile->metafile_type == MetafileTypeEmf)
4856 FIXME("stub!\n");
4857 return NotImplemented;
4860 inline_color = brush->bt == BrushTypeSolidColor;
4861 if (!inline_color)
4863 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4864 if (stat != Ok) return stat;
4867 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusFillEllipse), (void **)&record);
4868 if (stat != Ok) return stat;
4869 record->Header.Type = EmfPlusRecordTypeFillEllipse;
4870 if (inline_color)
4872 record->Header.Flags = 0x8000;
4873 record->BrushId = ((GpSolidFill *)brush)->color;
4875 else
4876 record->BrushId = brush_id;
4878 if (is_integer_rect(rect))
4880 record->Header.Flags |= 0x4000;
4881 record->RectData.rect.X = (SHORT)rect->X;
4882 record->RectData.rect.Y = (SHORT)rect->Y;
4883 record->RectData.rect.Width = (SHORT)rect->Width;
4884 record->RectData.rect.Height = (SHORT)rect->Height;
4886 else
4887 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4889 METAFILE_WriteRecords(metafile);
4890 return Ok;
4893 GpStatus METAFILE_FillPie(GpMetafile *metafile, GpBrush *brush, const GpRectF *rect,
4894 REAL startAngle, REAL sweepAngle)
4896 BOOL is_int_rect, inline_color;
4897 EmfPlusFillPie *record;
4898 DWORD brush_id = -1;
4899 GpStatus stat;
4901 if (metafile->metafile_type == MetafileTypeEmf)
4903 FIXME("stub!\n");
4904 return NotImplemented;
4907 inline_color = brush->bt == BrushTypeSolidColor;
4908 if (!inline_color)
4910 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4911 if (stat != Ok) return stat;
4914 is_int_rect = is_integer_rect(rect);
4916 stat = METAFILE_AllocateRecord(metafile, FIELD_OFFSET(EmfPlusFillPie, RectData) +
4917 is_int_rect ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF), (void **)&record);
4918 if (stat != Ok) return stat;
4919 record->Header.Type = EmfPlusRecordTypeFillPie;
4920 if (inline_color)
4922 record->Header.Flags = 0x8000;
4923 record->BrushId = ((GpSolidFill *)brush)->color;
4925 else
4926 record->BrushId = brush_id;
4928 record->StartAngle = startAngle;
4929 record->SweepAngle = sweepAngle;
4931 if (is_int_rect)
4933 record->Header.Flags |= 0x4000;
4934 record->RectData.rect.X = (SHORT)rect->X;
4935 record->RectData.rect.Y = (SHORT)rect->Y;
4936 record->RectData.rect.Width = (SHORT)rect->Width;
4937 record->RectData.rect.Height = (SHORT)rect->Height;
4939 else
4940 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
4942 METAFILE_WriteRecords(metafile);
4943 return Ok;
4946 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
4948 EmfPlusObject *object_record;
4949 EmfPlusFont *font_record;
4950 GpStatus stat;
4951 INT fn_len;
4952 INT style;
4954 *id = -1;
4956 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4957 metafile->metafile_type != MetafileTypeEmfPlusDual)
4958 return Ok;
4960 /* The following cast is ugly, but GdipGetFontStyle does treat
4961 its first parameter as const. */
4962 stat = GdipGetFontStyle((GpFont*)font, &style);
4963 if (stat != Ok)
4964 return stat;
4966 fn_len = lstrlenW(font->family->FamilyName);
4967 stat = METAFILE_AllocateRecord(metafile,
4968 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
4969 (void**)&object_record);
4970 if (stat != Ok)
4971 return stat;
4973 *id = METAFILE_AddObjectId(metafile);
4975 object_record->Header.Type = EmfPlusRecordTypeObject;
4976 object_record->Header.Flags = *id | ObjectTypeFont << 8;
4978 font_record = &object_record->ObjectData.font;
4979 font_record->Version = VERSION_MAGIC2;
4980 font_record->EmSize = font->emSize;
4981 font_record->SizeUnit = font->unit;
4982 font_record->FontStyleFlags = style;
4983 font_record->Reserved = 0;
4984 font_record->Length = fn_len;
4986 memcpy(font_record->FamilyName, font->family->FamilyName,
4987 fn_len * sizeof(*font->family->FamilyName));
4989 return Ok;
4992 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
4993 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
4994 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
4996 DWORD brush_id;
4997 DWORD font_id;
4998 DWORD alloc_size;
4999 GpStatus stat;
5000 EmfPlusDrawDriverString *draw_string_record;
5001 BYTE *cursor;
5002 BOOL inline_color;
5003 BOOL include_matrix = FALSE;
5005 if (length <= 0)
5006 return InvalidParameter;
5008 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5009 metafile->metafile_type != MetafileTypeEmfPlusDual)
5011 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5012 return NotImplemented;
5015 stat = METAFILE_AddFontObject(metafile, font, &font_id);
5016 if (stat != Ok)
5017 return stat;
5019 inline_color = (brush->bt == BrushTypeSolidColor);
5020 if (!inline_color)
5022 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5023 if (stat != Ok)
5024 return stat;
5027 if (matrix)
5029 BOOL identity;
5031 stat = GdipIsMatrixIdentity(matrix, &identity);
5032 if (stat != Ok)
5033 return stat;
5035 include_matrix = !identity;
5038 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
5039 length * (sizeof(*text) + sizeof(*positions));
5041 if (include_matrix)
5042 alloc_size += sizeof(*matrix);
5044 /* Pad record to DWORD alignment. */
5045 alloc_size = (alloc_size + 3) & ~3;
5047 stat = METAFILE_AllocateRecord(metafile, alloc_size, (void**)&draw_string_record);
5048 if (stat != Ok)
5049 return stat;
5051 draw_string_record->Header.Type = EmfPlusRecordTypeDrawDriverString;
5052 draw_string_record->Header.Flags = font_id;
5053 draw_string_record->DriverStringOptionsFlags = flags;
5054 draw_string_record->MatrixPresent = include_matrix;
5055 draw_string_record->GlyphCount = length;
5057 if (inline_color)
5059 draw_string_record->Header.Flags |= 0x8000;
5060 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
5062 else
5063 draw_string_record->brush.BrushId = brush_id;
5065 cursor = &draw_string_record->VariableData[0];
5067 memcpy(cursor, text, length * sizeof(*text));
5068 cursor += length * sizeof(*text);
5070 if (flags & DriverStringOptionsRealizedAdvance)
5072 static BOOL fixme_written = FALSE;
5074 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
5075 in the case of RealizedAdvance, each glyph position is computed
5076 and serialized.
5078 While native GDI+ is capable of playing back metafiles with this
5079 flag set, it is possible that some application might rely on
5080 metafiles produced from GDI+ not setting this flag. Ideally we
5081 would also compute the position of each glyph here, serialize those
5082 values, and not set DriverStringOptionsRealizedAdvance. */
5083 if (!fixme_written)
5085 fixme_written = TRUE;
5086 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
5089 *((PointF*)cursor) = *positions;
5091 else
5092 memcpy(cursor, positions, length * sizeof(*positions));
5094 if (include_matrix)
5096 cursor += length * sizeof(*positions);
5097 memcpy(cursor, matrix, sizeof(*matrix));
5100 METAFILE_WriteRecords(metafile);
5102 return Ok;
5105 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
5107 GpStatus stat;
5108 DWORD brush_id;
5109 DWORD region_id;
5110 EmfPlusFillRegion *fill_region_record;
5111 BOOL inline_color;
5113 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
5114 metafile->metafile_type != MetafileTypeEmfPlusDual)
5116 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
5117 return NotImplemented;
5120 inline_color = (brush->bt == BrushTypeSolidColor);
5121 if (!inline_color)
5123 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
5124 if (stat != Ok)
5125 return stat;
5128 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
5129 if (stat != Ok)
5130 return stat;
5132 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusFillRegion),
5133 (void**)&fill_region_record);
5134 if (stat != Ok)
5135 return stat;
5137 fill_region_record->Header.Type = EmfPlusRecordTypeFillRegion;
5138 fill_region_record->Header.Flags = region_id;
5140 if (inline_color)
5142 fill_region_record->Header.Flags |= 0x8000;
5143 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
5145 else
5146 fill_region_record->data.BrushId = brush_id;
5148 METAFILE_WriteRecords(metafile);
5150 return Ok;
5153 GpStatus METAFILE_DrawRectangles(GpMetafile *metafile, GpPen *pen, const GpRectF *rects, INT count)
5155 EmfPlusDrawRects *record;
5156 GpStatus stat;
5157 BOOL integer_rects = TRUE;
5158 DWORD pen_id;
5159 int i;
5161 if (metafile->metafile_type == MetafileTypeEmf)
5163 FIXME("stub!\n");
5164 return NotImplemented;
5167 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5168 if (stat != Ok) return stat;
5170 for (i = 0; i < count; i++)
5172 if (!is_integer_rect(&rects[i]))
5174 integer_rects = FALSE;
5175 break;
5179 stat = METAFILE_AllocateRecord(metafile, FIELD_OFFSET(EmfPlusDrawRects, RectData) +
5180 count * (integer_rects ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF)),
5181 (void **)&record);
5182 if (stat != Ok)
5183 return stat;
5185 record->Header.Type = EmfPlusRecordTypeDrawRects;
5186 record->Header.Flags = pen_id;
5187 if (integer_rects)
5188 record->Header.Flags |= 0x4000;
5189 record->Count = count;
5191 if (integer_rects)
5193 for (i = 0; i < count; i++)
5195 record->RectData.rect[i].X = (SHORT)rects[i].X;
5196 record->RectData.rect[i].Y = (SHORT)rects[i].Y;
5197 record->RectData.rect[i].Width = (SHORT)rects[i].Width;
5198 record->RectData.rect[i].Height = (SHORT)rects[i].Height;
5201 else
5202 memcpy(record->RectData.rectF, rects, sizeof(*rects) * count);
5204 METAFILE_WriteRecords(metafile);
5206 return Ok;
5209 GpStatus METAFILE_DrawArc(GpMetafile *metafile, GpPen *pen, const GpRectF *rect, REAL startAngle, REAL sweepAngle)
5211 EmfPlusDrawArc *record;
5212 GpStatus stat;
5213 BOOL integer_rect;
5214 DWORD pen_id;
5216 if (metafile->metafile_type == MetafileTypeEmf)
5218 FIXME("stub!\n");
5219 return NotImplemented;
5222 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
5223 if (stat != Ok) return stat;
5225 integer_rect = is_integer_rect(rect);
5227 stat = METAFILE_AllocateRecord(metafile, FIELD_OFFSET(EmfPlusDrawArc, RectData) +
5228 integer_rect ? sizeof(record->RectData.rect) : sizeof(record->RectData.rectF),
5229 (void **)&record);
5230 if (stat != Ok)
5231 return stat;
5233 record->Header.Type = EmfPlusRecordTypeDrawArc;
5234 record->Header.Flags = pen_id;
5235 if (integer_rect)
5236 record->Header.Flags |= 0x4000;
5237 record->StartAngle = startAngle;
5238 record->SweepAngle = sweepAngle;
5240 if (integer_rect)
5242 record->RectData.rect.X = (SHORT)rect->X;
5243 record->RectData.rect.Y = (SHORT)rect->Y;
5244 record->RectData.rect.Width = (SHORT)rect->Width;
5245 record->RectData.rect.Height = (SHORT)rect->Height;
5247 else
5248 memcpy(&record->RectData.rectF, rect, sizeof(*rect));
5250 METAFILE_WriteRecords(metafile);
5252 return Ok;