po: Update German translation.
[wine.git] / dlls / gdiplus / metafile.c
blob99e54837e45d5e6212e849af573f71fd274ba649
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 enum
244 BitmapDataTypePixel,
245 BitmapDataTypeCompressed,
246 } BitmapDataType;
248 typedef struct EmfPlusBitmap
250 DWORD Width;
251 DWORD Height;
252 DWORD Stride;
253 DWORD PixelFormat;
254 DWORD Type;
255 BYTE BitmapData[1];
256 } EmfPlusBitmap;
258 typedef struct EmfPlusMetafile
260 DWORD Type;
261 DWORD MetafileDataSize;
262 BYTE MetafileData[1];
263 } EmfPlusMetafile;
265 typedef enum ImageDataType
267 ImageDataTypeUnknown,
268 ImageDataTypeBitmap,
269 ImageDataTypeMetafile,
270 } ImageDataType;
272 typedef struct EmfPlusImage
274 DWORD Version;
275 ImageDataType Type;
276 union
278 EmfPlusBitmap bitmap;
279 EmfPlusMetafile metafile;
280 } ImageData;
281 } EmfPlusImage;
283 typedef struct EmfPlusImageAttributes
285 DWORD Version;
286 DWORD Reserved1;
287 DWORD WrapMode;
288 EmfPlusARGB ClampColor;
289 DWORD ObjectClamp;
290 DWORD Reserved2;
291 } EmfPlusImageAttributes;
293 typedef enum ObjectType
295 ObjectTypeInvalid,
296 ObjectTypeBrush,
297 ObjectTypePen,
298 ObjectTypePath,
299 ObjectTypeRegion,
300 ObjectTypeImage,
301 ObjectTypeFont,
302 ObjectTypeStringFormat,
303 ObjectTypeImageAttributes,
304 ObjectTypeCustomLineCap,
305 } ObjectType;
307 typedef struct EmfPlusObject
309 EmfPlusRecordHeader Header;
310 union
312 EmfPlusBrush brush;
313 EmfPlusPen pen;
314 EmfPlusPath path;
315 EmfPlusImage image;
316 EmfPlusImageAttributes image_attributes;
317 } ObjectData;
318 } EmfPlusObject;
320 typedef struct EmfPlusRectF
322 float X;
323 float Y;
324 float Width;
325 float Height;
326 } EmfPlusRectF;
328 typedef struct EmfPlusPointF
330 float X;
331 float Y;
332 } EmfPlusPointF;
334 typedef struct EmfPlusDrawImagePoints
336 EmfPlusRecordHeader Header;
337 DWORD ImageAttributesID;
338 DWORD SrcUnit;
339 EmfPlusRectF SrcRect;
340 DWORD count;
341 union
343 /*EmfPlusPointR pointR;
344 EmfPlusPoint point;*/
345 EmfPlusPointF pointF;
346 } PointData[3];
347 } EmfPlusDrawImagePoints;
349 typedef struct EmfPlusDrawPath
351 EmfPlusRecordHeader Header;
352 DWORD PenId;
353 } EmfPlusDrawPath;
355 typedef struct EmfPlusFillPath
357 EmfPlusRecordHeader Header;
358 union
360 DWORD BrushId;
361 EmfPlusARGB Color;
362 } data;
363 } EmfPlusFillPath;
365 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
367 return (metafile->next_object_id++) % 64;
370 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
372 DWORD size_needed;
373 EmfPlusRecordHeader *record;
375 if (!metafile->comment_data_size)
377 DWORD data_size = max(256, size * 2 + 4);
378 metafile->comment_data = heap_alloc_zero(data_size);
380 if (!metafile->comment_data)
381 return OutOfMemory;
383 memcpy(metafile->comment_data, "EMF+", 4);
385 metafile->comment_data_size = data_size;
386 metafile->comment_data_length = 4;
389 size_needed = size + metafile->comment_data_length;
391 if (size_needed > metafile->comment_data_size)
393 DWORD data_size = size_needed * 2;
394 BYTE *new_data = heap_alloc_zero(data_size);
396 if (!new_data)
397 return OutOfMemory;
399 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
401 metafile->comment_data_size = data_size;
402 heap_free(metafile->comment_data);
403 metafile->comment_data = new_data;
406 *result = metafile->comment_data + metafile->comment_data_length;
407 metafile->comment_data_length += size;
409 record = (EmfPlusRecordHeader*)*result;
410 record->Size = size;
411 record->DataSize = size - sizeof(EmfPlusRecordHeader);
413 return Ok;
416 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
418 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
419 metafile->comment_data_length -= record->Size;
422 static void METAFILE_WriteRecords(GpMetafile *metafile)
424 if (metafile->comment_data_length > 4)
426 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
427 metafile->comment_data_length = 4;
431 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
433 GpStatus stat;
435 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
437 EmfPlusHeader *header;
439 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
440 if (stat != Ok)
441 return stat;
443 header->Header.Type = EmfPlusRecordTypeHeader;
445 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
446 header->Header.Flags = 1;
447 else
448 header->Header.Flags = 0;
450 header->Version = VERSION_MAGIC2;
452 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
453 header->EmfPlusFlags = 1;
454 else
455 header->EmfPlusFlags = 0;
457 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
458 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
460 METAFILE_WriteRecords(metafile);
463 return Ok;
466 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
468 GpStatus stat;
470 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
472 EmfPlusRecordHeader *record;
474 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
475 if (stat != Ok)
476 return stat;
478 record->Type = EmfPlusRecordTypeEndOfFile;
479 record->Flags = 0;
481 METAFILE_WriteRecords(metafile);
484 return Ok;
487 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
488 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
490 HDC record_dc;
491 REAL dpix, dpiy;
492 REAL framerect_factor_x, framerect_factor_y;
493 RECT rc, *lprc;
494 GpStatus stat;
496 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
498 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
499 return InvalidParameter;
501 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
502 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
504 if (frameRect)
506 switch (frameUnit)
508 case MetafileFrameUnitPixel:
509 framerect_factor_x = 2540.0 / dpix;
510 framerect_factor_y = 2540.0 / dpiy;
511 break;
512 case MetafileFrameUnitPoint:
513 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
514 break;
515 case MetafileFrameUnitInch:
516 framerect_factor_x = framerect_factor_y = 2540.0;
517 break;
518 case MetafileFrameUnitDocument:
519 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
520 break;
521 case MetafileFrameUnitMillimeter:
522 framerect_factor_x = framerect_factor_y = 100.0;
523 break;
524 case MetafileFrameUnitGdi:
525 framerect_factor_x = framerect_factor_y = 1.0;
526 break;
527 default:
528 return InvalidParameter;
531 rc.left = framerect_factor_x * frameRect->X;
532 rc.top = framerect_factor_y * frameRect->Y;
533 rc.right = rc.left + framerect_factor_x * frameRect->Width;
534 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
536 lprc = &rc;
538 else
539 lprc = NULL;
541 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
543 if (!record_dc)
544 return GenericError;
546 *metafile = heap_alloc_zero(sizeof(GpMetafile));
547 if(!*metafile)
549 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
550 return OutOfMemory;
553 (*metafile)->image.type = ImageTypeMetafile;
554 (*metafile)->image.flags = ImageFlagsNone;
555 (*metafile)->image.palette = NULL;
556 (*metafile)->image.xres = dpix;
557 (*metafile)->image.yres = dpiy;
558 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
559 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
560 (*metafile)->unit = UnitPixel;
561 (*metafile)->metafile_type = type;
562 (*metafile)->record_dc = record_dc;
563 (*metafile)->comment_data = NULL;
564 (*metafile)->comment_data_size = 0;
565 (*metafile)->comment_data_length = 0;
566 (*metafile)->hemf = NULL;
567 list_init(&(*metafile)->containers);
569 if (!frameRect)
571 (*metafile)->auto_frame = TRUE;
572 (*metafile)->auto_frame_min.X = 0;
573 (*metafile)->auto_frame_min.Y = 0;
574 (*metafile)->auto_frame_max.X = -1;
575 (*metafile)->auto_frame_max.Y = -1;
578 stat = METAFILE_WriteHeader(*metafile, hdc);
580 if (stat != Ok)
582 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
583 heap_free(*metafile);
584 *metafile = NULL;
585 return OutOfMemory;
588 return stat;
591 /*****************************************************************************
592 * GdipRecordMetafileI [GDIPLUS.@]
594 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
595 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
597 GpRectF frameRectF, *pFrameRectF;
599 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
601 if (frameRect)
603 frameRectF.X = frameRect->X;
604 frameRectF.Y = frameRect->Y;
605 frameRectF.Width = frameRect->Width;
606 frameRectF.Height = frameRect->Height;
607 pFrameRectF = &frameRectF;
609 else
610 pFrameRectF = NULL;
612 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
615 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
616 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
618 GpStatus stat;
620 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
622 if (!stream)
623 return InvalidParameter;
625 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
627 if (stat == Ok)
629 (*metafile)->record_stream = stream;
630 IStream_AddRef(stream);
633 return stat;
636 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
637 UINT num_points)
639 int i;
641 if (!metafile->auto_frame || !num_points)
642 return;
644 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
645 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
647 for (i=0; i<num_points; i++)
649 if (points[i].X < metafile->auto_frame_min.X)
650 metafile->auto_frame_min.X = points[i].X;
651 if (points[i].X > metafile->auto_frame_max.X)
652 metafile->auto_frame_max.X = points[i].X;
653 if (points[i].Y < metafile->auto_frame_min.Y)
654 metafile->auto_frame_min.Y = points[i].Y;
655 if (points[i].Y > metafile->auto_frame_max.Y)
656 metafile->auto_frame_max.Y = points[i].Y;
660 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
662 GpStatus stat;
664 if (!metafile->record_dc || metafile->record_graphics)
665 return InvalidParameter;
667 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
669 if (stat == Ok)
671 *result = metafile->record_graphics;
672 metafile->record_graphics->xres = 96.0;
673 metafile->record_graphics->yres = 96.0;
676 return stat;
679 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
681 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
683 EmfPlusRecordHeader *record;
684 GpStatus stat;
686 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
687 if (stat != Ok)
688 return stat;
690 record->Type = EmfPlusRecordTypeGetDC;
691 record->Flags = 0;
693 METAFILE_WriteRecords(metafile);
696 *hdc = metafile->record_dc;
698 return Ok;
701 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
703 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
705 EmfPlusClear *record;
706 GpStatus stat;
708 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
709 if (stat != Ok)
710 return stat;
712 record->Header.Type = EmfPlusRecordTypeClear;
713 record->Header.Flags = 0;
714 record->Color = color;
716 METAFILE_WriteRecords(metafile);
719 return Ok;
722 static BOOL is_integer_rect(const GpRectF *rect)
724 SHORT x, y, width, height;
725 x = rect->X;
726 y = rect->Y;
727 width = rect->Width;
728 height = rect->Height;
729 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
730 rect->Width != (REAL)width || rect->Height != (REAL)height)
731 return FALSE;
732 return TRUE;
735 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
736 GDIPCONST GpRectF* rects, INT count)
738 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
740 EmfPlusFillRects *record;
741 GpStatus stat;
742 BOOL integer_rects = TRUE;
743 int i;
744 DWORD brushid;
745 int flags = 0;
747 if (brush->bt == BrushTypeSolidColor)
749 flags |= 0x8000;
750 brushid = ((GpSolidFill*)brush)->color;
752 else
754 FIXME("brush serialization not implemented\n");
755 return NotImplemented;
758 for (i=0; i<count; i++)
760 if (!is_integer_rect(&rects[i]))
762 integer_rects = FALSE;
763 break;
767 if (integer_rects)
768 flags |= 0x4000;
770 stat = METAFILE_AllocateRecord(metafile,
771 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
772 (void**)&record);
773 if (stat != Ok)
774 return stat;
776 record->Header.Type = EmfPlusRecordTypeFillRects;
777 record->Header.Flags = flags;
778 record->BrushID = brushid;
779 record->Count = count;
781 if (integer_rects)
783 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
784 for (i=0; i<count; i++)
786 record_rects[i].X = (SHORT)rects[i].X;
787 record_rects[i].Y = (SHORT)rects[i].Y;
788 record_rects[i].Width = (SHORT)rects[i].Width;
789 record_rects[i].Height = (SHORT)rects[i].Height;
792 else
793 memcpy(record+1, rects, sizeof(GpRectF) * count);
795 METAFILE_WriteRecords(metafile);
798 if (metafile->auto_frame)
800 GpPointF corners[4];
801 int i;
803 for (i=0; i<count; i++)
805 corners[0].X = rects[i].X;
806 corners[0].Y = rects[i].Y;
807 corners[1].X = rects[i].X + rects[i].Width;
808 corners[1].Y = rects[i].Y;
809 corners[2].X = rects[i].X;
810 corners[2].Y = rects[i].Y + rects[i].Height;
811 corners[3].X = rects[i].X + rects[i].Width;
812 corners[3].Y = rects[i].Y + rects[i].Height;
814 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
815 CoordinateSpaceWorld, corners, 4);
817 METAFILE_AdjustFrame(metafile, corners, 4);
821 return Ok;
824 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
826 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
828 EmfPlusSetClipRect *record;
829 GpStatus stat;
831 stat = METAFILE_AllocateRecord(metafile,
832 sizeof(EmfPlusSetClipRect),
833 (void**)&record);
834 if (stat != Ok)
835 return stat;
837 record->Header.Type = EmfPlusRecordTypeSetClipRect;
838 record->Header.Flags = (mode & 0xf) << 8;
839 record->ClipRect.X = x;
840 record->ClipRect.Y = y;
841 record->ClipRect.Width = width;
842 record->ClipRect.Height = height;
844 METAFILE_WriteRecords(metafile);
847 return Ok;
850 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
852 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
854 EmfPlusSetPageTransform *record;
855 GpStatus stat;
857 stat = METAFILE_AllocateRecord(metafile,
858 sizeof(EmfPlusSetPageTransform),
859 (void**)&record);
860 if (stat != Ok)
861 return stat;
863 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
864 record->Header.Flags = unit;
865 record->PageScale = scale;
867 METAFILE_WriteRecords(metafile);
870 return Ok;
873 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
875 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
877 EmfPlusSetWorldTransform *record;
878 GpStatus stat;
880 stat = METAFILE_AllocateRecord(metafile,
881 sizeof(EmfPlusSetWorldTransform),
882 (void**)&record);
883 if (stat != Ok)
884 return stat;
886 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
887 record->Header.Flags = 0;
888 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
890 METAFILE_WriteRecords(metafile);
893 return Ok;
896 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
898 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
900 EmfPlusScaleWorldTransform *record;
901 GpStatus stat;
903 stat = METAFILE_AllocateRecord(metafile,
904 sizeof(EmfPlusScaleWorldTransform),
905 (void**)&record);
906 if (stat != Ok)
907 return stat;
909 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
910 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
911 record->Sx = sx;
912 record->Sy = sy;
914 METAFILE_WriteRecords(metafile);
917 return Ok;
920 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
922 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
924 EmfPlusMultiplyWorldTransform *record;
925 GpStatus stat;
927 stat = METAFILE_AllocateRecord(metafile,
928 sizeof(EmfPlusMultiplyWorldTransform),
929 (void**)&record);
930 if (stat != Ok)
931 return stat;
933 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
934 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
935 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
937 METAFILE_WriteRecords(metafile);
940 return Ok;
943 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
945 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
947 EmfPlusRotateWorldTransform *record;
948 GpStatus stat;
950 stat = METAFILE_AllocateRecord(metafile,
951 sizeof(EmfPlusRotateWorldTransform),
952 (void**)&record);
953 if (stat != Ok)
954 return stat;
956 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
957 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
958 record->Angle = angle;
960 METAFILE_WriteRecords(metafile);
963 return Ok;
966 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
968 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
970 EmfPlusTranslateWorldTransform *record;
971 GpStatus stat;
973 stat = METAFILE_AllocateRecord(metafile,
974 sizeof(EmfPlusTranslateWorldTransform),
975 (void**)&record);
976 if (stat != Ok)
977 return stat;
979 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
980 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
981 record->dx = dx;
982 record->dy = dy;
984 METAFILE_WriteRecords(metafile);
987 return Ok;
990 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
992 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
994 EmfPlusRecordHeader *record;
995 GpStatus stat;
997 stat = METAFILE_AllocateRecord(metafile,
998 sizeof(EmfPlusRecordHeader),
999 (void**)&record);
1000 if (stat != Ok)
1001 return stat;
1003 record->Type = EmfPlusRecordTypeResetWorldTransform;
1004 record->Flags = 0;
1006 METAFILE_WriteRecords(metafile);
1009 return Ok;
1012 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1013 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1015 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1017 EmfPlusBeginContainer *record;
1018 GpStatus stat;
1020 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1021 if (stat != Ok)
1022 return stat;
1024 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1025 record->Header.Flags = unit & 0xff;
1026 record->DestRect = *dstrect;
1027 record->SrcRect = *srcrect;
1028 record->StackIndex = StackIndex;
1030 METAFILE_WriteRecords(metafile);
1033 return Ok;
1036 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1038 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1040 EmfPlusContainerRecord *record;
1041 GpStatus stat;
1043 stat = METAFILE_AllocateRecord(metafile,
1044 sizeof(EmfPlusContainerRecord),
1045 (void**)&record);
1046 if (stat != Ok)
1047 return stat;
1049 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1050 record->Header.Flags = 0;
1051 record->StackIndex = StackIndex;
1053 METAFILE_WriteRecords(metafile);
1056 return Ok;
1059 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1061 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1063 EmfPlusContainerRecord *record;
1064 GpStatus stat;
1066 stat = METAFILE_AllocateRecord(metafile,
1067 sizeof(EmfPlusContainerRecord),
1068 (void**)&record);
1069 if (stat != Ok)
1070 return stat;
1072 record->Header.Type = EmfPlusRecordTypeEndContainer;
1073 record->Header.Flags = 0;
1074 record->StackIndex = StackIndex;
1076 METAFILE_WriteRecords(metafile);
1079 return Ok;
1082 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1084 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1086 EmfPlusContainerRecord *record;
1087 GpStatus stat;
1089 stat = METAFILE_AllocateRecord(metafile,
1090 sizeof(EmfPlusContainerRecord),
1091 (void**)&record);
1092 if (stat != Ok)
1093 return stat;
1095 record->Header.Type = EmfPlusRecordTypeSave;
1096 record->Header.Flags = 0;
1097 record->StackIndex = StackIndex;
1099 METAFILE_WriteRecords(metafile);
1102 return Ok;
1105 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1107 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1109 EmfPlusContainerRecord *record;
1110 GpStatus stat;
1112 stat = METAFILE_AllocateRecord(metafile,
1113 sizeof(EmfPlusContainerRecord),
1114 (void**)&record);
1115 if (stat != Ok)
1116 return stat;
1118 record->Header.Type = EmfPlusRecordTypeRestore;
1119 record->Header.Flags = 0;
1120 record->StackIndex = StackIndex;
1122 METAFILE_WriteRecords(metafile);
1125 return Ok;
1128 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1130 if (hdc != metafile->record_dc)
1131 return InvalidParameter;
1133 return Ok;
1136 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1138 GpStatus stat;
1140 stat = METAFILE_WriteEndOfFile(metafile);
1141 metafile->record_graphics = NULL;
1143 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1144 metafile->record_dc = NULL;
1146 heap_free(metafile->comment_data);
1147 metafile->comment_data = NULL;
1148 metafile->comment_data_size = 0;
1150 if (stat == Ok)
1152 MetafileHeader header;
1154 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1155 if (stat == Ok && metafile->auto_frame &&
1156 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1158 RECTL bounds_rc, gdi_bounds_rc;
1159 REAL x_scale = 2540.0 / header.DpiX;
1160 REAL y_scale = 2540.0 / header.DpiY;
1161 BYTE* buffer;
1162 UINT buffer_size;
1164 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1165 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1166 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1167 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1169 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1170 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1172 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1173 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1174 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1175 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1178 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1179 buffer = heap_alloc(buffer_size);
1180 if (buffer)
1182 HENHMETAFILE new_hemf;
1184 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1186 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1188 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1190 if (new_hemf)
1192 DeleteEnhMetaFile(metafile->hemf);
1193 metafile->hemf = new_hemf;
1195 else
1196 stat = OutOfMemory;
1198 heap_free(buffer);
1200 else
1201 stat = OutOfMemory;
1203 if (stat == Ok)
1204 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1206 if (stat == Ok)
1208 metafile->bounds.X = header.X;
1209 metafile->bounds.Y = header.Y;
1210 metafile->bounds.Width = header.Width;
1211 metafile->bounds.Height = header.Height;
1215 if (stat == Ok && metafile->record_stream)
1217 BYTE *buffer;
1218 UINT buffer_size;
1220 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1222 buffer = heap_alloc(buffer_size);
1223 if (buffer)
1225 HRESULT hr;
1227 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1229 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1231 if (FAILED(hr))
1232 stat = hresult_to_status(hr);
1234 heap_free(buffer);
1236 else
1237 stat = OutOfMemory;
1240 if (metafile->record_stream)
1242 IStream_Release(metafile->record_stream);
1243 metafile->record_stream = NULL;
1246 return stat;
1249 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1251 TRACE("(%p,%p)\n", metafile, hEmf);
1253 if (!metafile || !hEmf || !metafile->hemf)
1254 return InvalidParameter;
1256 *hEmf = metafile->hemf;
1257 metafile->hemf = NULL;
1259 return Ok;
1262 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1264 const GpRectF *rect;
1265 const GpPointF *pt;
1267 /* This transforms metafile device space to output points. */
1268 rect = &metafile->src_rect;
1269 pt = metafile->playback_points;
1270 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1271 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1272 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1273 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1274 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1275 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1278 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1280 XFORM combined, final;
1282 METAFILE_GetFinalGdiTransform(metafile, &final);
1284 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1286 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1287 SetWorldTransform(metafile->playback_dc, &combined);
1289 return Ok;
1292 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1294 GpStatus stat = Ok;
1296 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1298 if (stat == Ok)
1300 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1302 metafile->gdiworldtransform = identity;
1303 METAFILE_PlaybackUpdateGdiTransform(metafile);
1306 return stat;
1309 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1311 if (metafile->playback_dc)
1313 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1314 metafile->playback_dc = NULL;
1318 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1320 GpStatus stat;
1321 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1322 if (stat == Ok)
1323 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1324 return stat;
1327 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1329 GpMatrix *real_transform;
1330 GpStatus stat;
1332 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1334 if (stat == Ok)
1336 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1338 if (metafile->page_unit != UnitDisplay)
1339 scale *= metafile->page_scale;
1341 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1343 if (stat == Ok)
1344 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1346 if (stat == Ok)
1347 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1349 GdipDeleteMatrix(real_transform);
1352 return stat;
1355 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
1356 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
1358 GpStatus stat;
1359 GpMetafile *real_metafile = (GpMetafile*)metafile;
1361 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
1363 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
1364 return InvalidParameter;
1366 if (recordType >= 1 && recordType <= 0x7a)
1368 /* regular EMF record */
1369 if (metafile->playback_dc)
1371 ENHMETARECORD *record;
1373 switch (recordType)
1375 case EMR_SETMAPMODE:
1376 case EMR_SAVEDC:
1377 case EMR_RESTOREDC:
1378 case EMR_SETWINDOWORGEX:
1379 case EMR_SETWINDOWEXTEX:
1380 case EMR_SETVIEWPORTORGEX:
1381 case EMR_SETVIEWPORTEXTEX:
1382 case EMR_SCALEVIEWPORTEXTEX:
1383 case EMR_SCALEWINDOWEXTEX:
1384 case EMR_MODIFYWORLDTRANSFORM:
1385 FIXME("not implemented for record type %x\n", recordType);
1386 break;
1387 case EMR_SETWORLDTRANSFORM:
1389 const XFORM* xform = (void*)data;
1390 real_metafile->gdiworldtransform = *xform;
1391 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
1392 break;
1394 case EMR_EXTSELECTCLIPRGN:
1396 DWORD rgndatasize = *(DWORD*)data;
1397 DWORD mode = *(DWORD*)(data + 4);
1398 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
1399 HRGN hrgn = NULL;
1401 if (dataSize > 8)
1403 XFORM final;
1405 METAFILE_GetFinalGdiTransform(metafile, &final);
1407 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
1410 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
1412 DeleteObject(hrgn);
1414 return Ok;
1416 default:
1417 break;
1420 record = heap_alloc_zero(dataSize + 8);
1422 if (record)
1424 record->iType = recordType;
1425 record->nSize = dataSize + 8;
1426 memcpy(record->dParm, data, dataSize);
1428 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
1429 record, metafile->handle_count);
1431 heap_free(record);
1433 else
1434 return OutOfMemory;
1437 else
1439 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
1441 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
1443 switch(recordType)
1445 case EmfPlusRecordTypeHeader:
1446 case EmfPlusRecordTypeEndOfFile:
1447 break;
1448 case EmfPlusRecordTypeGetDC:
1449 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
1450 break;
1451 case EmfPlusRecordTypeClear:
1453 EmfPlusClear *record = (EmfPlusClear*)header;
1455 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
1457 case EmfPlusRecordTypeFillRects:
1459 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
1460 GpBrush *brush, *temp_brush=NULL;
1461 GpRectF *rects, *temp_rects=NULL;
1463 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
1464 return InvalidParameter;
1466 if (flags & 0x4000)
1468 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
1469 return InvalidParameter;
1471 else
1473 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
1474 return InvalidParameter;
1477 if (flags & 0x8000)
1479 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
1480 brush = temp_brush;
1482 else
1484 FIXME("brush deserialization not implemented\n");
1485 return NotImplemented;
1488 if (stat == Ok)
1490 if (flags & 0x4000)
1492 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
1493 int i;
1495 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
1496 if (rects)
1498 for (i=0; i<record->Count; i++)
1500 rects[i].X = int_rects[i].X;
1501 rects[i].Y = int_rects[i].Y;
1502 rects[i].Width = int_rects[i].Width;
1503 rects[i].Height = int_rects[i].Height;
1506 else
1507 stat = OutOfMemory;
1509 else
1510 rects = (GpRectF*)(record+1);
1513 if (stat == Ok)
1515 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
1518 GdipDeleteBrush(temp_brush);
1519 heap_free(temp_rects);
1521 return stat;
1523 case EmfPlusRecordTypeSetClipRect:
1525 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
1526 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
1527 GpRegion *region;
1528 GpMatrix world_to_device;
1530 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
1531 return InvalidParameter;
1533 stat = GdipCreateRegionRect(&record->ClipRect, &region);
1535 if (stat == Ok)
1537 get_graphics_transform(real_metafile->playback_graphics,
1538 CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
1540 GdipTransformRegion(region, &world_to_device);
1542 GdipCombineRegionRegion(real_metafile->clip, region, mode);
1544 GdipDeleteRegion(region);
1547 return METAFILE_PlaybackUpdateClip(real_metafile);
1549 case EmfPlusRecordTypeSetPageTransform:
1551 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
1552 GpUnit unit = (GpUnit)flags;
1554 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
1555 return InvalidParameter;
1557 real_metafile->page_unit = unit;
1558 real_metafile->page_scale = record->PageScale;
1560 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1562 case EmfPlusRecordTypeSetWorldTransform:
1564 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
1566 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
1567 return InvalidParameter;
1569 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
1571 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1573 case EmfPlusRecordTypeScaleWorldTransform:
1575 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
1576 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1578 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
1579 return InvalidParameter;
1581 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
1583 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1585 case EmfPlusRecordTypeMultiplyWorldTransform:
1587 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
1588 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1589 GpMatrix matrix;
1591 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
1592 return InvalidParameter;
1594 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
1596 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
1598 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1600 case EmfPlusRecordTypeRotateWorldTransform:
1602 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
1603 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1605 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
1606 return InvalidParameter;
1608 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
1610 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1612 case EmfPlusRecordTypeTranslateWorldTransform:
1614 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
1615 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1617 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
1618 return InvalidParameter;
1620 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
1622 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1624 case EmfPlusRecordTypeResetWorldTransform:
1626 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1628 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1630 case EmfPlusRecordTypeBeginContainer:
1632 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
1633 container* cont;
1634 GpUnit unit;
1635 REAL scale_x, scale_y;
1636 GpRectF scaled_srcrect;
1637 GpMatrix transform;
1639 cont = heap_alloc_zero(sizeof(*cont));
1640 if (!cont)
1641 return OutOfMemory;
1643 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1644 if (stat != Ok)
1646 heap_free(cont);
1647 return stat;
1650 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1652 if (stat != Ok)
1654 GdipDeleteRegion(cont->clip);
1655 heap_free(cont);
1656 return stat;
1659 cont->id = record->StackIndex;
1660 cont->type = BEGIN_CONTAINER;
1661 cont->world_transform = *metafile->world_transform;
1662 cont->page_unit = metafile->page_unit;
1663 cont->page_scale = metafile->page_scale;
1664 list_add_head(&real_metafile->containers, &cont->entry);
1666 unit = record->Header.Flags & 0xff;
1668 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
1669 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
1671 scaled_srcrect.X = scale_x * record->SrcRect.X;
1672 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
1673 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
1674 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
1676 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
1677 transform.matrix[1] = 0.0;
1678 transform.matrix[2] = 0.0;
1679 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
1680 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
1681 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
1683 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
1685 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1687 case EmfPlusRecordTypeBeginContainerNoParams:
1688 case EmfPlusRecordTypeSave:
1690 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1691 container* cont;
1693 cont = heap_alloc_zero(sizeof(*cont));
1694 if (!cont)
1695 return OutOfMemory;
1697 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1698 if (stat != Ok)
1700 heap_free(cont);
1701 return stat;
1704 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1705 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1706 else
1707 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
1709 if (stat != Ok)
1711 GdipDeleteRegion(cont->clip);
1712 heap_free(cont);
1713 return stat;
1716 cont->id = record->StackIndex;
1717 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1718 cont->type = BEGIN_CONTAINER;
1719 else
1720 cont->type = SAVE_GRAPHICS;
1721 cont->world_transform = *metafile->world_transform;
1722 cont->page_unit = metafile->page_unit;
1723 cont->page_scale = metafile->page_scale;
1724 list_add_head(&real_metafile->containers, &cont->entry);
1726 break;
1728 case EmfPlusRecordTypeEndContainer:
1729 case EmfPlusRecordTypeRestore:
1731 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1732 container* cont;
1733 enum container_type type;
1734 BOOL found=FALSE;
1736 if (recordType == EmfPlusRecordTypeEndContainer)
1737 type = BEGIN_CONTAINER;
1738 else
1739 type = SAVE_GRAPHICS;
1741 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
1743 if (cont->id == record->StackIndex && cont->type == type)
1745 found = TRUE;
1746 break;
1750 if (found)
1752 container* cont2;
1754 /* pop any newer items on the stack */
1755 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
1757 list_remove(&cont2->entry);
1758 GdipDeleteRegion(cont2->clip);
1759 heap_free(cont2);
1762 if (type == BEGIN_CONTAINER)
1763 GdipEndContainer(real_metafile->playback_graphics, cont->state);
1764 else
1765 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
1767 *real_metafile->world_transform = cont->world_transform;
1768 real_metafile->page_unit = cont->page_unit;
1769 real_metafile->page_scale = cont->page_scale;
1770 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
1772 list_remove(&cont->entry);
1773 GdipDeleteRegion(cont->clip);
1774 heap_free(cont);
1777 break;
1779 default:
1780 FIXME("Not implemented for record type %x\n", recordType);
1781 return NotImplemented;
1785 return Ok;
1788 struct enum_metafile_data
1790 EnumerateMetafileProc callback;
1791 void *callback_data;
1792 GpMetafile *metafile;
1795 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1796 int nObj, LPARAM lpData)
1798 BOOL ret;
1799 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
1800 const BYTE* pStr;
1802 data->metafile->handle_table = lpHTable;
1803 data->metafile->handle_count = nObj;
1805 /* First check for an EMF+ record. */
1806 if (lpEMFR->iType == EMR_GDICOMMENT)
1808 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1810 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1812 int offset = 4;
1814 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
1816 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
1818 if (record->DataSize)
1819 pStr = (const BYTE*)(record+1);
1820 else
1821 pStr = NULL;
1823 ret = data->callback(record->Type, record->Flags, record->DataSize,
1824 pStr, data->callback_data);
1826 if (!ret)
1827 return 0;
1829 offset += record->Size;
1832 return 1;
1836 if (lpEMFR->nSize != 8)
1837 pStr = (const BYTE*)lpEMFR->dParm;
1838 else
1839 pStr = NULL;
1841 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
1842 pStr, data->callback_data);
1845 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
1846 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
1847 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
1848 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
1850 struct enum_metafile_data data;
1851 GpStatus stat;
1852 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
1853 GraphicsContainer state;
1854 GpPath *dst_path;
1856 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
1857 destPoints, count, srcRect, srcUnit, callback, callbackData,
1858 imageAttributes);
1860 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
1861 return InvalidParameter;
1863 if (!metafile->hemf)
1864 return InvalidParameter;
1866 if (metafile->playback_graphics)
1867 return ObjectBusy;
1869 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
1870 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
1871 debugstr_pointf(&destPoints[2]));
1873 data.callback = callback;
1874 data.callback_data = callbackData;
1875 data.metafile = real_metafile;
1877 real_metafile->playback_graphics = graphics;
1878 real_metafile->playback_dc = NULL;
1879 real_metafile->src_rect = *srcRect;
1881 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
1882 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
1884 if (stat == Ok)
1885 stat = GdipBeginContainer2(graphics, &state);
1887 if (stat == Ok)
1889 stat = GdipSetPageScale(graphics, 1.0);
1891 if (stat == Ok)
1892 stat = GdipSetPageUnit(graphics, UnitPixel);
1894 if (stat == Ok)
1895 stat = GdipResetWorldTransform(graphics);
1897 if (stat == Ok)
1898 stat = GdipCreateRegion(&real_metafile->base_clip);
1900 if (stat == Ok)
1901 stat = GdipGetClip(graphics, real_metafile->base_clip);
1903 if (stat == Ok)
1904 stat = GdipCreateRegion(&real_metafile->clip);
1906 if (stat == Ok)
1907 stat = GdipCreatePath(FillModeAlternate, &dst_path);
1909 if (stat == Ok)
1911 GpPointF clip_points[4];
1913 clip_points[0] = real_metafile->playback_points[0];
1914 clip_points[1] = real_metafile->playback_points[1];
1915 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
1916 - real_metafile->playback_points[0].X;
1917 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
1918 - real_metafile->playback_points[0].Y;
1919 clip_points[3] = real_metafile->playback_points[2];
1921 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
1923 if (stat == Ok)
1924 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
1926 GdipDeletePath(dst_path);
1929 if (stat == Ok)
1930 stat = GdipCreateMatrix(&real_metafile->world_transform);
1932 if (stat == Ok)
1934 real_metafile->page_unit = UnitDisplay;
1935 real_metafile->page_scale = 1.0;
1936 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1939 if (stat == Ok)
1941 stat = METAFILE_PlaybackUpdateClip(real_metafile);
1944 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
1945 metafile->metafile_type == MetafileTypeWmfPlaceable ||
1946 metafile->metafile_type == MetafileTypeWmf))
1947 stat = METAFILE_PlaybackGetDC(real_metafile);
1949 if (stat == Ok)
1950 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
1952 METAFILE_PlaybackReleaseDC(real_metafile);
1954 GdipDeleteMatrix(real_metafile->world_transform);
1955 real_metafile->world_transform = NULL;
1957 GdipDeleteRegion(real_metafile->base_clip);
1958 real_metafile->base_clip = NULL;
1960 GdipDeleteRegion(real_metafile->clip);
1961 real_metafile->clip = NULL;
1963 while (list_head(&real_metafile->containers))
1965 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
1966 list_remove(&cont->entry);
1967 GdipDeleteRegion(cont->clip);
1968 heap_free(cont);
1971 GdipEndContainer(graphics, state);
1974 real_metafile->playback_graphics = NULL;
1976 return stat;
1979 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
1980 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
1981 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1983 GpPointF points[3];
1985 if (!graphics || !metafile || !dest) return InvalidParameter;
1987 points[0].X = points[2].X = dest->X;
1988 points[0].Y = points[1].Y = dest->Y;
1989 points[1].X = dest->X + dest->Width;
1990 points[2].Y = dest->Y + dest->Height;
1992 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
1993 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
1996 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
1997 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
1998 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2000 GpRectF destf;
2002 if (!graphics || !metafile || !dest) return InvalidParameter;
2004 destf.X = dest->X;
2005 destf.Y = dest->Y;
2006 destf.Width = dest->Width;
2007 destf.Height = dest->Height;
2009 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2012 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
2013 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
2014 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2016 GpRectF destf;
2018 if (!graphics || !metafile || !dest) return InvalidParameter;
2020 destf.X = dest->X;
2021 destf.Y = dest->Y;
2022 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
2023 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
2025 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2028 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
2029 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
2030 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2032 GpPointF ptf;
2034 if (!graphics || !metafile || !dest) return InvalidParameter;
2036 ptf.X = dest->X;
2037 ptf.Y = dest->Y;
2039 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
2042 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
2043 MetafileHeader * header)
2045 GpStatus status;
2047 TRACE("(%p, %p)\n", metafile, header);
2049 if(!metafile || !header)
2050 return InvalidParameter;
2052 if (metafile->hemf)
2054 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
2055 if (status != Ok) return status;
2057 else
2059 memset(header, 0, sizeof(*header));
2060 header->Version = VERSION_MAGIC2;
2063 header->Type = metafile->metafile_type;
2064 header->DpiX = metafile->image.xres;
2065 header->DpiY = metafile->image.yres;
2066 header->Width = gdip_round(metafile->bounds.Width);
2067 header->Height = gdip_round(metafile->bounds.Height);
2069 return Ok;
2072 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
2073 int nObj, LPARAM lpData)
2075 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
2077 if (lpEMFR->iType == EMR_GDICOMMENT)
2079 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
2081 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
2083 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
2085 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
2086 header->Type == EmfPlusRecordTypeHeader)
2088 memcpy(dst_header, header, sizeof(*dst_header));
2092 else if (lpEMFR->iType == EMR_HEADER)
2093 return TRUE;
2095 return FALSE;
2098 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
2099 MetafileHeader *header)
2101 ENHMETAHEADER3 emfheader;
2102 EmfPlusHeader emfplusheader;
2103 MetafileType metafile_type;
2105 TRACE("(%p,%p)\n", hemf, header);
2107 if(!hemf || !header)
2108 return InvalidParameter;
2110 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
2111 return GenericError;
2113 emfplusheader.Header.Type = 0;
2115 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
2117 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
2119 if ((emfplusheader.Header.Flags & 1) == 1)
2120 metafile_type = MetafileTypeEmfPlusDual;
2121 else
2122 metafile_type = MetafileTypeEmfPlusOnly;
2124 else
2125 metafile_type = MetafileTypeEmf;
2127 header->Type = metafile_type;
2128 header->Size = emfheader.nBytes;
2129 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
2130 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
2131 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
2132 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
2133 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
2134 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
2135 header->u.EmfHeader = emfheader;
2137 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
2139 header->Version = emfplusheader.Version;
2140 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
2141 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
2142 header->LogicalDpiX = emfplusheader.LogicalDpiX;
2143 header->LogicalDpiY = emfplusheader.LogicalDpiY;
2145 else
2147 header->Version = emfheader.nVersion;
2148 header->EmfPlusFlags = 0;
2149 header->EmfPlusHeaderSize = 0;
2150 header->LogicalDpiX = 0;
2151 header->LogicalDpiY = 0;
2154 return Ok;
2157 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
2158 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
2160 GpStatus status;
2161 GpMetafile *metafile;
2163 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
2165 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
2166 if (status == Ok)
2168 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2169 GdipDisposeImage(&metafile->image);
2171 return status;
2174 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
2175 MetafileHeader *header)
2177 GpStatus status;
2178 GpMetafile *metafile;
2180 TRACE("(%s,%p)\n", debugstr_w(filename), header);
2182 if (!filename || !header)
2183 return InvalidParameter;
2185 status = GdipCreateMetafileFromFile(filename, &metafile);
2186 if (status == Ok)
2188 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2189 GdipDisposeImage(&metafile->image);
2191 return status;
2194 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
2195 MetafileHeader *header)
2197 GpStatus status;
2198 GpMetafile *metafile;
2200 TRACE("(%p,%p)\n", stream, header);
2202 if (!stream || !header)
2203 return InvalidParameter;
2205 status = GdipCreateMetafileFromStream(stream, &metafile);
2206 if (status == Ok)
2208 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2209 GdipDisposeImage(&metafile->image);
2211 return status;
2214 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
2215 GpMetafile **metafile)
2217 GpStatus stat;
2218 MetafileHeader header;
2220 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
2222 if(!hemf || !metafile)
2223 return InvalidParameter;
2225 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
2226 if (stat != Ok)
2227 return stat;
2229 *metafile = heap_alloc_zero(sizeof(GpMetafile));
2230 if (!*metafile)
2231 return OutOfMemory;
2233 (*metafile)->image.type = ImageTypeMetafile;
2234 (*metafile)->image.format = ImageFormatEMF;
2235 (*metafile)->image.frame_count = 1;
2236 (*metafile)->image.xres = header.DpiX;
2237 (*metafile)->image.yres = header.DpiY;
2238 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
2239 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
2240 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
2241 / 2540.0 * header.DpiX;
2242 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
2243 / 2540.0 * header.DpiY;
2244 (*metafile)->unit = UnitPixel;
2245 (*metafile)->metafile_type = header.Type;
2246 (*metafile)->hemf = hemf;
2247 (*metafile)->preserve_hemf = !delete;
2248 list_init(&(*metafile)->containers);
2250 TRACE("<-- %p\n", *metafile);
2252 return Ok;
2255 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
2256 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2258 UINT read;
2259 BYTE *copy;
2260 HENHMETAFILE hemf;
2261 GpStatus retval = Ok;
2263 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
2265 if(!hwmf || !metafile)
2266 return InvalidParameter;
2268 *metafile = NULL;
2269 read = GetMetaFileBitsEx(hwmf, 0, NULL);
2270 if(!read)
2271 return GenericError;
2272 copy = heap_alloc_zero(read);
2273 GetMetaFileBitsEx(hwmf, read, copy);
2275 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
2276 heap_free(copy);
2278 /* FIXME: We should store and use hwmf instead of converting to hemf */
2279 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
2281 if (retval == Ok)
2283 if (placeable)
2285 (*metafile)->image.xres = (REAL)placeable->Inch;
2286 (*metafile)->image.yres = (REAL)placeable->Inch;
2287 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
2288 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
2289 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
2290 placeable->BoundingBox.Left);
2291 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
2292 placeable->BoundingBox.Top);
2293 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
2295 else
2296 (*metafile)->metafile_type = MetafileTypeWmf;
2297 (*metafile)->image.format = ImageFormatWMF;
2299 if (delete) DeleteMetaFile(hwmf);
2301 else
2302 DeleteEnhMetaFile(hemf);
2303 return retval;
2306 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
2307 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2309 HMETAFILE hmf = GetMetaFileW(file);
2311 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
2313 if(!hmf) return InvalidParameter;
2315 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
2318 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
2319 GpMetafile **metafile)
2321 GpStatus status;
2322 IStream *stream;
2324 TRACE("(%p, %p)\n", file, metafile);
2326 if (!file || !metafile) return InvalidParameter;
2328 *metafile = NULL;
2330 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
2331 if (status == Ok)
2333 status = GdipCreateMetafileFromStream(stream, metafile);
2334 IStream_Release(stream);
2336 return status;
2339 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
2340 GpMetafile **metafile)
2342 GpStatus stat;
2344 TRACE("%p %p\n", stream, metafile);
2346 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
2347 if (stat != Ok) return stat;
2349 if ((*metafile)->image.type != ImageTypeMetafile)
2351 GdipDisposeImage(&(*metafile)->image);
2352 *metafile = NULL;
2353 return GenericError;
2356 return Ok;
2359 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
2360 UINT limitDpi)
2362 TRACE("(%p,%u)\n", metafile, limitDpi);
2364 return Ok;
2367 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
2368 GpMetafile* metafile, BOOL* succ, EmfType emfType,
2369 const WCHAR* description, GpMetafile** out_metafile)
2371 static int calls;
2373 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
2374 debugstr_w(description), out_metafile);
2376 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
2377 return InvalidParameter;
2379 if(succ)
2380 *succ = FALSE;
2381 *out_metafile = NULL;
2383 if(!(calls++))
2384 FIXME("not implemented\n");
2386 return NotImplemented;
2389 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
2390 LPBYTE pData16, INT iMapMode, INT eFlags)
2392 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
2393 return NotImplemented;
2396 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
2397 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
2398 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
2399 GpMetafile **metafile)
2401 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2402 frameUnit, debugstr_w(desc), metafile);
2404 return NotImplemented;
2407 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
2408 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
2409 GDIPCONST WCHAR *desc, GpMetafile **metafile)
2411 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2412 frameUnit, debugstr_w(desc), metafile);
2414 return NotImplemented;
2417 /*****************************************************************************
2418 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
2421 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
2422 GpMetafile* metafile, BOOL* conversionSuccess,
2423 const WCHAR* filename, EmfType emfType,
2424 const WCHAR* description, GpMetafile** out_metafile)
2426 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
2427 return NotImplemented;
2430 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
2432 LARGE_INTEGER zero;
2433 STATSTG statstg;
2434 GpStatus stat;
2435 HRESULT hr;
2437 *size = 0;
2439 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
2440 if (FAILED(hr)) return hresult_to_status(hr);
2442 stat = encode_image_png(image, *stream, NULL);
2443 if (stat != Ok)
2445 IStream_Release(*stream);
2446 return stat;
2449 hr = IStream_Stat(*stream, &statstg, 1);
2450 if (FAILED(hr))
2452 IStream_Release(*stream);
2453 return hresult_to_status(hr);
2455 *size = statstg.cbSize.u.LowPart;
2457 zero.QuadPart = 0;
2458 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
2459 if (FAILED(hr))
2461 IStream_Release(*stream);
2462 return hresult_to_status(hr);
2465 return Ok;
2468 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
2470 HRESULT hr;
2472 record->Width = 0;
2473 record->Height = 0;
2474 record->Stride = 0;
2475 record->PixelFormat = 0;
2476 record->Type = BitmapDataTypeCompressed;
2478 hr = IStream_Read(stream, record->BitmapData, size, NULL);
2479 if (FAILED(hr)) return hresult_to_status(hr);
2480 return Ok;
2483 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
2485 EmfPlusObject *object_record;
2486 GpStatus stat;
2487 DWORD size;
2489 *id = -1;
2491 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2492 return Ok;
2494 if (image->type == ImageTypeBitmap)
2496 IStream *stream;
2497 DWORD aligned_size;
2499 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
2500 if (stat != Ok) return stat;
2501 aligned_size = (size + 3) & ~3;
2503 stat = METAFILE_AllocateRecord(metafile,
2504 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
2505 (void**)&object_record);
2506 if (stat != Ok)
2508 IStream_Release(stream);
2509 return stat;
2511 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
2513 *id = METAFILE_AddObjectId(metafile);
2514 object_record->Header.Type = EmfPlusRecordTypeObject;
2515 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2516 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2517 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
2519 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
2520 IStream_Release(stream);
2521 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2522 return stat;
2524 else if (image->type == ImageTypeMetafile)
2526 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
2527 EmfPlusMetafile *metafile_record;
2529 if (!hemf) return InvalidParameter;
2531 size = GetEnhMetaFileBits(hemf, 0, NULL);
2532 if (!size) return GenericError;
2534 stat = METAFILE_AllocateRecord(metafile,
2535 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
2536 (void**)&object_record);
2537 if (stat != Ok) return stat;
2539 *id = METAFILE_AddObjectId(metafile);
2540 object_record->Header.Type = EmfPlusRecordTypeObject;
2541 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2542 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2543 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
2544 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
2545 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
2546 metafile_record->MetafileDataSize = size;
2547 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
2549 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2550 return GenericError;
2552 return Ok;
2554 else
2556 FIXME("not supported image type (%d)\n", image->type);
2557 return NotImplemented;
2561 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
2563 EmfPlusObject *object_record;
2564 EmfPlusImageAttributes *attrs_record;
2565 GpStatus stat;
2567 *id = -1;
2569 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2570 return Ok;
2572 if (!attrs)
2573 return Ok;
2575 stat = METAFILE_AllocateRecord(metafile,
2576 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
2577 (void**)&object_record);
2578 if (stat != Ok) return stat;
2580 *id = METAFILE_AddObjectId(metafile);
2581 object_record->Header.Type = EmfPlusRecordTypeObject;
2582 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
2583 attrs_record = &object_record->ObjectData.image_attributes;
2584 attrs_record->Version = VERSION_MAGIC2;
2585 attrs_record->Reserved1 = 0;
2586 attrs_record->WrapMode = attrs->wrap;
2587 attrs_record->ClampColor.Blue = attrs->outside_color & 0xff;
2588 attrs_record->ClampColor.Green = (attrs->outside_color >> 8) & 0xff;
2589 attrs_record->ClampColor.Red = (attrs->outside_color >> 16) & 0xff;
2590 attrs_record->ClampColor.Alpha = attrs->outside_color >> 24;
2591 attrs_record->ObjectClamp = attrs->clamp;
2592 attrs_record->Reserved2 = 0;
2593 return Ok;
2596 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
2597 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2598 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2599 DrawImageAbort callback, VOID *callbackData)
2601 EmfPlusDrawImagePoints *draw_image_record;
2602 DWORD image_id, attributes_id;
2603 GpStatus stat;
2605 if (count != 3) return InvalidParameter;
2607 if (metafile->metafile_type == MetafileTypeEmf)
2609 FIXME("MetafileTypeEmf metafiles not supported\n");
2610 return NotImplemented;
2612 else
2613 FIXME("semi-stub\n");
2615 if (!imageAttributes)
2617 stat = METAFILE_AddImageObject(metafile, image, &image_id);
2619 else if (image->type == ImageTypeBitmap)
2621 INT width = ((GpBitmap*)image)->width;
2622 INT height = ((GpBitmap*)image)->height;
2623 GpGraphics *graphics;
2624 GpBitmap *bitmap;
2626 stat = GdipCreateBitmapFromScan0(width, height,
2627 0, PixelFormat32bppARGB, NULL, &bitmap);
2628 if (stat != Ok) return stat;
2630 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
2631 if (stat != Ok)
2633 GdipDisposeImage((GpImage*)bitmap);
2634 return stat;
2637 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
2638 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
2639 GdipDeleteGraphics(graphics);
2640 if (stat != Ok)
2642 GdipDisposeImage((GpImage*)bitmap);
2643 return stat;
2646 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
2647 GdipDisposeImage((GpImage*)bitmap);
2649 else
2651 FIXME("imageAttributes not supported (image type %d)\n", image->type);
2652 return NotImplemented;
2654 if (stat != Ok) return stat;
2656 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
2657 if (stat != Ok) return stat;
2659 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
2660 if (stat != Ok) return stat;
2661 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
2662 draw_image_record->Header.Flags = image_id;
2663 draw_image_record->ImageAttributesID = attributes_id;
2664 draw_image_record->SrcUnit = UnitPixel;
2665 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
2666 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
2667 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
2668 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
2669 draw_image_record->count = 3;
2670 draw_image_record->PointData[0].pointF.X = points[0].X;
2671 draw_image_record->PointData[0].pointF.Y = points[0].Y;
2672 draw_image_record->PointData[1].pointF.X = points[1].X;
2673 draw_image_record->PointData[1].pointF.Y = points[1].Y;
2674 draw_image_record->PointData[2].pointF.X = points[2].X;
2675 draw_image_record->PointData[2].pointF.Y = points[2].Y;
2676 METAFILE_WriteRecords(metafile);
2677 return Ok;
2680 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
2682 EmfPlusRecordHeader *record;
2683 GpStatus stat;
2685 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2686 return Ok;
2688 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
2689 if (stat != Ok) return stat;
2691 record->Type = prop;
2692 record->Flags = val;
2694 METAFILE_WriteRecords(metafile);
2695 return Ok;
2698 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
2700 EmfPlusObject *object_record;
2701 GpStatus stat;
2702 DWORD size;
2704 *id = -1;
2705 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2706 return Ok;
2708 size = write_path_data(path, NULL);
2709 stat = METAFILE_AllocateRecord(metafile,
2710 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
2711 (void**)&object_record);
2712 if (stat != Ok) return stat;
2714 *id = METAFILE_AddObjectId(metafile);
2715 object_record->Header.Type = EmfPlusRecordTypeObject;
2716 object_record->Header.Flags = *id | ObjectTypePath << 8;
2717 write_path_data(path, &object_record->ObjectData.path);
2718 return Ok;
2721 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
2723 if (brush->bt == BrushTypeSolidColor)
2725 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.solid) + sizeof(EmfPlusSolidBrushData);
2726 return Ok;
2729 FIXME("unsupported brush type: %d\n", brush->bt);
2730 return NotImplemented;
2733 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
2735 if (brush->bt == BrushTypeSolidColor)
2737 GpSolidFill *solid = (GpSolidFill*)brush;
2739 data->Version = VERSION_MAGIC2;
2740 data->Type = solid->brush.bt;
2741 data->BrushData.solid.SolidColor.Blue = solid->color & 0xff;
2742 data->BrushData.solid.SolidColor.Green = (solid->color >> 8) & 0xff;
2743 data->BrushData.solid.SolidColor.Red = (solid->color >> 16) & 0xff;
2744 data->BrushData.solid.SolidColor.Alpha = solid->color >> 24;
2748 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
2750 DWORD i, data_flags, pen_data_size, brush_size;
2751 EmfPlusObject *object_record;
2752 EmfPlusPenData *pen_data;
2753 GpStatus stat;
2754 BOOL result;
2756 *id = -1;
2757 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2758 return Ok;
2760 data_flags = 0;
2761 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2763 GdipIsMatrixIdentity(&pen->transform, &result);
2764 if (!result)
2766 data_flags |= PenDataTransform;
2767 pen_data_size += sizeof(EmfPlusTransformMatrix);
2769 if (pen->startcap != LineCapFlat)
2771 data_flags |= PenDataStartCap;
2772 pen_data_size += sizeof(DWORD);
2774 if (pen->endcap != LineCapFlat)
2776 data_flags |= PenDataEndCap;
2777 pen_data_size += sizeof(DWORD);
2779 if (pen->join != LineJoinMiter)
2781 data_flags |= PenDataJoin;
2782 pen_data_size += sizeof(DWORD);
2784 if (pen->miterlimit != 10.0)
2786 data_flags |= PenDataMiterLimit;
2787 pen_data_size += sizeof(REAL);
2789 if (pen->style != GP_DEFAULT_PENSTYLE)
2791 data_flags |= PenDataLineStyle;
2792 pen_data_size += sizeof(DWORD);
2794 if (pen->dashcap != DashCapFlat)
2796 data_flags |= PenDataDashedLineCap;
2797 pen_data_size += sizeof(DWORD);
2799 data_flags |= PenDataDashedLineOffset;
2800 pen_data_size += sizeof(REAL);
2801 if (pen->numdashes)
2803 data_flags |= PenDataDashedLine;
2804 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
2806 if (pen->align != PenAlignmentCenter)
2808 data_flags |= PenDataNonCenter;
2809 pen_data_size += sizeof(DWORD);
2811 /* TODO: Add support for PenDataCompoundLine */
2812 if (pen->customstart)
2814 FIXME("ignoring custom start cup\n");
2816 if (pen->customend)
2818 FIXME("ignoring custom end cup\n");
2821 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
2822 if (stat != Ok) return stat;
2824 stat = METAFILE_AllocateRecord(metafile,
2825 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
2826 (void**)&object_record);
2827 if (stat != Ok) return stat;
2829 *id = METAFILE_AddObjectId(metafile);
2830 object_record->Header.Type = EmfPlusRecordTypeObject;
2831 object_record->Header.Flags = *id | ObjectTypePen << 8;
2832 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
2833 object_record->ObjectData.pen.Type = 0;
2835 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
2836 pen_data->PenDataFlags = data_flags;
2837 pen_data->PenUnit = pen->unit;
2838 pen_data->PenWidth = pen->width;
2840 i = 0;
2841 if (data_flags & PenDataTransform)
2843 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
2844 memcpy(m, &pen->transform, sizeof(*m));
2845 i += sizeof(EmfPlusTransformMatrix);
2847 if (data_flags & PenDataStartCap)
2849 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
2850 i += sizeof(DWORD);
2852 if (data_flags & PenDataEndCap)
2854 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
2855 i += sizeof(DWORD);
2857 if (data_flags & PenDataJoin)
2859 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
2860 i += sizeof(DWORD);
2862 if (data_flags & PenDataMiterLimit)
2864 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
2865 i += sizeof(REAL);
2867 if (data_flags & PenDataLineStyle)
2869 switch (pen->style & PS_STYLE_MASK)
2871 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
2872 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
2873 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
2874 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
2875 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
2876 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
2878 i += sizeof(DWORD);
2880 if (data_flags & PenDataDashedLineCap)
2882 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
2883 i += sizeof(DWORD);
2885 if (data_flags & PenDataDashedLineOffset)
2887 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
2888 i += sizeof(REAL);
2890 if (data_flags & PenDataDashedLine)
2892 int j;
2894 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
2895 i += sizeof(DWORD);
2897 for (j=0; j<pen->numdashes; j++)
2899 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
2900 i += sizeof(REAL);
2903 if (data_flags & PenDataNonCenter)
2905 *(REAL*)(pen_data->OptionalData + i) = pen->align;
2906 i += sizeof(DWORD);
2909 METAFILE_FillBrushData(pen->brush,
2910 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
2911 return Ok;
2914 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
2916 EmfPlusDrawPath *draw_path_record;
2917 DWORD path_id;
2918 DWORD pen_id;
2919 GpStatus stat;
2921 if (metafile->metafile_type == MetafileTypeEmf)
2923 FIXME("stub!\n");
2924 return NotImplemented;
2927 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
2928 if (stat != Ok) return stat;
2930 stat = METAFILE_AddPathObject(metafile, path, &path_id);
2931 if (stat != Ok) return stat;
2933 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
2934 if (stat != Ok) return stat;
2935 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
2936 draw_path_record->Header.Flags = path_id;
2937 draw_path_record->PenId = pen_id;
2939 METAFILE_WriteRecords(metafile);
2940 return Ok;
2943 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
2945 EmfPlusObject *object_record;
2946 GpStatus stat;
2947 DWORD size;
2949 *id = -1;
2950 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2951 return Ok;
2953 stat = METAFILE_PrepareBrushData(brush, &size);
2954 if (stat != Ok) return stat;
2956 stat = METAFILE_AllocateRecord(metafile,
2957 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
2958 if (stat != Ok) return stat;
2960 *id = METAFILE_AddObjectId(metafile);
2961 object_record->Header.Type = EmfPlusRecordTypeObject;
2962 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
2963 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
2964 return Ok;
2967 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
2969 EmfPlusFillPath *fill_path_record;
2970 DWORD brush_id = -1, path_id;
2971 BOOL inline_color;
2972 GpStatus stat;
2974 if (metafile->metafile_type == MetafileTypeEmf)
2976 FIXME("stub!\n");
2977 return NotImplemented;
2980 inline_color = brush->bt == BrushTypeSolidColor;
2981 if (!inline_color)
2983 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
2984 if (stat != Ok) return stat;
2987 stat = METAFILE_AddPathObject(metafile, path, &path_id);
2988 if (stat != Ok) return stat;
2990 stat = METAFILE_AllocateRecord(metafile,
2991 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
2992 if (stat != Ok) return stat;
2993 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
2994 if (inline_color)
2996 fill_path_record->Header.Flags = 0x8000 | path_id;
2997 fill_path_record->data.Color.Blue = ((GpSolidFill*)brush)->color & 0xff;
2998 fill_path_record->data.Color.Green = (((GpSolidFill*)brush)->color >> 8) & 0xff;
2999 fill_path_record->data.Color.Red = (((GpSolidFill*)brush)->color >> 16) & 0xff;
3000 fill_path_record->data.Color.Alpha = ((GpSolidFill*)brush)->color >> 24;
3002 else
3004 fill_path_record->Header.Flags = path_id;
3005 fill_path_record->data.BrushId = brush_id;
3008 METAFILE_WriteRecords(metafile);
3009 return Ok;