gdiplus/metafile: Implement playback for EmfPlusRecordTypeDrawRects.
[wine.git] / dlls / gdiplus / metafile.c
blob614fc43cf3f4c290922cf7263fc31056faa4c8c8
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"
28 #include "wine/unicode.h"
30 #define COBJMACROS
31 #include "objbase.h"
32 #include "ocidl.h"
33 #include "olectl.h"
34 #include "ole2.h"
36 #include "winreg.h"
37 #include "shlwapi.h"
39 #include "gdiplus.h"
40 #include "gdiplus_private.h"
41 #include "wine/debug.h"
42 #include "wine/list.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
46 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
48 typedef ARGB EmfPlusARGB;
50 typedef struct EmfPlusRecordHeader
52 WORD Type;
53 WORD Flags;
54 DWORD Size;
55 DWORD DataSize;
56 } EmfPlusRecordHeader;
58 typedef struct EmfPlusHeader
60 EmfPlusRecordHeader Header;
61 DWORD Version;
62 DWORD EmfPlusFlags;
63 DWORD LogicalDpiX;
64 DWORD LogicalDpiY;
65 } EmfPlusHeader;
67 typedef struct EmfPlusClear
69 EmfPlusRecordHeader Header;
70 DWORD Color;
71 } EmfPlusClear;
73 typedef struct EmfPlusFillRects
75 EmfPlusRecordHeader Header;
76 DWORD BrushID;
77 DWORD Count;
78 } EmfPlusFillRects;
80 typedef struct EmfPlusSetClipRect
82 EmfPlusRecordHeader Header;
83 GpRectF ClipRect;
84 } EmfPlusSetClipRect;
86 typedef struct EmfPlusSetPageTransform
88 EmfPlusRecordHeader Header;
89 REAL PageScale;
90 } EmfPlusSetPageTransform;
92 typedef struct EmfPlusRect
94 SHORT X;
95 SHORT Y;
96 SHORT Width;
97 SHORT Height;
98 } EmfPlusRect;
100 typedef struct EmfPlusSetWorldTransform
102 EmfPlusRecordHeader Header;
103 REAL MatrixData[6];
104 } EmfPlusSetWorldTransform;
106 typedef struct EmfPlusScaleWorldTransform
108 EmfPlusRecordHeader Header;
109 REAL Sx;
110 REAL Sy;
111 } EmfPlusScaleWorldTransform;
113 typedef struct EmfPlusMultiplyWorldTransform
115 EmfPlusRecordHeader Header;
116 REAL MatrixData[6];
117 } EmfPlusMultiplyWorldTransform;
119 typedef struct EmfPlusRotateWorldTransform
121 EmfPlusRecordHeader Header;
122 REAL Angle;
123 } EmfPlusRotateWorldTransform;
125 typedef struct EmfPlusTranslateWorldTransform
127 EmfPlusRecordHeader Header;
128 REAL dx;
129 REAL dy;
130 } EmfPlusTranslateWorldTransform;
132 typedef struct EmfPlusBeginContainer
134 EmfPlusRecordHeader Header;
135 GpRectF DestRect;
136 GpRectF SrcRect;
137 DWORD StackIndex;
138 } EmfPlusBeginContainer;
140 typedef struct EmfPlusContainerRecord
142 EmfPlusRecordHeader Header;
143 DWORD StackIndex;
144 } EmfPlusContainerRecord;
146 enum container_type
148 BEGIN_CONTAINER,
149 SAVE_GRAPHICS
152 typedef struct container
154 struct list entry;
155 DWORD id;
156 enum container_type type;
157 GraphicsContainer state;
158 GpMatrix world_transform;
159 GpUnit page_unit;
160 REAL page_scale;
161 GpRegion *clip;
162 } container;
164 enum PenDataFlags
166 PenDataTransform = 0x0001,
167 PenDataStartCap = 0x0002,
168 PenDataEndCap = 0x0004,
169 PenDataJoin = 0x0008,
170 PenDataMiterLimit = 0x0010,
171 PenDataLineStyle = 0x0020,
172 PenDataDashedLineCap = 0x0040,
173 PenDataDashedLineOffset = 0x0080,
174 PenDataDashedLine = 0x0100,
175 PenDataNonCenter = 0x0200,
176 PenDataCompoundLine = 0x0400,
177 PenDataCustomStartCap = 0x0800,
178 PenDataCustomEndCap = 0x1000
181 typedef struct EmfPlusTransformMatrix
183 REAL TransformMatrix[6];
184 } EmfPlusTransformMatrix;
186 enum LineStyle
188 LineStyleSolid,
189 LineStyleDash,
190 LineStyleDot,
191 LineStyleDashDot,
192 LineStyleDashDotDot,
193 LineStyleCustom
196 typedef struct EmfPlusDashedLineData
198 DWORD DashedLineDataSize;
199 BYTE data[1];
200 } EmfPlusDashedLineData;
202 typedef struct EmfPlusCompoundLineData
204 DWORD CompoundLineDataSize;
205 BYTE data[1];
206 } EmfPlusCompoundLineData;
208 typedef struct EmfPlusCustomStartCapData
210 DWORD CustomStartCapSize;
211 BYTE data[1];
212 } EmfPlusCustomStartCapData;
214 typedef struct EmfPlusCustomEndCapData
216 DWORD CustomEndCapSize;
217 BYTE data[1];
218 } EmfPlusCustomEndCapData;
220 typedef struct EmfPlusPenData
222 DWORD PenDataFlags;
223 DWORD PenUnit;
224 REAL PenWidth;
225 BYTE OptionalData[1];
226 } EmfPlusPenData;
228 enum BrushDataFlags
230 BrushDataPath = 1 << 0,
231 BrushDataTransform = 1 << 1,
232 BrushDataPresetColors = 1 << 2,
233 BrushDataBlendFactorsH = 1 << 3,
234 BrushDataBlendFactorsV = 1 << 4,
235 BrushDataFocusScales = 1 << 6,
236 BrushDataIsGammaCorrected = 1 << 7,
237 BrushDataDoNotTransform = 1 << 8,
240 typedef struct EmfPlusSolidBrushData
242 EmfPlusARGB SolidColor;
243 } EmfPlusSolidBrushData;
245 typedef struct EmfPlusHatchBrushData
247 DWORD HatchStyle;
248 EmfPlusARGB ForeColor;
249 EmfPlusARGB BackColor;
250 } EmfPlusHatchBrushData;
252 typedef struct EmfPlusTextureBrushData
254 DWORD BrushDataFlags;
255 INT WrapMode;
256 BYTE OptionalData[1];
257 } EmfPlusTextureBrushData;
259 typedef struct EmfPlusBrush
261 DWORD Version;
262 DWORD Type;
263 union {
264 EmfPlusSolidBrushData solid;
265 EmfPlusHatchBrushData hatch;
266 EmfPlusTextureBrushData texture;
267 } BrushData;
268 } EmfPlusBrush;
270 typedef struct EmfPlusPen
272 DWORD Version;
273 DWORD Type;
274 /* EmfPlusPenData */
275 /* EmfPlusBrush */
276 BYTE data[1];
277 } EmfPlusPen;
279 typedef struct EmfPlusPath
281 DWORD Version;
282 DWORD PathPointCount;
283 DWORD PathPointFlags;
284 /* PathPoints[] */
285 /* PathPointTypes[] */
286 /* AlignmentPadding */
287 BYTE data[1];
288 } EmfPlusPath;
290 typedef struct EmfPlusRegionNodePath
292 DWORD RegionNodePathLength;
293 EmfPlusPath RegionNodePath;
294 } EmfPlusRegionNodePath;
296 typedef struct EmfPlusRegion
298 DWORD Version;
299 DWORD RegionNodeCount;
300 BYTE RegionNode[1];
301 } EmfPlusRegion;
303 typedef struct EmfPlusPalette
305 DWORD PaletteStyleFlags;
306 DWORD PaletteCount;
307 BYTE PaletteEntries[1];
308 } EmfPlusPalette;
310 typedef enum
312 BitmapDataTypePixel,
313 BitmapDataTypeCompressed,
314 } BitmapDataType;
316 typedef struct EmfPlusBitmap
318 DWORD Width;
319 DWORD Height;
320 DWORD Stride;
321 DWORD PixelFormat;
322 DWORD Type;
323 BYTE BitmapData[1];
324 } EmfPlusBitmap;
326 typedef struct EmfPlusMetafile
328 DWORD Type;
329 DWORD MetafileDataSize;
330 BYTE MetafileData[1];
331 } EmfPlusMetafile;
333 typedef enum ImageDataType
335 ImageDataTypeUnknown,
336 ImageDataTypeBitmap,
337 ImageDataTypeMetafile,
338 } ImageDataType;
340 typedef struct EmfPlusImage
342 DWORD Version;
343 ImageDataType Type;
344 union
346 EmfPlusBitmap bitmap;
347 EmfPlusMetafile metafile;
348 } ImageData;
349 } EmfPlusImage;
351 typedef struct EmfPlusImageAttributes
353 DWORD Version;
354 DWORD Reserved1;
355 DWORD WrapMode;
356 EmfPlusARGB ClampColor;
357 DWORD ObjectClamp;
358 DWORD Reserved2;
359 } EmfPlusImageAttributes;
361 typedef struct EmfPlusObject
363 EmfPlusRecordHeader Header;
364 union
366 EmfPlusBrush brush;
367 EmfPlusPen pen;
368 EmfPlusPath path;
369 EmfPlusRegion region;
370 EmfPlusImage image;
371 EmfPlusImageAttributes image_attributes;
372 } ObjectData;
373 } EmfPlusObject;
375 typedef struct EmfPlusRectF
377 float X;
378 float Y;
379 float Width;
380 float Height;
381 } EmfPlusRectF;
383 typedef struct EmfPlusPointR7
385 BYTE X;
386 BYTE Y;
387 } EmfPlusPointR7;
389 typedef struct EmfPlusPoint
391 short X;
392 short Y;
393 } EmfPlusPoint;
395 typedef struct EmfPlusPointF
397 float X;
398 float Y;
399 } EmfPlusPointF;
401 typedef struct EmfPlusDrawImage
403 EmfPlusRecordHeader Header;
404 DWORD ImageAttributesID;
405 DWORD SrcUnit;
406 EmfPlusRectF SrcRect;
407 union
409 EmfPlusRect rect;
410 EmfPlusRectF rectF;
411 } RectData;
412 } EmfPlusDrawImage;
414 typedef struct EmfPlusDrawImagePoints
416 EmfPlusRecordHeader Header;
417 DWORD ImageAttributesID;
418 DWORD SrcUnit;
419 EmfPlusRectF SrcRect;
420 DWORD count;
421 union
423 EmfPlusPointR7 pointsR[3];
424 EmfPlusPoint points[3];
425 EmfPlusPointF pointsF[3];
426 } PointData;
427 } EmfPlusDrawImagePoints;
429 typedef struct EmfPlusDrawPath
431 EmfPlusRecordHeader Header;
432 DWORD PenId;
433 } EmfPlusDrawPath;
435 typedef struct EmfPlusDrawPie
437 EmfPlusRecordHeader Header;
438 float StartAngle;
439 float SweepAngle;
440 union
442 EmfPlusRect rect;
443 EmfPlusRectF rectF;
444 } RectData;
445 } EmfPlusDrawPie;
447 typedef struct EmfPlusDrawRects
449 EmfPlusRecordHeader Header;
450 DWORD Count;
451 union
453 EmfPlusRect rect[1];
454 EmfPlusRectF rectF[1];
455 } RectData;
456 } EmfPlusDrawRects;
458 typedef struct EmfPlusFillPath
460 EmfPlusRecordHeader Header;
461 union
463 DWORD BrushId;
464 EmfPlusARGB Color;
465 } data;
466 } EmfPlusFillPath;
468 typedef struct EmfPlusFont
470 DWORD Version;
471 float EmSize;
472 DWORD SizeUnit;
473 DWORD FontStyleFlags;
474 DWORD Reserved;
475 DWORD Length;
476 WCHAR FamilyName[1];
477 } EmfPlusFont;
479 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
481 struct emfplus_object *object = &metafile->objtable[id];
483 switch (object->type)
485 case ObjectTypeInvalid:
486 break;
487 case ObjectTypeBrush:
488 GdipDeleteBrush(object->u.brush);
489 break;
490 case ObjectTypePen:
491 GdipDeletePen(object->u.pen);
492 break;
493 case ObjectTypePath:
494 GdipDeletePath(object->u.path);
495 break;
496 case ObjectTypeRegion:
497 GdipDeleteRegion(object->u.region);
498 break;
499 case ObjectTypeImage:
500 GdipDisposeImage(object->u.image);
501 break;
502 case ObjectTypeFont:
503 GdipDeleteFont(object->u.font);
504 break;
505 case ObjectTypeImageAttributes:
506 GdipDisposeImageAttributes(object->u.image_attributes);
507 break;
508 default:
509 FIXME("not implemented for object type %u.\n", object->type);
510 return;
513 object->type = ObjectTypeInvalid;
514 object->u.object = NULL;
517 void METAFILE_Free(GpMetafile *metafile)
519 unsigned int i;
521 heap_free(metafile->comment_data);
522 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
523 if (!metafile->preserve_hemf)
524 DeleteEnhMetaFile(metafile->hemf);
525 if (metafile->record_graphics)
527 WARN("metafile closed while recording\n");
528 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
529 metafile->record_graphics->image = NULL;
530 metafile->record_graphics->busy = TRUE;
533 if (metafile->record_stream)
534 IStream_Release(metafile->record_stream);
536 for (i = 0; i < sizeof(metafile->objtable)/sizeof(metafile->objtable[0]); i++)
537 metafile_free_object_table_entry(metafile, i);
540 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
542 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
545 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
547 DWORD size_needed;
548 EmfPlusRecordHeader *record;
550 if (!metafile->comment_data_size)
552 DWORD data_size = max(256, size * 2 + 4);
553 metafile->comment_data = heap_alloc_zero(data_size);
555 if (!metafile->comment_data)
556 return OutOfMemory;
558 memcpy(metafile->comment_data, "EMF+", 4);
560 metafile->comment_data_size = data_size;
561 metafile->comment_data_length = 4;
564 size_needed = size + metafile->comment_data_length;
566 if (size_needed > metafile->comment_data_size)
568 DWORD data_size = size_needed * 2;
569 BYTE *new_data = heap_alloc_zero(data_size);
571 if (!new_data)
572 return OutOfMemory;
574 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
576 metafile->comment_data_size = data_size;
577 heap_free(metafile->comment_data);
578 metafile->comment_data = new_data;
581 *result = metafile->comment_data + metafile->comment_data_length;
582 metafile->comment_data_length += size;
584 record = (EmfPlusRecordHeader*)*result;
585 record->Size = size;
586 record->DataSize = size - sizeof(EmfPlusRecordHeader);
588 return Ok;
591 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
593 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
594 metafile->comment_data_length -= record->Size;
597 static void METAFILE_WriteRecords(GpMetafile *metafile)
599 if (metafile->comment_data_length > 4)
601 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
602 metafile->comment_data_length = 4;
606 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
608 GpStatus stat;
610 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
612 EmfPlusHeader *header;
614 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
615 if (stat != Ok)
616 return stat;
618 header->Header.Type = EmfPlusRecordTypeHeader;
620 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
621 header->Header.Flags = 1;
622 else
623 header->Header.Flags = 0;
625 header->Version = VERSION_MAGIC2;
627 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
628 header->EmfPlusFlags = 1;
629 else
630 header->EmfPlusFlags = 0;
632 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
633 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
635 METAFILE_WriteRecords(metafile);
638 return Ok;
641 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
643 GpStatus stat;
645 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
647 EmfPlusRecordHeader *record;
649 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
650 if (stat != Ok)
651 return stat;
653 record->Type = EmfPlusRecordTypeEndOfFile;
654 record->Flags = 0;
656 METAFILE_WriteRecords(metafile);
659 return Ok;
662 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
663 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
665 HDC record_dc;
666 REAL dpix, dpiy;
667 REAL framerect_factor_x, framerect_factor_y;
668 RECT rc, *lprc;
669 GpStatus stat;
671 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
673 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
674 return InvalidParameter;
676 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
677 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
679 if (frameRect)
681 switch (frameUnit)
683 case MetafileFrameUnitPixel:
684 framerect_factor_x = 2540.0 / dpix;
685 framerect_factor_y = 2540.0 / dpiy;
686 break;
687 case MetafileFrameUnitPoint:
688 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
689 break;
690 case MetafileFrameUnitInch:
691 framerect_factor_x = framerect_factor_y = 2540.0;
692 break;
693 case MetafileFrameUnitDocument:
694 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
695 break;
696 case MetafileFrameUnitMillimeter:
697 framerect_factor_x = framerect_factor_y = 100.0;
698 break;
699 case MetafileFrameUnitGdi:
700 framerect_factor_x = framerect_factor_y = 1.0;
701 break;
702 default:
703 return InvalidParameter;
706 rc.left = framerect_factor_x * frameRect->X;
707 rc.top = framerect_factor_y * frameRect->Y;
708 rc.right = rc.left + framerect_factor_x * frameRect->Width;
709 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
711 lprc = &rc;
713 else
714 lprc = NULL;
716 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
718 if (!record_dc)
719 return GenericError;
721 *metafile = heap_alloc_zero(sizeof(GpMetafile));
722 if(!*metafile)
724 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
725 return OutOfMemory;
728 (*metafile)->image.type = ImageTypeMetafile;
729 (*metafile)->image.flags = ImageFlagsNone;
730 (*metafile)->image.palette = NULL;
731 (*metafile)->image.xres = dpix;
732 (*metafile)->image.yres = dpiy;
733 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
734 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
735 (*metafile)->unit = UnitPixel;
736 (*metafile)->metafile_type = type;
737 (*metafile)->record_dc = record_dc;
738 (*metafile)->comment_data = NULL;
739 (*metafile)->comment_data_size = 0;
740 (*metafile)->comment_data_length = 0;
741 (*metafile)->hemf = NULL;
742 list_init(&(*metafile)->containers);
744 if (!frameRect)
746 (*metafile)->auto_frame = TRUE;
747 (*metafile)->auto_frame_min.X = 0;
748 (*metafile)->auto_frame_min.Y = 0;
749 (*metafile)->auto_frame_max.X = -1;
750 (*metafile)->auto_frame_max.Y = -1;
753 stat = METAFILE_WriteHeader(*metafile, hdc);
755 if (stat != Ok)
757 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
758 heap_free(*metafile);
759 *metafile = NULL;
760 return OutOfMemory;
763 return stat;
766 /*****************************************************************************
767 * GdipRecordMetafileI [GDIPLUS.@]
769 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
770 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
772 GpRectF frameRectF, *pFrameRectF;
774 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
776 if (frameRect)
778 frameRectF.X = frameRect->X;
779 frameRectF.Y = frameRect->Y;
780 frameRectF.Width = frameRect->Width;
781 frameRectF.Height = frameRect->Height;
782 pFrameRectF = &frameRectF;
784 else
785 pFrameRectF = NULL;
787 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
790 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
791 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
793 GpStatus stat;
795 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
797 if (!stream)
798 return InvalidParameter;
800 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
802 if (stat == Ok)
804 (*metafile)->record_stream = stream;
805 IStream_AddRef(stream);
808 return stat;
811 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
812 UINT num_points)
814 int i;
816 if (!metafile->auto_frame || !num_points)
817 return;
819 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
820 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
822 for (i=0; i<num_points; i++)
824 if (points[i].X < metafile->auto_frame_min.X)
825 metafile->auto_frame_min.X = points[i].X;
826 if (points[i].X > metafile->auto_frame_max.X)
827 metafile->auto_frame_max.X = points[i].X;
828 if (points[i].Y < metafile->auto_frame_min.Y)
829 metafile->auto_frame_min.Y = points[i].Y;
830 if (points[i].Y > metafile->auto_frame_max.Y)
831 metafile->auto_frame_max.Y = points[i].Y;
835 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
837 GpStatus stat;
839 if (!metafile->record_dc || metafile->record_graphics)
840 return InvalidParameter;
842 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
844 if (stat == Ok)
846 *result = metafile->record_graphics;
847 metafile->record_graphics->xres = 96.0;
848 metafile->record_graphics->yres = 96.0;
851 return stat;
854 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
856 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
858 EmfPlusRecordHeader *record;
859 GpStatus stat;
861 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
862 if (stat != Ok)
863 return stat;
865 record->Type = EmfPlusRecordTypeGetDC;
866 record->Flags = 0;
868 METAFILE_WriteRecords(metafile);
871 *hdc = metafile->record_dc;
873 return Ok;
876 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
878 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
880 EmfPlusClear *record;
881 GpStatus stat;
883 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
884 if (stat != Ok)
885 return stat;
887 record->Header.Type = EmfPlusRecordTypeClear;
888 record->Header.Flags = 0;
889 record->Color = color;
891 METAFILE_WriteRecords(metafile);
894 return Ok;
897 static BOOL is_integer_rect(const GpRectF *rect)
899 SHORT x, y, width, height;
900 x = rect->X;
901 y = rect->Y;
902 width = rect->Width;
903 height = rect->Height;
904 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
905 rect->Width != (REAL)width || rect->Height != (REAL)height)
906 return FALSE;
907 return TRUE;
910 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
911 GDIPCONST GpRectF* rects, INT count)
913 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
915 EmfPlusFillRects *record;
916 GpStatus stat;
917 BOOL integer_rects = TRUE;
918 int i;
919 DWORD brushid;
920 int flags = 0;
922 if (brush->bt == BrushTypeSolidColor)
924 flags |= 0x8000;
925 brushid = ((GpSolidFill*)brush)->color;
927 else
929 FIXME("brush serialization not implemented\n");
930 return NotImplemented;
933 for (i=0; i<count; i++)
935 if (!is_integer_rect(&rects[i]))
937 integer_rects = FALSE;
938 break;
942 if (integer_rects)
943 flags |= 0x4000;
945 stat = METAFILE_AllocateRecord(metafile,
946 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
947 (void**)&record);
948 if (stat != Ok)
949 return stat;
951 record->Header.Type = EmfPlusRecordTypeFillRects;
952 record->Header.Flags = flags;
953 record->BrushID = brushid;
954 record->Count = count;
956 if (integer_rects)
958 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
959 for (i=0; i<count; i++)
961 record_rects[i].X = (SHORT)rects[i].X;
962 record_rects[i].Y = (SHORT)rects[i].Y;
963 record_rects[i].Width = (SHORT)rects[i].Width;
964 record_rects[i].Height = (SHORT)rects[i].Height;
967 else
968 memcpy(record+1, rects, sizeof(GpRectF) * count);
970 METAFILE_WriteRecords(metafile);
973 if (metafile->auto_frame)
975 GpPointF corners[4];
976 int i;
978 for (i=0; i<count; i++)
980 corners[0].X = rects[i].X;
981 corners[0].Y = rects[i].Y;
982 corners[1].X = rects[i].X + rects[i].Width;
983 corners[1].Y = rects[i].Y;
984 corners[2].X = rects[i].X;
985 corners[2].Y = rects[i].Y + rects[i].Height;
986 corners[3].X = rects[i].X + rects[i].Width;
987 corners[3].Y = rects[i].Y + rects[i].Height;
989 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
990 CoordinateSpaceWorld, corners, 4);
992 METAFILE_AdjustFrame(metafile, corners, 4);
996 return Ok;
999 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1001 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1003 EmfPlusSetClipRect *record;
1004 GpStatus stat;
1006 stat = METAFILE_AllocateRecord(metafile,
1007 sizeof(EmfPlusSetClipRect),
1008 (void**)&record);
1009 if (stat != Ok)
1010 return stat;
1012 record->Header.Type = EmfPlusRecordTypeSetClipRect;
1013 record->Header.Flags = (mode & 0xf) << 8;
1014 record->ClipRect.X = x;
1015 record->ClipRect.Y = y;
1016 record->ClipRect.Width = width;
1017 record->ClipRect.Height = height;
1019 METAFILE_WriteRecords(metafile);
1022 return Ok;
1025 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1027 EmfPlusObject *object_record;
1028 DWORD size;
1029 GpStatus stat;
1031 *id = -1;
1032 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1033 return Ok;
1035 size = write_region_data(region, NULL);
1036 stat = METAFILE_AllocateRecord(metafile,
1037 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1038 if (stat != Ok) return stat;
1040 *id = METAFILE_AddObjectId(metafile);
1041 object_record->Header.Type = EmfPlusRecordTypeObject;
1042 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1043 write_region_data(region, &object_record->ObjectData.region);
1044 return Ok;
1047 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1049 EmfPlusRecordHeader *record;
1050 DWORD region_id;
1051 GpStatus stat;
1053 if (metafile->metafile_type == MetafileTypeEmf)
1055 FIXME("stub!\n");
1056 return NotImplemented;
1059 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1060 if (stat != Ok) return stat;
1062 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1063 if (stat != Ok) return stat;
1065 record->Type = EmfPlusRecordTypeSetClipRegion;
1066 record->Flags = region_id | mode << 8;
1068 METAFILE_WriteRecords(metafile);
1069 return Ok;
1072 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1074 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1076 EmfPlusSetPageTransform *record;
1077 GpStatus stat;
1079 stat = METAFILE_AllocateRecord(metafile,
1080 sizeof(EmfPlusSetPageTransform),
1081 (void**)&record);
1082 if (stat != Ok)
1083 return stat;
1085 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1086 record->Header.Flags = unit;
1087 record->PageScale = scale;
1089 METAFILE_WriteRecords(metafile);
1092 return Ok;
1095 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1097 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1099 EmfPlusSetWorldTransform *record;
1100 GpStatus stat;
1102 stat = METAFILE_AllocateRecord(metafile,
1103 sizeof(EmfPlusSetWorldTransform),
1104 (void**)&record);
1105 if (stat != Ok)
1106 return stat;
1108 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1109 record->Header.Flags = 0;
1110 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1112 METAFILE_WriteRecords(metafile);
1115 return Ok;
1118 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1120 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1122 EmfPlusScaleWorldTransform *record;
1123 GpStatus stat;
1125 stat = METAFILE_AllocateRecord(metafile,
1126 sizeof(EmfPlusScaleWorldTransform),
1127 (void**)&record);
1128 if (stat != Ok)
1129 return stat;
1131 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1132 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1133 record->Sx = sx;
1134 record->Sy = sy;
1136 METAFILE_WriteRecords(metafile);
1139 return Ok;
1142 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1144 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1146 EmfPlusMultiplyWorldTransform *record;
1147 GpStatus stat;
1149 stat = METAFILE_AllocateRecord(metafile,
1150 sizeof(EmfPlusMultiplyWorldTransform),
1151 (void**)&record);
1152 if (stat != Ok)
1153 return stat;
1155 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1156 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1157 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1159 METAFILE_WriteRecords(metafile);
1162 return Ok;
1165 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1167 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1169 EmfPlusRotateWorldTransform *record;
1170 GpStatus stat;
1172 stat = METAFILE_AllocateRecord(metafile,
1173 sizeof(EmfPlusRotateWorldTransform),
1174 (void**)&record);
1175 if (stat != Ok)
1176 return stat;
1178 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1179 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1180 record->Angle = angle;
1182 METAFILE_WriteRecords(metafile);
1185 return Ok;
1188 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1190 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1192 EmfPlusTranslateWorldTransform *record;
1193 GpStatus stat;
1195 stat = METAFILE_AllocateRecord(metafile,
1196 sizeof(EmfPlusTranslateWorldTransform),
1197 (void**)&record);
1198 if (stat != Ok)
1199 return stat;
1201 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1202 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1203 record->dx = dx;
1204 record->dy = dy;
1206 METAFILE_WriteRecords(metafile);
1209 return Ok;
1212 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1214 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1216 EmfPlusRecordHeader *record;
1217 GpStatus stat;
1219 stat = METAFILE_AllocateRecord(metafile,
1220 sizeof(EmfPlusRecordHeader),
1221 (void**)&record);
1222 if (stat != Ok)
1223 return stat;
1225 record->Type = EmfPlusRecordTypeResetWorldTransform;
1226 record->Flags = 0;
1228 METAFILE_WriteRecords(metafile);
1231 return Ok;
1234 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1235 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1237 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1239 EmfPlusBeginContainer *record;
1240 GpStatus stat;
1242 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1243 if (stat != Ok)
1244 return stat;
1246 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1247 record->Header.Flags = unit & 0xff;
1248 record->DestRect = *dstrect;
1249 record->SrcRect = *srcrect;
1250 record->StackIndex = StackIndex;
1252 METAFILE_WriteRecords(metafile);
1255 return Ok;
1258 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1260 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1262 EmfPlusContainerRecord *record;
1263 GpStatus stat;
1265 stat = METAFILE_AllocateRecord(metafile,
1266 sizeof(EmfPlusContainerRecord),
1267 (void**)&record);
1268 if (stat != Ok)
1269 return stat;
1271 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1272 record->Header.Flags = 0;
1273 record->StackIndex = StackIndex;
1275 METAFILE_WriteRecords(metafile);
1278 return Ok;
1281 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1283 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1285 EmfPlusContainerRecord *record;
1286 GpStatus stat;
1288 stat = METAFILE_AllocateRecord(metafile,
1289 sizeof(EmfPlusContainerRecord),
1290 (void**)&record);
1291 if (stat != Ok)
1292 return stat;
1294 record->Header.Type = EmfPlusRecordTypeEndContainer;
1295 record->Header.Flags = 0;
1296 record->StackIndex = StackIndex;
1298 METAFILE_WriteRecords(metafile);
1301 return Ok;
1304 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1306 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1308 EmfPlusContainerRecord *record;
1309 GpStatus stat;
1311 stat = METAFILE_AllocateRecord(metafile,
1312 sizeof(EmfPlusContainerRecord),
1313 (void**)&record);
1314 if (stat != Ok)
1315 return stat;
1317 record->Header.Type = EmfPlusRecordTypeSave;
1318 record->Header.Flags = 0;
1319 record->StackIndex = StackIndex;
1321 METAFILE_WriteRecords(metafile);
1324 return Ok;
1327 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1329 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1331 EmfPlusContainerRecord *record;
1332 GpStatus stat;
1334 stat = METAFILE_AllocateRecord(metafile,
1335 sizeof(EmfPlusContainerRecord),
1336 (void**)&record);
1337 if (stat != Ok)
1338 return stat;
1340 record->Header.Type = EmfPlusRecordTypeRestore;
1341 record->Header.Flags = 0;
1342 record->StackIndex = StackIndex;
1344 METAFILE_WriteRecords(metafile);
1347 return Ok;
1350 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1352 if (hdc != metafile->record_dc)
1353 return InvalidParameter;
1355 return Ok;
1358 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1360 GpStatus stat;
1362 stat = METAFILE_WriteEndOfFile(metafile);
1363 metafile->record_graphics = NULL;
1365 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1366 metafile->record_dc = NULL;
1368 heap_free(metafile->comment_data);
1369 metafile->comment_data = NULL;
1370 metafile->comment_data_size = 0;
1372 if (stat == Ok)
1374 MetafileHeader header;
1376 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1377 if (stat == Ok && metafile->auto_frame &&
1378 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1380 RECTL bounds_rc, gdi_bounds_rc;
1381 REAL x_scale = 2540.0 / header.DpiX;
1382 REAL y_scale = 2540.0 / header.DpiY;
1383 BYTE* buffer;
1384 UINT buffer_size;
1386 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1387 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1388 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1389 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1391 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1392 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1394 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1395 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1396 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1397 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1400 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1401 buffer = heap_alloc(buffer_size);
1402 if (buffer)
1404 HENHMETAFILE new_hemf;
1406 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1408 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1410 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1412 if (new_hemf)
1414 DeleteEnhMetaFile(metafile->hemf);
1415 metafile->hemf = new_hemf;
1417 else
1418 stat = OutOfMemory;
1420 heap_free(buffer);
1422 else
1423 stat = OutOfMemory;
1425 if (stat == Ok)
1426 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1428 if (stat == Ok)
1430 metafile->bounds.X = header.X;
1431 metafile->bounds.Y = header.Y;
1432 metafile->bounds.Width = header.Width;
1433 metafile->bounds.Height = header.Height;
1437 if (stat == Ok && metafile->record_stream)
1439 BYTE *buffer;
1440 UINT buffer_size;
1442 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1444 buffer = heap_alloc(buffer_size);
1445 if (buffer)
1447 HRESULT hr;
1449 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1451 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1453 if (FAILED(hr))
1454 stat = hresult_to_status(hr);
1456 heap_free(buffer);
1458 else
1459 stat = OutOfMemory;
1462 if (metafile->record_stream)
1464 IStream_Release(metafile->record_stream);
1465 metafile->record_stream = NULL;
1468 return stat;
1471 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1473 TRACE("(%p,%p)\n", metafile, hEmf);
1475 if (!metafile || !hEmf || !metafile->hemf)
1476 return InvalidParameter;
1478 *hEmf = metafile->hemf;
1479 metafile->hemf = NULL;
1481 return Ok;
1484 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1486 const GpRectF *rect;
1487 const GpPointF *pt;
1489 /* This transforms metafile device space to output points. */
1490 rect = &metafile->src_rect;
1491 pt = metafile->playback_points;
1492 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1493 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1494 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1495 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1496 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1497 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1500 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1502 XFORM combined, final;
1504 METAFILE_GetFinalGdiTransform(metafile, &final);
1506 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1508 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1509 SetWorldTransform(metafile->playback_dc, &combined);
1511 return Ok;
1514 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1516 GpStatus stat = Ok;
1518 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1520 if (stat == Ok)
1522 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1524 metafile->gdiworldtransform = identity;
1525 METAFILE_PlaybackUpdateGdiTransform(metafile);
1528 return stat;
1531 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1533 if (metafile->playback_dc)
1535 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1536 metafile->playback_dc = NULL;
1540 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1542 GpStatus stat;
1543 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1544 if (stat == Ok)
1545 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1546 return stat;
1549 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1551 GpMatrix *real_transform;
1552 GpStatus stat;
1554 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1556 if (stat == Ok)
1558 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1560 if (metafile->page_unit != UnitDisplay)
1561 scale *= metafile->page_scale;
1563 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1565 if (stat == Ok)
1566 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1568 if (stat == Ok)
1569 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1571 GdipDeleteMatrix(real_transform);
1574 return stat;
1577 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1579 metafile_free_object_table_entry(metafile, id);
1580 metafile->objtable[id].type = type;
1581 metafile->objtable[id].u.object = object;
1584 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1586 EmfPlusImage *data = (EmfPlusImage *)record_data;
1587 GpStatus status;
1589 *image = NULL;
1591 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1592 return InvalidParameter;
1593 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1595 switch (data->Type)
1597 case ImageDataTypeBitmap:
1599 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1601 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1602 return InvalidParameter;
1603 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1605 switch (bitmapdata->Type)
1607 case BitmapDataTypePixel:
1609 ColorPalette *palette;
1610 BYTE *scan0;
1612 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1614 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1615 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1617 if (data_size <= palette_size)
1618 return InvalidParameter;
1619 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1621 if (data_size < palette_size)
1622 return InvalidParameter;
1623 data_size -= palette_size;
1625 palette = (ColorPalette *)bitmapdata->BitmapData;
1626 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1628 else
1630 palette = NULL;
1631 scan0 = bitmapdata->BitmapData;
1634 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1635 return InvalidParameter;
1637 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1638 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1639 if (status == Ok && palette)
1641 status = GdipSetImagePalette(*image, palette);
1642 if (status != Ok)
1644 GdipDisposeImage(*image);
1645 *image = NULL;
1648 break;
1650 case BitmapDataTypeCompressed:
1652 IWICImagingFactory *factory;
1653 IWICStream *stream;
1654 HRESULT hr;
1656 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1657 return GenericError;
1659 hr = IWICImagingFactory_CreateStream(factory, &stream);
1660 IWICImagingFactory_Release(factory);
1661 if (hr != S_OK)
1662 return GenericError;
1664 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1665 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1666 else
1667 status = GenericError;
1669 IWICStream_Release(stream);
1670 break;
1672 default:
1673 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1674 return InvalidParameter;
1676 break;
1678 default:
1679 FIXME("image type %d not supported.\n", data->Type);
1680 return NotImplemented;
1683 return status;
1686 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1688 EmfPlusPath *data = (EmfPlusPath *)record_data;
1689 GpStatus status;
1690 BYTE *types;
1691 UINT size;
1692 DWORD i;
1694 *path = NULL;
1696 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1697 return InvalidParameter;
1698 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1700 if (data->PathPointFlags & 0x800) /* R */
1702 FIXME("RLE encoded path data is not supported.\n");
1703 return NotImplemented;
1705 else
1707 if (data->PathPointFlags & 0x4000) /* C */
1708 size = sizeof(EmfPlusPoint);
1709 else
1710 size = sizeof(EmfPlusPointF);
1711 size += sizeof(BYTE); /* EmfPlusPathPointType */
1712 size *= data->PathPointCount;
1715 if (data_size < size)
1716 return InvalidParameter;
1718 status = GdipCreatePath(FillModeAlternate, path);
1719 if (status != Ok)
1720 return status;
1722 (*path)->pathdata.Count = data->PathPointCount;
1723 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1724 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1725 (*path)->datalen = (*path)->pathdata.Count;
1727 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1729 GdipDeletePath(*path);
1730 return OutOfMemory;
1733 if (data->PathPointFlags & 0x4000) /* C */
1735 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1736 for (i = 0; i < data->PathPointCount; i++)
1738 (*path)->pathdata.Points[i].X = points[i].X;
1739 (*path)->pathdata.Points[i].Y = points[i].Y;
1741 types = (BYTE *)(points + i);
1743 else
1745 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1746 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1747 types = (BYTE *)(points + data->PathPointCount);
1750 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1752 return Ok;
1755 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1757 const DWORD *type;
1758 GpStatus status;
1760 type = buffer_read(mbuf, sizeof(*type));
1761 if (!type) return Ok;
1763 node->type = *type;
1765 switch (node->type)
1767 case CombineModeReplace:
1768 case CombineModeIntersect:
1769 case CombineModeUnion:
1770 case CombineModeXor:
1771 case CombineModeExclude:
1772 case CombineModeComplement:
1774 region_element *left, *right;
1776 left = heap_alloc_zero(sizeof(*left));
1777 if (!left)
1778 return OutOfMemory;
1780 right = heap_alloc_zero(sizeof(*right));
1781 if (!right)
1783 heap_free(left);
1784 return OutOfMemory;
1787 status = metafile_read_region_node(mbuf, region, left, count);
1788 if (status == Ok)
1790 status = metafile_read_region_node(mbuf, region, right, count);
1791 if (status == Ok)
1793 node->elementdata.combine.left = left;
1794 node->elementdata.combine.right = right;
1795 region->num_children += 2;
1796 return Ok;
1800 heap_free(left);
1801 heap_free(right);
1802 return status;
1804 case RegionDataRect:
1806 const EmfPlusRectF *rect;
1808 rect = buffer_read(mbuf, sizeof(*rect));
1809 if (!rect)
1810 return InvalidParameter;
1812 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1813 *count += 1;
1814 return Ok;
1816 case RegionDataPath:
1818 const BYTE *path_data;
1819 const UINT *data_size;
1820 GpPath *path;
1822 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1823 if (!data_size)
1824 return InvalidParameter;
1826 path_data = buffer_read(mbuf, *data_size);
1827 if (!path_data)
1828 return InvalidParameter;
1830 status = metafile_deserialize_path(path_data, *data_size, &path);
1831 if (status == Ok)
1833 node->elementdata.path = path;
1834 *count += 1;
1836 return Ok;
1838 case RegionDataEmptyRect:
1839 case RegionDataInfiniteRect:
1840 *count += 1;
1841 return Ok;
1842 default:
1843 FIXME("element type %#x is not supported\n", *type);
1844 break;
1847 return InvalidParameter;
1850 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1852 struct memory_buffer mbuf;
1853 GpStatus status;
1854 UINT count;
1856 *region = NULL;
1858 init_memory_buffer(&mbuf, record_data, data_size);
1860 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
1861 return InvalidParameter;
1863 status = GdipCreateRegion(region);
1864 if (status != Ok)
1865 return status;
1867 count = 0;
1868 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
1869 if (status == Ok && !count)
1870 status = InvalidParameter;
1872 if (status != Ok)
1874 GdipDeleteRegion(*region);
1875 *region = NULL;
1878 return status;
1881 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
1883 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
1884 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
1885 GpStatus status;
1887 *brush = NULL;
1889 if (data_size < header_size)
1890 return InvalidParameter;
1892 switch (data->Type)
1894 case BrushTypeSolidColor:
1895 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
1896 return InvalidParameter;
1898 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
1899 break;
1900 case BrushTypeHatchFill:
1901 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
1902 return InvalidParameter;
1904 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
1905 data->BrushData.hatch.BackColor, (GpHatch **)brush);
1906 break;
1907 case BrushTypeTextureFill:
1909 UINT offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
1910 EmfPlusTransformMatrix *transform = NULL;
1911 DWORD brushflags;
1912 GpImage *image;
1914 if (data_size <= offset)
1915 return InvalidParameter;
1917 brushflags = data->BrushData.texture.BrushDataFlags;
1918 if (brushflags & BrushDataTransform)
1920 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
1921 return InvalidParameter;
1922 transform = (EmfPlusTransformMatrix *)(record_data + offset);
1923 offset += sizeof(EmfPlusTransformMatrix);
1926 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
1927 if (status != Ok)
1928 return status;
1930 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
1931 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
1932 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
1934 GdipDisposeImage(image);
1935 break;
1937 default:
1938 FIXME("brush type %u is not supported.\n", data->Type);
1939 return NotImplemented;
1942 return status;
1945 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
1947 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
1948 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
1950 if (data_size <= offset)
1951 return InvalidParameter;
1953 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
1954 if (data_size <= offset)
1955 return InvalidParameter;
1957 if (pendata->PenDataFlags & PenDataTransform)
1958 offset += sizeof(EmfPlusTransformMatrix);
1960 if (pendata->PenDataFlags & PenDataStartCap)
1961 offset += sizeof(DWORD);
1963 if (pendata->PenDataFlags & PenDataEndCap)
1964 offset += sizeof(DWORD);
1966 if (pendata->PenDataFlags & PenDataJoin)
1967 offset += sizeof(DWORD);
1969 if (pendata->PenDataFlags & PenDataMiterLimit)
1970 offset += sizeof(REAL);
1972 if (pendata->PenDataFlags & PenDataLineStyle)
1973 offset += sizeof(DWORD);
1975 if (pendata->PenDataFlags & PenDataDashedLineCap)
1976 offset += sizeof(DWORD);
1978 if (pendata->PenDataFlags & PenDataDashedLineOffset)
1979 offset += sizeof(REAL);
1981 if (pendata->PenDataFlags & PenDataDashedLine)
1983 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
1985 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
1986 if (data_size <= offset)
1987 return InvalidParameter;
1989 offset += dashedline->DashedLineDataSize * sizeof(float);
1992 if (pendata->PenDataFlags & PenDataNonCenter)
1993 offset += sizeof(DWORD);
1995 if (pendata->PenDataFlags & PenDataCompoundLine)
1997 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
1999 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2000 if (data_size <= offset)
2001 return InvalidParameter;
2003 offset += compoundline->CompoundLineDataSize * sizeof(float);
2006 if (pendata->PenDataFlags & PenDataCustomStartCap)
2008 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2010 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2011 if (data_size <= offset)
2012 return InvalidParameter;
2014 offset += startcap->CustomStartCapSize;
2017 if (pendata->PenDataFlags & PenDataCustomEndCap)
2019 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2021 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2022 if (data_size <= offset)
2023 return InvalidParameter;
2025 offset += endcap->CustomEndCapSize;
2028 *ret = offset;
2029 return Ok;
2032 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2034 BYTE type = (flags >> 8) & 0xff;
2035 BYTE id = flags & 0xff;
2036 void *object = NULL;
2037 GpStatus status;
2039 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2040 return InvalidParameter;
2042 switch (type)
2044 case ObjectTypeBrush:
2045 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2046 break;
2047 case ObjectTypePen:
2049 EmfPlusPen *data = (EmfPlusPen *)record_data;
2050 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2051 GpBrush *brush;
2052 DWORD offset;
2053 GpPen *pen;
2055 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2056 if (status != Ok)
2057 return status;
2059 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2060 if (status != Ok)
2061 return status;
2063 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2064 GdipDeleteBrush(brush);
2065 if (status != Ok)
2066 return status;
2068 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2070 if (pendata->PenDataFlags & PenDataTransform)
2072 FIXME("PenDataTransform is not supported.\n");
2073 offset += sizeof(EmfPlusTransformMatrix);
2076 if (pendata->PenDataFlags & PenDataStartCap)
2078 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2079 goto penfailed;
2080 offset += sizeof(DWORD);
2083 if (pendata->PenDataFlags & PenDataEndCap)
2085 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2086 goto penfailed;
2087 offset += sizeof(DWORD);
2090 if (pendata->PenDataFlags & PenDataJoin)
2092 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2093 goto penfailed;
2094 offset += sizeof(DWORD);
2097 if (pendata->PenDataFlags & PenDataMiterLimit)
2099 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2100 goto penfailed;
2101 offset += sizeof(REAL);
2104 if (pendata->PenDataFlags & PenDataLineStyle)
2106 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2107 goto penfailed;
2108 offset += sizeof(DWORD);
2111 if (pendata->PenDataFlags & PenDataDashedLineCap)
2113 FIXME("PenDataDashedLineCap is not supported.\n");
2114 offset += sizeof(DWORD);
2117 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2119 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2120 goto penfailed;
2121 offset += sizeof(REAL);
2124 if (pendata->PenDataFlags & PenDataDashedLine)
2126 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2127 FIXME("PenDataDashedLine is not supported.\n");
2128 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2131 if (pendata->PenDataFlags & PenDataNonCenter)
2133 FIXME("PenDataNonCenter is not supported.\n");
2134 offset += sizeof(DWORD);
2137 if (pendata->PenDataFlags & PenDataCompoundLine)
2139 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2140 FIXME("PenDataCompundLine is not supported.\n");
2141 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2144 if (pendata->PenDataFlags & PenDataCustomStartCap)
2146 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2147 FIXME("PenDataCustomStartCap is not supported.\n");
2148 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2151 if (pendata->PenDataFlags & PenDataCustomEndCap)
2153 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2154 FIXME("PenDataCustomEndCap is not supported.\n");
2155 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2158 object = pen;
2159 break;
2161 penfailed:
2162 GdipDeletePen(pen);
2163 return status;
2165 case ObjectTypePath:
2166 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2167 break;
2168 case ObjectTypeRegion:
2169 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2170 break;
2171 case ObjectTypeImage:
2172 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2173 break;
2174 case ObjectTypeFont:
2176 EmfPlusFont *data = (EmfPlusFont *)record_data;
2177 GpFontFamily *family;
2178 WCHAR *familyname;
2180 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2181 return InvalidParameter;
2182 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2184 if (data_size < data->Length * sizeof(WCHAR))
2185 return InvalidParameter;
2187 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2188 return OutOfMemory;
2190 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2191 familyname[data->Length] = 0;
2193 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2194 GdipFree(familyname);
2195 if (status != Ok)
2196 return InvalidParameter;
2198 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2199 GdipDeleteFontFamily(family);
2200 break;
2202 case ObjectTypeImageAttributes:
2204 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2205 GpImageAttributes *attributes = NULL;
2207 if (data_size != sizeof(*data))
2208 return InvalidParameter;
2210 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2211 return status;
2213 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2214 !!data->ObjectClamp);
2215 if (status == Ok)
2216 object = attributes;
2217 else
2218 GdipDisposeImageAttributes(attributes);
2219 break;
2221 default:
2222 FIXME("not implemented for object type %d.\n", type);
2223 return NotImplemented;
2226 if (status == Ok)
2227 metafile_set_object_table_entry(metafile, id, type, object);
2229 return status;
2232 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2234 GpMatrix world_to_device;
2236 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2238 GdipTransformRegion(region, &world_to_device);
2239 GdipCombineRegionRegion(metafile->clip, region, mode);
2241 return METAFILE_PlaybackUpdateClip(metafile);
2244 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2245 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2247 GpStatus stat;
2248 GpMetafile *real_metafile = (GpMetafile*)metafile;
2250 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2252 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2253 return InvalidParameter;
2255 if (recordType >= 1 && recordType <= 0x7a)
2257 /* regular EMF record */
2258 if (metafile->playback_dc)
2260 switch (recordType)
2262 case EMR_SETMAPMODE:
2263 case EMR_SAVEDC:
2264 case EMR_RESTOREDC:
2265 case EMR_SETWINDOWORGEX:
2266 case EMR_SETWINDOWEXTEX:
2267 case EMR_SETVIEWPORTORGEX:
2268 case EMR_SETVIEWPORTEXTEX:
2269 case EMR_SCALEVIEWPORTEXTEX:
2270 case EMR_SCALEWINDOWEXTEX:
2271 case EMR_MODIFYWORLDTRANSFORM:
2272 FIXME("not implemented for record type %x\n", recordType);
2273 break;
2274 case EMR_SETWORLDTRANSFORM:
2276 const XFORM* xform = (void*)data;
2277 real_metafile->gdiworldtransform = *xform;
2278 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
2279 break;
2281 case EMR_EXTSELECTCLIPRGN:
2283 DWORD rgndatasize = *(DWORD*)data;
2284 DWORD mode = *(DWORD*)(data + 4);
2285 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
2286 HRGN hrgn = NULL;
2288 if (dataSize > 8)
2290 XFORM final;
2292 METAFILE_GetFinalGdiTransform(metafile, &final);
2294 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
2297 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
2299 DeleteObject(hrgn);
2301 return Ok;
2303 default:
2305 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2307 if (record)
2309 record->iType = recordType;
2310 record->nSize = dataSize + 8;
2311 memcpy(record->dParm, data, dataSize);
2313 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2314 record, metafile->handle_count) == 0)
2315 ERR("PlayEnhMetaFileRecord failed\n");
2317 heap_free(record);
2319 else
2320 return OutOfMemory;
2322 break;
2327 else
2329 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2331 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2333 switch(recordType)
2335 case EmfPlusRecordTypeHeader:
2336 case EmfPlusRecordTypeEndOfFile:
2337 break;
2338 case EmfPlusRecordTypeGetDC:
2339 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2340 break;
2341 case EmfPlusRecordTypeClear:
2343 EmfPlusClear *record = (EmfPlusClear*)header;
2345 if (dataSize != sizeof(record->Color))
2346 return InvalidParameter;
2348 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2350 case EmfPlusRecordTypeFillRects:
2352 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2353 GpBrush *brush, *temp_brush=NULL;
2354 GpRectF *rects, *temp_rects=NULL;
2356 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2357 return InvalidParameter;
2359 if (flags & 0x4000)
2361 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2362 return InvalidParameter;
2364 else
2366 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2367 return InvalidParameter;
2370 if (flags & 0x8000)
2372 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2373 brush = temp_brush;
2375 else
2377 if (record->BrushID >= EmfPlusObjectTableSize ||
2378 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2379 return InvalidParameter;
2381 brush = real_metafile->objtable[record->BrushID].u.brush;
2382 stat = Ok;
2385 if (stat == Ok)
2387 if (flags & 0x4000)
2389 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2390 int i;
2392 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2393 if (rects)
2395 for (i=0; i<record->Count; i++)
2397 rects[i].X = int_rects[i].X;
2398 rects[i].Y = int_rects[i].Y;
2399 rects[i].Width = int_rects[i].Width;
2400 rects[i].Height = int_rects[i].Height;
2403 else
2404 stat = OutOfMemory;
2406 else
2407 rects = (GpRectF*)(record+1);
2410 if (stat == Ok)
2412 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2415 GdipDeleteBrush(temp_brush);
2416 heap_free(temp_rects);
2418 return stat;
2420 case EmfPlusRecordTypeSetClipRect:
2422 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2423 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2424 GpRegion *region;
2426 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2427 return InvalidParameter;
2429 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2431 if (stat == Ok)
2433 stat = metafile_set_clip_region(real_metafile, region, mode);
2434 GdipDeleteRegion(region);
2437 return stat;
2439 case EmfPlusRecordTypeSetClipRegion:
2441 CombineMode mode = (flags >> 8) & 0xf;
2442 BYTE regionid = flags & 0xff;
2443 GpRegion *region;
2445 if (dataSize != 0)
2446 return InvalidParameter;
2448 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2449 return InvalidParameter;
2451 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2452 if (stat == Ok)
2454 stat = metafile_set_clip_region(real_metafile, region, mode);
2455 GdipDeleteRegion(region);
2458 return stat;
2460 case EmfPlusRecordTypeSetClipPath:
2462 CombineMode mode = (flags >> 8) & 0xf;
2463 BYTE pathid = flags & 0xff;
2464 GpRegion *region;
2466 if (dataSize != 0)
2467 return InvalidParameter;
2469 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2470 return InvalidParameter;
2472 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2473 if (stat == Ok)
2475 stat = metafile_set_clip_region(real_metafile, region, mode);
2476 GdipDeleteRegion(region);
2479 return stat;
2481 case EmfPlusRecordTypeSetPageTransform:
2483 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2484 GpUnit unit = (GpUnit)flags;
2486 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2487 return InvalidParameter;
2489 real_metafile->page_unit = unit;
2490 real_metafile->page_scale = record->PageScale;
2492 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2494 case EmfPlusRecordTypeSetWorldTransform:
2496 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2498 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2499 return InvalidParameter;
2501 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2503 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2505 case EmfPlusRecordTypeScaleWorldTransform:
2507 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2508 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2510 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2511 return InvalidParameter;
2513 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2515 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2517 case EmfPlusRecordTypeMultiplyWorldTransform:
2519 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2520 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2521 GpMatrix matrix;
2523 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2524 return InvalidParameter;
2526 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2528 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2530 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2532 case EmfPlusRecordTypeRotateWorldTransform:
2534 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2535 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2537 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2538 return InvalidParameter;
2540 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2542 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2544 case EmfPlusRecordTypeTranslateWorldTransform:
2546 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2547 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2549 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2550 return InvalidParameter;
2552 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2554 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2556 case EmfPlusRecordTypeResetWorldTransform:
2558 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2560 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2562 case EmfPlusRecordTypeBeginContainer:
2564 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2565 container* cont;
2566 GpUnit unit;
2567 REAL scale_x, scale_y;
2568 GpRectF scaled_srcrect;
2569 GpMatrix transform;
2571 cont = heap_alloc_zero(sizeof(*cont));
2572 if (!cont)
2573 return OutOfMemory;
2575 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2576 if (stat != Ok)
2578 heap_free(cont);
2579 return stat;
2582 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2584 if (stat != Ok)
2586 GdipDeleteRegion(cont->clip);
2587 heap_free(cont);
2588 return stat;
2591 cont->id = record->StackIndex;
2592 cont->type = BEGIN_CONTAINER;
2593 cont->world_transform = *metafile->world_transform;
2594 cont->page_unit = metafile->page_unit;
2595 cont->page_scale = metafile->page_scale;
2596 list_add_head(&real_metafile->containers, &cont->entry);
2598 unit = record->Header.Flags & 0xff;
2600 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
2601 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
2603 scaled_srcrect.X = scale_x * record->SrcRect.X;
2604 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2605 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2606 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2608 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2609 transform.matrix[1] = 0.0;
2610 transform.matrix[2] = 0.0;
2611 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2612 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2613 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2615 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2617 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2619 case EmfPlusRecordTypeBeginContainerNoParams:
2620 case EmfPlusRecordTypeSave:
2622 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2623 container* cont;
2625 cont = heap_alloc_zero(sizeof(*cont));
2626 if (!cont)
2627 return OutOfMemory;
2629 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2630 if (stat != Ok)
2632 heap_free(cont);
2633 return stat;
2636 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2637 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2638 else
2639 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2641 if (stat != Ok)
2643 GdipDeleteRegion(cont->clip);
2644 heap_free(cont);
2645 return stat;
2648 cont->id = record->StackIndex;
2649 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2650 cont->type = BEGIN_CONTAINER;
2651 else
2652 cont->type = SAVE_GRAPHICS;
2653 cont->world_transform = *metafile->world_transform;
2654 cont->page_unit = metafile->page_unit;
2655 cont->page_scale = metafile->page_scale;
2656 list_add_head(&real_metafile->containers, &cont->entry);
2658 break;
2660 case EmfPlusRecordTypeEndContainer:
2661 case EmfPlusRecordTypeRestore:
2663 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2664 container* cont;
2665 enum container_type type;
2666 BOOL found=FALSE;
2668 if (recordType == EmfPlusRecordTypeEndContainer)
2669 type = BEGIN_CONTAINER;
2670 else
2671 type = SAVE_GRAPHICS;
2673 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2675 if (cont->id == record->StackIndex && cont->type == type)
2677 found = TRUE;
2678 break;
2682 if (found)
2684 container* cont2;
2686 /* pop any newer items on the stack */
2687 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2689 list_remove(&cont2->entry);
2690 GdipDeleteRegion(cont2->clip);
2691 heap_free(cont2);
2694 if (type == BEGIN_CONTAINER)
2695 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2696 else
2697 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2699 *real_metafile->world_transform = cont->world_transform;
2700 real_metafile->page_unit = cont->page_unit;
2701 real_metafile->page_scale = cont->page_scale;
2702 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2704 list_remove(&cont->entry);
2705 GdipDeleteRegion(cont->clip);
2706 heap_free(cont);
2709 break;
2711 case EmfPlusRecordTypeSetPixelOffsetMode:
2713 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2715 case EmfPlusRecordTypeSetCompositingQuality:
2717 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2719 case EmfPlusRecordTypeSetInterpolationMode:
2721 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2723 case EmfPlusRecordTypeSetTextRenderingHint:
2725 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2727 case EmfPlusRecordTypeSetAntiAliasMode:
2729 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2731 case EmfPlusRecordTypeObject:
2733 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2735 case EmfPlusRecordTypeDrawImage:
2737 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2738 BYTE image = flags & 0xff;
2739 GpPointF points[3];
2741 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2742 return InvalidParameter;
2744 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2745 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2746 return InvalidParameter;
2748 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2749 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2750 return InvalidParameter;
2752 if (flags & 0x4000) /* C */
2754 points[0].X = draw->RectData.rect.X;
2755 points[0].Y = draw->RectData.rect.Y;
2756 points[1].X = points[0].X + draw->RectData.rect.Width;
2757 points[1].Y = points[0].Y;
2758 points[2].X = points[1].X;
2759 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2761 else
2763 points[0].X = draw->RectData.rectF.X;
2764 points[0].Y = draw->RectData.rectF.Y;
2765 points[1].X = points[0].X + draw->RectData.rectF.Width;
2766 points[1].Y = points[0].Y;
2767 points[2].X = points[1].X;
2768 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2771 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2772 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2773 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2775 case EmfPlusRecordTypeDrawImagePoints:
2777 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2778 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2779 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2780 BYTE image = flags & 0xff;
2781 GpPointF points[3];
2782 unsigned int i;
2783 UINT size;
2785 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2786 return InvalidParameter;
2788 if (dataSize <= fixed_part_size)
2789 return InvalidParameter;
2790 dataSize -= fixed_part_size;
2792 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2793 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2794 return InvalidParameter;
2796 if (draw->count != 3)
2797 return InvalidParameter;
2799 if ((flags >> 13) & 1) /* E */
2800 FIXME("image effects are not supported.\n");
2802 if ((flags >> 11) & 1) /* P */
2803 size = sizeof(EmfPlusPointR7) * draw->count;
2804 else if ((flags >> 14) & 1) /* C */
2805 size = sizeof(EmfPlusPoint) * draw->count;
2806 else
2807 size = sizeof(EmfPlusPointF) * draw->count;
2809 if (dataSize != size)
2810 return InvalidParameter;
2812 if ((flags >> 11) & 1) /* P */
2814 points[0].X = draw->PointData.pointsR[0].X;
2815 points[0].Y = draw->PointData.pointsR[0].Y;
2816 for (i = 1; i < 3; i++)
2818 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
2819 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
2822 else if ((flags >> 14) & 1) /* C */
2824 for (i = 0; i < 3; i++)
2826 points[i].X = draw->PointData.points[i].X;
2827 points[i].Y = draw->PointData.points[i].Y;
2830 else
2831 memcpy(points, draw->PointData.pointsF, sizeof(points));
2833 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2834 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2835 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2837 case EmfPlusRecordTypeFillPath:
2839 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
2840 GpSolidFill *solidfill = NULL;
2841 BYTE path = flags & 0xff;
2842 GpBrush *brush;
2844 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
2845 return InvalidParameter;
2847 if (dataSize != sizeof(fill->data.BrushId))
2848 return InvalidParameter;
2850 if (flags & 0x8000)
2852 stat = GdipCreateSolidFill(fill->data.Color, (GpSolidFill **)&solidfill);
2853 if (stat != Ok)
2854 return stat;
2855 brush = (GpBrush *)solidfill;
2857 else
2859 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
2860 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
2861 return InvalidParameter;
2863 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
2866 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
2867 GdipDeleteBrush((GpBrush *)solidfill);
2868 return stat;
2870 case EmfPlusRecordTypeDrawPath:
2872 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
2873 BYTE path = flags & 0xff;
2875 if (dataSize != sizeof(draw->PenId))
2876 return InvalidParameter;
2878 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
2879 return InvalidParameter;
2881 if (real_metafile->objtable[path].type != ObjectTypePath ||
2882 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
2883 return InvalidParameter;
2885 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
2886 real_metafile->objtable[path].u.path);
2888 case EmfPlusRecordTypeDrawPie:
2890 EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
2891 BYTE pen = flags & 0xff;
2893 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
2894 return InvalidParameter;
2896 if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
2897 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2898 return InvalidParameter;
2900 if (flags & 0x4000) /* C */
2901 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
2902 draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
2903 draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
2904 else
2905 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
2906 draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
2907 draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
2909 case EmfPlusRecordTypeDrawRects:
2911 EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
2912 BYTE pen = flags & 0xff;
2913 GpRectF *rects = NULL;
2915 if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
2916 return InvalidParameter;
2918 if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
2919 return InvalidParameter;
2920 dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
2922 if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2923 return InvalidParameter;
2925 if (flags & 0x4000)
2927 DWORD i;
2929 rects = GdipAlloc(draw->Count * sizeof(*rects));
2930 if (!rects)
2931 return OutOfMemory;
2933 for (i = 0; i < draw->Count; i++)
2935 rects[i].X = draw->RectData.rect[i].X;
2936 rects[i].Y = draw->RectData.rect[i].Y;
2937 rects[i].Width = draw->RectData.rect[i].Width;
2938 rects[i].Height = draw->RectData.rect[i].Height;
2942 stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
2943 rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
2944 GdipFree(rects);
2945 return stat;
2947 default:
2948 FIXME("Not implemented for record type %x\n", recordType);
2949 return NotImplemented;
2953 return Ok;
2956 struct enum_metafile_data
2958 EnumerateMetafileProc callback;
2959 void *callback_data;
2960 GpMetafile *metafile;
2963 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
2964 int nObj, LPARAM lpData)
2966 BOOL ret;
2967 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
2968 const BYTE* pStr;
2970 data->metafile->handle_table = lpHTable;
2971 data->metafile->handle_count = nObj;
2973 /* First check for an EMF+ record. */
2974 if (lpEMFR->iType == EMR_GDICOMMENT)
2976 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
2978 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
2980 int offset = 4;
2982 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
2984 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
2986 if (record->DataSize)
2987 pStr = (const BYTE*)(record+1);
2988 else
2989 pStr = NULL;
2991 ret = data->callback(record->Type, record->Flags, record->DataSize,
2992 pStr, data->callback_data);
2994 if (!ret)
2995 return 0;
2997 offset += record->Size;
3000 return 1;
3004 if (lpEMFR->nSize != 8)
3005 pStr = (const BYTE*)lpEMFR->dParm;
3006 else
3007 pStr = NULL;
3009 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3010 pStr, data->callback_data);
3013 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3014 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3015 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3016 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3018 struct enum_metafile_data data;
3019 GpStatus stat;
3020 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3021 GraphicsContainer state;
3022 GpPath *dst_path;
3024 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3025 destPoints, count, srcRect, srcUnit, callback, callbackData,
3026 imageAttributes);
3028 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3029 return InvalidParameter;
3031 if (!metafile->hemf)
3032 return InvalidParameter;
3034 if (metafile->playback_graphics)
3035 return ObjectBusy;
3037 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3038 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3039 debugstr_pointf(&destPoints[2]));
3041 data.callback = callback;
3042 data.callback_data = callbackData;
3043 data.metafile = real_metafile;
3045 real_metafile->playback_graphics = graphics;
3046 real_metafile->playback_dc = NULL;
3047 real_metafile->src_rect = *srcRect;
3049 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3050 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3052 if (stat == Ok)
3053 stat = GdipBeginContainer2(graphics, &state);
3055 if (stat == Ok)
3057 stat = GdipSetPageScale(graphics, 1.0);
3059 if (stat == Ok)
3060 stat = GdipSetPageUnit(graphics, UnitPixel);
3062 if (stat == Ok)
3063 stat = GdipResetWorldTransform(graphics);
3065 if (stat == Ok)
3066 stat = GdipCreateRegion(&real_metafile->base_clip);
3068 if (stat == Ok)
3069 stat = GdipGetClip(graphics, real_metafile->base_clip);
3071 if (stat == Ok)
3072 stat = GdipCreateRegion(&real_metafile->clip);
3074 if (stat == Ok)
3075 stat = GdipCreatePath(FillModeAlternate, &dst_path);
3077 if (stat == Ok)
3079 GpPointF clip_points[4];
3081 clip_points[0] = real_metafile->playback_points[0];
3082 clip_points[1] = real_metafile->playback_points[1];
3083 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3084 - real_metafile->playback_points[0].X;
3085 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3086 - real_metafile->playback_points[0].Y;
3087 clip_points[3] = real_metafile->playback_points[2];
3089 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3091 if (stat == Ok)
3092 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3094 GdipDeletePath(dst_path);
3097 if (stat == Ok)
3098 stat = GdipCreateMatrix(&real_metafile->world_transform);
3100 if (stat == Ok)
3102 real_metafile->page_unit = UnitDisplay;
3103 real_metafile->page_scale = 1.0;
3104 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3107 if (stat == Ok)
3109 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3112 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
3113 metafile->metafile_type == MetafileTypeWmfPlaceable ||
3114 metafile->metafile_type == MetafileTypeWmf))
3115 stat = METAFILE_PlaybackGetDC(real_metafile);
3117 if (stat == Ok)
3118 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
3120 METAFILE_PlaybackReleaseDC(real_metafile);
3122 GdipDeleteMatrix(real_metafile->world_transform);
3123 real_metafile->world_transform = NULL;
3125 GdipDeleteRegion(real_metafile->base_clip);
3126 real_metafile->base_clip = NULL;
3128 GdipDeleteRegion(real_metafile->clip);
3129 real_metafile->clip = NULL;
3131 while (list_head(&real_metafile->containers))
3133 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3134 list_remove(&cont->entry);
3135 GdipDeleteRegion(cont->clip);
3136 heap_free(cont);
3139 GdipEndContainer(graphics, state);
3142 real_metafile->playback_graphics = NULL;
3144 return stat;
3147 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3148 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3149 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3151 GpPointF points[3];
3153 if (!graphics || !metafile || !dest) return InvalidParameter;
3155 points[0].X = points[2].X = dest->X;
3156 points[0].Y = points[1].Y = dest->Y;
3157 points[1].X = dest->X + dest->Width;
3158 points[2].Y = dest->Y + dest->Height;
3160 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3161 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3164 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3165 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3166 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3168 GpRectF destf;
3170 if (!graphics || !metafile || !dest) return InvalidParameter;
3172 destf.X = dest->X;
3173 destf.Y = dest->Y;
3174 destf.Width = dest->Width;
3175 destf.Height = dest->Height;
3177 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3180 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3181 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3182 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3184 GpRectF destf;
3186 if (!graphics || !metafile || !dest) return InvalidParameter;
3188 destf.X = dest->X;
3189 destf.Y = dest->Y;
3190 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
3191 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
3193 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3196 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3197 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3198 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3200 GpPointF ptf;
3202 if (!graphics || !metafile || !dest) return InvalidParameter;
3204 ptf.X = dest->X;
3205 ptf.Y = dest->Y;
3207 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3210 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3211 MetafileHeader * header)
3213 GpStatus status;
3215 TRACE("(%p, %p)\n", metafile, header);
3217 if(!metafile || !header)
3218 return InvalidParameter;
3220 if (metafile->hemf)
3222 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3223 if (status != Ok) return status;
3225 else
3227 memset(header, 0, sizeof(*header));
3228 header->Version = VERSION_MAGIC2;
3231 header->Type = metafile->metafile_type;
3232 header->DpiX = metafile->image.xres;
3233 header->DpiY = metafile->image.yres;
3234 header->Width = gdip_round(metafile->bounds.Width);
3235 header->Height = gdip_round(metafile->bounds.Height);
3237 return Ok;
3240 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3241 int nObj, LPARAM lpData)
3243 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3245 if (lpEMFR->iType == EMR_GDICOMMENT)
3247 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3249 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3251 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3253 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3254 header->Type == EmfPlusRecordTypeHeader)
3256 memcpy(dst_header, header, sizeof(*dst_header));
3260 else if (lpEMFR->iType == EMR_HEADER)
3261 return TRUE;
3263 return FALSE;
3266 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3267 MetafileHeader *header)
3269 ENHMETAHEADER3 emfheader;
3270 EmfPlusHeader emfplusheader;
3271 MetafileType metafile_type;
3273 TRACE("(%p,%p)\n", hemf, header);
3275 if(!hemf || !header)
3276 return InvalidParameter;
3278 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3279 return GenericError;
3281 emfplusheader.Header.Type = 0;
3283 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3285 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3287 if ((emfplusheader.Header.Flags & 1) == 1)
3288 metafile_type = MetafileTypeEmfPlusDual;
3289 else
3290 metafile_type = MetafileTypeEmfPlusOnly;
3292 else
3293 metafile_type = MetafileTypeEmf;
3295 header->Type = metafile_type;
3296 header->Size = emfheader.nBytes;
3297 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3298 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3299 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3300 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3301 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3302 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3303 header->u.EmfHeader = emfheader;
3305 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3307 header->Version = emfplusheader.Version;
3308 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3309 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3310 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3311 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3313 else
3315 header->Version = emfheader.nVersion;
3316 header->EmfPlusFlags = 0;
3317 header->EmfPlusHeaderSize = 0;
3318 header->LogicalDpiX = 0;
3319 header->LogicalDpiY = 0;
3322 return Ok;
3325 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3326 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3328 GpStatus status;
3329 GpMetafile *metafile;
3331 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3333 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3334 if (status == Ok)
3336 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3337 GdipDisposeImage(&metafile->image);
3339 return status;
3342 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3343 MetafileHeader *header)
3345 GpStatus status;
3346 GpMetafile *metafile;
3348 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3350 if (!filename || !header)
3351 return InvalidParameter;
3353 status = GdipCreateMetafileFromFile(filename, &metafile);
3354 if (status == Ok)
3356 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3357 GdipDisposeImage(&metafile->image);
3359 return status;
3362 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3363 MetafileHeader *header)
3365 GpStatus status;
3366 GpMetafile *metafile;
3368 TRACE("(%p,%p)\n", stream, header);
3370 if (!stream || !header)
3371 return InvalidParameter;
3373 status = GdipCreateMetafileFromStream(stream, &metafile);
3374 if (status == Ok)
3376 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3377 GdipDisposeImage(&metafile->image);
3379 return status;
3382 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3383 GpMetafile **metafile)
3385 GpStatus stat;
3386 MetafileHeader header;
3388 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3390 if(!hemf || !metafile)
3391 return InvalidParameter;
3393 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3394 if (stat != Ok)
3395 return stat;
3397 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3398 if (!*metafile)
3399 return OutOfMemory;
3401 (*metafile)->image.type = ImageTypeMetafile;
3402 (*metafile)->image.format = ImageFormatEMF;
3403 (*metafile)->image.frame_count = 1;
3404 (*metafile)->image.xres = header.DpiX;
3405 (*metafile)->image.yres = header.DpiY;
3406 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3407 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3408 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3409 / 2540.0 * header.DpiX;
3410 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3411 / 2540.0 * header.DpiY;
3412 (*metafile)->unit = UnitPixel;
3413 (*metafile)->metafile_type = header.Type;
3414 (*metafile)->hemf = hemf;
3415 (*metafile)->preserve_hemf = !delete;
3416 list_init(&(*metafile)->containers);
3418 TRACE("<-- %p\n", *metafile);
3420 return Ok;
3423 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3424 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3426 UINT read;
3427 BYTE *copy;
3428 HENHMETAFILE hemf;
3429 GpStatus retval = Ok;
3431 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3433 if(!hwmf || !metafile)
3434 return InvalidParameter;
3436 *metafile = NULL;
3437 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3438 if(!read)
3439 return GenericError;
3440 copy = heap_alloc_zero(read);
3441 GetMetaFileBitsEx(hwmf, read, copy);
3443 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3444 heap_free(copy);
3446 /* FIXME: We should store and use hwmf instead of converting to hemf */
3447 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
3449 if (retval == Ok)
3451 if (placeable)
3453 (*metafile)->image.xres = (REAL)placeable->Inch;
3454 (*metafile)->image.yres = (REAL)placeable->Inch;
3455 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
3456 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
3457 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
3458 placeable->BoundingBox.Left);
3459 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
3460 placeable->BoundingBox.Top);
3461 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
3463 else
3464 (*metafile)->metafile_type = MetafileTypeWmf;
3465 (*metafile)->image.format = ImageFormatWMF;
3467 if (delete) DeleteMetaFile(hwmf);
3469 else
3470 DeleteEnhMetaFile(hemf);
3471 return retval;
3474 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
3475 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3477 HMETAFILE hmf;
3478 HENHMETAFILE emf;
3480 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
3482 hmf = GetMetaFileW(file);
3483 if(hmf)
3484 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
3486 emf = GetEnhMetaFileW(file);
3487 if(emf)
3488 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
3490 return GenericError;
3493 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
3494 GpMetafile **metafile)
3496 GpStatus status;
3497 IStream *stream;
3499 TRACE("(%p, %p)\n", file, metafile);
3501 if (!file || !metafile) return InvalidParameter;
3503 *metafile = NULL;
3505 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
3506 if (status == Ok)
3508 status = GdipCreateMetafileFromStream(stream, metafile);
3509 IStream_Release(stream);
3511 return status;
3514 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
3515 GpMetafile **metafile)
3517 GpStatus stat;
3519 TRACE("%p %p\n", stream, metafile);
3521 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
3522 if (stat != Ok) return stat;
3524 if ((*metafile)->image.type != ImageTypeMetafile)
3526 GdipDisposeImage(&(*metafile)->image);
3527 *metafile = NULL;
3528 return GenericError;
3531 return Ok;
3534 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3535 UINT limitDpi)
3537 TRACE("(%p,%u)\n", metafile, limitDpi);
3539 return Ok;
3542 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
3543 GpMetafile* metafile, BOOL* succ, EmfType emfType,
3544 const WCHAR* description, GpMetafile** out_metafile)
3546 static int calls;
3548 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
3549 debugstr_w(description), out_metafile);
3551 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
3552 return InvalidParameter;
3554 if(succ)
3555 *succ = FALSE;
3556 *out_metafile = NULL;
3558 if(!(calls++))
3559 FIXME("not implemented\n");
3561 return NotImplemented;
3564 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
3565 LPBYTE pData16, INT iMapMode, INT eFlags)
3567 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
3568 return NotImplemented;
3571 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
3572 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
3573 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
3574 GpMetafile **metafile)
3576 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3577 frameUnit, debugstr_w(desc), metafile);
3579 return NotImplemented;
3582 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
3583 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
3584 GDIPCONST WCHAR *desc, GpMetafile **metafile)
3586 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3587 frameUnit, debugstr_w(desc), metafile);
3589 return NotImplemented;
3592 /*****************************************************************************
3593 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
3596 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
3597 GpMetafile* metafile, BOOL* conversionSuccess,
3598 const WCHAR* filename, EmfType emfType,
3599 const WCHAR* description, GpMetafile** out_metafile)
3601 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
3602 return NotImplemented;
3605 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
3607 LARGE_INTEGER zero;
3608 STATSTG statstg;
3609 GpStatus stat;
3610 HRESULT hr;
3612 *size = 0;
3614 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
3615 if (FAILED(hr)) return hresult_to_status(hr);
3617 stat = encode_image_png(image, *stream, NULL);
3618 if (stat != Ok)
3620 IStream_Release(*stream);
3621 return stat;
3624 hr = IStream_Stat(*stream, &statstg, 1);
3625 if (FAILED(hr))
3627 IStream_Release(*stream);
3628 return hresult_to_status(hr);
3630 *size = statstg.cbSize.u.LowPart;
3632 zero.QuadPart = 0;
3633 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
3634 if (FAILED(hr))
3636 IStream_Release(*stream);
3637 return hresult_to_status(hr);
3640 return Ok;
3643 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
3645 HRESULT hr;
3647 record->Width = 0;
3648 record->Height = 0;
3649 record->Stride = 0;
3650 record->PixelFormat = 0;
3651 record->Type = BitmapDataTypeCompressed;
3653 hr = IStream_Read(stream, record->BitmapData, size, NULL);
3654 if (FAILED(hr)) return hresult_to_status(hr);
3655 return Ok;
3658 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
3660 EmfPlusObject *object_record;
3661 GpStatus stat;
3662 DWORD size;
3664 *id = -1;
3666 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3667 return Ok;
3669 if (image->type == ImageTypeBitmap)
3671 IStream *stream;
3672 DWORD aligned_size;
3674 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
3675 if (stat != Ok) return stat;
3676 aligned_size = (size + 3) & ~3;
3678 stat = METAFILE_AllocateRecord(metafile,
3679 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
3680 (void**)&object_record);
3681 if (stat != Ok)
3683 IStream_Release(stream);
3684 return stat;
3686 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
3688 *id = METAFILE_AddObjectId(metafile);
3689 object_record->Header.Type = EmfPlusRecordTypeObject;
3690 object_record->Header.Flags = *id | ObjectTypeImage << 8;
3691 object_record->ObjectData.image.Version = VERSION_MAGIC2;
3692 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
3694 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
3695 IStream_Release(stream);
3696 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
3697 return stat;
3699 else if (image->type == ImageTypeMetafile)
3701 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
3702 EmfPlusMetafile *metafile_record;
3704 if (!hemf) return InvalidParameter;
3706 size = GetEnhMetaFileBits(hemf, 0, NULL);
3707 if (!size) return GenericError;
3709 stat = METAFILE_AllocateRecord(metafile,
3710 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
3711 (void**)&object_record);
3712 if (stat != Ok) return stat;
3714 *id = METAFILE_AddObjectId(metafile);
3715 object_record->Header.Type = EmfPlusRecordTypeObject;
3716 object_record->Header.Flags = *id | ObjectTypeImage << 8;
3717 object_record->ObjectData.image.Version = VERSION_MAGIC2;
3718 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
3719 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
3720 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
3721 metafile_record->MetafileDataSize = size;
3722 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
3724 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
3725 return GenericError;
3727 return Ok;
3729 else
3731 FIXME("not supported image type (%d)\n", image->type);
3732 return NotImplemented;
3736 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
3738 EmfPlusObject *object_record;
3739 EmfPlusImageAttributes *attrs_record;
3740 GpStatus stat;
3742 *id = -1;
3744 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3745 return Ok;
3747 if (!attrs)
3748 return Ok;
3750 stat = METAFILE_AllocateRecord(metafile,
3751 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
3752 (void**)&object_record);
3753 if (stat != Ok) return stat;
3755 *id = METAFILE_AddObjectId(metafile);
3756 object_record->Header.Type = EmfPlusRecordTypeObject;
3757 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
3758 attrs_record = &object_record->ObjectData.image_attributes;
3759 attrs_record->Version = VERSION_MAGIC2;
3760 attrs_record->Reserved1 = 0;
3761 attrs_record->WrapMode = attrs->wrap;
3762 attrs_record->ClampColor = attrs->outside_color;
3763 attrs_record->ObjectClamp = attrs->clamp;
3764 attrs_record->Reserved2 = 0;
3765 return Ok;
3768 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
3769 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
3770 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3771 DrawImageAbort callback, VOID *callbackData)
3773 EmfPlusDrawImagePoints *draw_image_record;
3774 DWORD image_id, attributes_id;
3775 GpStatus stat;
3777 if (count != 3) return InvalidParameter;
3779 if (metafile->metafile_type == MetafileTypeEmf)
3781 FIXME("MetafileTypeEmf metafiles not supported\n");
3782 return NotImplemented;
3784 else
3785 FIXME("semi-stub\n");
3787 if (!imageAttributes)
3789 stat = METAFILE_AddImageObject(metafile, image, &image_id);
3791 else if (image->type == ImageTypeBitmap)
3793 INT width = ((GpBitmap*)image)->width;
3794 INT height = ((GpBitmap*)image)->height;
3795 GpGraphics *graphics;
3796 GpBitmap *bitmap;
3798 stat = GdipCreateBitmapFromScan0(width, height,
3799 0, PixelFormat32bppARGB, NULL, &bitmap);
3800 if (stat != Ok) return stat;
3802 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
3803 if (stat != Ok)
3805 GdipDisposeImage((GpImage*)bitmap);
3806 return stat;
3809 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
3810 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
3811 GdipDeleteGraphics(graphics);
3812 if (stat != Ok)
3814 GdipDisposeImage((GpImage*)bitmap);
3815 return stat;
3818 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
3819 GdipDisposeImage((GpImage*)bitmap);
3821 else
3823 FIXME("imageAttributes not supported (image type %d)\n", image->type);
3824 return NotImplemented;
3826 if (stat != Ok) return stat;
3828 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
3829 if (stat != Ok) return stat;
3831 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
3832 if (stat != Ok) return stat;
3833 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
3834 draw_image_record->Header.Flags = image_id;
3835 draw_image_record->ImageAttributesID = attributes_id;
3836 draw_image_record->SrcUnit = UnitPixel;
3837 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
3838 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
3839 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
3840 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
3841 draw_image_record->count = 3;
3842 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
3843 METAFILE_WriteRecords(metafile);
3844 return Ok;
3847 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
3849 EmfPlusRecordHeader *record;
3850 GpStatus stat;
3852 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3853 return Ok;
3855 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
3856 if (stat != Ok) return stat;
3858 record->Type = prop;
3859 record->Flags = val;
3861 METAFILE_WriteRecords(metafile);
3862 return Ok;
3865 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
3867 EmfPlusObject *object_record;
3868 GpStatus stat;
3869 DWORD size;
3871 *id = -1;
3872 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3873 return Ok;
3875 size = write_path_data(path, NULL);
3876 stat = METAFILE_AllocateRecord(metafile,
3877 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
3878 (void**)&object_record);
3879 if (stat != Ok) return stat;
3881 *id = METAFILE_AddObjectId(metafile);
3882 object_record->Header.Type = EmfPlusRecordTypeObject;
3883 object_record->Header.Flags = *id | ObjectTypePath << 8;
3884 write_path_data(path, &object_record->ObjectData.path);
3885 return Ok;
3888 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
3890 switch (brush->bt)
3892 case BrushTypeSolidColor:
3893 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
3894 break;
3895 case BrushTypeHatchFill:
3896 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
3897 break;
3898 default:
3899 FIXME("unsupported brush type: %d\n", brush->bt);
3900 return NotImplemented;
3903 return Ok;
3906 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
3908 data->Version = VERSION_MAGIC2;
3909 data->Type = brush->bt;
3911 switch (brush->bt)
3913 case BrushTypeSolidColor:
3915 GpSolidFill *solid = (GpSolidFill *)brush;
3916 data->BrushData.solid.SolidColor = solid->color;
3917 break;
3919 case BrushTypeHatchFill:
3921 GpHatch *hatch = (GpHatch *)brush;
3922 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
3923 data->BrushData.hatch.ForeColor = hatch->forecol;
3924 data->BrushData.hatch.BackColor = hatch->backcol;
3925 break;
3927 default:
3928 FIXME("unsupported brush type: %d\n", brush->bt);
3932 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
3934 DWORD i, data_flags, pen_data_size, brush_size;
3935 EmfPlusObject *object_record;
3936 EmfPlusPenData *pen_data;
3937 GpStatus stat;
3938 BOOL result;
3940 *id = -1;
3941 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3942 return Ok;
3944 data_flags = 0;
3945 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
3947 GdipIsMatrixIdentity(&pen->transform, &result);
3948 if (!result)
3950 data_flags |= PenDataTransform;
3951 pen_data_size += sizeof(EmfPlusTransformMatrix);
3953 if (pen->startcap != LineCapFlat)
3955 data_flags |= PenDataStartCap;
3956 pen_data_size += sizeof(DWORD);
3958 if (pen->endcap != LineCapFlat)
3960 data_flags |= PenDataEndCap;
3961 pen_data_size += sizeof(DWORD);
3963 if (pen->join != LineJoinMiter)
3965 data_flags |= PenDataJoin;
3966 pen_data_size += sizeof(DWORD);
3968 if (pen->miterlimit != 10.0)
3970 data_flags |= PenDataMiterLimit;
3971 pen_data_size += sizeof(REAL);
3973 if (pen->style != GP_DEFAULT_PENSTYLE)
3975 data_flags |= PenDataLineStyle;
3976 pen_data_size += sizeof(DWORD);
3978 if (pen->dashcap != DashCapFlat)
3980 data_flags |= PenDataDashedLineCap;
3981 pen_data_size += sizeof(DWORD);
3983 data_flags |= PenDataDashedLineOffset;
3984 pen_data_size += sizeof(REAL);
3985 if (pen->numdashes)
3987 data_flags |= PenDataDashedLine;
3988 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
3990 if (pen->align != PenAlignmentCenter)
3992 data_flags |= PenDataNonCenter;
3993 pen_data_size += sizeof(DWORD);
3995 /* TODO: Add support for PenDataCompoundLine */
3996 if (pen->customstart)
3998 FIXME("ignoring custom start cup\n");
4000 if (pen->customend)
4002 FIXME("ignoring custom end cup\n");
4005 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4006 if (stat != Ok) return stat;
4008 stat = METAFILE_AllocateRecord(metafile,
4009 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4010 (void**)&object_record);
4011 if (stat != Ok) return stat;
4013 *id = METAFILE_AddObjectId(metafile);
4014 object_record->Header.Type = EmfPlusRecordTypeObject;
4015 object_record->Header.Flags = *id | ObjectTypePen << 8;
4016 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4017 object_record->ObjectData.pen.Type = 0;
4019 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4020 pen_data->PenDataFlags = data_flags;
4021 pen_data->PenUnit = pen->unit;
4022 pen_data->PenWidth = pen->width;
4024 i = 0;
4025 if (data_flags & PenDataTransform)
4027 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4028 memcpy(m, &pen->transform, sizeof(*m));
4029 i += sizeof(EmfPlusTransformMatrix);
4031 if (data_flags & PenDataStartCap)
4033 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4034 i += sizeof(DWORD);
4036 if (data_flags & PenDataEndCap)
4038 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4039 i += sizeof(DWORD);
4041 if (data_flags & PenDataJoin)
4043 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4044 i += sizeof(DWORD);
4046 if (data_flags & PenDataMiterLimit)
4048 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4049 i += sizeof(REAL);
4051 if (data_flags & PenDataLineStyle)
4053 switch (pen->style & PS_STYLE_MASK)
4055 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4056 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4057 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4058 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4059 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4060 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4062 i += sizeof(DWORD);
4064 if (data_flags & PenDataDashedLineCap)
4066 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4067 i += sizeof(DWORD);
4069 if (data_flags & PenDataDashedLineOffset)
4071 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4072 i += sizeof(REAL);
4074 if (data_flags & PenDataDashedLine)
4076 int j;
4078 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4079 i += sizeof(DWORD);
4081 for (j=0; j<pen->numdashes; j++)
4083 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4084 i += sizeof(REAL);
4087 if (data_flags & PenDataNonCenter)
4089 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4090 i += sizeof(DWORD);
4093 METAFILE_FillBrushData(pen->brush,
4094 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4095 return Ok;
4098 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4100 EmfPlusDrawPath *draw_path_record;
4101 DWORD path_id;
4102 DWORD pen_id;
4103 GpStatus stat;
4105 if (metafile->metafile_type == MetafileTypeEmf)
4107 FIXME("stub!\n");
4108 return NotImplemented;
4111 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4112 if (stat != Ok) return stat;
4114 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4115 if (stat != Ok) return stat;
4117 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4118 if (stat != Ok) return stat;
4119 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4120 draw_path_record->Header.Flags = path_id;
4121 draw_path_record->PenId = pen_id;
4123 METAFILE_WriteRecords(metafile);
4124 return Ok;
4127 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
4129 EmfPlusObject *object_record;
4130 GpStatus stat;
4131 DWORD size;
4133 *id = -1;
4134 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4135 return Ok;
4137 stat = METAFILE_PrepareBrushData(brush, &size);
4138 if (stat != Ok) return stat;
4140 stat = METAFILE_AllocateRecord(metafile,
4141 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
4142 if (stat != Ok) return stat;
4144 *id = METAFILE_AddObjectId(metafile);
4145 object_record->Header.Type = EmfPlusRecordTypeObject;
4146 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
4147 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
4148 return Ok;
4151 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4153 EmfPlusFillPath *fill_path_record;
4154 DWORD brush_id = -1, path_id;
4155 BOOL inline_color;
4156 GpStatus stat;
4158 if (metafile->metafile_type == MetafileTypeEmf)
4160 FIXME("stub!\n");
4161 return NotImplemented;
4164 inline_color = brush->bt == BrushTypeSolidColor;
4165 if (!inline_color)
4167 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4168 if (stat != Ok) return stat;
4171 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4172 if (stat != Ok) return stat;
4174 stat = METAFILE_AllocateRecord(metafile,
4175 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4176 if (stat != Ok) return stat;
4177 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4178 if (inline_color)
4180 fill_path_record->Header.Flags = 0x8000 | path_id;
4181 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4183 else
4185 fill_path_record->Header.Flags = path_id;
4186 fill_path_record->data.BrushId = brush_id;
4189 METAFILE_WriteRecords(metafile);
4190 return Ok;