shell32/tests: Add tests for FolderItems_Item and FolderItems_get_Count.
[wine.git] / dlls / gdiplus / metafile.c
blob6260cb3c187659cec7ea9e24149f9d52f7b19be9
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 typedef struct EmfPlusARGB
48 BYTE Blue;
49 BYTE Green;
50 BYTE Red;
51 BYTE Alpha;
52 } EmfPlusARGB;
54 typedef struct EmfPlusRecordHeader
56 WORD Type;
57 WORD Flags;
58 DWORD Size;
59 DWORD DataSize;
60 } EmfPlusRecordHeader;
62 typedef struct EmfPlusHeader
64 EmfPlusRecordHeader Header;
65 DWORD Version;
66 DWORD EmfPlusFlags;
67 DWORD LogicalDpiX;
68 DWORD LogicalDpiY;
69 } EmfPlusHeader;
71 typedef struct EmfPlusClear
73 EmfPlusRecordHeader Header;
74 DWORD Color;
75 } EmfPlusClear;
77 typedef struct EmfPlusFillRects
79 EmfPlusRecordHeader Header;
80 DWORD BrushID;
81 DWORD Count;
82 } EmfPlusFillRects;
84 typedef struct EmfPlusSetClipRect
86 EmfPlusRecordHeader Header;
87 GpRectF ClipRect;
88 } EmfPlusSetClipRect;
90 typedef struct EmfPlusSetPageTransform
92 EmfPlusRecordHeader Header;
93 REAL PageScale;
94 } EmfPlusSetPageTransform;
96 typedef struct EmfPlusRect
98 SHORT X;
99 SHORT Y;
100 SHORT Width;
101 SHORT Height;
102 } EmfPlusRect;
104 typedef struct EmfPlusSetWorldTransform
106 EmfPlusRecordHeader Header;
107 REAL MatrixData[6];
108 } EmfPlusSetWorldTransform;
110 typedef struct EmfPlusScaleWorldTransform
112 EmfPlusRecordHeader Header;
113 REAL Sx;
114 REAL Sy;
115 } EmfPlusScaleWorldTransform;
117 typedef struct EmfPlusMultiplyWorldTransform
119 EmfPlusRecordHeader Header;
120 REAL MatrixData[6];
121 } EmfPlusMultiplyWorldTransform;
123 typedef struct EmfPlusRotateWorldTransform
125 EmfPlusRecordHeader Header;
126 REAL Angle;
127 } EmfPlusRotateWorldTransform;
129 typedef struct EmfPlusTranslateWorldTransform
131 EmfPlusRecordHeader Header;
132 REAL dx;
133 REAL dy;
134 } EmfPlusTranslateWorldTransform;
136 typedef struct EmfPlusBeginContainer
138 EmfPlusRecordHeader Header;
139 GpRectF DestRect;
140 GpRectF SrcRect;
141 DWORD StackIndex;
142 } EmfPlusBeginContainer;
144 typedef struct EmfPlusContainerRecord
146 EmfPlusRecordHeader Header;
147 DWORD StackIndex;
148 } EmfPlusContainerRecord;
150 enum container_type
152 BEGIN_CONTAINER,
153 SAVE_GRAPHICS
156 typedef struct container
158 struct list entry;
159 DWORD id;
160 enum container_type type;
161 GraphicsContainer state;
162 GpMatrix world_transform;
163 GpUnit page_unit;
164 REAL page_scale;
165 GpRegion *clip;
166 } container;
168 enum PenDataFlags
170 PenDataTransform = 0x0001,
171 PenDataStartCap = 0x0002,
172 PenDataEndCap = 0x0004,
173 PenDataJoin = 0x0008,
174 PenDataMiterLimit = 0x0010,
175 PenDataLineStyle = 0x0020,
176 PenDataDashedLineCap = 0x0040,
177 PenDataDashedLineOffset = 0x0080,
178 PenDataDashedLine = 0x0100,
179 PenDataNonCenter = 0x0200,
180 PenDataCompoundLine = 0x0400,
181 PenDataCustomStartCap = 0x0800,
182 PenDataCustomEndCap = 0x1000
185 typedef struct EmfPlusTransformMatrix
187 REAL TransformMatrix[6];
188 } EmfPlusTransformMatrix;
190 enum LineStyle
192 LineStyleSolid,
193 LineStyleDash,
194 LineStyleDot,
195 LineStyleDashDot,
196 LineStyleDashDotDot,
197 LineStyleCustom
200 typedef struct EmfPlusPenData
202 DWORD PenDataFlags;
203 DWORD PenUnit;
204 REAL PenWidth;
205 BYTE OptionalData[1];
206 } EmfPlusPenData;
208 typedef struct EmfPlusSolidBrushData
210 EmfPlusARGB SolidColor;
211 } EmfPlusSolidBrushData;
213 typedef struct EmfPlusBrush
215 DWORD Version;
216 DWORD Type;
217 union {
218 EmfPlusSolidBrushData solid;
219 } BrushData;
220 } EmfPlusBrush;
222 typedef struct EmfPlusPen
224 DWORD Version;
225 DWORD Type;
226 /* EmfPlusPenData */
227 /* EmfPlusBrush */
228 BYTE data[1];
229 } EmfPlusPen;
231 typedef struct EmfPlusPath
233 DWORD Version;
234 DWORD PathPointCount;
235 DWORD PathPointFlags;
236 /* PathPoints[] */
237 /* PathPointTypes[] */
238 /* AlignmentPadding */
239 BYTE data[1];
240 } EmfPlusPath;
242 typedef struct EmfPlusRegion
244 DWORD Version;
245 DWORD RegionNodeCount;
246 BYTE RegionNode[1];
247 } EmfPlusRegion;
249 typedef enum
251 BitmapDataTypePixel,
252 BitmapDataTypeCompressed,
253 } BitmapDataType;
255 typedef struct EmfPlusBitmap
257 DWORD Width;
258 DWORD Height;
259 DWORD Stride;
260 DWORD PixelFormat;
261 DWORD Type;
262 BYTE BitmapData[1];
263 } EmfPlusBitmap;
265 typedef struct EmfPlusMetafile
267 DWORD Type;
268 DWORD MetafileDataSize;
269 BYTE MetafileData[1];
270 } EmfPlusMetafile;
272 typedef enum ImageDataType
274 ImageDataTypeUnknown,
275 ImageDataTypeBitmap,
276 ImageDataTypeMetafile,
277 } ImageDataType;
279 typedef struct EmfPlusImage
281 DWORD Version;
282 ImageDataType Type;
283 union
285 EmfPlusBitmap bitmap;
286 EmfPlusMetafile metafile;
287 } ImageData;
288 } EmfPlusImage;
290 typedef struct EmfPlusImageAttributes
292 DWORD Version;
293 DWORD Reserved1;
294 DWORD WrapMode;
295 EmfPlusARGB ClampColor;
296 DWORD ObjectClamp;
297 DWORD Reserved2;
298 } EmfPlusImageAttributes;
300 typedef enum ObjectType
302 ObjectTypeInvalid,
303 ObjectTypeBrush,
304 ObjectTypePen,
305 ObjectTypePath,
306 ObjectTypeRegion,
307 ObjectTypeImage,
308 ObjectTypeFont,
309 ObjectTypeStringFormat,
310 ObjectTypeImageAttributes,
311 ObjectTypeCustomLineCap,
312 } ObjectType;
314 typedef struct EmfPlusObject
316 EmfPlusRecordHeader Header;
317 union
319 EmfPlusBrush brush;
320 EmfPlusPen pen;
321 EmfPlusPath path;
322 EmfPlusRegion region;
323 EmfPlusImage image;
324 EmfPlusImageAttributes image_attributes;
325 } ObjectData;
326 } EmfPlusObject;
328 typedef struct EmfPlusRectF
330 float X;
331 float Y;
332 float Width;
333 float Height;
334 } EmfPlusRectF;
336 typedef struct EmfPlusPointF
338 float X;
339 float Y;
340 } EmfPlusPointF;
342 typedef struct EmfPlusDrawImagePoints
344 EmfPlusRecordHeader Header;
345 DWORD ImageAttributesID;
346 DWORD SrcUnit;
347 EmfPlusRectF SrcRect;
348 DWORD count;
349 union
351 /*EmfPlusPointR pointR;
352 EmfPlusPoint point;*/
353 EmfPlusPointF pointF;
354 } PointData[3];
355 } EmfPlusDrawImagePoints;
357 typedef struct EmfPlusDrawPath
359 EmfPlusRecordHeader Header;
360 DWORD PenId;
361 } EmfPlusDrawPath;
363 typedef struct EmfPlusFillPath
365 EmfPlusRecordHeader Header;
366 union
368 DWORD BrushId;
369 EmfPlusARGB Color;
370 } data;
371 } EmfPlusFillPath;
373 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
375 return (metafile->next_object_id++) % 64;
378 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
380 DWORD size_needed;
381 EmfPlusRecordHeader *record;
383 if (!metafile->comment_data_size)
385 DWORD data_size = max(256, size * 2 + 4);
386 metafile->comment_data = heap_alloc_zero(data_size);
388 if (!metafile->comment_data)
389 return OutOfMemory;
391 memcpy(metafile->comment_data, "EMF+", 4);
393 metafile->comment_data_size = data_size;
394 metafile->comment_data_length = 4;
397 size_needed = size + metafile->comment_data_length;
399 if (size_needed > metafile->comment_data_size)
401 DWORD data_size = size_needed * 2;
402 BYTE *new_data = heap_alloc_zero(data_size);
404 if (!new_data)
405 return OutOfMemory;
407 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
409 metafile->comment_data_size = data_size;
410 heap_free(metafile->comment_data);
411 metafile->comment_data = new_data;
414 *result = metafile->comment_data + metafile->comment_data_length;
415 metafile->comment_data_length += size;
417 record = (EmfPlusRecordHeader*)*result;
418 record->Size = size;
419 record->DataSize = size - sizeof(EmfPlusRecordHeader);
421 return Ok;
424 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
426 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
427 metafile->comment_data_length -= record->Size;
430 static void METAFILE_WriteRecords(GpMetafile *metafile)
432 if (metafile->comment_data_length > 4)
434 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
435 metafile->comment_data_length = 4;
439 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
441 GpStatus stat;
443 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
445 EmfPlusHeader *header;
447 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
448 if (stat != Ok)
449 return stat;
451 header->Header.Type = EmfPlusRecordTypeHeader;
453 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
454 header->Header.Flags = 1;
455 else
456 header->Header.Flags = 0;
458 header->Version = VERSION_MAGIC2;
460 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
461 header->EmfPlusFlags = 1;
462 else
463 header->EmfPlusFlags = 0;
465 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
466 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
468 METAFILE_WriteRecords(metafile);
471 return Ok;
474 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
476 GpStatus stat;
478 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
480 EmfPlusRecordHeader *record;
482 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
483 if (stat != Ok)
484 return stat;
486 record->Type = EmfPlusRecordTypeEndOfFile;
487 record->Flags = 0;
489 METAFILE_WriteRecords(metafile);
492 return Ok;
495 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
496 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
498 HDC record_dc;
499 REAL dpix, dpiy;
500 REAL framerect_factor_x, framerect_factor_y;
501 RECT rc, *lprc;
502 GpStatus stat;
504 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
506 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
507 return InvalidParameter;
509 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
510 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
512 if (frameRect)
514 switch (frameUnit)
516 case MetafileFrameUnitPixel:
517 framerect_factor_x = 2540.0 / dpix;
518 framerect_factor_y = 2540.0 / dpiy;
519 break;
520 case MetafileFrameUnitPoint:
521 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
522 break;
523 case MetafileFrameUnitInch:
524 framerect_factor_x = framerect_factor_y = 2540.0;
525 break;
526 case MetafileFrameUnitDocument:
527 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
528 break;
529 case MetafileFrameUnitMillimeter:
530 framerect_factor_x = framerect_factor_y = 100.0;
531 break;
532 case MetafileFrameUnitGdi:
533 framerect_factor_x = framerect_factor_y = 1.0;
534 break;
535 default:
536 return InvalidParameter;
539 rc.left = framerect_factor_x * frameRect->X;
540 rc.top = framerect_factor_y * frameRect->Y;
541 rc.right = rc.left + framerect_factor_x * frameRect->Width;
542 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
544 lprc = &rc;
546 else
547 lprc = NULL;
549 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
551 if (!record_dc)
552 return GenericError;
554 *metafile = heap_alloc_zero(sizeof(GpMetafile));
555 if(!*metafile)
557 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
558 return OutOfMemory;
561 (*metafile)->image.type = ImageTypeMetafile;
562 (*metafile)->image.flags = ImageFlagsNone;
563 (*metafile)->image.palette = NULL;
564 (*metafile)->image.xres = dpix;
565 (*metafile)->image.yres = dpiy;
566 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
567 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
568 (*metafile)->unit = UnitPixel;
569 (*metafile)->metafile_type = type;
570 (*metafile)->record_dc = record_dc;
571 (*metafile)->comment_data = NULL;
572 (*metafile)->comment_data_size = 0;
573 (*metafile)->comment_data_length = 0;
574 (*metafile)->hemf = NULL;
575 list_init(&(*metafile)->containers);
577 if (!frameRect)
579 (*metafile)->auto_frame = TRUE;
580 (*metafile)->auto_frame_min.X = 0;
581 (*metafile)->auto_frame_min.Y = 0;
582 (*metafile)->auto_frame_max.X = -1;
583 (*metafile)->auto_frame_max.Y = -1;
586 stat = METAFILE_WriteHeader(*metafile, hdc);
588 if (stat != Ok)
590 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
591 heap_free(*metafile);
592 *metafile = NULL;
593 return OutOfMemory;
596 return stat;
599 /*****************************************************************************
600 * GdipRecordMetafileI [GDIPLUS.@]
602 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
603 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
605 GpRectF frameRectF, *pFrameRectF;
607 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
609 if (frameRect)
611 frameRectF.X = frameRect->X;
612 frameRectF.Y = frameRect->Y;
613 frameRectF.Width = frameRect->Width;
614 frameRectF.Height = frameRect->Height;
615 pFrameRectF = &frameRectF;
617 else
618 pFrameRectF = NULL;
620 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
623 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
624 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
626 GpStatus stat;
628 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
630 if (!stream)
631 return InvalidParameter;
633 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
635 if (stat == Ok)
637 (*metafile)->record_stream = stream;
638 IStream_AddRef(stream);
641 return stat;
644 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
645 UINT num_points)
647 int i;
649 if (!metafile->auto_frame || !num_points)
650 return;
652 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
653 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
655 for (i=0; i<num_points; i++)
657 if (points[i].X < metafile->auto_frame_min.X)
658 metafile->auto_frame_min.X = points[i].X;
659 if (points[i].X > metafile->auto_frame_max.X)
660 metafile->auto_frame_max.X = points[i].X;
661 if (points[i].Y < metafile->auto_frame_min.Y)
662 metafile->auto_frame_min.Y = points[i].Y;
663 if (points[i].Y > metafile->auto_frame_max.Y)
664 metafile->auto_frame_max.Y = points[i].Y;
668 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
670 GpStatus stat;
672 if (!metafile->record_dc || metafile->record_graphics)
673 return InvalidParameter;
675 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
677 if (stat == Ok)
679 *result = metafile->record_graphics;
680 metafile->record_graphics->xres = 96.0;
681 metafile->record_graphics->yres = 96.0;
684 return stat;
687 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
689 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
691 EmfPlusRecordHeader *record;
692 GpStatus stat;
694 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
695 if (stat != Ok)
696 return stat;
698 record->Type = EmfPlusRecordTypeGetDC;
699 record->Flags = 0;
701 METAFILE_WriteRecords(metafile);
704 *hdc = metafile->record_dc;
706 return Ok;
709 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
711 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
713 EmfPlusClear *record;
714 GpStatus stat;
716 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
717 if (stat != Ok)
718 return stat;
720 record->Header.Type = EmfPlusRecordTypeClear;
721 record->Header.Flags = 0;
722 record->Color = color;
724 METAFILE_WriteRecords(metafile);
727 return Ok;
730 static BOOL is_integer_rect(const GpRectF *rect)
732 SHORT x, y, width, height;
733 x = rect->X;
734 y = rect->Y;
735 width = rect->Width;
736 height = rect->Height;
737 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
738 rect->Width != (REAL)width || rect->Height != (REAL)height)
739 return FALSE;
740 return TRUE;
743 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
744 GDIPCONST GpRectF* rects, INT count)
746 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
748 EmfPlusFillRects *record;
749 GpStatus stat;
750 BOOL integer_rects = TRUE;
751 int i;
752 DWORD brushid;
753 int flags = 0;
755 if (brush->bt == BrushTypeSolidColor)
757 flags |= 0x8000;
758 brushid = ((GpSolidFill*)brush)->color;
760 else
762 FIXME("brush serialization not implemented\n");
763 return NotImplemented;
766 for (i=0; i<count; i++)
768 if (!is_integer_rect(&rects[i]))
770 integer_rects = FALSE;
771 break;
775 if (integer_rects)
776 flags |= 0x4000;
778 stat = METAFILE_AllocateRecord(metafile,
779 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
780 (void**)&record);
781 if (stat != Ok)
782 return stat;
784 record->Header.Type = EmfPlusRecordTypeFillRects;
785 record->Header.Flags = flags;
786 record->BrushID = brushid;
787 record->Count = count;
789 if (integer_rects)
791 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
792 for (i=0; i<count; i++)
794 record_rects[i].X = (SHORT)rects[i].X;
795 record_rects[i].Y = (SHORT)rects[i].Y;
796 record_rects[i].Width = (SHORT)rects[i].Width;
797 record_rects[i].Height = (SHORT)rects[i].Height;
800 else
801 memcpy(record+1, rects, sizeof(GpRectF) * count);
803 METAFILE_WriteRecords(metafile);
806 if (metafile->auto_frame)
808 GpPointF corners[4];
809 int i;
811 for (i=0; i<count; i++)
813 corners[0].X = rects[i].X;
814 corners[0].Y = rects[i].Y;
815 corners[1].X = rects[i].X + rects[i].Width;
816 corners[1].Y = rects[i].Y;
817 corners[2].X = rects[i].X;
818 corners[2].Y = rects[i].Y + rects[i].Height;
819 corners[3].X = rects[i].X + rects[i].Width;
820 corners[3].Y = rects[i].Y + rects[i].Height;
822 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
823 CoordinateSpaceWorld, corners, 4);
825 METAFILE_AdjustFrame(metafile, corners, 4);
829 return Ok;
832 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
834 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
836 EmfPlusSetClipRect *record;
837 GpStatus stat;
839 stat = METAFILE_AllocateRecord(metafile,
840 sizeof(EmfPlusSetClipRect),
841 (void**)&record);
842 if (stat != Ok)
843 return stat;
845 record->Header.Type = EmfPlusRecordTypeSetClipRect;
846 record->Header.Flags = (mode & 0xf) << 8;
847 record->ClipRect.X = x;
848 record->ClipRect.Y = y;
849 record->ClipRect.Width = width;
850 record->ClipRect.Height = height;
852 METAFILE_WriteRecords(metafile);
855 return Ok;
858 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
860 EmfPlusObject *object_record;
861 DWORD size;
862 GpStatus stat;
864 *id = -1;
865 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
866 return Ok;
868 size = write_region_data(region, NULL);
869 stat = METAFILE_AllocateRecord(metafile,
870 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
871 if (stat != Ok) return stat;
873 *id = METAFILE_AddObjectId(metafile);
874 object_record->Header.Type = EmfPlusRecordTypeObject;
875 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
876 write_region_data(region, &object_record->ObjectData.region);
877 return Ok;
880 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
882 EmfPlusRecordHeader *record;
883 DWORD region_id;
884 GpStatus stat;
886 if (metafile->metafile_type == MetafileTypeEmf)
888 FIXME("stub!\n");
889 return NotImplemented;
892 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
893 if (stat != Ok) return stat;
895 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
896 if (stat != Ok) return stat;
898 record->Type = EmfPlusRecordTypeSetClipRegion;
899 record->Flags = region_id | mode << 8;
901 METAFILE_WriteRecords(metafile);
902 return Ok;
905 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
907 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
909 EmfPlusSetPageTransform *record;
910 GpStatus stat;
912 stat = METAFILE_AllocateRecord(metafile,
913 sizeof(EmfPlusSetPageTransform),
914 (void**)&record);
915 if (stat != Ok)
916 return stat;
918 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
919 record->Header.Flags = unit;
920 record->PageScale = scale;
922 METAFILE_WriteRecords(metafile);
925 return Ok;
928 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
930 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
932 EmfPlusSetWorldTransform *record;
933 GpStatus stat;
935 stat = METAFILE_AllocateRecord(metafile,
936 sizeof(EmfPlusSetWorldTransform),
937 (void**)&record);
938 if (stat != Ok)
939 return stat;
941 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
942 record->Header.Flags = 0;
943 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
945 METAFILE_WriteRecords(metafile);
948 return Ok;
951 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
953 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
955 EmfPlusScaleWorldTransform *record;
956 GpStatus stat;
958 stat = METAFILE_AllocateRecord(metafile,
959 sizeof(EmfPlusScaleWorldTransform),
960 (void**)&record);
961 if (stat != Ok)
962 return stat;
964 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
965 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
966 record->Sx = sx;
967 record->Sy = sy;
969 METAFILE_WriteRecords(metafile);
972 return Ok;
975 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
977 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
979 EmfPlusMultiplyWorldTransform *record;
980 GpStatus stat;
982 stat = METAFILE_AllocateRecord(metafile,
983 sizeof(EmfPlusMultiplyWorldTransform),
984 (void**)&record);
985 if (stat != Ok)
986 return stat;
988 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
989 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
990 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
992 METAFILE_WriteRecords(metafile);
995 return Ok;
998 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1000 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1002 EmfPlusRotateWorldTransform *record;
1003 GpStatus stat;
1005 stat = METAFILE_AllocateRecord(metafile,
1006 sizeof(EmfPlusRotateWorldTransform),
1007 (void**)&record);
1008 if (stat != Ok)
1009 return stat;
1011 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1012 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1013 record->Angle = angle;
1015 METAFILE_WriteRecords(metafile);
1018 return Ok;
1021 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1023 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1025 EmfPlusTranslateWorldTransform *record;
1026 GpStatus stat;
1028 stat = METAFILE_AllocateRecord(metafile,
1029 sizeof(EmfPlusTranslateWorldTransform),
1030 (void**)&record);
1031 if (stat != Ok)
1032 return stat;
1034 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1035 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1036 record->dx = dx;
1037 record->dy = dy;
1039 METAFILE_WriteRecords(metafile);
1042 return Ok;
1045 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1047 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1049 EmfPlusRecordHeader *record;
1050 GpStatus stat;
1052 stat = METAFILE_AllocateRecord(metafile,
1053 sizeof(EmfPlusRecordHeader),
1054 (void**)&record);
1055 if (stat != Ok)
1056 return stat;
1058 record->Type = EmfPlusRecordTypeResetWorldTransform;
1059 record->Flags = 0;
1061 METAFILE_WriteRecords(metafile);
1064 return Ok;
1067 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1068 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1070 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1072 EmfPlusBeginContainer *record;
1073 GpStatus stat;
1075 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1076 if (stat != Ok)
1077 return stat;
1079 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1080 record->Header.Flags = unit & 0xff;
1081 record->DestRect = *dstrect;
1082 record->SrcRect = *srcrect;
1083 record->StackIndex = StackIndex;
1085 METAFILE_WriteRecords(metafile);
1088 return Ok;
1091 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1093 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1095 EmfPlusContainerRecord *record;
1096 GpStatus stat;
1098 stat = METAFILE_AllocateRecord(metafile,
1099 sizeof(EmfPlusContainerRecord),
1100 (void**)&record);
1101 if (stat != Ok)
1102 return stat;
1104 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1105 record->Header.Flags = 0;
1106 record->StackIndex = StackIndex;
1108 METAFILE_WriteRecords(metafile);
1111 return Ok;
1114 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1116 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1118 EmfPlusContainerRecord *record;
1119 GpStatus stat;
1121 stat = METAFILE_AllocateRecord(metafile,
1122 sizeof(EmfPlusContainerRecord),
1123 (void**)&record);
1124 if (stat != Ok)
1125 return stat;
1127 record->Header.Type = EmfPlusRecordTypeEndContainer;
1128 record->Header.Flags = 0;
1129 record->StackIndex = StackIndex;
1131 METAFILE_WriteRecords(metafile);
1134 return Ok;
1137 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1139 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1141 EmfPlusContainerRecord *record;
1142 GpStatus stat;
1144 stat = METAFILE_AllocateRecord(metafile,
1145 sizeof(EmfPlusContainerRecord),
1146 (void**)&record);
1147 if (stat != Ok)
1148 return stat;
1150 record->Header.Type = EmfPlusRecordTypeSave;
1151 record->Header.Flags = 0;
1152 record->StackIndex = StackIndex;
1154 METAFILE_WriteRecords(metafile);
1157 return Ok;
1160 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1162 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1164 EmfPlusContainerRecord *record;
1165 GpStatus stat;
1167 stat = METAFILE_AllocateRecord(metafile,
1168 sizeof(EmfPlusContainerRecord),
1169 (void**)&record);
1170 if (stat != Ok)
1171 return stat;
1173 record->Header.Type = EmfPlusRecordTypeRestore;
1174 record->Header.Flags = 0;
1175 record->StackIndex = StackIndex;
1177 METAFILE_WriteRecords(metafile);
1180 return Ok;
1183 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1185 if (hdc != metafile->record_dc)
1186 return InvalidParameter;
1188 return Ok;
1191 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1193 GpStatus stat;
1195 stat = METAFILE_WriteEndOfFile(metafile);
1196 metafile->record_graphics = NULL;
1198 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1199 metafile->record_dc = NULL;
1201 heap_free(metafile->comment_data);
1202 metafile->comment_data = NULL;
1203 metafile->comment_data_size = 0;
1205 if (stat == Ok)
1207 MetafileHeader header;
1209 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1210 if (stat == Ok && metafile->auto_frame &&
1211 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1213 RECTL bounds_rc, gdi_bounds_rc;
1214 REAL x_scale = 2540.0 / header.DpiX;
1215 REAL y_scale = 2540.0 / header.DpiY;
1216 BYTE* buffer;
1217 UINT buffer_size;
1219 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1220 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1221 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1222 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1224 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1225 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1227 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1228 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1229 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1230 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1233 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1234 buffer = heap_alloc(buffer_size);
1235 if (buffer)
1237 HENHMETAFILE new_hemf;
1239 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1241 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1243 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1245 if (new_hemf)
1247 DeleteEnhMetaFile(metafile->hemf);
1248 metafile->hemf = new_hemf;
1250 else
1251 stat = OutOfMemory;
1253 heap_free(buffer);
1255 else
1256 stat = OutOfMemory;
1258 if (stat == Ok)
1259 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1261 if (stat == Ok)
1263 metafile->bounds.X = header.X;
1264 metafile->bounds.Y = header.Y;
1265 metafile->bounds.Width = header.Width;
1266 metafile->bounds.Height = header.Height;
1270 if (stat == Ok && metafile->record_stream)
1272 BYTE *buffer;
1273 UINT buffer_size;
1275 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1277 buffer = heap_alloc(buffer_size);
1278 if (buffer)
1280 HRESULT hr;
1282 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1284 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1286 if (FAILED(hr))
1287 stat = hresult_to_status(hr);
1289 heap_free(buffer);
1291 else
1292 stat = OutOfMemory;
1295 if (metafile->record_stream)
1297 IStream_Release(metafile->record_stream);
1298 metafile->record_stream = NULL;
1301 return stat;
1304 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1306 TRACE("(%p,%p)\n", metafile, hEmf);
1308 if (!metafile || !hEmf || !metafile->hemf)
1309 return InvalidParameter;
1311 *hEmf = metafile->hemf;
1312 metafile->hemf = NULL;
1314 return Ok;
1317 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1319 const GpRectF *rect;
1320 const GpPointF *pt;
1322 /* This transforms metafile device space to output points. */
1323 rect = &metafile->src_rect;
1324 pt = metafile->playback_points;
1325 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1326 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1327 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1328 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1329 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1330 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1333 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1335 XFORM combined, final;
1337 METAFILE_GetFinalGdiTransform(metafile, &final);
1339 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1341 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1342 SetWorldTransform(metafile->playback_dc, &combined);
1344 return Ok;
1347 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1349 GpStatus stat = Ok;
1351 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1353 if (stat == Ok)
1355 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1357 metafile->gdiworldtransform = identity;
1358 METAFILE_PlaybackUpdateGdiTransform(metafile);
1361 return stat;
1364 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1366 if (metafile->playback_dc)
1368 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1369 metafile->playback_dc = NULL;
1373 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1375 GpStatus stat;
1376 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1377 if (stat == Ok)
1378 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1379 return stat;
1382 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1384 GpMatrix *real_transform;
1385 GpStatus stat;
1387 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1389 if (stat == Ok)
1391 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1393 if (metafile->page_unit != UnitDisplay)
1394 scale *= metafile->page_scale;
1396 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1398 if (stat == Ok)
1399 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1401 if (stat == Ok)
1402 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1404 GdipDeleteMatrix(real_transform);
1407 return stat;
1410 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
1411 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
1413 GpStatus stat;
1414 GpMetafile *real_metafile = (GpMetafile*)metafile;
1416 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
1418 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
1419 return InvalidParameter;
1421 if (recordType >= 1 && recordType <= 0x7a)
1423 /* regular EMF record */
1424 if (metafile->playback_dc)
1426 switch (recordType)
1428 case EMR_SETMAPMODE:
1429 case EMR_SAVEDC:
1430 case EMR_RESTOREDC:
1431 case EMR_SETWINDOWORGEX:
1432 case EMR_SETWINDOWEXTEX:
1433 case EMR_SETVIEWPORTORGEX:
1434 case EMR_SETVIEWPORTEXTEX:
1435 case EMR_SCALEVIEWPORTEXTEX:
1436 case EMR_SCALEWINDOWEXTEX:
1437 case EMR_MODIFYWORLDTRANSFORM:
1438 FIXME("not implemented for record type %x\n", recordType);
1439 break;
1440 case EMR_SETWORLDTRANSFORM:
1442 const XFORM* xform = (void*)data;
1443 real_metafile->gdiworldtransform = *xform;
1444 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
1445 break;
1447 case EMR_EXTSELECTCLIPRGN:
1449 DWORD rgndatasize = *(DWORD*)data;
1450 DWORD mode = *(DWORD*)(data + 4);
1451 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
1452 HRGN hrgn = NULL;
1454 if (dataSize > 8)
1456 XFORM final;
1458 METAFILE_GetFinalGdiTransform(metafile, &final);
1460 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
1463 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
1465 DeleteObject(hrgn);
1467 return Ok;
1469 default:
1471 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
1473 if (record)
1475 record->iType = recordType;
1476 record->nSize = dataSize + 8;
1477 memcpy(record->dParm, data, dataSize);
1479 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
1480 record, metafile->handle_count) == 0)
1481 ERR("PlayEnhMetaFileRecord failed\n");
1483 heap_free(record);
1485 else
1486 return OutOfMemory;
1488 break;
1493 else
1495 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
1497 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
1499 switch(recordType)
1501 case EmfPlusRecordTypeHeader:
1502 case EmfPlusRecordTypeEndOfFile:
1503 break;
1504 case EmfPlusRecordTypeGetDC:
1505 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
1506 break;
1507 case EmfPlusRecordTypeClear:
1509 EmfPlusClear *record = (EmfPlusClear*)header;
1511 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
1513 case EmfPlusRecordTypeFillRects:
1515 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
1516 GpBrush *brush, *temp_brush=NULL;
1517 GpRectF *rects, *temp_rects=NULL;
1519 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
1520 return InvalidParameter;
1522 if (flags & 0x4000)
1524 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
1525 return InvalidParameter;
1527 else
1529 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
1530 return InvalidParameter;
1533 if (flags & 0x8000)
1535 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
1536 brush = temp_brush;
1538 else
1540 FIXME("brush deserialization not implemented\n");
1541 return NotImplemented;
1544 if (stat == Ok)
1546 if (flags & 0x4000)
1548 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
1549 int i;
1551 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
1552 if (rects)
1554 for (i=0; i<record->Count; i++)
1556 rects[i].X = int_rects[i].X;
1557 rects[i].Y = int_rects[i].Y;
1558 rects[i].Width = int_rects[i].Width;
1559 rects[i].Height = int_rects[i].Height;
1562 else
1563 stat = OutOfMemory;
1565 else
1566 rects = (GpRectF*)(record+1);
1569 if (stat == Ok)
1571 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
1574 GdipDeleteBrush(temp_brush);
1575 heap_free(temp_rects);
1577 return stat;
1579 case EmfPlusRecordTypeSetClipRect:
1581 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
1582 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
1583 GpRegion *region;
1584 GpMatrix world_to_device;
1586 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
1587 return InvalidParameter;
1589 stat = GdipCreateRegionRect(&record->ClipRect, &region);
1591 if (stat == Ok)
1593 get_graphics_transform(real_metafile->playback_graphics,
1594 CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
1596 GdipTransformRegion(region, &world_to_device);
1598 GdipCombineRegionRegion(real_metafile->clip, region, mode);
1600 GdipDeleteRegion(region);
1603 return METAFILE_PlaybackUpdateClip(real_metafile);
1605 case EmfPlusRecordTypeSetPageTransform:
1607 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
1608 GpUnit unit = (GpUnit)flags;
1610 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
1611 return InvalidParameter;
1613 real_metafile->page_unit = unit;
1614 real_metafile->page_scale = record->PageScale;
1616 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1618 case EmfPlusRecordTypeSetWorldTransform:
1620 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
1622 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
1623 return InvalidParameter;
1625 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
1627 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1629 case EmfPlusRecordTypeScaleWorldTransform:
1631 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
1632 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1634 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
1635 return InvalidParameter;
1637 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
1639 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1641 case EmfPlusRecordTypeMultiplyWorldTransform:
1643 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
1644 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1645 GpMatrix matrix;
1647 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
1648 return InvalidParameter;
1650 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
1652 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
1654 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1656 case EmfPlusRecordTypeRotateWorldTransform:
1658 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
1659 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1661 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
1662 return InvalidParameter;
1664 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
1666 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1668 case EmfPlusRecordTypeTranslateWorldTransform:
1670 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
1671 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1673 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
1674 return InvalidParameter;
1676 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
1678 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1680 case EmfPlusRecordTypeResetWorldTransform:
1682 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1684 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1686 case EmfPlusRecordTypeBeginContainer:
1688 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
1689 container* cont;
1690 GpUnit unit;
1691 REAL scale_x, scale_y;
1692 GpRectF scaled_srcrect;
1693 GpMatrix transform;
1695 cont = heap_alloc_zero(sizeof(*cont));
1696 if (!cont)
1697 return OutOfMemory;
1699 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1700 if (stat != Ok)
1702 heap_free(cont);
1703 return stat;
1706 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1708 if (stat != Ok)
1710 GdipDeleteRegion(cont->clip);
1711 heap_free(cont);
1712 return stat;
1715 cont->id = record->StackIndex;
1716 cont->type = BEGIN_CONTAINER;
1717 cont->world_transform = *metafile->world_transform;
1718 cont->page_unit = metafile->page_unit;
1719 cont->page_scale = metafile->page_scale;
1720 list_add_head(&real_metafile->containers, &cont->entry);
1722 unit = record->Header.Flags & 0xff;
1724 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
1725 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
1727 scaled_srcrect.X = scale_x * record->SrcRect.X;
1728 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
1729 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
1730 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
1732 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
1733 transform.matrix[1] = 0.0;
1734 transform.matrix[2] = 0.0;
1735 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
1736 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
1737 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
1739 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
1741 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1743 case EmfPlusRecordTypeBeginContainerNoParams:
1744 case EmfPlusRecordTypeSave:
1746 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1747 container* cont;
1749 cont = heap_alloc_zero(sizeof(*cont));
1750 if (!cont)
1751 return OutOfMemory;
1753 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1754 if (stat != Ok)
1756 heap_free(cont);
1757 return stat;
1760 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1761 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1762 else
1763 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
1765 if (stat != Ok)
1767 GdipDeleteRegion(cont->clip);
1768 heap_free(cont);
1769 return stat;
1772 cont->id = record->StackIndex;
1773 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1774 cont->type = BEGIN_CONTAINER;
1775 else
1776 cont->type = SAVE_GRAPHICS;
1777 cont->world_transform = *metafile->world_transform;
1778 cont->page_unit = metafile->page_unit;
1779 cont->page_scale = metafile->page_scale;
1780 list_add_head(&real_metafile->containers, &cont->entry);
1782 break;
1784 case EmfPlusRecordTypeEndContainer:
1785 case EmfPlusRecordTypeRestore:
1787 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1788 container* cont;
1789 enum container_type type;
1790 BOOL found=FALSE;
1792 if (recordType == EmfPlusRecordTypeEndContainer)
1793 type = BEGIN_CONTAINER;
1794 else
1795 type = SAVE_GRAPHICS;
1797 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
1799 if (cont->id == record->StackIndex && cont->type == type)
1801 found = TRUE;
1802 break;
1806 if (found)
1808 container* cont2;
1810 /* pop any newer items on the stack */
1811 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
1813 list_remove(&cont2->entry);
1814 GdipDeleteRegion(cont2->clip);
1815 heap_free(cont2);
1818 if (type == BEGIN_CONTAINER)
1819 GdipEndContainer(real_metafile->playback_graphics, cont->state);
1820 else
1821 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
1823 *real_metafile->world_transform = cont->world_transform;
1824 real_metafile->page_unit = cont->page_unit;
1825 real_metafile->page_scale = cont->page_scale;
1826 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
1828 list_remove(&cont->entry);
1829 GdipDeleteRegion(cont->clip);
1830 heap_free(cont);
1833 break;
1835 default:
1836 FIXME("Not implemented for record type %x\n", recordType);
1837 return NotImplemented;
1841 return Ok;
1844 struct enum_metafile_data
1846 EnumerateMetafileProc callback;
1847 void *callback_data;
1848 GpMetafile *metafile;
1851 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1852 int nObj, LPARAM lpData)
1854 BOOL ret;
1855 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
1856 const BYTE* pStr;
1858 data->metafile->handle_table = lpHTable;
1859 data->metafile->handle_count = nObj;
1861 /* First check for an EMF+ record. */
1862 if (lpEMFR->iType == EMR_GDICOMMENT)
1864 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1866 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1868 int offset = 4;
1870 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
1872 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
1874 if (record->DataSize)
1875 pStr = (const BYTE*)(record+1);
1876 else
1877 pStr = NULL;
1879 ret = data->callback(record->Type, record->Flags, record->DataSize,
1880 pStr, data->callback_data);
1882 if (!ret)
1883 return 0;
1885 offset += record->Size;
1888 return 1;
1892 if (lpEMFR->nSize != 8)
1893 pStr = (const BYTE*)lpEMFR->dParm;
1894 else
1895 pStr = NULL;
1897 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
1898 pStr, data->callback_data);
1901 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
1902 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
1903 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
1904 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
1906 struct enum_metafile_data data;
1907 GpStatus stat;
1908 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
1909 GraphicsContainer state;
1910 GpPath *dst_path;
1912 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
1913 destPoints, count, srcRect, srcUnit, callback, callbackData,
1914 imageAttributes);
1916 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
1917 return InvalidParameter;
1919 if (!metafile->hemf)
1920 return InvalidParameter;
1922 if (metafile->playback_graphics)
1923 return ObjectBusy;
1925 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
1926 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
1927 debugstr_pointf(&destPoints[2]));
1929 data.callback = callback;
1930 data.callback_data = callbackData;
1931 data.metafile = real_metafile;
1933 real_metafile->playback_graphics = graphics;
1934 real_metafile->playback_dc = NULL;
1935 real_metafile->src_rect = *srcRect;
1937 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
1938 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
1940 if (stat == Ok)
1941 stat = GdipBeginContainer2(graphics, &state);
1943 if (stat == Ok)
1945 stat = GdipSetPageScale(graphics, 1.0);
1947 if (stat == Ok)
1948 stat = GdipSetPageUnit(graphics, UnitPixel);
1950 if (stat == Ok)
1951 stat = GdipResetWorldTransform(graphics);
1953 if (stat == Ok)
1954 stat = GdipCreateRegion(&real_metafile->base_clip);
1956 if (stat == Ok)
1957 stat = GdipGetClip(graphics, real_metafile->base_clip);
1959 if (stat == Ok)
1960 stat = GdipCreateRegion(&real_metafile->clip);
1962 if (stat == Ok)
1963 stat = GdipCreatePath(FillModeAlternate, &dst_path);
1965 if (stat == Ok)
1967 GpPointF clip_points[4];
1969 clip_points[0] = real_metafile->playback_points[0];
1970 clip_points[1] = real_metafile->playback_points[1];
1971 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
1972 - real_metafile->playback_points[0].X;
1973 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
1974 - real_metafile->playback_points[0].Y;
1975 clip_points[3] = real_metafile->playback_points[2];
1977 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
1979 if (stat == Ok)
1980 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
1982 GdipDeletePath(dst_path);
1985 if (stat == Ok)
1986 stat = GdipCreateMatrix(&real_metafile->world_transform);
1988 if (stat == Ok)
1990 real_metafile->page_unit = UnitDisplay;
1991 real_metafile->page_scale = 1.0;
1992 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1995 if (stat == Ok)
1997 stat = METAFILE_PlaybackUpdateClip(real_metafile);
2000 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
2001 metafile->metafile_type == MetafileTypeWmfPlaceable ||
2002 metafile->metafile_type == MetafileTypeWmf))
2003 stat = METAFILE_PlaybackGetDC(real_metafile);
2005 if (stat == Ok)
2006 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
2008 METAFILE_PlaybackReleaseDC(real_metafile);
2010 GdipDeleteMatrix(real_metafile->world_transform);
2011 real_metafile->world_transform = NULL;
2013 GdipDeleteRegion(real_metafile->base_clip);
2014 real_metafile->base_clip = NULL;
2016 GdipDeleteRegion(real_metafile->clip);
2017 real_metafile->clip = NULL;
2019 while (list_head(&real_metafile->containers))
2021 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
2022 list_remove(&cont->entry);
2023 GdipDeleteRegion(cont->clip);
2024 heap_free(cont);
2027 GdipEndContainer(graphics, state);
2030 real_metafile->playback_graphics = NULL;
2032 return stat;
2035 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
2036 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
2037 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2039 GpPointF points[3];
2041 if (!graphics || !metafile || !dest) return InvalidParameter;
2043 points[0].X = points[2].X = dest->X;
2044 points[0].Y = points[1].Y = dest->Y;
2045 points[1].X = dest->X + dest->Width;
2046 points[2].Y = dest->Y + dest->Height;
2048 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
2049 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
2052 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
2053 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
2054 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2056 GpRectF destf;
2058 if (!graphics || !metafile || !dest) return InvalidParameter;
2060 destf.X = dest->X;
2061 destf.Y = dest->Y;
2062 destf.Width = dest->Width;
2063 destf.Height = dest->Height;
2065 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2068 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
2069 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
2070 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2072 GpRectF destf;
2074 if (!graphics || !metafile || !dest) return InvalidParameter;
2076 destf.X = dest->X;
2077 destf.Y = dest->Y;
2078 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
2079 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
2081 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2084 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
2085 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
2086 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2088 GpPointF ptf;
2090 if (!graphics || !metafile || !dest) return InvalidParameter;
2092 ptf.X = dest->X;
2093 ptf.Y = dest->Y;
2095 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
2098 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
2099 MetafileHeader * header)
2101 GpStatus status;
2103 TRACE("(%p, %p)\n", metafile, header);
2105 if(!metafile || !header)
2106 return InvalidParameter;
2108 if (metafile->hemf)
2110 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
2111 if (status != Ok) return status;
2113 else
2115 memset(header, 0, sizeof(*header));
2116 header->Version = VERSION_MAGIC2;
2119 header->Type = metafile->metafile_type;
2120 header->DpiX = metafile->image.xres;
2121 header->DpiY = metafile->image.yres;
2122 header->Width = gdip_round(metafile->bounds.Width);
2123 header->Height = gdip_round(metafile->bounds.Height);
2125 return Ok;
2128 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
2129 int nObj, LPARAM lpData)
2131 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
2133 if (lpEMFR->iType == EMR_GDICOMMENT)
2135 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
2137 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
2139 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
2141 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
2142 header->Type == EmfPlusRecordTypeHeader)
2144 memcpy(dst_header, header, sizeof(*dst_header));
2148 else if (lpEMFR->iType == EMR_HEADER)
2149 return TRUE;
2151 return FALSE;
2154 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
2155 MetafileHeader *header)
2157 ENHMETAHEADER3 emfheader;
2158 EmfPlusHeader emfplusheader;
2159 MetafileType metafile_type;
2161 TRACE("(%p,%p)\n", hemf, header);
2163 if(!hemf || !header)
2164 return InvalidParameter;
2166 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
2167 return GenericError;
2169 emfplusheader.Header.Type = 0;
2171 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
2173 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
2175 if ((emfplusheader.Header.Flags & 1) == 1)
2176 metafile_type = MetafileTypeEmfPlusDual;
2177 else
2178 metafile_type = MetafileTypeEmfPlusOnly;
2180 else
2181 metafile_type = MetafileTypeEmf;
2183 header->Type = metafile_type;
2184 header->Size = emfheader.nBytes;
2185 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
2186 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
2187 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
2188 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
2189 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
2190 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
2191 header->u.EmfHeader = emfheader;
2193 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
2195 header->Version = emfplusheader.Version;
2196 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
2197 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
2198 header->LogicalDpiX = emfplusheader.LogicalDpiX;
2199 header->LogicalDpiY = emfplusheader.LogicalDpiY;
2201 else
2203 header->Version = emfheader.nVersion;
2204 header->EmfPlusFlags = 0;
2205 header->EmfPlusHeaderSize = 0;
2206 header->LogicalDpiX = 0;
2207 header->LogicalDpiY = 0;
2210 return Ok;
2213 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
2214 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
2216 GpStatus status;
2217 GpMetafile *metafile;
2219 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
2221 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
2222 if (status == Ok)
2224 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2225 GdipDisposeImage(&metafile->image);
2227 return status;
2230 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
2231 MetafileHeader *header)
2233 GpStatus status;
2234 GpMetafile *metafile;
2236 TRACE("(%s,%p)\n", debugstr_w(filename), header);
2238 if (!filename || !header)
2239 return InvalidParameter;
2241 status = GdipCreateMetafileFromFile(filename, &metafile);
2242 if (status == Ok)
2244 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2245 GdipDisposeImage(&metafile->image);
2247 return status;
2250 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
2251 MetafileHeader *header)
2253 GpStatus status;
2254 GpMetafile *metafile;
2256 TRACE("(%p,%p)\n", stream, header);
2258 if (!stream || !header)
2259 return InvalidParameter;
2261 status = GdipCreateMetafileFromStream(stream, &metafile);
2262 if (status == Ok)
2264 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2265 GdipDisposeImage(&metafile->image);
2267 return status;
2270 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
2271 GpMetafile **metafile)
2273 GpStatus stat;
2274 MetafileHeader header;
2276 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
2278 if(!hemf || !metafile)
2279 return InvalidParameter;
2281 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
2282 if (stat != Ok)
2283 return stat;
2285 *metafile = heap_alloc_zero(sizeof(GpMetafile));
2286 if (!*metafile)
2287 return OutOfMemory;
2289 (*metafile)->image.type = ImageTypeMetafile;
2290 (*metafile)->image.format = ImageFormatEMF;
2291 (*metafile)->image.frame_count = 1;
2292 (*metafile)->image.xres = header.DpiX;
2293 (*metafile)->image.yres = header.DpiY;
2294 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
2295 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
2296 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
2297 / 2540.0 * header.DpiX;
2298 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
2299 / 2540.0 * header.DpiY;
2300 (*metafile)->unit = UnitPixel;
2301 (*metafile)->metafile_type = header.Type;
2302 (*metafile)->hemf = hemf;
2303 (*metafile)->preserve_hemf = !delete;
2304 list_init(&(*metafile)->containers);
2306 TRACE("<-- %p\n", *metafile);
2308 return Ok;
2311 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
2312 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2314 UINT read;
2315 BYTE *copy;
2316 HENHMETAFILE hemf;
2317 GpStatus retval = Ok;
2319 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
2321 if(!hwmf || !metafile)
2322 return InvalidParameter;
2324 *metafile = NULL;
2325 read = GetMetaFileBitsEx(hwmf, 0, NULL);
2326 if(!read)
2327 return GenericError;
2328 copy = heap_alloc_zero(read);
2329 GetMetaFileBitsEx(hwmf, read, copy);
2331 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
2332 heap_free(copy);
2334 /* FIXME: We should store and use hwmf instead of converting to hemf */
2335 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
2337 if (retval == Ok)
2339 if (placeable)
2341 (*metafile)->image.xres = (REAL)placeable->Inch;
2342 (*metafile)->image.yres = (REAL)placeable->Inch;
2343 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
2344 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
2345 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
2346 placeable->BoundingBox.Left);
2347 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
2348 placeable->BoundingBox.Top);
2349 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
2351 else
2352 (*metafile)->metafile_type = MetafileTypeWmf;
2353 (*metafile)->image.format = ImageFormatWMF;
2355 if (delete) DeleteMetaFile(hwmf);
2357 else
2358 DeleteEnhMetaFile(hemf);
2359 return retval;
2362 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
2363 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2365 HMETAFILE hmf;
2366 HENHMETAFILE emf;
2368 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
2370 hmf = GetMetaFileW(file);
2371 if(hmf)
2372 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
2374 emf = GetEnhMetaFileW(file);
2375 if(emf)
2376 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
2378 return GenericError;
2381 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
2382 GpMetafile **metafile)
2384 GpStatus status;
2385 IStream *stream;
2387 TRACE("(%p, %p)\n", file, metafile);
2389 if (!file || !metafile) return InvalidParameter;
2391 *metafile = NULL;
2393 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
2394 if (status == Ok)
2396 status = GdipCreateMetafileFromStream(stream, metafile);
2397 IStream_Release(stream);
2399 return status;
2402 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
2403 GpMetafile **metafile)
2405 GpStatus stat;
2407 TRACE("%p %p\n", stream, metafile);
2409 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
2410 if (stat != Ok) return stat;
2412 if ((*metafile)->image.type != ImageTypeMetafile)
2414 GdipDisposeImage(&(*metafile)->image);
2415 *metafile = NULL;
2416 return GenericError;
2419 return Ok;
2422 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
2423 UINT limitDpi)
2425 TRACE("(%p,%u)\n", metafile, limitDpi);
2427 return Ok;
2430 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
2431 GpMetafile* metafile, BOOL* succ, EmfType emfType,
2432 const WCHAR* description, GpMetafile** out_metafile)
2434 static int calls;
2436 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
2437 debugstr_w(description), out_metafile);
2439 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
2440 return InvalidParameter;
2442 if(succ)
2443 *succ = FALSE;
2444 *out_metafile = NULL;
2446 if(!(calls++))
2447 FIXME("not implemented\n");
2449 return NotImplemented;
2452 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
2453 LPBYTE pData16, INT iMapMode, INT eFlags)
2455 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
2456 return NotImplemented;
2459 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
2460 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
2461 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
2462 GpMetafile **metafile)
2464 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2465 frameUnit, debugstr_w(desc), metafile);
2467 return NotImplemented;
2470 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
2471 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
2472 GDIPCONST WCHAR *desc, GpMetafile **metafile)
2474 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2475 frameUnit, debugstr_w(desc), metafile);
2477 return NotImplemented;
2480 /*****************************************************************************
2481 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
2484 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
2485 GpMetafile* metafile, BOOL* conversionSuccess,
2486 const WCHAR* filename, EmfType emfType,
2487 const WCHAR* description, GpMetafile** out_metafile)
2489 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
2490 return NotImplemented;
2493 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
2495 LARGE_INTEGER zero;
2496 STATSTG statstg;
2497 GpStatus stat;
2498 HRESULT hr;
2500 *size = 0;
2502 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
2503 if (FAILED(hr)) return hresult_to_status(hr);
2505 stat = encode_image_png(image, *stream, NULL);
2506 if (stat != Ok)
2508 IStream_Release(*stream);
2509 return stat;
2512 hr = IStream_Stat(*stream, &statstg, 1);
2513 if (FAILED(hr))
2515 IStream_Release(*stream);
2516 return hresult_to_status(hr);
2518 *size = statstg.cbSize.u.LowPart;
2520 zero.QuadPart = 0;
2521 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
2522 if (FAILED(hr))
2524 IStream_Release(*stream);
2525 return hresult_to_status(hr);
2528 return Ok;
2531 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
2533 HRESULT hr;
2535 record->Width = 0;
2536 record->Height = 0;
2537 record->Stride = 0;
2538 record->PixelFormat = 0;
2539 record->Type = BitmapDataTypeCompressed;
2541 hr = IStream_Read(stream, record->BitmapData, size, NULL);
2542 if (FAILED(hr)) return hresult_to_status(hr);
2543 return Ok;
2546 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
2548 EmfPlusObject *object_record;
2549 GpStatus stat;
2550 DWORD size;
2552 *id = -1;
2554 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2555 return Ok;
2557 if (image->type == ImageTypeBitmap)
2559 IStream *stream;
2560 DWORD aligned_size;
2562 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
2563 if (stat != Ok) return stat;
2564 aligned_size = (size + 3) & ~3;
2566 stat = METAFILE_AllocateRecord(metafile,
2567 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
2568 (void**)&object_record);
2569 if (stat != Ok)
2571 IStream_Release(stream);
2572 return stat;
2574 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
2576 *id = METAFILE_AddObjectId(metafile);
2577 object_record->Header.Type = EmfPlusRecordTypeObject;
2578 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2579 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2580 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
2582 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
2583 IStream_Release(stream);
2584 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2585 return stat;
2587 else if (image->type == ImageTypeMetafile)
2589 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
2590 EmfPlusMetafile *metafile_record;
2592 if (!hemf) return InvalidParameter;
2594 size = GetEnhMetaFileBits(hemf, 0, NULL);
2595 if (!size) return GenericError;
2597 stat = METAFILE_AllocateRecord(metafile,
2598 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
2599 (void**)&object_record);
2600 if (stat != Ok) return stat;
2602 *id = METAFILE_AddObjectId(metafile);
2603 object_record->Header.Type = EmfPlusRecordTypeObject;
2604 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2605 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2606 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
2607 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
2608 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
2609 metafile_record->MetafileDataSize = size;
2610 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
2612 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2613 return GenericError;
2615 return Ok;
2617 else
2619 FIXME("not supported image type (%d)\n", image->type);
2620 return NotImplemented;
2624 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
2626 EmfPlusObject *object_record;
2627 EmfPlusImageAttributes *attrs_record;
2628 GpStatus stat;
2630 *id = -1;
2632 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2633 return Ok;
2635 if (!attrs)
2636 return Ok;
2638 stat = METAFILE_AllocateRecord(metafile,
2639 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
2640 (void**)&object_record);
2641 if (stat != Ok) return stat;
2643 *id = METAFILE_AddObjectId(metafile);
2644 object_record->Header.Type = EmfPlusRecordTypeObject;
2645 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
2646 attrs_record = &object_record->ObjectData.image_attributes;
2647 attrs_record->Version = VERSION_MAGIC2;
2648 attrs_record->Reserved1 = 0;
2649 attrs_record->WrapMode = attrs->wrap;
2650 attrs_record->ClampColor.Blue = attrs->outside_color & 0xff;
2651 attrs_record->ClampColor.Green = (attrs->outside_color >> 8) & 0xff;
2652 attrs_record->ClampColor.Red = (attrs->outside_color >> 16) & 0xff;
2653 attrs_record->ClampColor.Alpha = attrs->outside_color >> 24;
2654 attrs_record->ObjectClamp = attrs->clamp;
2655 attrs_record->Reserved2 = 0;
2656 return Ok;
2659 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
2660 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2661 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2662 DrawImageAbort callback, VOID *callbackData)
2664 EmfPlusDrawImagePoints *draw_image_record;
2665 DWORD image_id, attributes_id;
2666 GpStatus stat;
2668 if (count != 3) return InvalidParameter;
2670 if (metafile->metafile_type == MetafileTypeEmf)
2672 FIXME("MetafileTypeEmf metafiles not supported\n");
2673 return NotImplemented;
2675 else
2676 FIXME("semi-stub\n");
2678 if (!imageAttributes)
2680 stat = METAFILE_AddImageObject(metafile, image, &image_id);
2682 else if (image->type == ImageTypeBitmap)
2684 INT width = ((GpBitmap*)image)->width;
2685 INT height = ((GpBitmap*)image)->height;
2686 GpGraphics *graphics;
2687 GpBitmap *bitmap;
2689 stat = GdipCreateBitmapFromScan0(width, height,
2690 0, PixelFormat32bppARGB, NULL, &bitmap);
2691 if (stat != Ok) return stat;
2693 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
2694 if (stat != Ok)
2696 GdipDisposeImage((GpImage*)bitmap);
2697 return stat;
2700 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
2701 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
2702 GdipDeleteGraphics(graphics);
2703 if (stat != Ok)
2705 GdipDisposeImage((GpImage*)bitmap);
2706 return stat;
2709 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
2710 GdipDisposeImage((GpImage*)bitmap);
2712 else
2714 FIXME("imageAttributes not supported (image type %d)\n", image->type);
2715 return NotImplemented;
2717 if (stat != Ok) return stat;
2719 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
2720 if (stat != Ok) return stat;
2722 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
2723 if (stat != Ok) return stat;
2724 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
2725 draw_image_record->Header.Flags = image_id;
2726 draw_image_record->ImageAttributesID = attributes_id;
2727 draw_image_record->SrcUnit = UnitPixel;
2728 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
2729 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
2730 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
2731 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
2732 draw_image_record->count = 3;
2733 draw_image_record->PointData[0].pointF.X = points[0].X;
2734 draw_image_record->PointData[0].pointF.Y = points[0].Y;
2735 draw_image_record->PointData[1].pointF.X = points[1].X;
2736 draw_image_record->PointData[1].pointF.Y = points[1].Y;
2737 draw_image_record->PointData[2].pointF.X = points[2].X;
2738 draw_image_record->PointData[2].pointF.Y = points[2].Y;
2739 METAFILE_WriteRecords(metafile);
2740 return Ok;
2743 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
2745 EmfPlusRecordHeader *record;
2746 GpStatus stat;
2748 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2749 return Ok;
2751 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
2752 if (stat != Ok) return stat;
2754 record->Type = prop;
2755 record->Flags = val;
2757 METAFILE_WriteRecords(metafile);
2758 return Ok;
2761 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
2763 EmfPlusObject *object_record;
2764 GpStatus stat;
2765 DWORD size;
2767 *id = -1;
2768 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2769 return Ok;
2771 size = write_path_data(path, NULL);
2772 stat = METAFILE_AllocateRecord(metafile,
2773 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
2774 (void**)&object_record);
2775 if (stat != Ok) return stat;
2777 *id = METAFILE_AddObjectId(metafile);
2778 object_record->Header.Type = EmfPlusRecordTypeObject;
2779 object_record->Header.Flags = *id | ObjectTypePath << 8;
2780 write_path_data(path, &object_record->ObjectData.path);
2781 return Ok;
2784 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
2786 if (brush->bt == BrushTypeSolidColor)
2788 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.solid) + sizeof(EmfPlusSolidBrushData);
2789 return Ok;
2792 FIXME("unsupported brush type: %d\n", brush->bt);
2793 return NotImplemented;
2796 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
2798 if (brush->bt == BrushTypeSolidColor)
2800 GpSolidFill *solid = (GpSolidFill*)brush;
2802 data->Version = VERSION_MAGIC2;
2803 data->Type = solid->brush.bt;
2804 data->BrushData.solid.SolidColor.Blue = solid->color & 0xff;
2805 data->BrushData.solid.SolidColor.Green = (solid->color >> 8) & 0xff;
2806 data->BrushData.solid.SolidColor.Red = (solid->color >> 16) & 0xff;
2807 data->BrushData.solid.SolidColor.Alpha = solid->color >> 24;
2811 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
2813 DWORD i, data_flags, pen_data_size, brush_size;
2814 EmfPlusObject *object_record;
2815 EmfPlusPenData *pen_data;
2816 GpStatus stat;
2817 BOOL result;
2819 *id = -1;
2820 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2821 return Ok;
2823 data_flags = 0;
2824 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2826 GdipIsMatrixIdentity(&pen->transform, &result);
2827 if (!result)
2829 data_flags |= PenDataTransform;
2830 pen_data_size += sizeof(EmfPlusTransformMatrix);
2832 if (pen->startcap != LineCapFlat)
2834 data_flags |= PenDataStartCap;
2835 pen_data_size += sizeof(DWORD);
2837 if (pen->endcap != LineCapFlat)
2839 data_flags |= PenDataEndCap;
2840 pen_data_size += sizeof(DWORD);
2842 if (pen->join != LineJoinMiter)
2844 data_flags |= PenDataJoin;
2845 pen_data_size += sizeof(DWORD);
2847 if (pen->miterlimit != 10.0)
2849 data_flags |= PenDataMiterLimit;
2850 pen_data_size += sizeof(REAL);
2852 if (pen->style != GP_DEFAULT_PENSTYLE)
2854 data_flags |= PenDataLineStyle;
2855 pen_data_size += sizeof(DWORD);
2857 if (pen->dashcap != DashCapFlat)
2859 data_flags |= PenDataDashedLineCap;
2860 pen_data_size += sizeof(DWORD);
2862 data_flags |= PenDataDashedLineOffset;
2863 pen_data_size += sizeof(REAL);
2864 if (pen->numdashes)
2866 data_flags |= PenDataDashedLine;
2867 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
2869 if (pen->align != PenAlignmentCenter)
2871 data_flags |= PenDataNonCenter;
2872 pen_data_size += sizeof(DWORD);
2874 /* TODO: Add support for PenDataCompoundLine */
2875 if (pen->customstart)
2877 FIXME("ignoring custom start cup\n");
2879 if (pen->customend)
2881 FIXME("ignoring custom end cup\n");
2884 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
2885 if (stat != Ok) return stat;
2887 stat = METAFILE_AllocateRecord(metafile,
2888 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
2889 (void**)&object_record);
2890 if (stat != Ok) return stat;
2892 *id = METAFILE_AddObjectId(metafile);
2893 object_record->Header.Type = EmfPlusRecordTypeObject;
2894 object_record->Header.Flags = *id | ObjectTypePen << 8;
2895 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
2896 object_record->ObjectData.pen.Type = 0;
2898 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
2899 pen_data->PenDataFlags = data_flags;
2900 pen_data->PenUnit = pen->unit;
2901 pen_data->PenWidth = pen->width;
2903 i = 0;
2904 if (data_flags & PenDataTransform)
2906 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
2907 memcpy(m, &pen->transform, sizeof(*m));
2908 i += sizeof(EmfPlusTransformMatrix);
2910 if (data_flags & PenDataStartCap)
2912 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
2913 i += sizeof(DWORD);
2915 if (data_flags & PenDataEndCap)
2917 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
2918 i += sizeof(DWORD);
2920 if (data_flags & PenDataJoin)
2922 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
2923 i += sizeof(DWORD);
2925 if (data_flags & PenDataMiterLimit)
2927 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
2928 i += sizeof(REAL);
2930 if (data_flags & PenDataLineStyle)
2932 switch (pen->style & PS_STYLE_MASK)
2934 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
2935 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
2936 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
2937 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
2938 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
2939 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
2941 i += sizeof(DWORD);
2943 if (data_flags & PenDataDashedLineCap)
2945 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
2946 i += sizeof(DWORD);
2948 if (data_flags & PenDataDashedLineOffset)
2950 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
2951 i += sizeof(REAL);
2953 if (data_flags & PenDataDashedLine)
2955 int j;
2957 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
2958 i += sizeof(DWORD);
2960 for (j=0; j<pen->numdashes; j++)
2962 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
2963 i += sizeof(REAL);
2966 if (data_flags & PenDataNonCenter)
2968 *(REAL*)(pen_data->OptionalData + i) = pen->align;
2969 i += sizeof(DWORD);
2972 METAFILE_FillBrushData(pen->brush,
2973 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
2974 return Ok;
2977 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
2979 EmfPlusDrawPath *draw_path_record;
2980 DWORD path_id;
2981 DWORD pen_id;
2982 GpStatus stat;
2984 if (metafile->metafile_type == MetafileTypeEmf)
2986 FIXME("stub!\n");
2987 return NotImplemented;
2990 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
2991 if (stat != Ok) return stat;
2993 stat = METAFILE_AddPathObject(metafile, path, &path_id);
2994 if (stat != Ok) return stat;
2996 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
2997 if (stat != Ok) return stat;
2998 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
2999 draw_path_record->Header.Flags = path_id;
3000 draw_path_record->PenId = pen_id;
3002 METAFILE_WriteRecords(metafile);
3003 return Ok;
3006 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
3008 EmfPlusObject *object_record;
3009 GpStatus stat;
3010 DWORD size;
3012 *id = -1;
3013 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3014 return Ok;
3016 stat = METAFILE_PrepareBrushData(brush, &size);
3017 if (stat != Ok) return stat;
3019 stat = METAFILE_AllocateRecord(metafile,
3020 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
3021 if (stat != Ok) return stat;
3023 *id = METAFILE_AddObjectId(metafile);
3024 object_record->Header.Type = EmfPlusRecordTypeObject;
3025 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
3026 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
3027 return Ok;
3030 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
3032 EmfPlusFillPath *fill_path_record;
3033 DWORD brush_id = -1, path_id;
3034 BOOL inline_color;
3035 GpStatus stat;
3037 if (metafile->metafile_type == MetafileTypeEmf)
3039 FIXME("stub!\n");
3040 return NotImplemented;
3043 inline_color = brush->bt == BrushTypeSolidColor;
3044 if (!inline_color)
3046 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
3047 if (stat != Ok) return stat;
3050 stat = METAFILE_AddPathObject(metafile, path, &path_id);
3051 if (stat != Ok) return stat;
3053 stat = METAFILE_AllocateRecord(metafile,
3054 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
3055 if (stat != Ok) return stat;
3056 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
3057 if (inline_color)
3059 fill_path_record->Header.Flags = 0x8000 | path_id;
3060 fill_path_record->data.Color.Blue = ((GpSolidFill*)brush)->color & 0xff;
3061 fill_path_record->data.Color.Green = (((GpSolidFill*)brush)->color >> 8) & 0xff;
3062 fill_path_record->data.Color.Red = (((GpSolidFill*)brush)->color >> 16) & 0xff;
3063 fill_path_record->data.Color.Alpha = ((GpSolidFill*)brush)->color >> 24;
3065 else
3067 fill_path_record->Header.Flags = path_id;
3068 fill_path_record->data.BrushId = brush_id;
3071 METAFILE_WriteRecords(metafile);
3072 return Ok;