combase: Fix the trailing linefeed of a TRACE().
[wine/zf.git] / dlls / gdiplus / metafile.c
blobe0ff6cae7a4b123be0103b140c9524ced58678c3
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)
762 HDC record_dc;
763 REAL dpix, dpiy;
764 REAL framerect_factor_x, framerect_factor_y;
765 RECT rc, *lprc;
766 GpStatus stat;
768 TRACE("(%p %d %s %d %p %p)\n", hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
770 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
771 return InvalidParameter;
773 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
774 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
776 if (frameRect)
778 switch (frameUnit)
780 case MetafileFrameUnitPixel:
781 framerect_factor_x = 2540.0 / dpix;
782 framerect_factor_y = 2540.0 / dpiy;
783 break;
784 case MetafileFrameUnitPoint:
785 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
786 break;
787 case MetafileFrameUnitInch:
788 framerect_factor_x = framerect_factor_y = 2540.0;
789 break;
790 case MetafileFrameUnitDocument:
791 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
792 break;
793 case MetafileFrameUnitMillimeter:
794 framerect_factor_x = framerect_factor_y = 100.0;
795 break;
796 case MetafileFrameUnitGdi:
797 framerect_factor_x = framerect_factor_y = 1.0;
798 break;
799 default:
800 return InvalidParameter;
803 rc.left = framerect_factor_x * frameRect->X;
804 rc.top = framerect_factor_y * frameRect->Y;
805 rc.right = rc.left + framerect_factor_x * frameRect->Width;
806 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
808 lprc = &rc;
810 else
811 lprc = NULL;
813 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
815 if (!record_dc)
816 return GenericError;
818 *metafile = heap_alloc_zero(sizeof(GpMetafile));
819 if(!*metafile)
821 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
822 return OutOfMemory;
825 (*metafile)->image.type = ImageTypeMetafile;
826 (*metafile)->image.flags = ImageFlagsNone;
827 (*metafile)->image.palette = NULL;
828 (*metafile)->image.xres = dpix;
829 (*metafile)->image.yres = dpiy;
830 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
831 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
832 (*metafile)->unit = UnitPixel;
833 (*metafile)->metafile_type = type;
834 (*metafile)->record_dc = record_dc;
835 (*metafile)->comment_data = NULL;
836 (*metafile)->comment_data_size = 0;
837 (*metafile)->comment_data_length = 0;
838 (*metafile)->limit_dpi = 96;
839 (*metafile)->hemf = NULL;
840 list_init(&(*metafile)->containers);
842 if (!frameRect)
844 (*metafile)->auto_frame = TRUE;
845 (*metafile)->auto_frame_min.X = 0;
846 (*metafile)->auto_frame_min.Y = 0;
847 (*metafile)->auto_frame_max.X = -1;
848 (*metafile)->auto_frame_max.Y = -1;
851 stat = METAFILE_WriteHeader(*metafile, hdc);
853 if (stat != Ok)
855 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
856 heap_free(*metafile);
857 *metafile = NULL;
858 return OutOfMemory;
861 return stat;
864 /*****************************************************************************
865 * GdipRecordMetafileI [GDIPLUS.@]
867 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
868 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
870 GpRectF frameRectF, *pFrameRectF;
872 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
874 if (frameRect)
876 frameRectF.X = frameRect->X;
877 frameRectF.Y = frameRect->Y;
878 frameRectF.Width = frameRect->Width;
879 frameRectF.Height = frameRect->Height;
880 pFrameRectF = &frameRectF;
882 else
883 pFrameRectF = NULL;
885 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
888 GpStatus WINGDIPAPI GdipRecordMetafileStreamI(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
889 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
891 GpRectF frameRectF, *pFrameRectF;
893 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
895 if (frameRect)
897 frameRectF.X = frameRect->X;
898 frameRectF.Y = frameRect->Y;
899 frameRectF.Width = frameRect->Width;
900 frameRectF.Height = frameRect->Height;
901 pFrameRectF = &frameRectF;
903 else
904 pFrameRectF = NULL;
906 return GdipRecordMetafileStream(stream, hdc, type, pFrameRectF, frameUnit, desc, metafile);
909 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
910 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
912 GpStatus stat;
914 TRACE("(%p %p %d %s %d %p %p)\n", stream, hdc, type, debugstr_rectf(frameRect), frameUnit, desc, metafile);
916 if (!stream)
917 return InvalidParameter;
919 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
921 if (stat == Ok)
923 (*metafile)->record_stream = stream;
924 IStream_AddRef(stream);
927 return stat;
930 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
931 UINT num_points)
933 int i;
935 if (!metafile->auto_frame || !num_points)
936 return;
938 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
939 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
941 for (i=0; i<num_points; i++)
943 if (points[i].X < metafile->auto_frame_min.X)
944 metafile->auto_frame_min.X = points[i].X;
945 if (points[i].X > metafile->auto_frame_max.X)
946 metafile->auto_frame_max.X = points[i].X;
947 if (points[i].Y < metafile->auto_frame_min.Y)
948 metafile->auto_frame_min.Y = points[i].Y;
949 if (points[i].Y > metafile->auto_frame_max.Y)
950 metafile->auto_frame_max.Y = points[i].Y;
954 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
956 GpStatus stat;
958 if (!metafile->record_dc || metafile->record_graphics)
959 return InvalidParameter;
961 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
963 if (stat == Ok)
965 *result = metafile->record_graphics;
966 metafile->record_graphics->xres = 96.0;
967 metafile->record_graphics->yres = 96.0;
970 return stat;
973 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
975 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
977 EmfPlusRecordHeader *record;
978 GpStatus stat;
980 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
981 if (stat != Ok)
982 return stat;
984 record->Type = EmfPlusRecordTypeGetDC;
985 record->Flags = 0;
987 METAFILE_WriteRecords(metafile);
990 *hdc = metafile->record_dc;
992 return Ok;
995 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
997 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
999 EmfPlusClear *record;
1000 GpStatus stat;
1002 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
1003 if (stat != Ok)
1004 return stat;
1006 record->Header.Type = EmfPlusRecordTypeClear;
1007 record->Header.Flags = 0;
1008 record->Color = color;
1010 METAFILE_WriteRecords(metafile);
1013 return Ok;
1016 static BOOL is_integer_rect(const GpRectF *rect)
1018 SHORT x, y, width, height;
1019 x = rect->X;
1020 y = rect->Y;
1021 width = rect->Width;
1022 height = rect->Height;
1023 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
1024 rect->Width != (REAL)width || rect->Height != (REAL)height)
1025 return FALSE;
1026 return TRUE;
1029 static GpStatus METAFILE_PrepareBrushData(GDIPCONST GpBrush *brush, DWORD *size)
1031 switch (brush->bt)
1033 case BrushTypeSolidColor:
1034 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
1035 break;
1036 case BrushTypeHatchFill:
1037 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
1038 break;
1039 case BrushTypeLinearGradient:
1041 BOOL ignore_xform;
1042 GpLineGradient *gradient = (GpLineGradient*)brush;
1044 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.lineargradient.OptionalData);
1046 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1047 if (!ignore_xform)
1048 *size += sizeof(gradient->transform);
1050 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1051 *size += sizeof(DWORD) + gradient->pblendcount *
1052 (sizeof(*gradient->pblendcolor) + sizeof(*gradient->pblendpos));
1053 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1054 *size += sizeof(DWORD) + gradient->blendcount *
1055 (sizeof(*gradient->blendfac) + sizeof(*gradient->blendpos));
1057 break;
1059 default:
1060 FIXME("unsupported brush type: %d\n", brush->bt);
1061 return NotImplemented;
1064 return Ok;
1067 static void METAFILE_FillBrushData(GDIPCONST GpBrush *brush, EmfPlusBrush *data)
1069 data->Version = VERSION_MAGIC2;
1070 data->Type = brush->bt;
1072 switch (brush->bt)
1074 case BrushTypeSolidColor:
1076 GpSolidFill *solid = (GpSolidFill *)brush;
1077 data->BrushData.solid.SolidColor = solid->color;
1078 break;
1080 case BrushTypeHatchFill:
1082 GpHatch *hatch = (GpHatch *)brush;
1083 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
1084 data->BrushData.hatch.ForeColor = hatch->forecol;
1085 data->BrushData.hatch.BackColor = hatch->backcol;
1086 break;
1088 case BrushTypeLinearGradient:
1090 BYTE *cursor;
1091 BOOL ignore_xform;
1092 GpLineGradient *gradient = (GpLineGradient*)brush;
1094 data->BrushData.lineargradient.BrushDataFlags = 0;
1095 data->BrushData.lineargradient.WrapMode = gradient->wrap;
1096 data->BrushData.lineargradient.RectF.X = gradient->rect.X;
1097 data->BrushData.lineargradient.RectF.Y = gradient->rect.Y;
1098 data->BrushData.lineargradient.RectF.Width = gradient->rect.Width;
1099 data->BrushData.lineargradient.RectF.Height = gradient->rect.Height;
1100 data->BrushData.lineargradient.StartColor = gradient->startcolor;
1101 data->BrushData.lineargradient.EndColor = gradient->endcolor;
1102 data->BrushData.lineargradient.Reserved1 = gradient->startcolor;
1103 data->BrushData.lineargradient.Reserved2 = gradient->endcolor;
1105 if (gradient->gamma)
1106 data->BrushData.lineargradient.BrushDataFlags |= BrushDataIsGammaCorrected;
1108 cursor = &data->BrushData.lineargradient.OptionalData[0];
1110 GdipIsMatrixIdentity(&gradient->transform, &ignore_xform);
1111 if (!ignore_xform)
1113 data->BrushData.lineargradient.BrushDataFlags |= BrushDataTransform;
1114 memcpy(cursor, &gradient->transform, sizeof(gradient->transform));
1115 cursor += sizeof(gradient->transform);
1118 if (gradient->pblendcount > 1 && gradient->pblendcolor && gradient->pblendpos)
1120 const DWORD count = gradient->pblendcount;
1122 data->BrushData.lineargradient.BrushDataFlags |= BrushDataPresetColors;
1124 memcpy(cursor, &count, sizeof(count));
1125 cursor += sizeof(count);
1127 memcpy(cursor, gradient->pblendpos, count * sizeof(*gradient->pblendpos));
1128 cursor += count * sizeof(*gradient->pblendpos);
1130 memcpy(cursor, gradient->pblendcolor, count * sizeof(*gradient->pblendcolor));
1132 else if (gradient->blendcount > 1 && gradient->blendfac && gradient->blendpos)
1134 const DWORD count = gradient->blendcount;
1136 data->BrushData.lineargradient.BrushDataFlags |= BrushDataBlendFactorsH;
1138 memcpy(cursor, &count, sizeof(count));
1139 cursor += sizeof(count);
1141 memcpy(cursor, gradient->blendpos, count * sizeof(*gradient->blendpos));
1142 cursor += count * sizeof(*gradient->blendpos);
1144 memcpy(cursor, gradient->blendfac, count * sizeof(*gradient->blendfac));
1147 break;
1149 default:
1150 FIXME("unsupported brush type: %d\n", brush->bt);
1154 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GDIPCONST GpBrush *brush, DWORD *id)
1156 EmfPlusObject *object_record;
1157 GpStatus stat;
1158 DWORD size;
1160 *id = -1;
1161 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1162 return Ok;
1164 stat = METAFILE_PrepareBrushData(brush, &size);
1165 if (stat != Ok) return stat;
1167 stat = METAFILE_AllocateRecord(metafile,
1168 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1169 if (stat != Ok) return stat;
1171 *id = METAFILE_AddObjectId(metafile);
1172 object_record->Header.Type = EmfPlusRecordTypeObject;
1173 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1174 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1175 return Ok;
1178 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1179 GDIPCONST GpRectF* rects, INT count)
1181 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1183 EmfPlusFillRects *record;
1184 GpStatus stat;
1185 BOOL integer_rects = TRUE;
1186 int i;
1187 DWORD brushid;
1188 int flags = 0;
1190 if (brush->bt == BrushTypeSolidColor)
1192 flags |= 0x8000;
1193 brushid = ((GpSolidFill*)brush)->color;
1195 else
1197 stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1198 if (stat != Ok)
1199 return stat;
1202 for (i=0; i<count; i++)
1204 if (!is_integer_rect(&rects[i]))
1206 integer_rects = FALSE;
1207 break;
1211 if (integer_rects)
1212 flags |= 0x4000;
1214 stat = METAFILE_AllocateRecord(metafile,
1215 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1216 (void**)&record);
1217 if (stat != Ok)
1218 return stat;
1220 record->Header.Type = EmfPlusRecordTypeFillRects;
1221 record->Header.Flags = flags;
1222 record->BrushID = brushid;
1223 record->Count = count;
1225 if (integer_rects)
1227 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1228 for (i=0; i<count; i++)
1230 record_rects[i].X = (SHORT)rects[i].X;
1231 record_rects[i].Y = (SHORT)rects[i].Y;
1232 record_rects[i].Width = (SHORT)rects[i].Width;
1233 record_rects[i].Height = (SHORT)rects[i].Height;
1236 else
1237 memcpy(record+1, rects, sizeof(GpRectF) * count);
1239 METAFILE_WriteRecords(metafile);
1242 if (metafile->auto_frame)
1244 GpPointF corners[4];
1245 int i;
1247 for (i=0; i<count; i++)
1249 corners[0].X = rects[i].X;
1250 corners[0].Y = rects[i].Y;
1251 corners[1].X = rects[i].X + rects[i].Width;
1252 corners[1].Y = rects[i].Y;
1253 corners[2].X = rects[i].X;
1254 corners[2].Y = rects[i].Y + rects[i].Height;
1255 corners[3].X = rects[i].X + rects[i].Width;
1256 corners[3].Y = rects[i].Y + rects[i].Height;
1258 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1259 CoordinateSpaceWorld, corners, 4);
1261 METAFILE_AdjustFrame(metafile, corners, 4);
1265 return Ok;
1268 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1270 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1272 EmfPlusSetClipRect *record;
1273 GpStatus stat;
1275 stat = METAFILE_AllocateRecord(metafile,
1276 sizeof(EmfPlusSetClipRect),
1277 (void**)&record);
1278 if (stat != Ok)
1279 return stat;
1281 record->Header.Type = EmfPlusRecordTypeSetClipRect;
1282 record->Header.Flags = (mode & 0xf) << 8;
1283 record->ClipRect.X = x;
1284 record->ClipRect.Y = y;
1285 record->ClipRect.Width = width;
1286 record->ClipRect.Height = height;
1288 METAFILE_WriteRecords(metafile);
1291 return Ok;
1294 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1296 EmfPlusObject *object_record;
1297 DWORD size;
1298 GpStatus stat;
1300 *id = -1;
1301 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1302 return Ok;
1304 size = write_region_data(region, NULL);
1305 stat = METAFILE_AllocateRecord(metafile,
1306 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1307 if (stat != Ok) return stat;
1309 *id = METAFILE_AddObjectId(metafile);
1310 object_record->Header.Type = EmfPlusRecordTypeObject;
1311 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1312 write_region_data(region, &object_record->ObjectData.region);
1313 return Ok;
1316 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1318 EmfPlusRecordHeader *record;
1319 DWORD region_id;
1320 GpStatus stat;
1322 if (metafile->metafile_type == MetafileTypeEmf)
1324 FIXME("stub!\n");
1325 return NotImplemented;
1328 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1329 if (stat != Ok) return stat;
1331 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1332 if (stat != Ok) return stat;
1334 record->Type = EmfPlusRecordTypeSetClipRegion;
1335 record->Flags = region_id | mode << 8;
1337 METAFILE_WriteRecords(metafile);
1338 return Ok;
1341 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1343 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1345 EmfPlusSetPageTransform *record;
1346 GpStatus stat;
1348 stat = METAFILE_AllocateRecord(metafile,
1349 sizeof(EmfPlusSetPageTransform),
1350 (void**)&record);
1351 if (stat != Ok)
1352 return stat;
1354 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1355 record->Header.Flags = unit;
1356 record->PageScale = scale;
1358 METAFILE_WriteRecords(metafile);
1361 return Ok;
1364 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1366 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1368 EmfPlusSetWorldTransform *record;
1369 GpStatus stat;
1371 stat = METAFILE_AllocateRecord(metafile,
1372 sizeof(EmfPlusSetWorldTransform),
1373 (void**)&record);
1374 if (stat != Ok)
1375 return stat;
1377 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1378 record->Header.Flags = 0;
1379 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1381 METAFILE_WriteRecords(metafile);
1384 return Ok;
1387 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1389 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1391 EmfPlusScaleWorldTransform *record;
1392 GpStatus stat;
1394 stat = METAFILE_AllocateRecord(metafile,
1395 sizeof(EmfPlusScaleWorldTransform),
1396 (void**)&record);
1397 if (stat != Ok)
1398 return stat;
1400 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1401 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1402 record->Sx = sx;
1403 record->Sy = sy;
1405 METAFILE_WriteRecords(metafile);
1408 return Ok;
1411 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1413 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1415 EmfPlusMultiplyWorldTransform *record;
1416 GpStatus stat;
1418 stat = METAFILE_AllocateRecord(metafile,
1419 sizeof(EmfPlusMultiplyWorldTransform),
1420 (void**)&record);
1421 if (stat != Ok)
1422 return stat;
1424 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1425 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1426 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1428 METAFILE_WriteRecords(metafile);
1431 return Ok;
1434 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1436 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1438 EmfPlusRotateWorldTransform *record;
1439 GpStatus stat;
1441 stat = METAFILE_AllocateRecord(metafile,
1442 sizeof(EmfPlusRotateWorldTransform),
1443 (void**)&record);
1444 if (stat != Ok)
1445 return stat;
1447 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1448 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1449 record->Angle = angle;
1451 METAFILE_WriteRecords(metafile);
1454 return Ok;
1457 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1459 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1461 EmfPlusTranslateWorldTransform *record;
1462 GpStatus stat;
1464 stat = METAFILE_AllocateRecord(metafile,
1465 sizeof(EmfPlusTranslateWorldTransform),
1466 (void**)&record);
1467 if (stat != Ok)
1468 return stat;
1470 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1471 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1472 record->dx = dx;
1473 record->dy = dy;
1475 METAFILE_WriteRecords(metafile);
1478 return Ok;
1481 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1483 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1485 EmfPlusRecordHeader *record;
1486 GpStatus stat;
1488 stat = METAFILE_AllocateRecord(metafile,
1489 sizeof(EmfPlusRecordHeader),
1490 (void**)&record);
1491 if (stat != Ok)
1492 return stat;
1494 record->Type = EmfPlusRecordTypeResetWorldTransform;
1495 record->Flags = 0;
1497 METAFILE_WriteRecords(metafile);
1500 return Ok;
1503 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1504 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1506 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1508 EmfPlusBeginContainer *record;
1509 GpStatus stat;
1511 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1512 if (stat != Ok)
1513 return stat;
1515 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1516 record->Header.Flags = unit & 0xff;
1517 record->DestRect = *dstrect;
1518 record->SrcRect = *srcrect;
1519 record->StackIndex = StackIndex;
1521 METAFILE_WriteRecords(metafile);
1524 return Ok;
1527 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1529 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1531 EmfPlusContainerRecord *record;
1532 GpStatus stat;
1534 stat = METAFILE_AllocateRecord(metafile,
1535 sizeof(EmfPlusContainerRecord),
1536 (void**)&record);
1537 if (stat != Ok)
1538 return stat;
1540 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1541 record->Header.Flags = 0;
1542 record->StackIndex = StackIndex;
1544 METAFILE_WriteRecords(metafile);
1547 return Ok;
1550 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1552 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1554 EmfPlusContainerRecord *record;
1555 GpStatus stat;
1557 stat = METAFILE_AllocateRecord(metafile,
1558 sizeof(EmfPlusContainerRecord),
1559 (void**)&record);
1560 if (stat != Ok)
1561 return stat;
1563 record->Header.Type = EmfPlusRecordTypeEndContainer;
1564 record->Header.Flags = 0;
1565 record->StackIndex = StackIndex;
1567 METAFILE_WriteRecords(metafile);
1570 return Ok;
1573 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1575 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1577 EmfPlusContainerRecord *record;
1578 GpStatus stat;
1580 stat = METAFILE_AllocateRecord(metafile,
1581 sizeof(EmfPlusContainerRecord),
1582 (void**)&record);
1583 if (stat != Ok)
1584 return stat;
1586 record->Header.Type = EmfPlusRecordTypeSave;
1587 record->Header.Flags = 0;
1588 record->StackIndex = StackIndex;
1590 METAFILE_WriteRecords(metafile);
1593 return Ok;
1596 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1598 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1600 EmfPlusContainerRecord *record;
1601 GpStatus stat;
1603 stat = METAFILE_AllocateRecord(metafile,
1604 sizeof(EmfPlusContainerRecord),
1605 (void**)&record);
1606 if (stat != Ok)
1607 return stat;
1609 record->Header.Type = EmfPlusRecordTypeRestore;
1610 record->Header.Flags = 0;
1611 record->StackIndex = StackIndex;
1613 METAFILE_WriteRecords(metafile);
1616 return Ok;
1619 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1621 if (hdc != metafile->record_dc)
1622 return InvalidParameter;
1624 return Ok;
1627 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1629 GpStatus stat;
1631 stat = METAFILE_WriteEndOfFile(metafile);
1632 metafile->record_graphics = NULL;
1634 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1635 metafile->record_dc = NULL;
1637 heap_free(metafile->comment_data);
1638 metafile->comment_data = NULL;
1639 metafile->comment_data_size = 0;
1641 if (stat == Ok)
1643 MetafileHeader header;
1645 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1646 if (stat == Ok && metafile->auto_frame &&
1647 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1649 RECTL bounds_rc, gdi_bounds_rc;
1650 REAL x_scale = 2540.0 / header.DpiX;
1651 REAL y_scale = 2540.0 / header.DpiY;
1652 BYTE* buffer;
1653 UINT buffer_size;
1655 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1656 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1657 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1658 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1660 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1661 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1663 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1664 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1665 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1666 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1669 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1670 buffer = heap_alloc(buffer_size);
1671 if (buffer)
1673 HENHMETAFILE new_hemf;
1675 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1677 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1679 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1681 if (new_hemf)
1683 DeleteEnhMetaFile(metafile->hemf);
1684 metafile->hemf = new_hemf;
1686 else
1687 stat = OutOfMemory;
1689 heap_free(buffer);
1691 else
1692 stat = OutOfMemory;
1694 if (stat == Ok)
1695 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1697 if (stat == Ok)
1699 metafile->bounds.X = header.X;
1700 metafile->bounds.Y = header.Y;
1701 metafile->bounds.Width = header.Width;
1702 metafile->bounds.Height = header.Height;
1706 if (stat == Ok && metafile->record_stream)
1708 BYTE *buffer;
1709 UINT buffer_size;
1711 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1713 buffer = heap_alloc(buffer_size);
1714 if (buffer)
1716 HRESULT hr;
1718 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1720 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1722 if (FAILED(hr))
1723 stat = hresult_to_status(hr);
1725 heap_free(buffer);
1727 else
1728 stat = OutOfMemory;
1731 if (metafile->record_stream)
1733 IStream_Release(metafile->record_stream);
1734 metafile->record_stream = NULL;
1737 return stat;
1740 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1742 TRACE("(%p,%p)\n", metafile, hEmf);
1744 if (!metafile || !hEmf || !metafile->hemf)
1745 return InvalidParameter;
1747 *hEmf = metafile->hemf;
1748 metafile->hemf = NULL;
1750 return Ok;
1753 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1755 GpStatus stat = Ok;
1757 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1759 return stat;
1762 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1764 if (metafile->playback_dc)
1766 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1767 metafile->playback_dc = NULL;
1771 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1773 GpStatus stat;
1774 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1775 if (stat == Ok)
1776 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1777 return stat;
1780 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1782 GpMatrix *real_transform;
1783 GpStatus stat;
1785 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1787 if (stat == Ok)
1789 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1791 if (metafile->page_unit != UnitDisplay)
1792 scale *= metafile->page_scale;
1794 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1796 if (stat == Ok)
1797 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1799 if (stat == Ok)
1800 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1802 GdipDeleteMatrix(real_transform);
1805 return stat;
1808 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1810 metafile_free_object_table_entry(metafile, id);
1811 metafile->objtable[id].type = type;
1812 metafile->objtable[id].u.object = object;
1815 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1817 EmfPlusImage *data = (EmfPlusImage *)record_data;
1818 GpStatus status;
1820 *image = NULL;
1822 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1823 return InvalidParameter;
1824 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1826 switch (data->Type)
1828 case ImageDataTypeBitmap:
1830 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1832 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1833 return InvalidParameter;
1834 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1836 switch (bitmapdata->Type)
1838 case BitmapDataTypePixel:
1840 ColorPalette *palette;
1841 BYTE *scan0;
1843 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1845 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1846 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1848 if (data_size <= palette_size)
1849 return InvalidParameter;
1850 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1852 if (data_size < palette_size)
1853 return InvalidParameter;
1854 data_size -= palette_size;
1856 palette = (ColorPalette *)bitmapdata->BitmapData;
1857 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1859 else
1861 palette = NULL;
1862 scan0 = bitmapdata->BitmapData;
1865 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1866 return InvalidParameter;
1868 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1869 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1870 if (status == Ok && palette)
1872 status = GdipSetImagePalette(*image, palette);
1873 if (status != Ok)
1875 GdipDisposeImage(*image);
1876 *image = NULL;
1879 break;
1881 case BitmapDataTypeCompressed:
1883 IWICImagingFactory *factory;
1884 IWICStream *stream;
1885 HRESULT hr;
1887 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1888 return GenericError;
1890 hr = IWICImagingFactory_CreateStream(factory, &stream);
1891 IWICImagingFactory_Release(factory);
1892 if (hr != S_OK)
1893 return GenericError;
1895 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1896 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1897 else
1898 status = GenericError;
1900 IWICStream_Release(stream);
1901 break;
1903 default:
1904 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1905 return InvalidParameter;
1907 break;
1909 case ImageDataTypeMetafile:
1911 EmfPlusMetafile *metafiledata = &data->ImageData.metafile;
1913 if (data_size <= FIELD_OFFSET(EmfPlusMetafile, MetafileData))
1914 return InvalidParameter;
1915 data_size -= FIELD_OFFSET(EmfPlusMetafile, MetafileData);
1917 switch (metafiledata->Type) {
1918 case MetafileTypeEmf:
1919 case MetafileTypeEmfPlusOnly:
1920 case MetafileTypeEmfPlusDual:
1922 HENHMETAFILE hemf;
1924 hemf = SetEnhMetaFileBits(data_size, metafiledata->MetafileData);
1926 if (!hemf)
1927 return GenericError;
1929 status = GdipCreateMetafileFromEmf(hemf, TRUE, (GpMetafile**)image);
1931 if (status != Ok)
1932 DeleteEnhMetaFile(hemf);
1934 break;
1936 default:
1937 FIXME("metafile type %d not supported.\n", metafiledata->Type);
1938 return NotImplemented;
1940 break;
1942 default:
1943 FIXME("image type %d not supported.\n", data->Type);
1944 return NotImplemented;
1947 return status;
1950 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1952 EmfPlusPath *data = (EmfPlusPath *)record_data;
1953 GpStatus status;
1954 BYTE *types;
1955 UINT size;
1956 DWORD i;
1958 *path = NULL;
1960 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1961 return InvalidParameter;
1962 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1964 if (data->PathPointFlags & 0x800) /* R */
1966 FIXME("RLE encoded path data is not supported.\n");
1967 return NotImplemented;
1969 else
1971 if (data->PathPointFlags & 0x4000) /* C */
1972 size = sizeof(EmfPlusPoint);
1973 else
1974 size = sizeof(EmfPlusPointF);
1975 size += sizeof(BYTE); /* EmfPlusPathPointType */
1976 size *= data->PathPointCount;
1979 if (data_size < size)
1980 return InvalidParameter;
1982 status = GdipCreatePath(FillModeAlternate, path);
1983 if (status != Ok)
1984 return status;
1986 (*path)->pathdata.Count = data->PathPointCount;
1987 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1988 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1989 (*path)->datalen = (*path)->pathdata.Count;
1991 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1993 GdipDeletePath(*path);
1994 return OutOfMemory;
1997 if (data->PathPointFlags & 0x4000) /* C */
1999 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
2000 for (i = 0; i < data->PathPointCount; i++)
2002 (*path)->pathdata.Points[i].X = points[i].X;
2003 (*path)->pathdata.Points[i].Y = points[i].Y;
2005 types = (BYTE *)(points + i);
2007 else
2009 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
2010 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
2011 types = (BYTE *)(points + data->PathPointCount);
2014 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
2016 return Ok;
2019 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
2021 const DWORD *type;
2022 GpStatus status;
2024 type = buffer_read(mbuf, sizeof(*type));
2025 if (!type) return Ok;
2027 node->type = *type;
2029 switch (node->type)
2031 case CombineModeReplace:
2032 case CombineModeIntersect:
2033 case CombineModeUnion:
2034 case CombineModeXor:
2035 case CombineModeExclude:
2036 case CombineModeComplement:
2038 region_element *left, *right;
2040 left = heap_alloc_zero(sizeof(*left));
2041 if (!left)
2042 return OutOfMemory;
2044 right = heap_alloc_zero(sizeof(*right));
2045 if (!right)
2047 heap_free(left);
2048 return OutOfMemory;
2051 status = metafile_read_region_node(mbuf, region, left, count);
2052 if (status == Ok)
2054 status = metafile_read_region_node(mbuf, region, right, count);
2055 if (status == Ok)
2057 node->elementdata.combine.left = left;
2058 node->elementdata.combine.right = right;
2059 region->num_children += 2;
2060 return Ok;
2064 heap_free(left);
2065 heap_free(right);
2066 return status;
2068 case RegionDataRect:
2070 const EmfPlusRectF *rect;
2072 rect = buffer_read(mbuf, sizeof(*rect));
2073 if (!rect)
2074 return InvalidParameter;
2076 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
2077 *count += 1;
2078 return Ok;
2080 case RegionDataPath:
2082 const BYTE *path_data;
2083 const UINT *data_size;
2084 GpPath *path;
2086 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
2087 if (!data_size)
2088 return InvalidParameter;
2090 path_data = buffer_read(mbuf, *data_size);
2091 if (!path_data)
2092 return InvalidParameter;
2094 status = metafile_deserialize_path(path_data, *data_size, &path);
2095 if (status == Ok)
2097 node->elementdata.path = path;
2098 *count += 1;
2100 return Ok;
2102 case RegionDataEmptyRect:
2103 case RegionDataInfiniteRect:
2104 *count += 1;
2105 return Ok;
2106 default:
2107 FIXME("element type %#x is not supported\n", *type);
2108 break;
2111 return InvalidParameter;
2114 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
2116 struct memory_buffer mbuf;
2117 GpStatus status;
2118 UINT count;
2120 *region = NULL;
2122 init_memory_buffer(&mbuf, record_data, data_size);
2124 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2125 return InvalidParameter;
2127 status = GdipCreateRegion(region);
2128 if (status != Ok)
2129 return status;
2131 count = 0;
2132 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2133 if (status == Ok && !count)
2134 status = InvalidParameter;
2136 if (status != Ok)
2138 GdipDeleteRegion(*region);
2139 *region = NULL;
2142 return status;
2145 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2147 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2148 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2149 EmfPlusTransformMatrix *transform = NULL;
2150 DWORD brushflags;
2151 GpStatus status;
2152 UINT offset;
2154 *brush = NULL;
2156 if (data_size < header_size)
2157 return InvalidParameter;
2159 switch (data->Type)
2161 case BrushTypeSolidColor:
2162 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2163 return InvalidParameter;
2165 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2166 break;
2167 case BrushTypeHatchFill:
2168 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2169 return InvalidParameter;
2171 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2172 data->BrushData.hatch.BackColor, (GpHatch **)brush);
2173 break;
2174 case BrushTypeTextureFill:
2176 GpImage *image;
2178 offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2179 if (data_size <= offset)
2180 return InvalidParameter;
2182 brushflags = data->BrushData.texture.BrushDataFlags;
2183 if (brushflags & BrushDataTransform)
2185 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2186 return InvalidParameter;
2187 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2188 offset += sizeof(EmfPlusTransformMatrix);
2191 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2192 if (status != Ok)
2193 return status;
2195 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2196 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2197 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2199 GdipDisposeImage(image);
2200 break;
2202 case BrushTypeLinearGradient:
2204 GpLineGradient *gradient = NULL;
2205 GpRectF rect;
2206 UINT position_count = 0;
2208 offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2209 if (data_size < offset)
2210 return InvalidParameter;
2212 brushflags = data->BrushData.lineargradient.BrushDataFlags;
2213 if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2214 return InvalidParameter;
2216 if (brushflags & BrushDataTransform)
2218 if (data_size < offset + sizeof(EmfPlusTransformMatrix))
2219 return InvalidParameter;
2220 transform = (EmfPlusTransformMatrix *)(record_data + offset);
2221 offset += sizeof(EmfPlusTransformMatrix);
2224 if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2226 if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2227 return InvalidParameter;
2228 position_count = *(DWORD *)(record_data + offset);
2229 offset += sizeof(DWORD);
2232 if (brushflags & BrushDataPresetColors)
2234 if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2235 return InvalidParameter;
2237 else if (brushflags & BrushDataBlendFactorsH)
2239 if (data_size != offset + position_count * 2 * sizeof(float))
2240 return InvalidParameter;
2243 rect.X = data->BrushData.lineargradient.RectF.X;
2244 rect.Y = data->BrushData.lineargradient.RectF.Y;
2245 rect.Width = data->BrushData.lineargradient.RectF.Width;
2246 rect.Height = data->BrushData.lineargradient.RectF.Height;
2248 status = GdipCreateLineBrushFromRect(&rect, data->BrushData.lineargradient.StartColor,
2249 data->BrushData.lineargradient.EndColor, LinearGradientModeHorizontal,
2250 data->BrushData.lineargradient.WrapMode, &gradient);
2251 if (status == Ok)
2253 if (transform)
2254 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2256 if (status == Ok)
2258 if (brushflags & BrushDataPresetColors)
2259 status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2260 position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2261 else if (brushflags & BrushDataBlendFactorsH)
2262 status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2263 (REAL *)(record_data + offset), position_count);
2265 if (brushflags & BrushDataIsGammaCorrected)
2266 FIXME("BrushDataIsGammaCorrected is not handled.\n");
2270 if (status == Ok)
2271 *brush = (GpBrush *)gradient;
2272 else
2273 GdipDeleteBrush((GpBrush *)gradient);
2275 break;
2277 default:
2278 FIXME("brush type %u is not supported.\n", data->Type);
2279 return NotImplemented;
2282 return status;
2285 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2287 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2288 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2290 if (data_size <= offset)
2291 return InvalidParameter;
2293 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2294 if (data_size <= offset)
2295 return InvalidParameter;
2297 if (pendata->PenDataFlags & PenDataTransform)
2298 offset += sizeof(EmfPlusTransformMatrix);
2300 if (pendata->PenDataFlags & PenDataStartCap)
2301 offset += sizeof(DWORD);
2303 if (pendata->PenDataFlags & PenDataEndCap)
2304 offset += sizeof(DWORD);
2306 if (pendata->PenDataFlags & PenDataJoin)
2307 offset += sizeof(DWORD);
2309 if (pendata->PenDataFlags & PenDataMiterLimit)
2310 offset += sizeof(REAL);
2312 if (pendata->PenDataFlags & PenDataLineStyle)
2313 offset += sizeof(DWORD);
2315 if (pendata->PenDataFlags & PenDataDashedLineCap)
2316 offset += sizeof(DWORD);
2318 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2319 offset += sizeof(REAL);
2321 if (pendata->PenDataFlags & PenDataDashedLine)
2323 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2325 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2326 if (data_size <= offset)
2327 return InvalidParameter;
2329 offset += dashedline->DashedLineDataSize * sizeof(float);
2332 if (pendata->PenDataFlags & PenDataNonCenter)
2333 offset += sizeof(DWORD);
2335 if (pendata->PenDataFlags & PenDataCompoundLine)
2337 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2339 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2340 if (data_size <= offset)
2341 return InvalidParameter;
2343 offset += compoundline->CompoundLineDataSize * sizeof(float);
2346 if (pendata->PenDataFlags & PenDataCustomStartCap)
2348 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2350 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2351 if (data_size <= offset)
2352 return InvalidParameter;
2354 offset += startcap->CustomStartCapSize;
2357 if (pendata->PenDataFlags & PenDataCustomEndCap)
2359 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2361 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2362 if (data_size <= offset)
2363 return InvalidParameter;
2365 offset += endcap->CustomEndCapSize;
2368 *ret = offset;
2369 return Ok;
2372 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2374 BYTE type = (flags >> 8) & 0xff;
2375 BYTE id = flags & 0xff;
2376 void *object = NULL;
2377 GpStatus status;
2379 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2380 return InvalidParameter;
2382 switch (type)
2384 case ObjectTypeBrush:
2385 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2386 break;
2387 case ObjectTypePen:
2389 EmfPlusPen *data = (EmfPlusPen *)record_data;
2390 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2391 GpBrush *brush;
2392 DWORD offset;
2393 GpPen *pen;
2395 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2396 if (status != Ok)
2397 return status;
2399 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2400 if (status != Ok)
2401 return status;
2403 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2404 GdipDeleteBrush(brush);
2405 if (status != Ok)
2406 return status;
2408 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2410 if (pendata->PenDataFlags & PenDataTransform)
2412 FIXME("PenDataTransform is not supported.\n");
2413 offset += sizeof(EmfPlusTransformMatrix);
2416 if (pendata->PenDataFlags & PenDataStartCap)
2418 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2419 goto penfailed;
2420 offset += sizeof(DWORD);
2423 if (pendata->PenDataFlags & PenDataEndCap)
2425 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2426 goto penfailed;
2427 offset += sizeof(DWORD);
2430 if (pendata->PenDataFlags & PenDataJoin)
2432 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2433 goto penfailed;
2434 offset += sizeof(DWORD);
2437 if (pendata->PenDataFlags & PenDataMiterLimit)
2439 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2440 goto penfailed;
2441 offset += sizeof(REAL);
2444 if (pendata->PenDataFlags & PenDataLineStyle)
2446 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2447 goto penfailed;
2448 offset += sizeof(DWORD);
2451 if (pendata->PenDataFlags & PenDataDashedLineCap)
2453 FIXME("PenDataDashedLineCap is not supported.\n");
2454 offset += sizeof(DWORD);
2457 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2459 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2460 goto penfailed;
2461 offset += sizeof(REAL);
2464 if (pendata->PenDataFlags & PenDataDashedLine)
2466 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2467 FIXME("PenDataDashedLine is not supported.\n");
2468 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2471 if (pendata->PenDataFlags & PenDataNonCenter)
2473 FIXME("PenDataNonCenter is not supported.\n");
2474 offset += sizeof(DWORD);
2477 if (pendata->PenDataFlags & PenDataCompoundLine)
2479 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2480 FIXME("PenDataCompoundLine is not supported.\n");
2481 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2484 if (pendata->PenDataFlags & PenDataCustomStartCap)
2486 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2487 FIXME("PenDataCustomStartCap is not supported.\n");
2488 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2491 if (pendata->PenDataFlags & PenDataCustomEndCap)
2493 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2494 FIXME("PenDataCustomEndCap is not supported.\n");
2495 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2498 object = pen;
2499 break;
2501 penfailed:
2502 GdipDeletePen(pen);
2503 return status;
2505 case ObjectTypePath:
2506 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2507 break;
2508 case ObjectTypeRegion:
2509 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2510 break;
2511 case ObjectTypeImage:
2512 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2513 break;
2514 case ObjectTypeFont:
2516 EmfPlusFont *data = (EmfPlusFont *)record_data;
2517 GpFontFamily *family;
2518 WCHAR *familyname;
2520 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2521 return InvalidParameter;
2522 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2524 if (data_size < data->Length * sizeof(WCHAR))
2525 return InvalidParameter;
2527 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2528 return OutOfMemory;
2530 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2531 familyname[data->Length] = 0;
2533 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2534 GdipFree(familyname);
2536 /* If a font family cannot be created from family name, native
2537 falls back to a sans serif font. */
2538 if (status != Ok)
2539 status = GdipGetGenericFontFamilySansSerif(&family);
2540 if (status != Ok)
2541 return status;
2543 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2544 GdipDeleteFontFamily(family);
2545 break;
2547 case ObjectTypeImageAttributes:
2549 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2550 GpImageAttributes *attributes = NULL;
2552 if (data_size != sizeof(*data))
2553 return InvalidParameter;
2555 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2556 return status;
2558 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2559 !!data->ObjectClamp);
2560 if (status == Ok)
2561 object = attributes;
2562 else
2563 GdipDisposeImageAttributes(attributes);
2564 break;
2566 default:
2567 FIXME("not implemented for object type %d.\n", type);
2568 return NotImplemented;
2571 if (status == Ok)
2572 metafile_set_object_table_entry(metafile, id, type, object);
2574 return status;
2577 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2579 GpMatrix world_to_device;
2581 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2583 GdipTransformRegion(region, &world_to_device);
2584 GdipCombineRegionRegion(metafile->clip, region, mode);
2586 return METAFILE_PlaybackUpdateClip(metafile);
2589 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2590 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2592 GpStatus stat;
2593 GpMetafile *real_metafile = (GpMetafile*)metafile;
2595 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2597 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2598 return InvalidParameter;
2600 if (recordType >= 1 && recordType <= 0x7a)
2602 /* regular EMF record */
2603 if (metafile->playback_dc)
2605 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2607 if (record)
2609 record->iType = recordType;
2610 record->nSize = dataSize + 8;
2611 memcpy(record->dParm, data, dataSize);
2613 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2614 record, metafile->handle_count) == 0)
2615 ERR("PlayEnhMetaFileRecord failed\n");
2617 heap_free(record);
2619 else
2620 return OutOfMemory;
2623 else
2625 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2627 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2629 switch(recordType)
2631 case EmfPlusRecordTypeHeader:
2632 case EmfPlusRecordTypeEndOfFile:
2633 break;
2634 case EmfPlusRecordTypeGetDC:
2635 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2636 break;
2637 case EmfPlusRecordTypeClear:
2639 EmfPlusClear *record = (EmfPlusClear*)header;
2641 if (dataSize != sizeof(record->Color))
2642 return InvalidParameter;
2644 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2646 case EmfPlusRecordTypeFillRects:
2648 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2649 GpBrush *brush, *temp_brush=NULL;
2650 GpRectF *rects, *temp_rects=NULL;
2652 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2653 return InvalidParameter;
2655 if (flags & 0x4000)
2657 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2658 return InvalidParameter;
2660 else
2662 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2663 return InvalidParameter;
2666 if (flags & 0x8000)
2668 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2669 brush = temp_brush;
2671 else
2673 if (record->BrushID >= EmfPlusObjectTableSize ||
2674 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2675 return InvalidParameter;
2677 brush = real_metafile->objtable[record->BrushID].u.brush;
2678 stat = Ok;
2681 if (stat == Ok)
2683 if (flags & 0x4000)
2685 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2686 int i;
2688 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2689 if (rects)
2691 for (i=0; i<record->Count; i++)
2693 rects[i].X = int_rects[i].X;
2694 rects[i].Y = int_rects[i].Y;
2695 rects[i].Width = int_rects[i].Width;
2696 rects[i].Height = int_rects[i].Height;
2699 else
2700 stat = OutOfMemory;
2702 else
2703 rects = (GpRectF*)(record+1);
2706 if (stat == Ok)
2708 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2711 GdipDeleteBrush(temp_brush);
2712 heap_free(temp_rects);
2714 return stat;
2716 case EmfPlusRecordTypeSetClipRect:
2718 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2719 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2720 GpRegion *region;
2722 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2723 return InvalidParameter;
2725 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2727 if (stat == Ok)
2729 stat = metafile_set_clip_region(real_metafile, region, mode);
2730 GdipDeleteRegion(region);
2733 return stat;
2735 case EmfPlusRecordTypeSetClipRegion:
2737 CombineMode mode = (flags >> 8) & 0xf;
2738 BYTE regionid = flags & 0xff;
2739 GpRegion *region;
2741 if (dataSize != 0)
2742 return InvalidParameter;
2744 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2745 return InvalidParameter;
2747 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2748 if (stat == Ok)
2750 stat = metafile_set_clip_region(real_metafile, region, mode);
2751 GdipDeleteRegion(region);
2754 return stat;
2756 case EmfPlusRecordTypeSetClipPath:
2758 CombineMode mode = (flags >> 8) & 0xf;
2759 BYTE pathid = flags & 0xff;
2760 GpRegion *region;
2762 if (dataSize != 0)
2763 return InvalidParameter;
2765 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2766 return InvalidParameter;
2768 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2769 if (stat == Ok)
2771 stat = metafile_set_clip_region(real_metafile, region, mode);
2772 GdipDeleteRegion(region);
2775 return stat;
2777 case EmfPlusRecordTypeSetPageTransform:
2779 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2780 GpUnit unit = (GpUnit)flags;
2782 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2783 return InvalidParameter;
2785 real_metafile->page_unit = unit;
2786 real_metafile->page_scale = record->PageScale;
2788 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2790 case EmfPlusRecordTypeSetWorldTransform:
2792 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2794 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2795 return InvalidParameter;
2797 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2799 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2801 case EmfPlusRecordTypeScaleWorldTransform:
2803 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2804 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2806 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2807 return InvalidParameter;
2809 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2811 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2813 case EmfPlusRecordTypeMultiplyWorldTransform:
2815 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2816 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2817 GpMatrix matrix;
2819 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2820 return InvalidParameter;
2822 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2824 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2826 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2828 case EmfPlusRecordTypeRotateWorldTransform:
2830 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2831 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2833 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2834 return InvalidParameter;
2836 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2838 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2840 case EmfPlusRecordTypeTranslateWorldTransform:
2842 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2843 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2845 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2846 return InvalidParameter;
2848 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2850 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2852 case EmfPlusRecordTypeResetWorldTransform:
2854 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2856 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2858 case EmfPlusRecordTypeBeginContainer:
2860 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2861 container* cont;
2862 GpUnit unit;
2863 REAL scale_x, scale_y;
2864 GpRectF scaled_srcrect;
2865 GpMatrix transform;
2867 cont = heap_alloc_zero(sizeof(*cont));
2868 if (!cont)
2869 return OutOfMemory;
2871 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2872 if (stat != Ok)
2874 heap_free(cont);
2875 return stat;
2878 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2880 if (stat != Ok)
2882 GdipDeleteRegion(cont->clip);
2883 heap_free(cont);
2884 return stat;
2887 cont->id = record->StackIndex;
2888 cont->type = BEGIN_CONTAINER;
2889 cont->world_transform = *metafile->world_transform;
2890 cont->page_unit = metafile->page_unit;
2891 cont->page_scale = metafile->page_scale;
2892 list_add_head(&real_metafile->containers, &cont->entry);
2894 unit = record->Header.Flags & 0xff;
2896 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
2897 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
2899 scaled_srcrect.X = scale_x * record->SrcRect.X;
2900 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2901 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2902 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2904 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2905 transform.matrix[1] = 0.0;
2906 transform.matrix[2] = 0.0;
2907 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2908 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2909 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2911 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2913 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2915 case EmfPlusRecordTypeBeginContainerNoParams:
2916 case EmfPlusRecordTypeSave:
2918 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2919 container* cont;
2921 cont = heap_alloc_zero(sizeof(*cont));
2922 if (!cont)
2923 return OutOfMemory;
2925 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2926 if (stat != Ok)
2928 heap_free(cont);
2929 return stat;
2932 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2933 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2934 else
2935 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2937 if (stat != Ok)
2939 GdipDeleteRegion(cont->clip);
2940 heap_free(cont);
2941 return stat;
2944 cont->id = record->StackIndex;
2945 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2946 cont->type = BEGIN_CONTAINER;
2947 else
2948 cont->type = SAVE_GRAPHICS;
2949 cont->world_transform = *metafile->world_transform;
2950 cont->page_unit = metafile->page_unit;
2951 cont->page_scale = metafile->page_scale;
2952 list_add_head(&real_metafile->containers, &cont->entry);
2954 break;
2956 case EmfPlusRecordTypeEndContainer:
2957 case EmfPlusRecordTypeRestore:
2959 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2960 container* cont;
2961 enum container_type type;
2962 BOOL found=FALSE;
2964 if (recordType == EmfPlusRecordTypeEndContainer)
2965 type = BEGIN_CONTAINER;
2966 else
2967 type = SAVE_GRAPHICS;
2969 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2971 if (cont->id == record->StackIndex && cont->type == type)
2973 found = TRUE;
2974 break;
2978 if (found)
2980 container* cont2;
2982 /* pop any newer items on the stack */
2983 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2985 list_remove(&cont2->entry);
2986 GdipDeleteRegion(cont2->clip);
2987 heap_free(cont2);
2990 if (type == BEGIN_CONTAINER)
2991 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2992 else
2993 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2995 *real_metafile->world_transform = cont->world_transform;
2996 real_metafile->page_unit = cont->page_unit;
2997 real_metafile->page_scale = cont->page_scale;
2998 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
3000 list_remove(&cont->entry);
3001 GdipDeleteRegion(cont->clip);
3002 heap_free(cont);
3005 break;
3007 case EmfPlusRecordTypeSetPixelOffsetMode:
3009 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
3011 case EmfPlusRecordTypeSetCompositingQuality:
3013 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
3015 case EmfPlusRecordTypeSetInterpolationMode:
3017 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
3019 case EmfPlusRecordTypeSetTextRenderingHint:
3021 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
3023 case EmfPlusRecordTypeSetAntiAliasMode:
3025 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
3027 case EmfPlusRecordTypeSetCompositingMode:
3029 return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
3031 case EmfPlusRecordTypeObject:
3033 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
3035 case EmfPlusRecordTypeDrawImage:
3037 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
3038 BYTE image = flags & 0xff;
3039 GpPointF points[3];
3041 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3042 return InvalidParameter;
3044 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
3045 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3046 return InvalidParameter;
3048 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3049 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3050 return InvalidParameter;
3052 if (flags & 0x4000) /* C */
3054 points[0].X = draw->RectData.rect.X;
3055 points[0].Y = draw->RectData.rect.Y;
3056 points[1].X = points[0].X + draw->RectData.rect.Width;
3057 points[1].Y = points[0].Y;
3058 points[2].X = points[1].X;
3059 points[2].Y = points[1].Y + draw->RectData.rect.Height;
3061 else
3063 points[0].X = draw->RectData.rectF.X;
3064 points[0].Y = draw->RectData.rectF.Y;
3065 points[1].X = points[0].X + draw->RectData.rectF.Width;
3066 points[1].Y = points[0].Y;
3067 points[2].X = points[1].X;
3068 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
3071 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3072 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3073 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3075 case EmfPlusRecordTypeDrawImagePoints:
3077 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
3078 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
3079 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
3080 BYTE image = flags & 0xff;
3081 GpPointF points[3];
3082 unsigned int i;
3083 UINT size;
3085 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3086 return InvalidParameter;
3088 if (dataSize <= fixed_part_size)
3089 return InvalidParameter;
3090 dataSize -= fixed_part_size;
3092 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3093 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3094 return InvalidParameter;
3096 if (draw->count != 3)
3097 return InvalidParameter;
3099 if ((flags >> 13) & 1) /* E */
3100 FIXME("image effects are not supported.\n");
3102 if ((flags >> 11) & 1) /* P */
3103 size = sizeof(EmfPlusPointR7) * draw->count;
3104 else if ((flags >> 14) & 1) /* C */
3105 size = sizeof(EmfPlusPoint) * draw->count;
3106 else
3107 size = sizeof(EmfPlusPointF) * draw->count;
3109 if (dataSize != size)
3110 return InvalidParameter;
3112 if ((flags >> 11) & 1) /* P */
3114 points[0].X = draw->PointData.pointsR[0].X;
3115 points[0].Y = draw->PointData.pointsR[0].Y;
3116 for (i = 1; i < 3; i++)
3118 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3119 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3122 else if ((flags >> 14) & 1) /* C */
3124 for (i = 0; i < 3; i++)
3126 points[i].X = draw->PointData.points[i].X;
3127 points[i].Y = draw->PointData.points[i].Y;
3130 else
3131 memcpy(points, draw->PointData.pointsF, sizeof(points));
3133 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3134 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3135 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3137 case EmfPlusRecordTypeFillPath:
3139 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3140 GpSolidFill *solidfill = NULL;
3141 BYTE path = flags & 0xff;
3142 GpBrush *brush;
3144 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3145 return InvalidParameter;
3147 if (dataSize != sizeof(fill->data.BrushId))
3148 return InvalidParameter;
3150 if (flags & 0x8000)
3152 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3153 if (stat != Ok)
3154 return stat;
3155 brush = (GpBrush *)solidfill;
3157 else
3159 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3160 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3161 return InvalidParameter;
3163 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3166 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3167 GdipDeleteBrush((GpBrush *)solidfill);
3168 return stat;
3170 case EmfPlusRecordTypeFillClosedCurve:
3172 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3173 sizeof(EmfPlusRecordHeader);
3174 EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3175 GpSolidFill *solidfill = NULL;
3176 GpFillMode mode;
3177 GpBrush *brush;
3178 UINT size, i;
3180 if (dataSize <= fixed_part_size)
3181 return InvalidParameter;
3183 if (fill->Count == 0)
3184 return InvalidParameter;
3186 if (flags & 0x800) /* P */
3187 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3188 else if (flags & 0x4000) /* C */
3189 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3190 else
3191 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3193 if (dataSize != size)
3194 return InvalidParameter;
3196 mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3198 if (flags & 0x8000) /* S */
3200 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3201 if (stat != Ok)
3202 return stat;
3203 brush = (GpBrush *)solidfill;
3205 else
3207 if (fill->BrushId >= EmfPlusObjectTableSize ||
3208 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3209 return InvalidParameter;
3211 brush = real_metafile->objtable[fill->BrushId].u.brush;
3214 if (flags & (0x800 | 0x4000))
3216 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3217 if (points)
3219 if (flags & 0x800) /* P */
3221 for (i = 1; i < fill->Count; i++)
3223 points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3224 points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3227 else
3229 for (i = 0; i < fill->Count; i++)
3231 points[i].X = fill->PointData.points[i].X;
3232 points[i].Y = fill->PointData.points[i].Y;
3236 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3237 points, fill->Count, fill->Tension, mode);
3238 GdipFree(points);
3240 else
3241 stat = OutOfMemory;
3243 else
3244 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3245 (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3247 GdipDeleteBrush((GpBrush *)solidfill);
3248 return stat;
3250 case EmfPlusRecordTypeFillEllipse:
3252 EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3253 GpSolidFill *solidfill = NULL;
3254 GpBrush *brush;
3256 if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3257 return InvalidParameter;
3258 dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3260 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3261 return InvalidParameter;
3263 if (flags & 0x8000)
3265 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3266 if (stat != Ok)
3267 return stat;
3268 brush = (GpBrush *)solidfill;
3270 else
3272 if (fill->BrushId >= EmfPlusObjectTableSize ||
3273 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3274 return InvalidParameter;
3276 brush = real_metafile->objtable[fill->BrushId].u.brush;
3279 if (flags & 0x4000)
3280 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3281 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3282 else
3283 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3284 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3286 GdipDeleteBrush((GpBrush *)solidfill);
3287 return stat;
3289 case EmfPlusRecordTypeFillPie:
3291 EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3292 GpSolidFill *solidfill = NULL;
3293 GpBrush *brush;
3295 if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3296 return InvalidParameter;
3297 dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3299 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3300 return InvalidParameter;
3302 if (flags & 0x8000) /* S */
3304 stat = GdipCreateSolidFill(fill->BrushId, &solidfill);
3305 if (stat != Ok)
3306 return stat;
3307 brush = (GpBrush *)solidfill;
3309 else
3311 if (fill->BrushId >= EmfPlusObjectTableSize ||
3312 real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3313 return InvalidParameter;
3315 brush = real_metafile->objtable[fill->BrushId].u.brush;
3318 if (flags & 0x4000) /* C */
3319 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3320 fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3321 fill->StartAngle, fill->SweepAngle);
3322 else
3323 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3324 fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3325 fill->StartAngle, fill->SweepAngle);
3327 GdipDeleteBrush((GpBrush *)solidfill);
3328 return stat;
3330 case EmfPlusRecordTypeDrawPath:
3332 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3333 BYTE path = flags & 0xff;
3335 if (dataSize != sizeof(draw->PenId))
3336 return InvalidParameter;
3338 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3339 return InvalidParameter;
3341 if (real_metafile->objtable[path].type != ObjectTypePath ||
3342 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3343 return InvalidParameter;
3345 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3346 real_metafile->objtable[path].u.path);
3348 case EmfPlusRecordTypeDrawArc:
3350 EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3351 BYTE pen = flags & 0xff;
3353 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3354 return InvalidParameter;
3356 if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3357 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3358 return InvalidParameter;
3360 if (flags & 0x4000) /* C */
3361 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3362 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3363 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3364 else
3365 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3366 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3367 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3369 case EmfPlusRecordTypeDrawEllipse:
3371 EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3372 BYTE pen = flags & 0xff;
3374 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3375 return InvalidParameter;
3377 if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3378 return InvalidParameter;
3380 if (flags & 0x4000) /* C */
3381 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3382 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3383 draw->RectData.rect.Height);
3384 else
3385 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3386 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3387 draw->RectData.rectF.Height);
3389 case EmfPlusRecordTypeDrawPie:
3391 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3392 BYTE pen = flags & 0xff;
3394 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3395 return InvalidParameter;
3397 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3398 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3399 return InvalidParameter;
3401 if (flags & 0x4000) /* C */
3402 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3403 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3404 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3405 else
3406 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3407 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3408 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3410 case EmfPlusRecordTypeDrawRects:
3412 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3413 BYTE pen = flags & 0xff;
3414 GpRectF *rects = NULL;
3416 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3417 return InvalidParameter;
3419 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3420 return InvalidParameter;
3421 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3423 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3424 return InvalidParameter;
3426 if (flags & 0x4000)
3428 DWORD i;
3430 rects = GdipAlloc(draw->Count * sizeof(*rects));
3431 if (!rects)
3432 return OutOfMemory;
3434 for (i = 0; i < draw->Count; i++)
3436 rects[i].X = draw->RectData.rect[i].X;
3437 rects[i].Y = draw->RectData.rect[i].Y;
3438 rects[i].Width = draw->RectData.rect[i].Width;
3439 rects[i].Height = draw->RectData.rect[i].Height;
3443 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3444 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3445 GdipFree(rects);
3446 return stat;
3448 case EmfPlusRecordTypeDrawDriverString:
3450 GpBrush *brush;
3451 DWORD expected_size;
3452 UINT16 *text;
3453 PointF *positions;
3454 GpSolidFill *solidfill = NULL;
3455 void* alignedmem = NULL;
3456 GpMatrix *matrix = NULL;
3457 BYTE font = flags & 0xff;
3458 EmfPlusDrawDriverString *draw = (EmfPlusDrawDriverString*)header;
3460 if (font >= EmfPlusObjectTableSize ||
3461 real_metafile->objtable[font].type != ObjectTypeFont)
3462 return InvalidParameter;
3464 expected_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) -
3465 sizeof(EmfPlusRecordHeader);
3466 if (dataSize < expected_size || draw->GlyphCount <= 0)
3467 return InvalidParameter;
3469 expected_size += draw->GlyphCount * (sizeof(*text) + sizeof(*positions));
3470 if (draw->MatrixPresent)
3471 expected_size += sizeof(*matrix);
3473 /* Pad expected size to DWORD alignment. */
3474 expected_size = (expected_size + 3) & ~3;
3476 if (dataSize != expected_size)
3477 return InvalidParameter;
3479 if (flags & 0x8000)
3481 stat = GdipCreateSolidFill(draw->brush.Color, (GpSolidFill**)&solidfill);
3483 if (stat != Ok)
3484 return InvalidParameter;
3486 brush = (GpBrush*)solidfill;
3488 else
3490 if (draw->brush.BrushId >= EmfPlusObjectTableSize ||
3491 real_metafile->objtable[draw->brush.BrushId].type != ObjectTypeBrush)
3492 return InvalidParameter;
3494 brush = real_metafile->objtable[draw->brush.BrushId].u.brush;
3497 text = (UINT16*)&draw->VariableData[0];
3499 /* If GlyphCount is odd, all subsequent fields will be 2-byte
3500 aligned rather than 4-byte aligned, which may lead to access
3501 issues. Handle this case by making our own copy of positions. */
3502 if (draw->GlyphCount % 2)
3504 SIZE_T alloc_size = draw->GlyphCount * sizeof(*positions);
3506 if (draw->MatrixPresent)
3507 alloc_size += sizeof(*matrix);
3509 positions = alignedmem = heap_alloc(alloc_size);
3510 if (!positions)
3512 GdipDeleteBrush((GpBrush*)solidfill);
3513 return OutOfMemory;
3516 memcpy(positions, &text[draw->GlyphCount], alloc_size);
3518 else
3519 positions = (PointF*)&text[draw->GlyphCount];
3521 if (draw->MatrixPresent)
3522 matrix = (GpMatrix*)&positions[draw->GlyphCount];
3524 stat = GdipDrawDriverString(real_metafile->playback_graphics, text, draw->GlyphCount,
3525 real_metafile->objtable[font].u.font, brush, positions,
3526 draw->DriverStringOptionsFlags, matrix);
3528 GdipDeleteBrush((GpBrush*)solidfill);
3529 heap_free(alignedmem);
3531 return stat;
3533 case EmfPlusRecordTypeFillRegion:
3535 EmfPlusFillRegion * const fill = (EmfPlusFillRegion*)header;
3536 GpSolidFill *solidfill = NULL;
3537 GpBrush *brush;
3538 BYTE region = flags & 0xff;
3540 if (dataSize != sizeof(EmfPlusFillRegion) - sizeof(EmfPlusRecordHeader))
3541 return InvalidParameter;
3543 if (region >= EmfPlusObjectTableSize ||
3544 real_metafile->objtable[region].type != ObjectTypeRegion)
3545 return InvalidParameter;
3547 if (flags & 0x8000)
3549 stat = GdipCreateSolidFill(fill->data.Color, &solidfill);
3550 if (stat != Ok)
3551 return stat;
3552 brush = (GpBrush*)solidfill;
3554 else
3556 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3557 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3558 return InvalidParameter;
3560 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3563 stat = GdipFillRegion(real_metafile->playback_graphics, brush,
3564 real_metafile->objtable[region].u.region);
3565 GdipDeleteBrush((GpBrush*)solidfill);
3567 return stat;
3569 default:
3570 FIXME("Not implemented for record type %x\n", recordType);
3571 return NotImplemented;
3575 return Ok;
3578 struct enum_metafile_data
3580 EnumerateMetafileProc callback;
3581 void *callback_data;
3582 GpMetafile *metafile;
3585 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3586 int nObj, LPARAM lpData)
3588 BOOL ret;
3589 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3590 const BYTE* pStr;
3592 data->metafile->handle_table = lpHTable;
3593 data->metafile->handle_count = nObj;
3595 /* First check for an EMF+ record. */
3596 if (lpEMFR->iType == EMR_GDICOMMENT)
3598 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3600 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3602 int offset = 4;
3604 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3606 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3608 if (record->DataSize)
3609 pStr = (const BYTE*)(record+1);
3610 else
3611 pStr = NULL;
3613 ret = data->callback(record->Type, record->Flags, record->DataSize,
3614 pStr, data->callback_data);
3616 if (!ret)
3617 return 0;
3619 offset += record->Size;
3622 return 1;
3626 if (lpEMFR->nSize != 8)
3627 pStr = (const BYTE*)lpEMFR->dParm;
3628 else
3629 pStr = NULL;
3631 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3632 pStr, data->callback_data);
3635 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3636 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3637 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3638 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3640 struct enum_metafile_data data;
3641 GpStatus stat;
3642 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3643 GraphicsContainer state;
3644 GpPath *dst_path;
3645 RECT dst_bounds;
3647 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3648 destPoints, count, srcRect, srcUnit, callback, callbackData,
3649 imageAttributes);
3651 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3652 return InvalidParameter;
3654 if (!metafile->hemf)
3655 return InvalidParameter;
3657 if (metafile->playback_graphics)
3658 return ObjectBusy;
3660 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3661 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3662 debugstr_pointf(&destPoints[2]));
3664 data.callback = callback;
3665 data.callback_data = callbackData;
3666 data.metafile = real_metafile;
3668 real_metafile->playback_graphics = graphics;
3669 real_metafile->playback_dc = NULL;
3670 real_metafile->src_rect = *srcRect;
3672 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3673 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3675 if (stat == Ok)
3676 stat = GdipBeginContainer2(graphics, &state);
3678 if (stat == Ok)
3680 stat = GdipSetPageScale(graphics, 1.0);
3682 if (stat == Ok)
3683 stat = GdipSetPageUnit(graphics, UnitPixel);
3685 if (stat == Ok)
3686 stat = GdipResetWorldTransform(graphics);
3688 if (stat == Ok)
3689 stat = GdipCreateRegion(&real_metafile->base_clip);
3691 if (stat == Ok)
3692 stat = GdipGetClip(graphics, real_metafile->base_clip);
3694 if (stat == Ok)
3695 stat = GdipCreateRegion(&real_metafile->clip);
3697 if (stat == Ok)
3698 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3700 if (stat == Ok)
3702 GpPointF clip_points[4];
3704 clip_points[0] = real_metafile->playback_points[0];
3705 clip_points[1] = real_metafile->playback_points[1];
3706 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3707 - real_metafile->playback_points[0].X;
3708 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3709 - real_metafile->playback_points[0].Y;
3710 clip_points[3] = real_metafile->playback_points[2];
3712 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3714 if (stat == Ok)
3715 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3717 GdipDeletePath(dst_path);
3720 if (stat == Ok)
3721 stat = GdipCreateMatrix(&real_metafile->world_transform);
3723 if (stat == Ok)
3725 real_metafile->page_unit = UnitDisplay;
3726 real_metafile->page_scale = 1.0;
3727 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3730 if (stat == Ok)
3732 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3735 if (stat == Ok)
3737 stat = METAFILE_PlaybackGetDC(real_metafile);
3739 dst_bounds.left = real_metafile->playback_points[0].X;
3740 dst_bounds.right = real_metafile->playback_points[1].X;
3741 dst_bounds.top = real_metafile->playback_points[0].Y;
3742 dst_bounds.bottom = real_metafile->playback_points[2].Y;
3745 if (stat == Ok)
3746 EnumEnhMetaFile(real_metafile->playback_dc, metafile->hemf, enum_metafile_proc,
3747 &data, &dst_bounds);
3749 METAFILE_PlaybackReleaseDC(real_metafile);
3751 GdipDeleteMatrix(real_metafile->world_transform);
3752 real_metafile->world_transform = NULL;
3754 GdipDeleteRegion(real_metafile->base_clip);
3755 real_metafile->base_clip = NULL;
3757 GdipDeleteRegion(real_metafile->clip);
3758 real_metafile->clip = NULL;
3760 while (list_head(&real_metafile->containers))
3762 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3763 list_remove(&cont->entry);
3764 GdipDeleteRegion(cont->clip);
3765 heap_free(cont);
3768 GdipEndContainer(graphics, state);
3771 real_metafile->playback_graphics = NULL;
3773 return stat;
3776 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3777 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3778 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3780 GpPointF points[3];
3782 if (!graphics || !metafile || !dest) return InvalidParameter;
3784 points[0].X = points[2].X = dest->X;
3785 points[0].Y = points[1].Y = dest->Y;
3786 points[1].X = dest->X + dest->Width;
3787 points[2].Y = dest->Y + dest->Height;
3789 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3790 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3793 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3794 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3795 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3797 GpRectF destf;
3799 if (!graphics || !metafile || !dest) return InvalidParameter;
3801 destf.X = dest->X;
3802 destf.Y = dest->Y;
3803 destf.Width = dest->Width;
3804 destf.Height = dest->Height;
3806 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3809 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3810 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3811 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3813 GpRectF destf;
3815 if (!graphics || !metafile || !dest) return InvalidParameter;
3817 destf.X = dest->X;
3818 destf.Y = dest->Y;
3819 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
3820 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
3822 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3825 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3826 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3827 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3829 GpPointF ptf;
3831 if (!graphics || !metafile || !dest) return InvalidParameter;
3833 ptf.X = dest->X;
3834 ptf.Y = dest->Y;
3836 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3839 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3840 MetafileHeader * header)
3842 GpStatus status;
3844 TRACE("(%p, %p)\n", metafile, header);
3846 if(!metafile || !header)
3847 return InvalidParameter;
3849 if (metafile->hemf)
3851 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3852 if (status != Ok) return status;
3854 else
3856 memset(header, 0, sizeof(*header));
3857 header->Version = VERSION_MAGIC2;
3860 header->Type = metafile->metafile_type;
3861 header->DpiX = metafile->image.xres;
3862 header->DpiY = metafile->image.yres;
3863 header->Width = gdip_round(metafile->bounds.Width);
3864 header->Height = gdip_round(metafile->bounds.Height);
3866 return Ok;
3869 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3870 int nObj, LPARAM lpData)
3872 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3874 if (lpEMFR->iType == EMR_GDICOMMENT)
3876 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3878 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3880 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3882 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3883 header->Type == EmfPlusRecordTypeHeader)
3885 memcpy(dst_header, header, sizeof(*dst_header));
3889 else if (lpEMFR->iType == EMR_HEADER)
3890 return TRUE;
3892 return FALSE;
3895 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3896 MetafileHeader *header)
3898 ENHMETAHEADER3 emfheader;
3899 EmfPlusHeader emfplusheader;
3900 MetafileType metafile_type;
3902 TRACE("(%p,%p)\n", hemf, header);
3904 if(!hemf || !header)
3905 return InvalidParameter;
3907 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3908 return GenericError;
3910 emfplusheader.Header.Type = 0;
3912 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3914 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3916 if ((emfplusheader.Header.Flags & 1) == 1)
3917 metafile_type = MetafileTypeEmfPlusDual;
3918 else
3919 metafile_type = MetafileTypeEmfPlusOnly;
3921 else
3922 metafile_type = MetafileTypeEmf;
3924 header->Type = metafile_type;
3925 header->Size = emfheader.nBytes;
3926 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3927 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3928 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3929 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3930 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3931 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3932 header->u.EmfHeader = emfheader;
3934 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3936 header->Version = emfplusheader.Version;
3937 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3938 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3939 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3940 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3942 else
3944 header->Version = emfheader.nVersion;
3945 header->EmfPlusFlags = 0;
3946 header->EmfPlusHeaderSize = 0;
3947 header->LogicalDpiX = 0;
3948 header->LogicalDpiY = 0;
3951 return Ok;
3954 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3955 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3957 GpStatus status;
3958 GpMetafile *metafile;
3960 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3962 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3963 if (status == Ok)
3965 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3966 GdipDisposeImage(&metafile->image);
3968 return status;
3971 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3972 MetafileHeader *header)
3974 GpStatus status;
3975 GpMetafile *metafile;
3977 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3979 if (!filename || !header)
3980 return InvalidParameter;
3982 status = GdipCreateMetafileFromFile(filename, &metafile);
3983 if (status == Ok)
3985 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3986 GdipDisposeImage(&metafile->image);
3988 return status;
3991 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3992 MetafileHeader *header)
3994 GpStatus status;
3995 GpMetafile *metafile;
3997 TRACE("(%p,%p)\n", stream, header);
3999 if (!stream || !header)
4000 return InvalidParameter;
4002 status = GdipCreateMetafileFromStream(stream, &metafile);
4003 if (status == Ok)
4005 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
4006 GdipDisposeImage(&metafile->image);
4008 return status;
4011 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
4012 GpMetafile **metafile)
4014 GpStatus stat;
4015 MetafileHeader header;
4017 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
4019 if(!hemf || !metafile)
4020 return InvalidParameter;
4022 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
4023 if (stat != Ok)
4024 return stat;
4026 *metafile = heap_alloc_zero(sizeof(GpMetafile));
4027 if (!*metafile)
4028 return OutOfMemory;
4030 (*metafile)->image.type = ImageTypeMetafile;
4031 (*metafile)->image.format = ImageFormatEMF;
4032 (*metafile)->image.frame_count = 1;
4033 (*metafile)->image.xres = header.DpiX;
4034 (*metafile)->image.yres = header.DpiY;
4035 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
4036 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
4037 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
4038 / 2540.0 * header.DpiX;
4039 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
4040 / 2540.0 * header.DpiY;
4041 (*metafile)->unit = UnitPixel;
4042 (*metafile)->metafile_type = header.Type;
4043 (*metafile)->hemf = hemf;
4044 (*metafile)->preserve_hemf = !delete;
4045 list_init(&(*metafile)->containers);
4047 TRACE("<-- %p\n", *metafile);
4049 return Ok;
4052 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
4053 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4055 UINT read;
4056 BYTE *copy;
4057 HENHMETAFILE hemf;
4058 GpStatus retval = Ok;
4060 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
4062 if(!hwmf || !metafile)
4063 return InvalidParameter;
4065 *metafile = NULL;
4066 read = GetMetaFileBitsEx(hwmf, 0, NULL);
4067 if(!read)
4068 return GenericError;
4069 copy = heap_alloc_zero(read);
4070 GetMetaFileBitsEx(hwmf, read, copy);
4072 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
4073 heap_free(copy);
4075 /* FIXME: We should store and use hwmf instead of converting to hemf */
4076 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
4078 if (retval == Ok)
4080 if (placeable)
4082 (*metafile)->image.xres = (REAL)placeable->Inch;
4083 (*metafile)->image.yres = (REAL)placeable->Inch;
4084 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
4085 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
4086 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
4087 placeable->BoundingBox.Left);
4088 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
4089 placeable->BoundingBox.Top);
4090 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
4092 else
4093 (*metafile)->metafile_type = MetafileTypeWmf;
4094 (*metafile)->image.format = ImageFormatWMF;
4096 if (delete) DeleteMetaFile(hwmf);
4098 else
4099 DeleteEnhMetaFile(hemf);
4100 return retval;
4103 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
4104 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
4106 HMETAFILE hmf;
4107 HENHMETAFILE emf;
4109 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
4111 hmf = GetMetaFileW(file);
4112 if(hmf)
4113 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
4115 emf = GetEnhMetaFileW(file);
4116 if(emf)
4117 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
4119 return GenericError;
4122 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
4123 GpMetafile **metafile)
4125 GpStatus status;
4126 IStream *stream;
4128 TRACE("(%p, %p)\n", file, metafile);
4130 if (!file || !metafile) return InvalidParameter;
4132 *metafile = NULL;
4134 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
4135 if (status == Ok)
4137 status = GdipCreateMetafileFromStream(stream, metafile);
4138 IStream_Release(stream);
4140 return status;
4143 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
4144 GpMetafile **metafile)
4146 GpStatus stat;
4148 TRACE("%p %p\n", stream, metafile);
4150 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
4151 if (stat != Ok) return stat;
4153 if ((*metafile)->image.type != ImageTypeMetafile)
4155 GdipDisposeImage(&(*metafile)->image);
4156 *metafile = NULL;
4157 return GenericError;
4160 return Ok;
4163 GpStatus WINGDIPAPI GdipGetMetafileDownLevelRasterizationLimit(GDIPCONST GpMetafile *metafile,
4164 UINT *limitDpi)
4166 TRACE("(%p,%p)\n", metafile, limitDpi);
4168 if (!metafile || !limitDpi)
4169 return InvalidParameter;
4171 if (!metafile->record_dc)
4172 return WrongState;
4174 *limitDpi = metafile->limit_dpi;
4176 return Ok;
4179 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4180 UINT limitDpi)
4182 TRACE("(%p,%u)\n", metafile, limitDpi);
4184 if (limitDpi == 0)
4185 limitDpi = 96;
4187 if (!metafile || limitDpi < 10)
4188 return InvalidParameter;
4190 if (!metafile->record_dc)
4191 return WrongState;
4193 metafile->limit_dpi = limitDpi;
4195 return Ok;
4198 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
4199 GpMetafile* metafile, BOOL* succ, EmfType emfType,
4200 const WCHAR* description, GpMetafile** out_metafile)
4202 static int calls;
4204 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
4205 debugstr_w(description), out_metafile);
4207 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
4208 return InvalidParameter;
4210 if(succ)
4211 *succ = FALSE;
4212 *out_metafile = NULL;
4214 if(!(calls++))
4215 FIXME("not implemented\n");
4217 return NotImplemented;
4220 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
4221 LPBYTE pData16, INT iMapMode, INT eFlags)
4223 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
4224 return NotImplemented;
4227 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
4228 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
4229 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
4230 GpMetafile **metafile)
4232 FIXME("%s %p %d %s %d %s %p stub!\n", debugstr_w(fileName), hdc, type, debugstr_rectf(pFrameRect),
4233 frameUnit, debugstr_w(desc), metafile);
4235 return NotImplemented;
4238 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4239 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4240 GDIPCONST WCHAR *desc, GpMetafile **metafile)
4242 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4243 frameUnit, debugstr_w(desc), metafile);
4245 return NotImplemented;
4248 /*****************************************************************************
4249 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4252 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4253 GpMetafile* metafile, BOOL* conversionSuccess,
4254 const WCHAR* filename, EmfType emfType,
4255 const WCHAR* description, GpMetafile** out_metafile)
4257 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4258 return NotImplemented;
4261 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4263 LARGE_INTEGER zero;
4264 STATSTG statstg;
4265 GpStatus stat;
4266 HRESULT hr;
4268 *size = 0;
4270 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4271 if (FAILED(hr)) return hresult_to_status(hr);
4273 stat = encode_image_png(image, *stream, NULL);
4274 if (stat != Ok)
4276 IStream_Release(*stream);
4277 return stat;
4280 hr = IStream_Stat(*stream, &statstg, 1);
4281 if (FAILED(hr))
4283 IStream_Release(*stream);
4284 return hresult_to_status(hr);
4286 *size = statstg.cbSize.u.LowPart;
4288 zero.QuadPart = 0;
4289 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4290 if (FAILED(hr))
4292 IStream_Release(*stream);
4293 return hresult_to_status(hr);
4296 return Ok;
4299 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4301 HRESULT hr;
4303 record->Width = 0;
4304 record->Height = 0;
4305 record->Stride = 0;
4306 record->PixelFormat = 0;
4307 record->Type = BitmapDataTypeCompressed;
4309 hr = IStream_Read(stream, record->BitmapData, size, NULL);
4310 if (FAILED(hr)) return hresult_to_status(hr);
4311 return Ok;
4314 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4316 EmfPlusObject *object_record;
4317 GpStatus stat;
4318 DWORD size;
4320 *id = -1;
4322 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4323 return Ok;
4325 if (image->type == ImageTypeBitmap)
4327 IStream *stream;
4328 DWORD aligned_size;
4330 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4331 if (stat != Ok) return stat;
4332 aligned_size = (size + 3) & ~3;
4334 stat = METAFILE_AllocateRecord(metafile,
4335 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4336 (void**)&object_record);
4337 if (stat != Ok)
4339 IStream_Release(stream);
4340 return stat;
4342 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4344 *id = METAFILE_AddObjectId(metafile);
4345 object_record->Header.Type = EmfPlusRecordTypeObject;
4346 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4347 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4348 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4350 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4351 IStream_Release(stream);
4352 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4353 return stat;
4355 else if (image->type == ImageTypeMetafile)
4357 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4358 EmfPlusMetafile *metafile_record;
4360 if (!hemf) return InvalidParameter;
4362 size = GetEnhMetaFileBits(hemf, 0, NULL);
4363 if (!size) return GenericError;
4365 stat = METAFILE_AllocateRecord(metafile,
4366 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4367 (void**)&object_record);
4368 if (stat != Ok) return stat;
4370 *id = METAFILE_AddObjectId(metafile);
4371 object_record->Header.Type = EmfPlusRecordTypeObject;
4372 object_record->Header.Flags = *id | ObjectTypeImage << 8;
4373 object_record->ObjectData.image.Version = VERSION_MAGIC2;
4374 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4375 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4376 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4377 metafile_record->MetafileDataSize = size;
4378 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4380 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4381 return GenericError;
4383 return Ok;
4385 else
4387 FIXME("not supported image type (%d)\n", image->type);
4388 return NotImplemented;
4392 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4394 EmfPlusObject *object_record;
4395 EmfPlusImageAttributes *attrs_record;
4396 GpStatus stat;
4398 *id = -1;
4400 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4401 return Ok;
4403 if (!attrs)
4404 return Ok;
4406 stat = METAFILE_AllocateRecord(metafile,
4407 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4408 (void**)&object_record);
4409 if (stat != Ok) return stat;
4411 *id = METAFILE_AddObjectId(metafile);
4412 object_record->Header.Type = EmfPlusRecordTypeObject;
4413 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4414 attrs_record = &object_record->ObjectData.image_attributes;
4415 attrs_record->Version = VERSION_MAGIC2;
4416 attrs_record->Reserved1 = 0;
4417 attrs_record->WrapMode = attrs->wrap;
4418 attrs_record->ClampColor = attrs->outside_color;
4419 attrs_record->ObjectClamp = attrs->clamp;
4420 attrs_record->Reserved2 = 0;
4421 return Ok;
4424 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4425 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4426 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4427 DrawImageAbort callback, VOID *callbackData)
4429 EmfPlusDrawImagePoints *draw_image_record;
4430 DWORD image_id, attributes_id;
4431 GpStatus stat;
4433 if (count != 3) return InvalidParameter;
4435 if (metafile->metafile_type == MetafileTypeEmf)
4437 FIXME("MetafileTypeEmf metafiles not supported\n");
4438 return NotImplemented;
4440 else
4441 FIXME("semi-stub\n");
4443 if (!imageAttributes)
4445 stat = METAFILE_AddImageObject(metafile, image, &image_id);
4447 else if (image->type == ImageTypeBitmap)
4449 INT width = ((GpBitmap*)image)->width;
4450 INT height = ((GpBitmap*)image)->height;
4451 GpGraphics *graphics;
4452 GpBitmap *bitmap;
4454 stat = GdipCreateBitmapFromScan0(width, height,
4455 0, PixelFormat32bppARGB, NULL, &bitmap);
4456 if (stat != Ok) return stat;
4458 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4459 if (stat != Ok)
4461 GdipDisposeImage((GpImage*)bitmap);
4462 return stat;
4465 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4466 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4467 GdipDeleteGraphics(graphics);
4468 if (stat != Ok)
4470 GdipDisposeImage((GpImage*)bitmap);
4471 return stat;
4474 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4475 GdipDisposeImage((GpImage*)bitmap);
4477 else
4479 FIXME("imageAttributes not supported (image type %d)\n", image->type);
4480 return NotImplemented;
4482 if (stat != Ok) return stat;
4484 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4485 if (stat != Ok) return stat;
4487 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
4488 if (stat != Ok) return stat;
4489 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
4490 draw_image_record->Header.Flags = image_id;
4491 draw_image_record->ImageAttributesID = attributes_id;
4492 draw_image_record->SrcUnit = UnitPixel;
4493 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
4494 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
4495 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
4496 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
4497 draw_image_record->count = 3;
4498 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4499 METAFILE_WriteRecords(metafile);
4500 return Ok;
4503 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4505 EmfPlusRecordHeader *record;
4506 GpStatus stat;
4508 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4509 return Ok;
4511 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
4512 if (stat != Ok) return stat;
4514 record->Type = prop;
4515 record->Flags = val;
4517 METAFILE_WriteRecords(metafile);
4518 return Ok;
4521 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4523 EmfPlusObject *object_record;
4524 GpStatus stat;
4525 DWORD size;
4527 *id = -1;
4528 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4529 return Ok;
4531 size = write_path_data(path, NULL);
4532 stat = METAFILE_AllocateRecord(metafile,
4533 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4534 (void**)&object_record);
4535 if (stat != Ok) return stat;
4537 *id = METAFILE_AddObjectId(metafile);
4538 object_record->Header.Type = EmfPlusRecordTypeObject;
4539 object_record->Header.Flags = *id | ObjectTypePath << 8;
4540 write_path_data(path, &object_record->ObjectData.path);
4541 return Ok;
4544 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4546 DWORD i, data_flags, pen_data_size, brush_size;
4547 EmfPlusObject *object_record;
4548 EmfPlusPenData *pen_data;
4549 GpStatus stat;
4550 BOOL result;
4552 *id = -1;
4553 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4554 return Ok;
4556 data_flags = 0;
4557 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4559 GdipIsMatrixIdentity(&pen->transform, &result);
4560 if (!result)
4562 data_flags |= PenDataTransform;
4563 pen_data_size += sizeof(EmfPlusTransformMatrix);
4565 if (pen->startcap != LineCapFlat)
4567 data_flags |= PenDataStartCap;
4568 pen_data_size += sizeof(DWORD);
4570 if (pen->endcap != LineCapFlat)
4572 data_flags |= PenDataEndCap;
4573 pen_data_size += sizeof(DWORD);
4575 if (pen->join != LineJoinMiter)
4577 data_flags |= PenDataJoin;
4578 pen_data_size += sizeof(DWORD);
4580 if (pen->miterlimit != 10.0)
4582 data_flags |= PenDataMiterLimit;
4583 pen_data_size += sizeof(REAL);
4585 if (pen->style != GP_DEFAULT_PENSTYLE)
4587 data_flags |= PenDataLineStyle;
4588 pen_data_size += sizeof(DWORD);
4590 if (pen->dashcap != DashCapFlat)
4592 data_flags |= PenDataDashedLineCap;
4593 pen_data_size += sizeof(DWORD);
4595 data_flags |= PenDataDashedLineOffset;
4596 pen_data_size += sizeof(REAL);
4597 if (pen->numdashes)
4599 data_flags |= PenDataDashedLine;
4600 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4602 if (pen->align != PenAlignmentCenter)
4604 data_flags |= PenDataNonCenter;
4605 pen_data_size += sizeof(DWORD);
4607 /* TODO: Add support for PenDataCompoundLine */
4608 if (pen->customstart)
4610 FIXME("ignoring custom start cup\n");
4612 if (pen->customend)
4614 FIXME("ignoring custom end cup\n");
4617 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4618 if (stat != Ok) return stat;
4620 stat = METAFILE_AllocateRecord(metafile,
4621 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4622 (void**)&object_record);
4623 if (stat != Ok) return stat;
4625 *id = METAFILE_AddObjectId(metafile);
4626 object_record->Header.Type = EmfPlusRecordTypeObject;
4627 object_record->Header.Flags = *id | ObjectTypePen << 8;
4628 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4629 object_record->ObjectData.pen.Type = 0;
4631 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4632 pen_data->PenDataFlags = data_flags;
4633 pen_data->PenUnit = pen->unit;
4634 pen_data->PenWidth = pen->width;
4636 i = 0;
4637 if (data_flags & PenDataTransform)
4639 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4640 memcpy(m, &pen->transform, sizeof(*m));
4641 i += sizeof(EmfPlusTransformMatrix);
4643 if (data_flags & PenDataStartCap)
4645 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4646 i += sizeof(DWORD);
4648 if (data_flags & PenDataEndCap)
4650 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4651 i += sizeof(DWORD);
4653 if (data_flags & PenDataJoin)
4655 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4656 i += sizeof(DWORD);
4658 if (data_flags & PenDataMiterLimit)
4660 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4661 i += sizeof(REAL);
4663 if (data_flags & PenDataLineStyle)
4665 switch (pen->style & PS_STYLE_MASK)
4667 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4668 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4669 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4670 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4671 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4672 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4674 i += sizeof(DWORD);
4676 if (data_flags & PenDataDashedLineCap)
4678 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4679 i += sizeof(DWORD);
4681 if (data_flags & PenDataDashedLineOffset)
4683 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4684 i += sizeof(REAL);
4686 if (data_flags & PenDataDashedLine)
4688 int j;
4690 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4691 i += sizeof(DWORD);
4693 for (j=0; j<pen->numdashes; j++)
4695 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4696 i += sizeof(REAL);
4699 if (data_flags & PenDataNonCenter)
4701 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4702 i += sizeof(DWORD);
4705 METAFILE_FillBrushData(pen->brush,
4706 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4707 return Ok;
4710 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4712 EmfPlusDrawPath *draw_path_record;
4713 DWORD path_id;
4714 DWORD pen_id;
4715 GpStatus stat;
4717 if (metafile->metafile_type == MetafileTypeEmf)
4719 FIXME("stub!\n");
4720 return NotImplemented;
4723 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4724 if (stat != Ok) return stat;
4726 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4727 if (stat != Ok) return stat;
4729 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4730 if (stat != Ok) return stat;
4731 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4732 draw_path_record->Header.Flags = path_id;
4733 draw_path_record->PenId = pen_id;
4735 METAFILE_WriteRecords(metafile);
4736 return Ok;
4739 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4741 EmfPlusFillPath *fill_path_record;
4742 DWORD brush_id = -1, path_id;
4743 BOOL inline_color;
4744 GpStatus stat;
4746 if (metafile->metafile_type == MetafileTypeEmf)
4748 FIXME("stub!\n");
4749 return NotImplemented;
4752 inline_color = brush->bt == BrushTypeSolidColor;
4753 if (!inline_color)
4755 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4756 if (stat != Ok) return stat;
4759 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4760 if (stat != Ok) return stat;
4762 stat = METAFILE_AllocateRecord(metafile,
4763 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4764 if (stat != Ok) return stat;
4765 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4766 if (inline_color)
4768 fill_path_record->Header.Flags = 0x8000 | path_id;
4769 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4771 else
4773 fill_path_record->Header.Flags = path_id;
4774 fill_path_record->data.BrushId = brush_id;
4777 METAFILE_WriteRecords(metafile);
4778 return Ok;
4781 static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
4783 EmfPlusObject *object_record;
4784 EmfPlusFont *font_record;
4785 GpStatus stat;
4786 INT fn_len;
4787 INT style;
4789 *id = -1;
4791 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4792 metafile->metafile_type != MetafileTypeEmfPlusDual)
4793 return Ok;
4795 /* The following cast is ugly, but GdipGetFontStyle does treat
4796 its first parameter as const. */
4797 stat = GdipGetFontStyle((GpFont*)font, &style);
4798 if (stat != Ok)
4799 return stat;
4801 fn_len = lstrlenW(font->family->FamilyName);
4802 stat = METAFILE_AllocateRecord(metafile,
4803 FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
4804 (void**)&object_record);
4805 if (stat != Ok)
4806 return stat;
4808 *id = METAFILE_AddObjectId(metafile);
4810 object_record->Header.Type = EmfPlusRecordTypeObject;
4811 object_record->Header.Flags = *id | ObjectTypeFont << 8;
4813 font_record = &object_record->ObjectData.font;
4814 font_record->Version = VERSION_MAGIC2;
4815 font_record->EmSize = font->emSize;
4816 font_record->SizeUnit = font->unit;
4817 font_record->FontStyleFlags = style;
4818 font_record->Reserved = 0;
4819 font_record->Length = fn_len;
4821 memcpy(font_record->FamilyName, font->family->FamilyName,
4822 fn_len * sizeof(*font->family->FamilyName));
4824 return Ok;
4827 GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
4828 GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
4829 GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
4831 DWORD brush_id;
4832 DWORD font_id;
4833 DWORD alloc_size;
4834 GpStatus stat;
4835 EmfPlusDrawDriverString *draw_string_record;
4836 BYTE *cursor;
4837 BOOL inline_color;
4838 BOOL include_matrix = FALSE;
4840 if (length <= 0)
4841 return InvalidParameter;
4843 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4844 metafile->metafile_type != MetafileTypeEmfPlusDual)
4846 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
4847 return NotImplemented;
4850 stat = METAFILE_AddFontObject(metafile, font, &font_id);
4851 if (stat != Ok)
4852 return stat;
4854 inline_color = (brush->bt == BrushTypeSolidColor);
4855 if (!inline_color)
4857 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4858 if (stat != Ok)
4859 return stat;
4862 if (matrix)
4864 BOOL identity;
4866 stat = GdipIsMatrixIdentity(matrix, &identity);
4867 if (stat != Ok)
4868 return stat;
4870 include_matrix = !identity;
4873 alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
4874 length * (sizeof(*text) + sizeof(*positions));
4876 if (include_matrix)
4877 alloc_size += sizeof(*matrix);
4879 /* Pad record to DWORD alignment. */
4880 alloc_size = (alloc_size + 3) & ~3;
4882 stat = METAFILE_AllocateRecord(metafile, alloc_size, (void**)&draw_string_record);
4883 if (stat != Ok)
4884 return stat;
4886 draw_string_record->Header.Type = EmfPlusRecordTypeDrawDriverString;
4887 draw_string_record->Header.Flags = font_id;
4888 draw_string_record->DriverStringOptionsFlags = flags;
4889 draw_string_record->MatrixPresent = include_matrix;
4890 draw_string_record->GlyphCount = length;
4892 if (inline_color)
4894 draw_string_record->Header.Flags |= 0x8000;
4895 draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
4897 else
4898 draw_string_record->brush.BrushId = brush_id;
4900 cursor = &draw_string_record->VariableData[0];
4902 memcpy(cursor, text, length * sizeof(*text));
4903 cursor += length * sizeof(*text);
4905 if (flags & DriverStringOptionsRealizedAdvance)
4907 static BOOL fixme_written = FALSE;
4909 /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
4910 in the case of RealizedAdvance, each glyph position is computed
4911 and serialized.
4913 While native GDI+ is capable of playing back metafiles with this
4914 flag set, it is possible that some application might rely on
4915 metafiles produced from GDI+ not setting this flag. Ideally we
4916 would also compute the position of each glyph here, serialize those
4917 values, and not set DriverStringOptionsRealizedAdvance. */
4918 if (!fixme_written)
4920 fixme_written = TRUE;
4921 FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
4924 *((PointF*)cursor) = *positions;
4926 else
4927 memcpy(cursor, positions, length * sizeof(*positions));
4929 if (include_matrix)
4931 cursor += length * sizeof(*positions);
4932 memcpy(cursor, matrix, sizeof(*matrix));
4935 METAFILE_WriteRecords(metafile);
4937 return Ok;
4940 GpStatus METAFILE_FillRegion(GpMetafile* metafile, GpBrush* brush, GpRegion* region)
4942 GpStatus stat;
4943 DWORD brush_id;
4944 DWORD region_id;
4945 EmfPlusFillRegion *fill_region_record;
4946 BOOL inline_color;
4948 if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
4949 metafile->metafile_type != MetafileTypeEmfPlusDual)
4951 FIXME("metafile type not supported: %i\n", metafile->metafile_type);
4952 return NotImplemented;
4955 inline_color = (brush->bt == BrushTypeSolidColor);
4956 if (!inline_color)
4958 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4959 if (stat != Ok)
4960 return stat;
4963 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
4964 if (stat != Ok)
4965 return stat;
4967 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusFillRegion),
4968 (void**)&fill_region_record);
4969 if (stat != Ok)
4970 return stat;
4972 fill_region_record->Header.Type = EmfPlusRecordTypeFillRegion;
4973 fill_region_record->Header.Flags = region_id;
4975 if (inline_color)
4977 fill_region_record->Header.Flags |= 0x8000;
4978 fill_region_record->data.Color = ((GpSolidFill*)brush)->color;
4980 else
4981 fill_region_record->data.BrushId = brush_id;
4983 METAFILE_WriteRecords(metafile);
4985 return Ok;