gdiplus/metafile: Implement playback for EmfPlusRecordTypeDrawImage.
[wine.git] / dlls / gdiplus / metafile.c
blob9beb3a372e6087d2a4541155eac84cae16a7a259
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 EmfPlusFillPath
437 EmfPlusRecordHeader Header;
438 union
440 DWORD BrushId;
441 EmfPlusARGB Color;
442 } data;
443 } EmfPlusFillPath;
445 typedef struct EmfPlusFont
447 DWORD Version;
448 float EmSize;
449 DWORD SizeUnit;
450 DWORD FontStyleFlags;
451 DWORD Reserved;
452 DWORD Length;
453 WCHAR FamilyName[1];
454 } EmfPlusFont;
456 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
458 struct emfplus_object *object = &metafile->objtable[id];
460 switch (object->type)
462 case ObjectTypeInvalid:
463 break;
464 case ObjectTypeBrush:
465 GdipDeleteBrush(object->u.brush);
466 break;
467 case ObjectTypePen:
468 GdipDeletePen(object->u.pen);
469 break;
470 case ObjectTypePath:
471 GdipDeletePath(object->u.path);
472 break;
473 case ObjectTypeRegion:
474 GdipDeleteRegion(object->u.region);
475 break;
476 case ObjectTypeImage:
477 GdipDisposeImage(object->u.image);
478 break;
479 case ObjectTypeFont:
480 GdipDeleteFont(object->u.font);
481 break;
482 case ObjectTypeImageAttributes:
483 GdipDisposeImageAttributes(object->u.image_attributes);
484 break;
485 default:
486 FIXME("not implemented for object type %u.\n", object->type);
487 return;
490 object->type = ObjectTypeInvalid;
491 object->u.object = NULL;
494 void METAFILE_Free(GpMetafile *metafile)
496 unsigned int i;
498 heap_free(metafile->comment_data);
499 DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
500 if (!metafile->preserve_hemf)
501 DeleteEnhMetaFile(metafile->hemf);
502 if (metafile->record_graphics)
504 WARN("metafile closed while recording\n");
505 /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
506 metafile->record_graphics->image = NULL;
507 metafile->record_graphics->busy = TRUE;
510 if (metafile->record_stream)
511 IStream_Release(metafile->record_stream);
513 for (i = 0; i < sizeof(metafile->objtable)/sizeof(metafile->objtable[0]); i++)
514 metafile_free_object_table_entry(metafile, i);
517 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
519 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
522 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
524 DWORD size_needed;
525 EmfPlusRecordHeader *record;
527 if (!metafile->comment_data_size)
529 DWORD data_size = max(256, size * 2 + 4);
530 metafile->comment_data = heap_alloc_zero(data_size);
532 if (!metafile->comment_data)
533 return OutOfMemory;
535 memcpy(metafile->comment_data, "EMF+", 4);
537 metafile->comment_data_size = data_size;
538 metafile->comment_data_length = 4;
541 size_needed = size + metafile->comment_data_length;
543 if (size_needed > metafile->comment_data_size)
545 DWORD data_size = size_needed * 2;
546 BYTE *new_data = heap_alloc_zero(data_size);
548 if (!new_data)
549 return OutOfMemory;
551 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
553 metafile->comment_data_size = data_size;
554 heap_free(metafile->comment_data);
555 metafile->comment_data = new_data;
558 *result = metafile->comment_data + metafile->comment_data_length;
559 metafile->comment_data_length += size;
561 record = (EmfPlusRecordHeader*)*result;
562 record->Size = size;
563 record->DataSize = size - sizeof(EmfPlusRecordHeader);
565 return Ok;
568 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
570 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
571 metafile->comment_data_length -= record->Size;
574 static void METAFILE_WriteRecords(GpMetafile *metafile)
576 if (metafile->comment_data_length > 4)
578 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
579 metafile->comment_data_length = 4;
583 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
585 GpStatus stat;
587 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
589 EmfPlusHeader *header;
591 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
592 if (stat != Ok)
593 return stat;
595 header->Header.Type = EmfPlusRecordTypeHeader;
597 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
598 header->Header.Flags = 1;
599 else
600 header->Header.Flags = 0;
602 header->Version = VERSION_MAGIC2;
604 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
605 header->EmfPlusFlags = 1;
606 else
607 header->EmfPlusFlags = 0;
609 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
610 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
612 METAFILE_WriteRecords(metafile);
615 return Ok;
618 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
620 GpStatus stat;
622 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
624 EmfPlusRecordHeader *record;
626 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
627 if (stat != Ok)
628 return stat;
630 record->Type = EmfPlusRecordTypeEndOfFile;
631 record->Flags = 0;
633 METAFILE_WriteRecords(metafile);
636 return Ok;
639 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
640 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
642 HDC record_dc;
643 REAL dpix, dpiy;
644 REAL framerect_factor_x, framerect_factor_y;
645 RECT rc, *lprc;
646 GpStatus stat;
648 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
650 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
651 return InvalidParameter;
653 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
654 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
656 if (frameRect)
658 switch (frameUnit)
660 case MetafileFrameUnitPixel:
661 framerect_factor_x = 2540.0 / dpix;
662 framerect_factor_y = 2540.0 / dpiy;
663 break;
664 case MetafileFrameUnitPoint:
665 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
666 break;
667 case MetafileFrameUnitInch:
668 framerect_factor_x = framerect_factor_y = 2540.0;
669 break;
670 case MetafileFrameUnitDocument:
671 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
672 break;
673 case MetafileFrameUnitMillimeter:
674 framerect_factor_x = framerect_factor_y = 100.0;
675 break;
676 case MetafileFrameUnitGdi:
677 framerect_factor_x = framerect_factor_y = 1.0;
678 break;
679 default:
680 return InvalidParameter;
683 rc.left = framerect_factor_x * frameRect->X;
684 rc.top = framerect_factor_y * frameRect->Y;
685 rc.right = rc.left + framerect_factor_x * frameRect->Width;
686 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
688 lprc = &rc;
690 else
691 lprc = NULL;
693 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
695 if (!record_dc)
696 return GenericError;
698 *metafile = heap_alloc_zero(sizeof(GpMetafile));
699 if(!*metafile)
701 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
702 return OutOfMemory;
705 (*metafile)->image.type = ImageTypeMetafile;
706 (*metafile)->image.flags = ImageFlagsNone;
707 (*metafile)->image.palette = NULL;
708 (*metafile)->image.xres = dpix;
709 (*metafile)->image.yres = dpiy;
710 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
711 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
712 (*metafile)->unit = UnitPixel;
713 (*metafile)->metafile_type = type;
714 (*metafile)->record_dc = record_dc;
715 (*metafile)->comment_data = NULL;
716 (*metafile)->comment_data_size = 0;
717 (*metafile)->comment_data_length = 0;
718 (*metafile)->hemf = NULL;
719 list_init(&(*metafile)->containers);
721 if (!frameRect)
723 (*metafile)->auto_frame = TRUE;
724 (*metafile)->auto_frame_min.X = 0;
725 (*metafile)->auto_frame_min.Y = 0;
726 (*metafile)->auto_frame_max.X = -1;
727 (*metafile)->auto_frame_max.Y = -1;
730 stat = METAFILE_WriteHeader(*metafile, hdc);
732 if (stat != Ok)
734 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
735 heap_free(*metafile);
736 *metafile = NULL;
737 return OutOfMemory;
740 return stat;
743 /*****************************************************************************
744 * GdipRecordMetafileI [GDIPLUS.@]
746 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
747 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
749 GpRectF frameRectF, *pFrameRectF;
751 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
753 if (frameRect)
755 frameRectF.X = frameRect->X;
756 frameRectF.Y = frameRect->Y;
757 frameRectF.Width = frameRect->Width;
758 frameRectF.Height = frameRect->Height;
759 pFrameRectF = &frameRectF;
761 else
762 pFrameRectF = NULL;
764 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
767 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
768 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
770 GpStatus stat;
772 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
774 if (!stream)
775 return InvalidParameter;
777 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
779 if (stat == Ok)
781 (*metafile)->record_stream = stream;
782 IStream_AddRef(stream);
785 return stat;
788 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
789 UINT num_points)
791 int i;
793 if (!metafile->auto_frame || !num_points)
794 return;
796 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
797 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
799 for (i=0; i<num_points; i++)
801 if (points[i].X < metafile->auto_frame_min.X)
802 metafile->auto_frame_min.X = points[i].X;
803 if (points[i].X > metafile->auto_frame_max.X)
804 metafile->auto_frame_max.X = points[i].X;
805 if (points[i].Y < metafile->auto_frame_min.Y)
806 metafile->auto_frame_min.Y = points[i].Y;
807 if (points[i].Y > metafile->auto_frame_max.Y)
808 metafile->auto_frame_max.Y = points[i].Y;
812 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
814 GpStatus stat;
816 if (!metafile->record_dc || metafile->record_graphics)
817 return InvalidParameter;
819 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
821 if (stat == Ok)
823 *result = metafile->record_graphics;
824 metafile->record_graphics->xres = 96.0;
825 metafile->record_graphics->yres = 96.0;
828 return stat;
831 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
833 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
835 EmfPlusRecordHeader *record;
836 GpStatus stat;
838 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
839 if (stat != Ok)
840 return stat;
842 record->Type = EmfPlusRecordTypeGetDC;
843 record->Flags = 0;
845 METAFILE_WriteRecords(metafile);
848 *hdc = metafile->record_dc;
850 return Ok;
853 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
855 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
857 EmfPlusClear *record;
858 GpStatus stat;
860 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
861 if (stat != Ok)
862 return stat;
864 record->Header.Type = EmfPlusRecordTypeClear;
865 record->Header.Flags = 0;
866 record->Color = color;
868 METAFILE_WriteRecords(metafile);
871 return Ok;
874 static BOOL is_integer_rect(const GpRectF *rect)
876 SHORT x, y, width, height;
877 x = rect->X;
878 y = rect->Y;
879 width = rect->Width;
880 height = rect->Height;
881 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
882 rect->Width != (REAL)width || rect->Height != (REAL)height)
883 return FALSE;
884 return TRUE;
887 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
888 GDIPCONST GpRectF* rects, INT count)
890 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
892 EmfPlusFillRects *record;
893 GpStatus stat;
894 BOOL integer_rects = TRUE;
895 int i;
896 DWORD brushid;
897 int flags = 0;
899 if (brush->bt == BrushTypeSolidColor)
901 flags |= 0x8000;
902 brushid = ((GpSolidFill*)brush)->color;
904 else
906 FIXME("brush serialization not implemented\n");
907 return NotImplemented;
910 for (i=0; i<count; i++)
912 if (!is_integer_rect(&rects[i]))
914 integer_rects = FALSE;
915 break;
919 if (integer_rects)
920 flags |= 0x4000;
922 stat = METAFILE_AllocateRecord(metafile,
923 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
924 (void**)&record);
925 if (stat != Ok)
926 return stat;
928 record->Header.Type = EmfPlusRecordTypeFillRects;
929 record->Header.Flags = flags;
930 record->BrushID = brushid;
931 record->Count = count;
933 if (integer_rects)
935 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
936 for (i=0; i<count; i++)
938 record_rects[i].X = (SHORT)rects[i].X;
939 record_rects[i].Y = (SHORT)rects[i].Y;
940 record_rects[i].Width = (SHORT)rects[i].Width;
941 record_rects[i].Height = (SHORT)rects[i].Height;
944 else
945 memcpy(record+1, rects, sizeof(GpRectF) * count);
947 METAFILE_WriteRecords(metafile);
950 if (metafile->auto_frame)
952 GpPointF corners[4];
953 int i;
955 for (i=0; i<count; i++)
957 corners[0].X = rects[i].X;
958 corners[0].Y = rects[i].Y;
959 corners[1].X = rects[i].X + rects[i].Width;
960 corners[1].Y = rects[i].Y;
961 corners[2].X = rects[i].X;
962 corners[2].Y = rects[i].Y + rects[i].Height;
963 corners[3].X = rects[i].X + rects[i].Width;
964 corners[3].Y = rects[i].Y + rects[i].Height;
966 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
967 CoordinateSpaceWorld, corners, 4);
969 METAFILE_AdjustFrame(metafile, corners, 4);
973 return Ok;
976 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
978 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
980 EmfPlusSetClipRect *record;
981 GpStatus stat;
983 stat = METAFILE_AllocateRecord(metafile,
984 sizeof(EmfPlusSetClipRect),
985 (void**)&record);
986 if (stat != Ok)
987 return stat;
989 record->Header.Type = EmfPlusRecordTypeSetClipRect;
990 record->Header.Flags = (mode & 0xf) << 8;
991 record->ClipRect.X = x;
992 record->ClipRect.Y = y;
993 record->ClipRect.Width = width;
994 record->ClipRect.Height = height;
996 METAFILE_WriteRecords(metafile);
999 return Ok;
1002 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1004 EmfPlusObject *object_record;
1005 DWORD size;
1006 GpStatus stat;
1008 *id = -1;
1009 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1010 return Ok;
1012 size = write_region_data(region, NULL);
1013 stat = METAFILE_AllocateRecord(metafile,
1014 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1015 if (stat != Ok) return stat;
1017 *id = METAFILE_AddObjectId(metafile);
1018 object_record->Header.Type = EmfPlusRecordTypeObject;
1019 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1020 write_region_data(region, &object_record->ObjectData.region);
1021 return Ok;
1024 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1026 EmfPlusRecordHeader *record;
1027 DWORD region_id;
1028 GpStatus stat;
1030 if (metafile->metafile_type == MetafileTypeEmf)
1032 FIXME("stub!\n");
1033 return NotImplemented;
1036 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1037 if (stat != Ok) return stat;
1039 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1040 if (stat != Ok) return stat;
1042 record->Type = EmfPlusRecordTypeSetClipRegion;
1043 record->Flags = region_id | mode << 8;
1045 METAFILE_WriteRecords(metafile);
1046 return Ok;
1049 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1051 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1053 EmfPlusSetPageTransform *record;
1054 GpStatus stat;
1056 stat = METAFILE_AllocateRecord(metafile,
1057 sizeof(EmfPlusSetPageTransform),
1058 (void**)&record);
1059 if (stat != Ok)
1060 return stat;
1062 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1063 record->Header.Flags = unit;
1064 record->PageScale = scale;
1066 METAFILE_WriteRecords(metafile);
1069 return Ok;
1072 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1074 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1076 EmfPlusSetWorldTransform *record;
1077 GpStatus stat;
1079 stat = METAFILE_AllocateRecord(metafile,
1080 sizeof(EmfPlusSetWorldTransform),
1081 (void**)&record);
1082 if (stat != Ok)
1083 return stat;
1085 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1086 record->Header.Flags = 0;
1087 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1089 METAFILE_WriteRecords(metafile);
1092 return Ok;
1095 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1097 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1099 EmfPlusScaleWorldTransform *record;
1100 GpStatus stat;
1102 stat = METAFILE_AllocateRecord(metafile,
1103 sizeof(EmfPlusScaleWorldTransform),
1104 (void**)&record);
1105 if (stat != Ok)
1106 return stat;
1108 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1109 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1110 record->Sx = sx;
1111 record->Sy = sy;
1113 METAFILE_WriteRecords(metafile);
1116 return Ok;
1119 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1121 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1123 EmfPlusMultiplyWorldTransform *record;
1124 GpStatus stat;
1126 stat = METAFILE_AllocateRecord(metafile,
1127 sizeof(EmfPlusMultiplyWorldTransform),
1128 (void**)&record);
1129 if (stat != Ok)
1130 return stat;
1132 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1133 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1134 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1136 METAFILE_WriteRecords(metafile);
1139 return Ok;
1142 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1144 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1146 EmfPlusRotateWorldTransform *record;
1147 GpStatus stat;
1149 stat = METAFILE_AllocateRecord(metafile,
1150 sizeof(EmfPlusRotateWorldTransform),
1151 (void**)&record);
1152 if (stat != Ok)
1153 return stat;
1155 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1156 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1157 record->Angle = angle;
1159 METAFILE_WriteRecords(metafile);
1162 return Ok;
1165 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1167 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1169 EmfPlusTranslateWorldTransform *record;
1170 GpStatus stat;
1172 stat = METAFILE_AllocateRecord(metafile,
1173 sizeof(EmfPlusTranslateWorldTransform),
1174 (void**)&record);
1175 if (stat != Ok)
1176 return stat;
1178 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1179 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1180 record->dx = dx;
1181 record->dy = dy;
1183 METAFILE_WriteRecords(metafile);
1186 return Ok;
1189 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1191 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1193 EmfPlusRecordHeader *record;
1194 GpStatus stat;
1196 stat = METAFILE_AllocateRecord(metafile,
1197 sizeof(EmfPlusRecordHeader),
1198 (void**)&record);
1199 if (stat != Ok)
1200 return stat;
1202 record->Type = EmfPlusRecordTypeResetWorldTransform;
1203 record->Flags = 0;
1205 METAFILE_WriteRecords(metafile);
1208 return Ok;
1211 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1212 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1214 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1216 EmfPlusBeginContainer *record;
1217 GpStatus stat;
1219 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1220 if (stat != Ok)
1221 return stat;
1223 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1224 record->Header.Flags = unit & 0xff;
1225 record->DestRect = *dstrect;
1226 record->SrcRect = *srcrect;
1227 record->StackIndex = StackIndex;
1229 METAFILE_WriteRecords(metafile);
1232 return Ok;
1235 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1237 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1239 EmfPlusContainerRecord *record;
1240 GpStatus stat;
1242 stat = METAFILE_AllocateRecord(metafile,
1243 sizeof(EmfPlusContainerRecord),
1244 (void**)&record);
1245 if (stat != Ok)
1246 return stat;
1248 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1249 record->Header.Flags = 0;
1250 record->StackIndex = StackIndex;
1252 METAFILE_WriteRecords(metafile);
1255 return Ok;
1258 GpStatus METAFILE_EndContainer(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 = EmfPlusRecordTypeEndContainer;
1272 record->Header.Flags = 0;
1273 record->StackIndex = StackIndex;
1275 METAFILE_WriteRecords(metafile);
1278 return Ok;
1281 GpStatus METAFILE_SaveGraphics(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 = EmfPlusRecordTypeSave;
1295 record->Header.Flags = 0;
1296 record->StackIndex = StackIndex;
1298 METAFILE_WriteRecords(metafile);
1301 return Ok;
1304 GpStatus METAFILE_RestoreGraphics(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 = EmfPlusRecordTypeRestore;
1318 record->Header.Flags = 0;
1319 record->StackIndex = StackIndex;
1321 METAFILE_WriteRecords(metafile);
1324 return Ok;
1327 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1329 if (hdc != metafile->record_dc)
1330 return InvalidParameter;
1332 return Ok;
1335 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1337 GpStatus stat;
1339 stat = METAFILE_WriteEndOfFile(metafile);
1340 metafile->record_graphics = NULL;
1342 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1343 metafile->record_dc = NULL;
1345 heap_free(metafile->comment_data);
1346 metafile->comment_data = NULL;
1347 metafile->comment_data_size = 0;
1349 if (stat == Ok)
1351 MetafileHeader header;
1353 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1354 if (stat == Ok && metafile->auto_frame &&
1355 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1357 RECTL bounds_rc, gdi_bounds_rc;
1358 REAL x_scale = 2540.0 / header.DpiX;
1359 REAL y_scale = 2540.0 / header.DpiY;
1360 BYTE* buffer;
1361 UINT buffer_size;
1363 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1364 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1365 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1366 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1368 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1369 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1371 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1372 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1373 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1374 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1377 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1378 buffer = heap_alloc(buffer_size);
1379 if (buffer)
1381 HENHMETAFILE new_hemf;
1383 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1385 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1387 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1389 if (new_hemf)
1391 DeleteEnhMetaFile(metafile->hemf);
1392 metafile->hemf = new_hemf;
1394 else
1395 stat = OutOfMemory;
1397 heap_free(buffer);
1399 else
1400 stat = OutOfMemory;
1402 if (stat == Ok)
1403 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1405 if (stat == Ok)
1407 metafile->bounds.X = header.X;
1408 metafile->bounds.Y = header.Y;
1409 metafile->bounds.Width = header.Width;
1410 metafile->bounds.Height = header.Height;
1414 if (stat == Ok && metafile->record_stream)
1416 BYTE *buffer;
1417 UINT buffer_size;
1419 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1421 buffer = heap_alloc(buffer_size);
1422 if (buffer)
1424 HRESULT hr;
1426 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1428 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1430 if (FAILED(hr))
1431 stat = hresult_to_status(hr);
1433 heap_free(buffer);
1435 else
1436 stat = OutOfMemory;
1439 if (metafile->record_stream)
1441 IStream_Release(metafile->record_stream);
1442 metafile->record_stream = NULL;
1445 return stat;
1448 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1450 TRACE("(%p,%p)\n", metafile, hEmf);
1452 if (!metafile || !hEmf || !metafile->hemf)
1453 return InvalidParameter;
1455 *hEmf = metafile->hemf;
1456 metafile->hemf = NULL;
1458 return Ok;
1461 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1463 const GpRectF *rect;
1464 const GpPointF *pt;
1466 /* This transforms metafile device space to output points. */
1467 rect = &metafile->src_rect;
1468 pt = metafile->playback_points;
1469 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1470 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1471 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1472 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1473 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1474 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1477 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1479 XFORM combined, final;
1481 METAFILE_GetFinalGdiTransform(metafile, &final);
1483 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1485 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1486 SetWorldTransform(metafile->playback_dc, &combined);
1488 return Ok;
1491 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1493 GpStatus stat = Ok;
1495 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1497 if (stat == Ok)
1499 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1501 metafile->gdiworldtransform = identity;
1502 METAFILE_PlaybackUpdateGdiTransform(metafile);
1505 return stat;
1508 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1510 if (metafile->playback_dc)
1512 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1513 metafile->playback_dc = NULL;
1517 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1519 GpStatus stat;
1520 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1521 if (stat == Ok)
1522 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1523 return stat;
1526 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1528 GpMatrix *real_transform;
1529 GpStatus stat;
1531 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1533 if (stat == Ok)
1535 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1537 if (metafile->page_unit != UnitDisplay)
1538 scale *= metafile->page_scale;
1540 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1542 if (stat == Ok)
1543 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1545 if (stat == Ok)
1546 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1548 GdipDeleteMatrix(real_transform);
1551 return stat;
1554 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1556 metafile_free_object_table_entry(metafile, id);
1557 metafile->objtable[id].type = type;
1558 metafile->objtable[id].u.object = object;
1561 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1563 EmfPlusImage *data = (EmfPlusImage *)record_data;
1564 GpStatus status;
1566 *image = NULL;
1568 if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1569 return InvalidParameter;
1570 data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1572 switch (data->Type)
1574 case ImageDataTypeBitmap:
1576 EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1578 if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1579 return InvalidParameter;
1580 data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1582 switch (bitmapdata->Type)
1584 case BitmapDataTypePixel:
1586 ColorPalette *palette;
1587 BYTE *scan0;
1589 if (bitmapdata->PixelFormat & PixelFormatIndexed)
1591 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1592 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1594 if (data_size <= palette_size)
1595 return InvalidParameter;
1596 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1598 if (data_size < palette_size)
1599 return InvalidParameter;
1600 data_size -= palette_size;
1602 palette = (ColorPalette *)bitmapdata->BitmapData;
1603 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1605 else
1607 palette = NULL;
1608 scan0 = bitmapdata->BitmapData;
1611 if (data_size < bitmapdata->Height * bitmapdata->Stride)
1612 return InvalidParameter;
1614 status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1615 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1616 if (status == Ok && palette)
1618 status = GdipSetImagePalette(*image, palette);
1619 if (status != Ok)
1621 GdipDisposeImage(*image);
1622 *image = NULL;
1625 break;
1627 case BitmapDataTypeCompressed:
1629 IWICImagingFactory *factory;
1630 IWICStream *stream;
1631 HRESULT hr;
1633 if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1634 return GenericError;
1636 hr = IWICImagingFactory_CreateStream(factory, &stream);
1637 IWICImagingFactory_Release(factory);
1638 if (hr != S_OK)
1639 return GenericError;
1641 if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1642 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1643 else
1644 status = GenericError;
1646 IWICStream_Release(stream);
1647 break;
1649 default:
1650 WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1651 return InvalidParameter;
1653 break;
1655 default:
1656 FIXME("image type %d not supported.\n", data->Type);
1657 return NotImplemented;
1660 return status;
1663 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1665 EmfPlusPath *data = (EmfPlusPath *)record_data;
1666 GpStatus status;
1667 BYTE *types;
1668 UINT size;
1669 DWORD i;
1671 *path = NULL;
1673 if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1674 return InvalidParameter;
1675 data_size -= FIELD_OFFSET(EmfPlusPath, data);
1677 if (data->PathPointFlags & 0x800) /* R */
1679 FIXME("RLE encoded path data is not supported.\n");
1680 return NotImplemented;
1682 else
1684 if (data->PathPointFlags & 0x4000) /* C */
1685 size = sizeof(EmfPlusPoint);
1686 else
1687 size = sizeof(EmfPlusPointF);
1688 size += sizeof(BYTE); /* EmfPlusPathPointType */
1689 size *= data->PathPointCount;
1692 if (data_size < size)
1693 return InvalidParameter;
1695 status = GdipCreatePath(FillModeAlternate, path);
1696 if (status != Ok)
1697 return status;
1699 (*path)->pathdata.Count = data->PathPointCount;
1700 (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1701 (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1702 (*path)->datalen = (*path)->pathdata.Count;
1704 if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1706 GdipDeletePath(*path);
1707 return OutOfMemory;
1710 if (data->PathPointFlags & 0x4000) /* C */
1712 EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1713 for (i = 0; i < data->PathPointCount; i++)
1715 (*path)->pathdata.Points[i].X = points[i].X;
1716 (*path)->pathdata.Points[i].Y = points[i].Y;
1718 types = (BYTE *)(points + i);
1720 else
1722 EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1723 memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1724 types = (BYTE *)(points + data->PathPointCount);
1727 memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1729 return Ok;
1732 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1734 const DWORD *type;
1735 GpStatus status;
1737 type = buffer_read(mbuf, sizeof(*type));
1738 if (!type) return Ok;
1740 node->type = *type;
1742 switch (node->type)
1744 case CombineModeReplace:
1745 case CombineModeIntersect:
1746 case CombineModeUnion:
1747 case CombineModeXor:
1748 case CombineModeExclude:
1749 case CombineModeComplement:
1751 region_element *left, *right;
1753 left = heap_alloc_zero(sizeof(*left));
1754 if (!left)
1755 return OutOfMemory;
1757 right = heap_alloc_zero(sizeof(*right));
1758 if (!right)
1760 heap_free(left);
1761 return OutOfMemory;
1764 status = metafile_read_region_node(mbuf, region, left, count);
1765 if (status == Ok)
1767 status = metafile_read_region_node(mbuf, region, right, count);
1768 if (status == Ok)
1770 node->elementdata.combine.left = left;
1771 node->elementdata.combine.right = right;
1772 region->num_children += 2;
1773 return Ok;
1777 heap_free(left);
1778 heap_free(right);
1779 return status;
1781 case RegionDataRect:
1783 const EmfPlusRectF *rect;
1785 rect = buffer_read(mbuf, sizeof(*rect));
1786 if (!rect)
1787 return InvalidParameter;
1789 memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1790 *count += 1;
1791 return Ok;
1793 case RegionDataPath:
1795 const BYTE *path_data;
1796 const UINT *data_size;
1797 GpPath *path;
1799 data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1800 if (!data_size)
1801 return InvalidParameter;
1803 path_data = buffer_read(mbuf, *data_size);
1804 if (!path_data)
1805 return InvalidParameter;
1807 status = metafile_deserialize_path(path_data, *data_size, &path);
1808 if (status == Ok)
1810 node->elementdata.path = path;
1811 *count += 1;
1813 return Ok;
1815 case RegionDataEmptyRect:
1816 case RegionDataInfiniteRect:
1817 *count += 1;
1818 return Ok;
1819 default:
1820 FIXME("element type %#x is not supported\n", *type);
1821 break;
1824 return InvalidParameter;
1827 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1829 struct memory_buffer mbuf;
1830 GpStatus status;
1831 UINT count;
1833 *region = NULL;
1835 init_memory_buffer(&mbuf, record_data, data_size);
1837 if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
1838 return InvalidParameter;
1840 status = GdipCreateRegion(region);
1841 if (status != Ok)
1842 return status;
1844 count = 0;
1845 status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
1846 if (status == Ok && !count)
1847 status = InvalidParameter;
1849 if (status != Ok)
1851 GdipDeleteRegion(*region);
1852 *region = NULL;
1855 return status;
1858 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
1860 static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
1861 EmfPlusBrush *data = (EmfPlusBrush *)record_data;
1862 GpStatus status;
1864 *brush = NULL;
1866 if (data_size < header_size)
1867 return InvalidParameter;
1869 switch (data->Type)
1871 case BrushTypeSolidColor:
1872 if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
1873 return InvalidParameter;
1875 status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
1876 break;
1877 case BrushTypeHatchFill:
1878 if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
1879 return InvalidParameter;
1881 status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
1882 data->BrushData.hatch.BackColor, (GpHatch **)brush);
1883 break;
1884 case BrushTypeTextureFill:
1886 UINT offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
1887 EmfPlusTransformMatrix *transform = NULL;
1888 DWORD brushflags;
1889 GpImage *image;
1891 if (data_size <= offset)
1892 return InvalidParameter;
1894 brushflags = data->BrushData.texture.BrushDataFlags;
1895 if (brushflags & BrushDataTransform)
1897 if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
1898 return InvalidParameter;
1899 transform = (EmfPlusTransformMatrix *)(record_data + offset);
1900 offset += sizeof(EmfPlusTransformMatrix);
1903 status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
1904 if (status != Ok)
1905 return status;
1907 status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
1908 if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
1909 GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
1911 GdipDisposeImage(image);
1912 break;
1914 default:
1915 FIXME("brush type %u is not supported.\n", data->Type);
1916 return NotImplemented;
1919 return status;
1922 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
1924 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
1925 DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
1927 if (data_size <= offset)
1928 return InvalidParameter;
1930 offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
1931 if (data_size <= offset)
1932 return InvalidParameter;
1934 if (pendata->PenDataFlags & PenDataTransform)
1935 offset += sizeof(EmfPlusTransformMatrix);
1937 if (pendata->PenDataFlags & PenDataStartCap)
1938 offset += sizeof(DWORD);
1940 if (pendata->PenDataFlags & PenDataEndCap)
1941 offset += sizeof(DWORD);
1943 if (pendata->PenDataFlags & PenDataJoin)
1944 offset += sizeof(DWORD);
1946 if (pendata->PenDataFlags & PenDataMiterLimit)
1947 offset += sizeof(REAL);
1949 if (pendata->PenDataFlags & PenDataLineStyle)
1950 offset += sizeof(DWORD);
1952 if (pendata->PenDataFlags & PenDataDashedLineCap)
1953 offset += sizeof(DWORD);
1955 if (pendata->PenDataFlags & PenDataDashedLineOffset)
1956 offset += sizeof(REAL);
1958 if (pendata->PenDataFlags & PenDataDashedLine)
1960 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
1962 offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
1963 if (data_size <= offset)
1964 return InvalidParameter;
1966 offset += dashedline->DashedLineDataSize * sizeof(float);
1969 if (pendata->PenDataFlags & PenDataNonCenter)
1970 offset += sizeof(DWORD);
1972 if (pendata->PenDataFlags & PenDataCompoundLine)
1974 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
1976 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
1977 if (data_size <= offset)
1978 return InvalidParameter;
1980 offset += compoundline->CompoundLineDataSize * sizeof(float);
1983 if (pendata->PenDataFlags & PenDataCustomStartCap)
1985 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
1987 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
1988 if (data_size <= offset)
1989 return InvalidParameter;
1991 offset += startcap->CustomStartCapSize;
1994 if (pendata->PenDataFlags & PenDataCustomEndCap)
1996 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
1998 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
1999 if (data_size <= offset)
2000 return InvalidParameter;
2002 offset += endcap->CustomEndCapSize;
2005 *ret = offset;
2006 return Ok;
2009 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2011 BYTE type = (flags >> 8) & 0xff;
2012 BYTE id = flags & 0xff;
2013 void *object = NULL;
2014 GpStatus status;
2016 if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2017 return InvalidParameter;
2019 switch (type)
2021 case ObjectTypeBrush:
2022 status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2023 break;
2024 case ObjectTypePen:
2026 EmfPlusPen *data = (EmfPlusPen *)record_data;
2027 EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2028 GpBrush *brush;
2029 DWORD offset;
2030 GpPen *pen;
2032 status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2033 if (status != Ok)
2034 return status;
2036 status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2037 if (status != Ok)
2038 return status;
2040 status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2041 GdipDeleteBrush(brush);
2042 if (status != Ok)
2043 return status;
2045 offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2047 if (pendata->PenDataFlags & PenDataTransform)
2049 FIXME("PenDataTransform is not supported.\n");
2050 offset += sizeof(EmfPlusTransformMatrix);
2053 if (pendata->PenDataFlags & PenDataStartCap)
2055 if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2056 goto penfailed;
2057 offset += sizeof(DWORD);
2060 if (pendata->PenDataFlags & PenDataEndCap)
2062 if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2063 goto penfailed;
2064 offset += sizeof(DWORD);
2067 if (pendata->PenDataFlags & PenDataJoin)
2069 if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2070 goto penfailed;
2071 offset += sizeof(DWORD);
2074 if (pendata->PenDataFlags & PenDataMiterLimit)
2076 if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2077 goto penfailed;
2078 offset += sizeof(REAL);
2081 if (pendata->PenDataFlags & PenDataLineStyle)
2083 if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2084 goto penfailed;
2085 offset += sizeof(DWORD);
2088 if (pendata->PenDataFlags & PenDataDashedLineCap)
2090 FIXME("PenDataDashedLineCap is not supported.\n");
2091 offset += sizeof(DWORD);
2094 if (pendata->PenDataFlags & PenDataDashedLineOffset)
2096 if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2097 goto penfailed;
2098 offset += sizeof(REAL);
2101 if (pendata->PenDataFlags & PenDataDashedLine)
2103 EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2104 FIXME("PenDataDashedLine is not supported.\n");
2105 offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2108 if (pendata->PenDataFlags & PenDataNonCenter)
2110 FIXME("PenDataNonCenter is not supported.\n");
2111 offset += sizeof(DWORD);
2114 if (pendata->PenDataFlags & PenDataCompoundLine)
2116 EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2117 FIXME("PenDataCompundLine is not supported.\n");
2118 offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2121 if (pendata->PenDataFlags & PenDataCustomStartCap)
2123 EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2124 FIXME("PenDataCustomStartCap is not supported.\n");
2125 offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2128 if (pendata->PenDataFlags & PenDataCustomEndCap)
2130 EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2131 FIXME("PenDataCustomEndCap is not supported.\n");
2132 offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2135 object = pen;
2136 break;
2138 penfailed:
2139 GdipDeletePen(pen);
2140 return status;
2142 case ObjectTypePath:
2143 status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2144 break;
2145 case ObjectTypeRegion:
2146 status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2147 break;
2148 case ObjectTypeImage:
2149 status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2150 break;
2151 case ObjectTypeFont:
2153 EmfPlusFont *data = (EmfPlusFont *)record_data;
2154 GpFontFamily *family;
2155 WCHAR *familyname;
2157 if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2158 return InvalidParameter;
2159 data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2161 if (data_size < data->Length * sizeof(WCHAR))
2162 return InvalidParameter;
2164 if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2165 return OutOfMemory;
2167 memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2168 familyname[data->Length] = 0;
2170 status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2171 GdipFree(familyname);
2172 if (status != Ok)
2173 return InvalidParameter;
2175 status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2176 GdipDeleteFontFamily(family);
2177 break;
2179 case ObjectTypeImageAttributes:
2181 EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2182 GpImageAttributes *attributes = NULL;
2184 if (data_size != sizeof(*data))
2185 return InvalidParameter;
2187 if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2188 return status;
2190 status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2191 !!data->ObjectClamp);
2192 if (status == Ok)
2193 object = attributes;
2194 else
2195 GdipDisposeImageAttributes(attributes);
2196 break;
2198 default:
2199 FIXME("not implemented for object type %d.\n", type);
2200 return NotImplemented;
2203 if (status == Ok)
2204 metafile_set_object_table_entry(metafile, id, type, object);
2206 return status;
2209 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2211 GpMatrix world_to_device;
2213 get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2215 GdipTransformRegion(region, &world_to_device);
2216 GdipCombineRegionRegion(metafile->clip, region, mode);
2218 return METAFILE_PlaybackUpdateClip(metafile);
2221 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2222 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2224 GpStatus stat;
2225 GpMetafile *real_metafile = (GpMetafile*)metafile;
2227 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2229 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2230 return InvalidParameter;
2232 if (recordType >= 1 && recordType <= 0x7a)
2234 /* regular EMF record */
2235 if (metafile->playback_dc)
2237 switch (recordType)
2239 case EMR_SETMAPMODE:
2240 case EMR_SAVEDC:
2241 case EMR_RESTOREDC:
2242 case EMR_SETWINDOWORGEX:
2243 case EMR_SETWINDOWEXTEX:
2244 case EMR_SETVIEWPORTORGEX:
2245 case EMR_SETVIEWPORTEXTEX:
2246 case EMR_SCALEVIEWPORTEXTEX:
2247 case EMR_SCALEWINDOWEXTEX:
2248 case EMR_MODIFYWORLDTRANSFORM:
2249 FIXME("not implemented for record type %x\n", recordType);
2250 break;
2251 case EMR_SETWORLDTRANSFORM:
2253 const XFORM* xform = (void*)data;
2254 real_metafile->gdiworldtransform = *xform;
2255 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
2256 break;
2258 case EMR_EXTSELECTCLIPRGN:
2260 DWORD rgndatasize = *(DWORD*)data;
2261 DWORD mode = *(DWORD*)(data + 4);
2262 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
2263 HRGN hrgn = NULL;
2265 if (dataSize > 8)
2267 XFORM final;
2269 METAFILE_GetFinalGdiTransform(metafile, &final);
2271 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
2274 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
2276 DeleteObject(hrgn);
2278 return Ok;
2280 default:
2282 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2284 if (record)
2286 record->iType = recordType;
2287 record->nSize = dataSize + 8;
2288 memcpy(record->dParm, data, dataSize);
2290 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2291 record, metafile->handle_count) == 0)
2292 ERR("PlayEnhMetaFileRecord failed\n");
2294 heap_free(record);
2296 else
2297 return OutOfMemory;
2299 break;
2304 else
2306 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2308 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2310 switch(recordType)
2312 case EmfPlusRecordTypeHeader:
2313 case EmfPlusRecordTypeEndOfFile:
2314 break;
2315 case EmfPlusRecordTypeGetDC:
2316 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2317 break;
2318 case EmfPlusRecordTypeClear:
2320 EmfPlusClear *record = (EmfPlusClear*)header;
2322 if (dataSize != sizeof(record->Color))
2323 return InvalidParameter;
2325 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2327 case EmfPlusRecordTypeFillRects:
2329 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2330 GpBrush *brush, *temp_brush=NULL;
2331 GpRectF *rects, *temp_rects=NULL;
2333 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2334 return InvalidParameter;
2336 if (flags & 0x4000)
2338 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2339 return InvalidParameter;
2341 else
2343 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2344 return InvalidParameter;
2347 if (flags & 0x8000)
2349 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2350 brush = temp_brush;
2352 else
2354 if (record->BrushID >= EmfPlusObjectTableSize ||
2355 real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2356 return InvalidParameter;
2358 brush = real_metafile->objtable[record->BrushID].u.brush;
2359 stat = Ok;
2362 if (stat == Ok)
2364 if (flags & 0x4000)
2366 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2367 int i;
2369 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2370 if (rects)
2372 for (i=0; i<record->Count; i++)
2374 rects[i].X = int_rects[i].X;
2375 rects[i].Y = int_rects[i].Y;
2376 rects[i].Width = int_rects[i].Width;
2377 rects[i].Height = int_rects[i].Height;
2380 else
2381 stat = OutOfMemory;
2383 else
2384 rects = (GpRectF*)(record+1);
2387 if (stat == Ok)
2389 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2392 GdipDeleteBrush(temp_brush);
2393 heap_free(temp_rects);
2395 return stat;
2397 case EmfPlusRecordTypeSetClipRect:
2399 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2400 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2401 GpRegion *region;
2403 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2404 return InvalidParameter;
2406 stat = GdipCreateRegionRect(&record->ClipRect, &region);
2408 if (stat == Ok)
2410 stat = metafile_set_clip_region(real_metafile, region, mode);
2411 GdipDeleteRegion(region);
2414 return stat;
2416 case EmfPlusRecordTypeSetClipRegion:
2418 CombineMode mode = (flags >> 8) & 0xf;
2419 BYTE regionid = flags & 0xff;
2420 GpRegion *region;
2422 if (dataSize != 0)
2423 return InvalidParameter;
2425 if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2426 return InvalidParameter;
2428 stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2429 if (stat == Ok)
2431 stat = metafile_set_clip_region(real_metafile, region, mode);
2432 GdipDeleteRegion(region);
2435 return stat;
2437 case EmfPlusRecordTypeSetClipPath:
2439 CombineMode mode = (flags >> 8) & 0xf;
2440 BYTE pathid = flags & 0xff;
2441 GpRegion *region;
2443 if (dataSize != 0)
2444 return InvalidParameter;
2446 if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2447 return InvalidParameter;
2449 stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2450 if (stat == Ok)
2452 stat = metafile_set_clip_region(real_metafile, region, mode);
2453 GdipDeleteRegion(region);
2456 return stat;
2458 case EmfPlusRecordTypeSetPageTransform:
2460 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2461 GpUnit unit = (GpUnit)flags;
2463 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2464 return InvalidParameter;
2466 real_metafile->page_unit = unit;
2467 real_metafile->page_scale = record->PageScale;
2469 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2471 case EmfPlusRecordTypeSetWorldTransform:
2473 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2475 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2476 return InvalidParameter;
2478 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2480 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2482 case EmfPlusRecordTypeScaleWorldTransform:
2484 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2485 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2487 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2488 return InvalidParameter;
2490 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2492 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2494 case EmfPlusRecordTypeMultiplyWorldTransform:
2496 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2497 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2498 GpMatrix matrix;
2500 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2501 return InvalidParameter;
2503 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2505 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2507 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2509 case EmfPlusRecordTypeRotateWorldTransform:
2511 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2512 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2514 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2515 return InvalidParameter;
2517 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2519 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2521 case EmfPlusRecordTypeTranslateWorldTransform:
2523 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2524 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2526 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2527 return InvalidParameter;
2529 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2531 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2533 case EmfPlusRecordTypeResetWorldTransform:
2535 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2537 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2539 case EmfPlusRecordTypeBeginContainer:
2541 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2542 container* cont;
2543 GpUnit unit;
2544 REAL scale_x, scale_y;
2545 GpRectF scaled_srcrect;
2546 GpMatrix transform;
2548 cont = heap_alloc_zero(sizeof(*cont));
2549 if (!cont)
2550 return OutOfMemory;
2552 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2553 if (stat != Ok)
2555 heap_free(cont);
2556 return stat;
2559 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2561 if (stat != Ok)
2563 GdipDeleteRegion(cont->clip);
2564 heap_free(cont);
2565 return stat;
2568 cont->id = record->StackIndex;
2569 cont->type = BEGIN_CONTAINER;
2570 cont->world_transform = *metafile->world_transform;
2571 cont->page_unit = metafile->page_unit;
2572 cont->page_scale = metafile->page_scale;
2573 list_add_head(&real_metafile->containers, &cont->entry);
2575 unit = record->Header.Flags & 0xff;
2577 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
2578 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
2580 scaled_srcrect.X = scale_x * record->SrcRect.X;
2581 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2582 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2583 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2585 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2586 transform.matrix[1] = 0.0;
2587 transform.matrix[2] = 0.0;
2588 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2589 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2590 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2592 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2594 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2596 case EmfPlusRecordTypeBeginContainerNoParams:
2597 case EmfPlusRecordTypeSave:
2599 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2600 container* cont;
2602 cont = heap_alloc_zero(sizeof(*cont));
2603 if (!cont)
2604 return OutOfMemory;
2606 stat = GdipCloneRegion(metafile->clip, &cont->clip);
2607 if (stat != Ok)
2609 heap_free(cont);
2610 return stat;
2613 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2614 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2615 else
2616 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2618 if (stat != Ok)
2620 GdipDeleteRegion(cont->clip);
2621 heap_free(cont);
2622 return stat;
2625 cont->id = record->StackIndex;
2626 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2627 cont->type = BEGIN_CONTAINER;
2628 else
2629 cont->type = SAVE_GRAPHICS;
2630 cont->world_transform = *metafile->world_transform;
2631 cont->page_unit = metafile->page_unit;
2632 cont->page_scale = metafile->page_scale;
2633 list_add_head(&real_metafile->containers, &cont->entry);
2635 break;
2637 case EmfPlusRecordTypeEndContainer:
2638 case EmfPlusRecordTypeRestore:
2640 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2641 container* cont;
2642 enum container_type type;
2643 BOOL found=FALSE;
2645 if (recordType == EmfPlusRecordTypeEndContainer)
2646 type = BEGIN_CONTAINER;
2647 else
2648 type = SAVE_GRAPHICS;
2650 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2652 if (cont->id == record->StackIndex && cont->type == type)
2654 found = TRUE;
2655 break;
2659 if (found)
2661 container* cont2;
2663 /* pop any newer items on the stack */
2664 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2666 list_remove(&cont2->entry);
2667 GdipDeleteRegion(cont2->clip);
2668 heap_free(cont2);
2671 if (type == BEGIN_CONTAINER)
2672 GdipEndContainer(real_metafile->playback_graphics, cont->state);
2673 else
2674 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2676 *real_metafile->world_transform = cont->world_transform;
2677 real_metafile->page_unit = cont->page_unit;
2678 real_metafile->page_scale = cont->page_scale;
2679 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2681 list_remove(&cont->entry);
2682 GdipDeleteRegion(cont->clip);
2683 heap_free(cont);
2686 break;
2688 case EmfPlusRecordTypeSetPixelOffsetMode:
2690 return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2692 case EmfPlusRecordTypeSetCompositingQuality:
2694 return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2696 case EmfPlusRecordTypeSetInterpolationMode:
2698 return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2700 case EmfPlusRecordTypeSetTextRenderingHint:
2702 return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2704 case EmfPlusRecordTypeSetAntiAliasMode:
2706 return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2708 case EmfPlusRecordTypeObject:
2710 return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2712 case EmfPlusRecordTypeDrawImage:
2714 EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2715 BYTE image = flags & 0xff;
2716 GpPointF points[3];
2718 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2719 return InvalidParameter;
2721 if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2722 (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2723 return InvalidParameter;
2725 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2726 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2727 return InvalidParameter;
2729 if (flags & 0x4000) /* C */
2731 points[0].X = draw->RectData.rect.X;
2732 points[0].Y = draw->RectData.rect.Y;
2733 points[1].X = points[0].X + draw->RectData.rect.Width;
2734 points[1].Y = points[0].Y;
2735 points[2].X = points[1].X;
2736 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2738 else
2740 points[0].X = draw->RectData.rectF.X;
2741 points[0].Y = draw->RectData.rectF.Y;
2742 points[1].X = points[0].X + draw->RectData.rectF.Width;
2743 points[1].Y = points[0].Y;
2744 points[2].X = points[1].X;
2745 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2748 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2749 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2750 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2752 case EmfPlusRecordTypeDrawImagePoints:
2754 EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2755 static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
2756 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
2757 BYTE image = flags & 0xff;
2758 GpPointF points[3];
2759 unsigned int i;
2760 UINT size;
2762 if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2763 return InvalidParameter;
2765 if (dataSize <= fixed_part_size)
2766 return InvalidParameter;
2767 dataSize -= fixed_part_size;
2769 if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2770 real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2771 return InvalidParameter;
2773 if (draw->count != 3)
2774 return InvalidParameter;
2776 if ((flags >> 13) & 1) /* E */
2777 FIXME("image effects are not supported.\n");
2779 if ((flags >> 11) & 1) /* P */
2780 size = sizeof(EmfPlusPointR7) * draw->count;
2781 else if ((flags >> 14) & 1) /* C */
2782 size = sizeof(EmfPlusPoint) * draw->count;
2783 else
2784 size = sizeof(EmfPlusPointF) * draw->count;
2786 if (dataSize != size)
2787 return InvalidParameter;
2789 if ((flags >> 11) & 1) /* P */
2791 points[0].X = draw->PointData.pointsR[0].X;
2792 points[0].Y = draw->PointData.pointsR[0].Y;
2793 for (i = 1; i < 3; i++)
2795 points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
2796 points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
2799 else if ((flags >> 14) & 1) /* C */
2801 for (i = 0; i < 3; i++)
2803 points[i].X = draw->PointData.points[i].X;
2804 points[i].Y = draw->PointData.points[i].Y;
2807 else
2808 memcpy(points, draw->PointData.pointsF, sizeof(points));
2810 return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2811 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2812 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2814 case EmfPlusRecordTypeFillPath:
2816 EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
2817 GpSolidFill *solidfill = NULL;
2818 BYTE path = flags & 0xff;
2819 GpBrush *brush;
2821 if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
2822 return InvalidParameter;
2824 if (dataSize != sizeof(fill->data.BrushId))
2825 return InvalidParameter;
2827 if (flags & 0x8000)
2829 stat = GdipCreateSolidFill(fill->data.Color, (GpSolidFill **)&solidfill);
2830 if (stat != Ok)
2831 return stat;
2832 brush = (GpBrush *)solidfill;
2834 else
2836 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
2837 real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
2838 return InvalidParameter;
2840 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
2843 stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
2844 GdipDeleteBrush((GpBrush *)solidfill);
2845 return stat;
2847 case EmfPlusRecordTypeDrawPath:
2849 EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
2850 BYTE path = flags & 0xff;
2852 if (dataSize != sizeof(draw->PenId))
2853 return InvalidParameter;
2855 if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
2856 return InvalidParameter;
2858 if (real_metafile->objtable[path].type != ObjectTypePath ||
2859 real_metafile->objtable[draw->PenId].type != ObjectTypePen)
2860 return InvalidParameter;
2862 return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
2863 real_metafile->objtable[path].u.path);
2865 default:
2866 FIXME("Not implemented for record type %x\n", recordType);
2867 return NotImplemented;
2871 return Ok;
2874 struct enum_metafile_data
2876 EnumerateMetafileProc callback;
2877 void *callback_data;
2878 GpMetafile *metafile;
2881 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
2882 int nObj, LPARAM lpData)
2884 BOOL ret;
2885 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
2886 const BYTE* pStr;
2888 data->metafile->handle_table = lpHTable;
2889 data->metafile->handle_count = nObj;
2891 /* First check for an EMF+ record. */
2892 if (lpEMFR->iType == EMR_GDICOMMENT)
2894 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
2896 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
2898 int offset = 4;
2900 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
2902 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
2904 if (record->DataSize)
2905 pStr = (const BYTE*)(record+1);
2906 else
2907 pStr = NULL;
2909 ret = data->callback(record->Type, record->Flags, record->DataSize,
2910 pStr, data->callback_data);
2912 if (!ret)
2913 return 0;
2915 offset += record->Size;
2918 return 1;
2922 if (lpEMFR->nSize != 8)
2923 pStr = (const BYTE*)lpEMFR->dParm;
2924 else
2925 pStr = NULL;
2927 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
2928 pStr, data->callback_data);
2931 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
2932 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
2933 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
2934 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
2936 struct enum_metafile_data data;
2937 GpStatus stat;
2938 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
2939 GraphicsContainer state;
2940 GpPath *dst_path;
2942 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
2943 destPoints, count, srcRect, srcUnit, callback, callbackData,
2944 imageAttributes);
2946 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
2947 return InvalidParameter;
2949 if (!metafile->hemf)
2950 return InvalidParameter;
2952 if (metafile->playback_graphics)
2953 return ObjectBusy;
2955 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
2956 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
2957 debugstr_pointf(&destPoints[2]));
2959 data.callback = callback;
2960 data.callback_data = callbackData;
2961 data.metafile = real_metafile;
2963 real_metafile->playback_graphics = graphics;
2964 real_metafile->playback_dc = NULL;
2965 real_metafile->src_rect = *srcRect;
2967 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
2968 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
2970 if (stat == Ok)
2971 stat = GdipBeginContainer2(graphics, &state);
2973 if (stat == Ok)
2975 stat = GdipSetPageScale(graphics, 1.0);
2977 if (stat == Ok)
2978 stat = GdipSetPageUnit(graphics, UnitPixel);
2980 if (stat == Ok)
2981 stat = GdipResetWorldTransform(graphics);
2983 if (stat == Ok)
2984 stat = GdipCreateRegion(&real_metafile->base_clip);
2986 if (stat == Ok)
2987 stat = GdipGetClip(graphics, real_metafile->base_clip);
2989 if (stat == Ok)
2990 stat = GdipCreateRegion(&real_metafile->clip);
2992 if (stat == Ok)
2993 stat = GdipCreatePath(FillModeAlternate, &dst_path);
2995 if (stat == Ok)
2997 GpPointF clip_points[4];
2999 clip_points[0] = real_metafile->playback_points[0];
3000 clip_points[1] = real_metafile->playback_points[1];
3001 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3002 - real_metafile->playback_points[0].X;
3003 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3004 - real_metafile->playback_points[0].Y;
3005 clip_points[3] = real_metafile->playback_points[2];
3007 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3009 if (stat == Ok)
3010 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3012 GdipDeletePath(dst_path);
3015 if (stat == Ok)
3016 stat = GdipCreateMatrix(&real_metafile->world_transform);
3018 if (stat == Ok)
3020 real_metafile->page_unit = UnitDisplay;
3021 real_metafile->page_scale = 1.0;
3022 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3025 if (stat == Ok)
3027 stat = METAFILE_PlaybackUpdateClip(real_metafile);
3030 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
3031 metafile->metafile_type == MetafileTypeWmfPlaceable ||
3032 metafile->metafile_type == MetafileTypeWmf))
3033 stat = METAFILE_PlaybackGetDC(real_metafile);
3035 if (stat == Ok)
3036 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
3038 METAFILE_PlaybackReleaseDC(real_metafile);
3040 GdipDeleteMatrix(real_metafile->world_transform);
3041 real_metafile->world_transform = NULL;
3043 GdipDeleteRegion(real_metafile->base_clip);
3044 real_metafile->base_clip = NULL;
3046 GdipDeleteRegion(real_metafile->clip);
3047 real_metafile->clip = NULL;
3049 while (list_head(&real_metafile->containers))
3051 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3052 list_remove(&cont->entry);
3053 GdipDeleteRegion(cont->clip);
3054 heap_free(cont);
3057 GdipEndContainer(graphics, state);
3060 real_metafile->playback_graphics = NULL;
3062 return stat;
3065 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3066 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3067 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3069 GpPointF points[3];
3071 if (!graphics || !metafile || !dest) return InvalidParameter;
3073 points[0].X = points[2].X = dest->X;
3074 points[0].Y = points[1].Y = dest->Y;
3075 points[1].X = dest->X + dest->Width;
3076 points[2].Y = dest->Y + dest->Height;
3078 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3079 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3082 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3083 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3084 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3086 GpRectF destf;
3088 if (!graphics || !metafile || !dest) return InvalidParameter;
3090 destf.X = dest->X;
3091 destf.Y = dest->Y;
3092 destf.Width = dest->Width;
3093 destf.Height = dest->Height;
3095 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3098 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3099 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3100 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3102 GpRectF destf;
3104 if (!graphics || !metafile || !dest) return InvalidParameter;
3106 destf.X = dest->X;
3107 destf.Y = dest->Y;
3108 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
3109 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
3111 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3114 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3115 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3116 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3118 GpPointF ptf;
3120 if (!graphics || !metafile || !dest) return InvalidParameter;
3122 ptf.X = dest->X;
3123 ptf.Y = dest->Y;
3125 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3128 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3129 MetafileHeader * header)
3131 GpStatus status;
3133 TRACE("(%p, %p)\n", metafile, header);
3135 if(!metafile || !header)
3136 return InvalidParameter;
3138 if (metafile->hemf)
3140 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3141 if (status != Ok) return status;
3143 else
3145 memset(header, 0, sizeof(*header));
3146 header->Version = VERSION_MAGIC2;
3149 header->Type = metafile->metafile_type;
3150 header->DpiX = metafile->image.xres;
3151 header->DpiY = metafile->image.yres;
3152 header->Width = gdip_round(metafile->bounds.Width);
3153 header->Height = gdip_round(metafile->bounds.Height);
3155 return Ok;
3158 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3159 int nObj, LPARAM lpData)
3161 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3163 if (lpEMFR->iType == EMR_GDICOMMENT)
3165 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3167 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3169 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3171 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3172 header->Type == EmfPlusRecordTypeHeader)
3174 memcpy(dst_header, header, sizeof(*dst_header));
3178 else if (lpEMFR->iType == EMR_HEADER)
3179 return TRUE;
3181 return FALSE;
3184 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3185 MetafileHeader *header)
3187 ENHMETAHEADER3 emfheader;
3188 EmfPlusHeader emfplusheader;
3189 MetafileType metafile_type;
3191 TRACE("(%p,%p)\n", hemf, header);
3193 if(!hemf || !header)
3194 return InvalidParameter;
3196 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3197 return GenericError;
3199 emfplusheader.Header.Type = 0;
3201 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3203 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3205 if ((emfplusheader.Header.Flags & 1) == 1)
3206 metafile_type = MetafileTypeEmfPlusDual;
3207 else
3208 metafile_type = MetafileTypeEmfPlusOnly;
3210 else
3211 metafile_type = MetafileTypeEmf;
3213 header->Type = metafile_type;
3214 header->Size = emfheader.nBytes;
3215 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3216 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3217 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3218 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3219 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3220 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3221 header->u.EmfHeader = emfheader;
3223 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3225 header->Version = emfplusheader.Version;
3226 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3227 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3228 header->LogicalDpiX = emfplusheader.LogicalDpiX;
3229 header->LogicalDpiY = emfplusheader.LogicalDpiY;
3231 else
3233 header->Version = emfheader.nVersion;
3234 header->EmfPlusFlags = 0;
3235 header->EmfPlusHeaderSize = 0;
3236 header->LogicalDpiX = 0;
3237 header->LogicalDpiY = 0;
3240 return Ok;
3243 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3244 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3246 GpStatus status;
3247 GpMetafile *metafile;
3249 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3251 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3252 if (status == Ok)
3254 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3255 GdipDisposeImage(&metafile->image);
3257 return status;
3260 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3261 MetafileHeader *header)
3263 GpStatus status;
3264 GpMetafile *metafile;
3266 TRACE("(%s,%p)\n", debugstr_w(filename), header);
3268 if (!filename || !header)
3269 return InvalidParameter;
3271 status = GdipCreateMetafileFromFile(filename, &metafile);
3272 if (status == Ok)
3274 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3275 GdipDisposeImage(&metafile->image);
3277 return status;
3280 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3281 MetafileHeader *header)
3283 GpStatus status;
3284 GpMetafile *metafile;
3286 TRACE("(%p,%p)\n", stream, header);
3288 if (!stream || !header)
3289 return InvalidParameter;
3291 status = GdipCreateMetafileFromStream(stream, &metafile);
3292 if (status == Ok)
3294 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3295 GdipDisposeImage(&metafile->image);
3297 return status;
3300 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3301 GpMetafile **metafile)
3303 GpStatus stat;
3304 MetafileHeader header;
3306 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3308 if(!hemf || !metafile)
3309 return InvalidParameter;
3311 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3312 if (stat != Ok)
3313 return stat;
3315 *metafile = heap_alloc_zero(sizeof(GpMetafile));
3316 if (!*metafile)
3317 return OutOfMemory;
3319 (*metafile)->image.type = ImageTypeMetafile;
3320 (*metafile)->image.format = ImageFormatEMF;
3321 (*metafile)->image.frame_count = 1;
3322 (*metafile)->image.xres = header.DpiX;
3323 (*metafile)->image.yres = header.DpiY;
3324 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3325 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3326 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3327 / 2540.0 * header.DpiX;
3328 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3329 / 2540.0 * header.DpiY;
3330 (*metafile)->unit = UnitPixel;
3331 (*metafile)->metafile_type = header.Type;
3332 (*metafile)->hemf = hemf;
3333 (*metafile)->preserve_hemf = !delete;
3334 list_init(&(*metafile)->containers);
3336 TRACE("<-- %p\n", *metafile);
3338 return Ok;
3341 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3342 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3344 UINT read;
3345 BYTE *copy;
3346 HENHMETAFILE hemf;
3347 GpStatus retval = Ok;
3349 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3351 if(!hwmf || !metafile)
3352 return InvalidParameter;
3354 *metafile = NULL;
3355 read = GetMetaFileBitsEx(hwmf, 0, NULL);
3356 if(!read)
3357 return GenericError;
3358 copy = heap_alloc_zero(read);
3359 GetMetaFileBitsEx(hwmf, read, copy);
3361 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3362 heap_free(copy);
3364 /* FIXME: We should store and use hwmf instead of converting to hemf */
3365 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
3367 if (retval == Ok)
3369 if (placeable)
3371 (*metafile)->image.xres = (REAL)placeable->Inch;
3372 (*metafile)->image.yres = (REAL)placeable->Inch;
3373 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
3374 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
3375 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
3376 placeable->BoundingBox.Left);
3377 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
3378 placeable->BoundingBox.Top);
3379 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
3381 else
3382 (*metafile)->metafile_type = MetafileTypeWmf;
3383 (*metafile)->image.format = ImageFormatWMF;
3385 if (delete) DeleteMetaFile(hwmf);
3387 else
3388 DeleteEnhMetaFile(hemf);
3389 return retval;
3392 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
3393 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3395 HMETAFILE hmf;
3396 HENHMETAFILE emf;
3398 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
3400 hmf = GetMetaFileW(file);
3401 if(hmf)
3402 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
3404 emf = GetEnhMetaFileW(file);
3405 if(emf)
3406 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
3408 return GenericError;
3411 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
3412 GpMetafile **metafile)
3414 GpStatus status;
3415 IStream *stream;
3417 TRACE("(%p, %p)\n", file, metafile);
3419 if (!file || !metafile) return InvalidParameter;
3421 *metafile = NULL;
3423 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
3424 if (status == Ok)
3426 status = GdipCreateMetafileFromStream(stream, metafile);
3427 IStream_Release(stream);
3429 return status;
3432 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
3433 GpMetafile **metafile)
3435 GpStatus stat;
3437 TRACE("%p %p\n", stream, metafile);
3439 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
3440 if (stat != Ok) return stat;
3442 if ((*metafile)->image.type != ImageTypeMetafile)
3444 GdipDisposeImage(&(*metafile)->image);
3445 *metafile = NULL;
3446 return GenericError;
3449 return Ok;
3452 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3453 UINT limitDpi)
3455 TRACE("(%p,%u)\n", metafile, limitDpi);
3457 return Ok;
3460 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
3461 GpMetafile* metafile, BOOL* succ, EmfType emfType,
3462 const WCHAR* description, GpMetafile** out_metafile)
3464 static int calls;
3466 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
3467 debugstr_w(description), out_metafile);
3469 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
3470 return InvalidParameter;
3472 if(succ)
3473 *succ = FALSE;
3474 *out_metafile = NULL;
3476 if(!(calls++))
3477 FIXME("not implemented\n");
3479 return NotImplemented;
3482 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
3483 LPBYTE pData16, INT iMapMode, INT eFlags)
3485 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
3486 return NotImplemented;
3489 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
3490 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
3491 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
3492 GpMetafile **metafile)
3494 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3495 frameUnit, debugstr_w(desc), metafile);
3497 return NotImplemented;
3500 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
3501 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
3502 GDIPCONST WCHAR *desc, GpMetafile **metafile)
3504 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3505 frameUnit, debugstr_w(desc), metafile);
3507 return NotImplemented;
3510 /*****************************************************************************
3511 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
3514 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
3515 GpMetafile* metafile, BOOL* conversionSuccess,
3516 const WCHAR* filename, EmfType emfType,
3517 const WCHAR* description, GpMetafile** out_metafile)
3519 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
3520 return NotImplemented;
3523 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
3525 LARGE_INTEGER zero;
3526 STATSTG statstg;
3527 GpStatus stat;
3528 HRESULT hr;
3530 *size = 0;
3532 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
3533 if (FAILED(hr)) return hresult_to_status(hr);
3535 stat = encode_image_png(image, *stream, NULL);
3536 if (stat != Ok)
3538 IStream_Release(*stream);
3539 return stat;
3542 hr = IStream_Stat(*stream, &statstg, 1);
3543 if (FAILED(hr))
3545 IStream_Release(*stream);
3546 return hresult_to_status(hr);
3548 *size = statstg.cbSize.u.LowPart;
3550 zero.QuadPart = 0;
3551 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
3552 if (FAILED(hr))
3554 IStream_Release(*stream);
3555 return hresult_to_status(hr);
3558 return Ok;
3561 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
3563 HRESULT hr;
3565 record->Width = 0;
3566 record->Height = 0;
3567 record->Stride = 0;
3568 record->PixelFormat = 0;
3569 record->Type = BitmapDataTypeCompressed;
3571 hr = IStream_Read(stream, record->BitmapData, size, NULL);
3572 if (FAILED(hr)) return hresult_to_status(hr);
3573 return Ok;
3576 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
3578 EmfPlusObject *object_record;
3579 GpStatus stat;
3580 DWORD size;
3582 *id = -1;
3584 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3585 return Ok;
3587 if (image->type == ImageTypeBitmap)
3589 IStream *stream;
3590 DWORD aligned_size;
3592 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
3593 if (stat != Ok) return stat;
3594 aligned_size = (size + 3) & ~3;
3596 stat = METAFILE_AllocateRecord(metafile,
3597 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
3598 (void**)&object_record);
3599 if (stat != Ok)
3601 IStream_Release(stream);
3602 return stat;
3604 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
3606 *id = METAFILE_AddObjectId(metafile);
3607 object_record->Header.Type = EmfPlusRecordTypeObject;
3608 object_record->Header.Flags = *id | ObjectTypeImage << 8;
3609 object_record->ObjectData.image.Version = VERSION_MAGIC2;
3610 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
3612 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
3613 IStream_Release(stream);
3614 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
3615 return stat;
3617 else if (image->type == ImageTypeMetafile)
3619 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
3620 EmfPlusMetafile *metafile_record;
3622 if (!hemf) return InvalidParameter;
3624 size = GetEnhMetaFileBits(hemf, 0, NULL);
3625 if (!size) return GenericError;
3627 stat = METAFILE_AllocateRecord(metafile,
3628 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
3629 (void**)&object_record);
3630 if (stat != Ok) return stat;
3632 *id = METAFILE_AddObjectId(metafile);
3633 object_record->Header.Type = EmfPlusRecordTypeObject;
3634 object_record->Header.Flags = *id | ObjectTypeImage << 8;
3635 object_record->ObjectData.image.Version = VERSION_MAGIC2;
3636 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
3637 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
3638 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
3639 metafile_record->MetafileDataSize = size;
3640 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
3642 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
3643 return GenericError;
3645 return Ok;
3647 else
3649 FIXME("not supported image type (%d)\n", image->type);
3650 return NotImplemented;
3654 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
3656 EmfPlusObject *object_record;
3657 EmfPlusImageAttributes *attrs_record;
3658 GpStatus stat;
3660 *id = -1;
3662 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3663 return Ok;
3665 if (!attrs)
3666 return Ok;
3668 stat = METAFILE_AllocateRecord(metafile,
3669 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
3670 (void**)&object_record);
3671 if (stat != Ok) return stat;
3673 *id = METAFILE_AddObjectId(metafile);
3674 object_record->Header.Type = EmfPlusRecordTypeObject;
3675 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
3676 attrs_record = &object_record->ObjectData.image_attributes;
3677 attrs_record->Version = VERSION_MAGIC2;
3678 attrs_record->Reserved1 = 0;
3679 attrs_record->WrapMode = attrs->wrap;
3680 attrs_record->ClampColor = attrs->outside_color;
3681 attrs_record->ObjectClamp = attrs->clamp;
3682 attrs_record->Reserved2 = 0;
3683 return Ok;
3686 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
3687 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
3688 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
3689 DrawImageAbort callback, VOID *callbackData)
3691 EmfPlusDrawImagePoints *draw_image_record;
3692 DWORD image_id, attributes_id;
3693 GpStatus stat;
3695 if (count != 3) return InvalidParameter;
3697 if (metafile->metafile_type == MetafileTypeEmf)
3699 FIXME("MetafileTypeEmf metafiles not supported\n");
3700 return NotImplemented;
3702 else
3703 FIXME("semi-stub\n");
3705 if (!imageAttributes)
3707 stat = METAFILE_AddImageObject(metafile, image, &image_id);
3709 else if (image->type == ImageTypeBitmap)
3711 INT width = ((GpBitmap*)image)->width;
3712 INT height = ((GpBitmap*)image)->height;
3713 GpGraphics *graphics;
3714 GpBitmap *bitmap;
3716 stat = GdipCreateBitmapFromScan0(width, height,
3717 0, PixelFormat32bppARGB, NULL, &bitmap);
3718 if (stat != Ok) return stat;
3720 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
3721 if (stat != Ok)
3723 GdipDisposeImage((GpImage*)bitmap);
3724 return stat;
3727 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
3728 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
3729 GdipDeleteGraphics(graphics);
3730 if (stat != Ok)
3732 GdipDisposeImage((GpImage*)bitmap);
3733 return stat;
3736 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
3737 GdipDisposeImage((GpImage*)bitmap);
3739 else
3741 FIXME("imageAttributes not supported (image type %d)\n", image->type);
3742 return NotImplemented;
3744 if (stat != Ok) return stat;
3746 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
3747 if (stat != Ok) return stat;
3749 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
3750 if (stat != Ok) return stat;
3751 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
3752 draw_image_record->Header.Flags = image_id;
3753 draw_image_record->ImageAttributesID = attributes_id;
3754 draw_image_record->SrcUnit = UnitPixel;
3755 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
3756 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
3757 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
3758 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
3759 draw_image_record->count = 3;
3760 memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
3761 METAFILE_WriteRecords(metafile);
3762 return Ok;
3765 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
3767 EmfPlusRecordHeader *record;
3768 GpStatus stat;
3770 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3771 return Ok;
3773 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
3774 if (stat != Ok) return stat;
3776 record->Type = prop;
3777 record->Flags = val;
3779 METAFILE_WriteRecords(metafile);
3780 return Ok;
3783 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
3785 EmfPlusObject *object_record;
3786 GpStatus stat;
3787 DWORD size;
3789 *id = -1;
3790 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3791 return Ok;
3793 size = write_path_data(path, NULL);
3794 stat = METAFILE_AllocateRecord(metafile,
3795 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
3796 (void**)&object_record);
3797 if (stat != Ok) return stat;
3799 *id = METAFILE_AddObjectId(metafile);
3800 object_record->Header.Type = EmfPlusRecordTypeObject;
3801 object_record->Header.Flags = *id | ObjectTypePath << 8;
3802 write_path_data(path, &object_record->ObjectData.path);
3803 return Ok;
3806 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
3808 switch (brush->bt)
3810 case BrushTypeSolidColor:
3811 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
3812 break;
3813 case BrushTypeHatchFill:
3814 *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
3815 break;
3816 default:
3817 FIXME("unsupported brush type: %d\n", brush->bt);
3818 return NotImplemented;
3821 return Ok;
3824 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
3826 data->Version = VERSION_MAGIC2;
3827 data->Type = brush->bt;
3829 switch (brush->bt)
3831 case BrushTypeSolidColor:
3833 GpSolidFill *solid = (GpSolidFill *)brush;
3834 data->BrushData.solid.SolidColor = solid->color;
3835 break;
3837 case BrushTypeHatchFill:
3839 GpHatch *hatch = (GpHatch *)brush;
3840 data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
3841 data->BrushData.hatch.ForeColor = hatch->forecol;
3842 data->BrushData.hatch.BackColor = hatch->backcol;
3843 break;
3845 default:
3846 FIXME("unsupported brush type: %d\n", brush->bt);
3850 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
3852 DWORD i, data_flags, pen_data_size, brush_size;
3853 EmfPlusObject *object_record;
3854 EmfPlusPenData *pen_data;
3855 GpStatus stat;
3856 BOOL result;
3858 *id = -1;
3859 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3860 return Ok;
3862 data_flags = 0;
3863 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
3865 GdipIsMatrixIdentity(&pen->transform, &result);
3866 if (!result)
3868 data_flags |= PenDataTransform;
3869 pen_data_size += sizeof(EmfPlusTransformMatrix);
3871 if (pen->startcap != LineCapFlat)
3873 data_flags |= PenDataStartCap;
3874 pen_data_size += sizeof(DWORD);
3876 if (pen->endcap != LineCapFlat)
3878 data_flags |= PenDataEndCap;
3879 pen_data_size += sizeof(DWORD);
3881 if (pen->join != LineJoinMiter)
3883 data_flags |= PenDataJoin;
3884 pen_data_size += sizeof(DWORD);
3886 if (pen->miterlimit != 10.0)
3888 data_flags |= PenDataMiterLimit;
3889 pen_data_size += sizeof(REAL);
3891 if (pen->style != GP_DEFAULT_PENSTYLE)
3893 data_flags |= PenDataLineStyle;
3894 pen_data_size += sizeof(DWORD);
3896 if (pen->dashcap != DashCapFlat)
3898 data_flags |= PenDataDashedLineCap;
3899 pen_data_size += sizeof(DWORD);
3901 data_flags |= PenDataDashedLineOffset;
3902 pen_data_size += sizeof(REAL);
3903 if (pen->numdashes)
3905 data_flags |= PenDataDashedLine;
3906 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
3908 if (pen->align != PenAlignmentCenter)
3910 data_flags |= PenDataNonCenter;
3911 pen_data_size += sizeof(DWORD);
3913 /* TODO: Add support for PenDataCompoundLine */
3914 if (pen->customstart)
3916 FIXME("ignoring custom start cup\n");
3918 if (pen->customend)
3920 FIXME("ignoring custom end cup\n");
3923 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
3924 if (stat != Ok) return stat;
3926 stat = METAFILE_AllocateRecord(metafile,
3927 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
3928 (void**)&object_record);
3929 if (stat != Ok) return stat;
3931 *id = METAFILE_AddObjectId(metafile);
3932 object_record->Header.Type = EmfPlusRecordTypeObject;
3933 object_record->Header.Flags = *id | ObjectTypePen << 8;
3934 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
3935 object_record->ObjectData.pen.Type = 0;
3937 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
3938 pen_data->PenDataFlags = data_flags;
3939 pen_data->PenUnit = pen->unit;
3940 pen_data->PenWidth = pen->width;
3942 i = 0;
3943 if (data_flags & PenDataTransform)
3945 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
3946 memcpy(m, &pen->transform, sizeof(*m));
3947 i += sizeof(EmfPlusTransformMatrix);
3949 if (data_flags & PenDataStartCap)
3951 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
3952 i += sizeof(DWORD);
3954 if (data_flags & PenDataEndCap)
3956 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
3957 i += sizeof(DWORD);
3959 if (data_flags & PenDataJoin)
3961 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
3962 i += sizeof(DWORD);
3964 if (data_flags & PenDataMiterLimit)
3966 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
3967 i += sizeof(REAL);
3969 if (data_flags & PenDataLineStyle)
3971 switch (pen->style & PS_STYLE_MASK)
3973 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
3974 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
3975 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
3976 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
3977 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
3978 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
3980 i += sizeof(DWORD);
3982 if (data_flags & PenDataDashedLineCap)
3984 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
3985 i += sizeof(DWORD);
3987 if (data_flags & PenDataDashedLineOffset)
3989 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
3990 i += sizeof(REAL);
3992 if (data_flags & PenDataDashedLine)
3994 int j;
3996 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
3997 i += sizeof(DWORD);
3999 for (j=0; j<pen->numdashes; j++)
4001 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4002 i += sizeof(REAL);
4005 if (data_flags & PenDataNonCenter)
4007 *(REAL*)(pen_data->OptionalData + i) = pen->align;
4008 i += sizeof(DWORD);
4011 METAFILE_FillBrushData(pen->brush,
4012 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4013 return Ok;
4016 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4018 EmfPlusDrawPath *draw_path_record;
4019 DWORD path_id;
4020 DWORD pen_id;
4021 GpStatus stat;
4023 if (metafile->metafile_type == MetafileTypeEmf)
4025 FIXME("stub!\n");
4026 return NotImplemented;
4029 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4030 if (stat != Ok) return stat;
4032 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4033 if (stat != Ok) return stat;
4035 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4036 if (stat != Ok) return stat;
4037 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4038 draw_path_record->Header.Flags = path_id;
4039 draw_path_record->PenId = pen_id;
4041 METAFILE_WriteRecords(metafile);
4042 return Ok;
4045 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
4047 EmfPlusObject *object_record;
4048 GpStatus stat;
4049 DWORD size;
4051 *id = -1;
4052 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4053 return Ok;
4055 stat = METAFILE_PrepareBrushData(brush, &size);
4056 if (stat != Ok) return stat;
4058 stat = METAFILE_AllocateRecord(metafile,
4059 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
4060 if (stat != Ok) return stat;
4062 *id = METAFILE_AddObjectId(metafile);
4063 object_record->Header.Type = EmfPlusRecordTypeObject;
4064 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
4065 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
4066 return Ok;
4069 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4071 EmfPlusFillPath *fill_path_record;
4072 DWORD brush_id = -1, path_id;
4073 BOOL inline_color;
4074 GpStatus stat;
4076 if (metafile->metafile_type == MetafileTypeEmf)
4078 FIXME("stub!\n");
4079 return NotImplemented;
4082 inline_color = brush->bt == BrushTypeSolidColor;
4083 if (!inline_color)
4085 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4086 if (stat != Ok) return stat;
4089 stat = METAFILE_AddPathObject(metafile, path, &path_id);
4090 if (stat != Ok) return stat;
4092 stat = METAFILE_AllocateRecord(metafile,
4093 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4094 if (stat != Ok) return stat;
4095 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4096 if (inline_color)
4098 fill_path_record->Header.Flags = 0x8000 | path_id;
4099 fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4101 else
4103 fill_path_record->Header.Flags = path_id;
4104 fill_path_record->data.BrushId = brush_id;
4107 METAFILE_WriteRecords(metafile);
4108 return Ok;