po: Update Simplified Chinese translation.
[wine.git] / dlls / gdiplus / metafile.c
blob0bd3033a1dbf14fcf717028c38a4ce5511b65551
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 ENHMETARECORD *record;
1428 switch (recordType)
1430 case EMR_SETMAPMODE:
1431 case EMR_SAVEDC:
1432 case EMR_RESTOREDC:
1433 case EMR_SETWINDOWORGEX:
1434 case EMR_SETWINDOWEXTEX:
1435 case EMR_SETVIEWPORTORGEX:
1436 case EMR_SETVIEWPORTEXTEX:
1437 case EMR_SCALEVIEWPORTEXTEX:
1438 case EMR_SCALEWINDOWEXTEX:
1439 case EMR_MODIFYWORLDTRANSFORM:
1440 FIXME("not implemented for record type %x\n", recordType);
1441 break;
1442 case EMR_SETWORLDTRANSFORM:
1444 const XFORM* xform = (void*)data;
1445 real_metafile->gdiworldtransform = *xform;
1446 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
1447 break;
1449 case EMR_EXTSELECTCLIPRGN:
1451 DWORD rgndatasize = *(DWORD*)data;
1452 DWORD mode = *(DWORD*)(data + 4);
1453 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
1454 HRGN hrgn = NULL;
1456 if (dataSize > 8)
1458 XFORM final;
1460 METAFILE_GetFinalGdiTransform(metafile, &final);
1462 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
1465 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
1467 DeleteObject(hrgn);
1469 return Ok;
1471 default:
1472 break;
1475 record = heap_alloc_zero(dataSize + 8);
1477 if (record)
1479 record->iType = recordType;
1480 record->nSize = dataSize + 8;
1481 memcpy(record->dParm, data, dataSize);
1483 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
1484 record, metafile->handle_count);
1486 heap_free(record);
1488 else
1489 return OutOfMemory;
1492 else
1494 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
1496 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
1498 switch(recordType)
1500 case EmfPlusRecordTypeHeader:
1501 case EmfPlusRecordTypeEndOfFile:
1502 break;
1503 case EmfPlusRecordTypeGetDC:
1504 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
1505 break;
1506 case EmfPlusRecordTypeClear:
1508 EmfPlusClear *record = (EmfPlusClear*)header;
1510 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
1512 case EmfPlusRecordTypeFillRects:
1514 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
1515 GpBrush *brush, *temp_brush=NULL;
1516 GpRectF *rects, *temp_rects=NULL;
1518 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
1519 return InvalidParameter;
1521 if (flags & 0x4000)
1523 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
1524 return InvalidParameter;
1526 else
1528 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
1529 return InvalidParameter;
1532 if (flags & 0x8000)
1534 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
1535 brush = temp_brush;
1537 else
1539 FIXME("brush deserialization not implemented\n");
1540 return NotImplemented;
1543 if (stat == Ok)
1545 if (flags & 0x4000)
1547 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
1548 int i;
1550 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
1551 if (rects)
1553 for (i=0; i<record->Count; i++)
1555 rects[i].X = int_rects[i].X;
1556 rects[i].Y = int_rects[i].Y;
1557 rects[i].Width = int_rects[i].Width;
1558 rects[i].Height = int_rects[i].Height;
1561 else
1562 stat = OutOfMemory;
1564 else
1565 rects = (GpRectF*)(record+1);
1568 if (stat == Ok)
1570 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
1573 GdipDeleteBrush(temp_brush);
1574 heap_free(temp_rects);
1576 return stat;
1578 case EmfPlusRecordTypeSetClipRect:
1580 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
1581 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
1582 GpRegion *region;
1583 GpMatrix world_to_device;
1585 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
1586 return InvalidParameter;
1588 stat = GdipCreateRegionRect(&record->ClipRect, &region);
1590 if (stat == Ok)
1592 get_graphics_transform(real_metafile->playback_graphics,
1593 CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
1595 GdipTransformRegion(region, &world_to_device);
1597 GdipCombineRegionRegion(real_metafile->clip, region, mode);
1599 GdipDeleteRegion(region);
1602 return METAFILE_PlaybackUpdateClip(real_metafile);
1604 case EmfPlusRecordTypeSetPageTransform:
1606 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
1607 GpUnit unit = (GpUnit)flags;
1609 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
1610 return InvalidParameter;
1612 real_metafile->page_unit = unit;
1613 real_metafile->page_scale = record->PageScale;
1615 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1617 case EmfPlusRecordTypeSetWorldTransform:
1619 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
1621 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
1622 return InvalidParameter;
1624 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
1626 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1628 case EmfPlusRecordTypeScaleWorldTransform:
1630 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
1631 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1633 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
1634 return InvalidParameter;
1636 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
1638 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1640 case EmfPlusRecordTypeMultiplyWorldTransform:
1642 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
1643 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1644 GpMatrix matrix;
1646 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
1647 return InvalidParameter;
1649 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
1651 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
1653 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1655 case EmfPlusRecordTypeRotateWorldTransform:
1657 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
1658 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1660 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
1661 return InvalidParameter;
1663 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
1665 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1667 case EmfPlusRecordTypeTranslateWorldTransform:
1669 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
1670 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1672 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
1673 return InvalidParameter;
1675 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
1677 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1679 case EmfPlusRecordTypeResetWorldTransform:
1681 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1683 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1685 case EmfPlusRecordTypeBeginContainer:
1687 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
1688 container* cont;
1689 GpUnit unit;
1690 REAL scale_x, scale_y;
1691 GpRectF scaled_srcrect;
1692 GpMatrix transform;
1694 cont = heap_alloc_zero(sizeof(*cont));
1695 if (!cont)
1696 return OutOfMemory;
1698 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1699 if (stat != Ok)
1701 heap_free(cont);
1702 return stat;
1705 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1707 if (stat != Ok)
1709 GdipDeleteRegion(cont->clip);
1710 heap_free(cont);
1711 return stat;
1714 cont->id = record->StackIndex;
1715 cont->type = BEGIN_CONTAINER;
1716 cont->world_transform = *metafile->world_transform;
1717 cont->page_unit = metafile->page_unit;
1718 cont->page_scale = metafile->page_scale;
1719 list_add_head(&real_metafile->containers, &cont->entry);
1721 unit = record->Header.Flags & 0xff;
1723 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
1724 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
1726 scaled_srcrect.X = scale_x * record->SrcRect.X;
1727 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
1728 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
1729 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
1731 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
1732 transform.matrix[1] = 0.0;
1733 transform.matrix[2] = 0.0;
1734 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
1735 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
1736 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
1738 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
1740 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1742 case EmfPlusRecordTypeBeginContainerNoParams:
1743 case EmfPlusRecordTypeSave:
1745 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1746 container* cont;
1748 cont = heap_alloc_zero(sizeof(*cont));
1749 if (!cont)
1750 return OutOfMemory;
1752 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1753 if (stat != Ok)
1755 heap_free(cont);
1756 return stat;
1759 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1760 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1761 else
1762 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
1764 if (stat != Ok)
1766 GdipDeleteRegion(cont->clip);
1767 heap_free(cont);
1768 return stat;
1771 cont->id = record->StackIndex;
1772 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1773 cont->type = BEGIN_CONTAINER;
1774 else
1775 cont->type = SAVE_GRAPHICS;
1776 cont->world_transform = *metafile->world_transform;
1777 cont->page_unit = metafile->page_unit;
1778 cont->page_scale = metafile->page_scale;
1779 list_add_head(&real_metafile->containers, &cont->entry);
1781 break;
1783 case EmfPlusRecordTypeEndContainer:
1784 case EmfPlusRecordTypeRestore:
1786 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1787 container* cont;
1788 enum container_type type;
1789 BOOL found=FALSE;
1791 if (recordType == EmfPlusRecordTypeEndContainer)
1792 type = BEGIN_CONTAINER;
1793 else
1794 type = SAVE_GRAPHICS;
1796 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
1798 if (cont->id == record->StackIndex && cont->type == type)
1800 found = TRUE;
1801 break;
1805 if (found)
1807 container* cont2;
1809 /* pop any newer items on the stack */
1810 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
1812 list_remove(&cont2->entry);
1813 GdipDeleteRegion(cont2->clip);
1814 heap_free(cont2);
1817 if (type == BEGIN_CONTAINER)
1818 GdipEndContainer(real_metafile->playback_graphics, cont->state);
1819 else
1820 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
1822 *real_metafile->world_transform = cont->world_transform;
1823 real_metafile->page_unit = cont->page_unit;
1824 real_metafile->page_scale = cont->page_scale;
1825 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
1827 list_remove(&cont->entry);
1828 GdipDeleteRegion(cont->clip);
1829 heap_free(cont);
1832 break;
1834 default:
1835 FIXME("Not implemented for record type %x\n", recordType);
1836 return NotImplemented;
1840 return Ok;
1843 struct enum_metafile_data
1845 EnumerateMetafileProc callback;
1846 void *callback_data;
1847 GpMetafile *metafile;
1850 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1851 int nObj, LPARAM lpData)
1853 BOOL ret;
1854 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
1855 const BYTE* pStr;
1857 data->metafile->handle_table = lpHTable;
1858 data->metafile->handle_count = nObj;
1860 /* First check for an EMF+ record. */
1861 if (lpEMFR->iType == EMR_GDICOMMENT)
1863 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1865 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1867 int offset = 4;
1869 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
1871 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
1873 if (record->DataSize)
1874 pStr = (const BYTE*)(record+1);
1875 else
1876 pStr = NULL;
1878 ret = data->callback(record->Type, record->Flags, record->DataSize,
1879 pStr, data->callback_data);
1881 if (!ret)
1882 return 0;
1884 offset += record->Size;
1887 return 1;
1891 if (lpEMFR->nSize != 8)
1892 pStr = (const BYTE*)lpEMFR->dParm;
1893 else
1894 pStr = NULL;
1896 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
1897 pStr, data->callback_data);
1900 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
1901 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
1902 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
1903 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
1905 struct enum_metafile_data data;
1906 GpStatus stat;
1907 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
1908 GraphicsContainer state;
1909 GpPath *dst_path;
1911 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
1912 destPoints, count, srcRect, srcUnit, callback, callbackData,
1913 imageAttributes);
1915 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
1916 return InvalidParameter;
1918 if (!metafile->hemf)
1919 return InvalidParameter;
1921 if (metafile->playback_graphics)
1922 return ObjectBusy;
1924 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
1925 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
1926 debugstr_pointf(&destPoints[2]));
1928 data.callback = callback;
1929 data.callback_data = callbackData;
1930 data.metafile = real_metafile;
1932 real_metafile->playback_graphics = graphics;
1933 real_metafile->playback_dc = NULL;
1934 real_metafile->src_rect = *srcRect;
1936 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
1937 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
1939 if (stat == Ok)
1940 stat = GdipBeginContainer2(graphics, &state);
1942 if (stat == Ok)
1944 stat = GdipSetPageScale(graphics, 1.0);
1946 if (stat == Ok)
1947 stat = GdipSetPageUnit(graphics, UnitPixel);
1949 if (stat == Ok)
1950 stat = GdipResetWorldTransform(graphics);
1952 if (stat == Ok)
1953 stat = GdipCreateRegion(&real_metafile->base_clip);
1955 if (stat == Ok)
1956 stat = GdipGetClip(graphics, real_metafile->base_clip);
1958 if (stat == Ok)
1959 stat = GdipCreateRegion(&real_metafile->clip);
1961 if (stat == Ok)
1962 stat = GdipCreatePath(FillModeAlternate, &dst_path);
1964 if (stat == Ok)
1966 GpPointF clip_points[4];
1968 clip_points[0] = real_metafile->playback_points[0];
1969 clip_points[1] = real_metafile->playback_points[1];
1970 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
1971 - real_metafile->playback_points[0].X;
1972 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
1973 - real_metafile->playback_points[0].Y;
1974 clip_points[3] = real_metafile->playback_points[2];
1976 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
1978 if (stat == Ok)
1979 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
1981 GdipDeletePath(dst_path);
1984 if (stat == Ok)
1985 stat = GdipCreateMatrix(&real_metafile->world_transform);
1987 if (stat == Ok)
1989 real_metafile->page_unit = UnitDisplay;
1990 real_metafile->page_scale = 1.0;
1991 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1994 if (stat == Ok)
1996 stat = METAFILE_PlaybackUpdateClip(real_metafile);
1999 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
2000 metafile->metafile_type == MetafileTypeWmfPlaceable ||
2001 metafile->metafile_type == MetafileTypeWmf))
2002 stat = METAFILE_PlaybackGetDC(real_metafile);
2004 if (stat == Ok)
2005 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
2007 METAFILE_PlaybackReleaseDC(real_metafile);
2009 GdipDeleteMatrix(real_metafile->world_transform);
2010 real_metafile->world_transform = NULL;
2012 GdipDeleteRegion(real_metafile->base_clip);
2013 real_metafile->base_clip = NULL;
2015 GdipDeleteRegion(real_metafile->clip);
2016 real_metafile->clip = NULL;
2018 while (list_head(&real_metafile->containers))
2020 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
2021 list_remove(&cont->entry);
2022 GdipDeleteRegion(cont->clip);
2023 heap_free(cont);
2026 GdipEndContainer(graphics, state);
2029 real_metafile->playback_graphics = NULL;
2031 return stat;
2034 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
2035 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
2036 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2038 GpPointF points[3];
2040 if (!graphics || !metafile || !dest) return InvalidParameter;
2042 points[0].X = points[2].X = dest->X;
2043 points[0].Y = points[1].Y = dest->Y;
2044 points[1].X = dest->X + dest->Width;
2045 points[2].Y = dest->Y + dest->Height;
2047 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
2048 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
2051 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
2052 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
2053 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2055 GpRectF destf;
2057 if (!graphics || !metafile || !dest) return InvalidParameter;
2059 destf.X = dest->X;
2060 destf.Y = dest->Y;
2061 destf.Width = dest->Width;
2062 destf.Height = dest->Height;
2064 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2067 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
2068 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
2069 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2071 GpRectF destf;
2073 if (!graphics || !metafile || !dest) return InvalidParameter;
2075 destf.X = dest->X;
2076 destf.Y = dest->Y;
2077 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
2078 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
2080 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2083 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
2084 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
2085 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2087 GpPointF ptf;
2089 if (!graphics || !metafile || !dest) return InvalidParameter;
2091 ptf.X = dest->X;
2092 ptf.Y = dest->Y;
2094 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
2097 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
2098 MetafileHeader * header)
2100 GpStatus status;
2102 TRACE("(%p, %p)\n", metafile, header);
2104 if(!metafile || !header)
2105 return InvalidParameter;
2107 if (metafile->hemf)
2109 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
2110 if (status != Ok) return status;
2112 else
2114 memset(header, 0, sizeof(*header));
2115 header->Version = VERSION_MAGIC2;
2118 header->Type = metafile->metafile_type;
2119 header->DpiX = metafile->image.xres;
2120 header->DpiY = metafile->image.yres;
2121 header->Width = gdip_round(metafile->bounds.Width);
2122 header->Height = gdip_round(metafile->bounds.Height);
2124 return Ok;
2127 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
2128 int nObj, LPARAM lpData)
2130 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
2132 if (lpEMFR->iType == EMR_GDICOMMENT)
2134 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
2136 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
2138 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
2140 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
2141 header->Type == EmfPlusRecordTypeHeader)
2143 memcpy(dst_header, header, sizeof(*dst_header));
2147 else if (lpEMFR->iType == EMR_HEADER)
2148 return TRUE;
2150 return FALSE;
2153 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
2154 MetafileHeader *header)
2156 ENHMETAHEADER3 emfheader;
2157 EmfPlusHeader emfplusheader;
2158 MetafileType metafile_type;
2160 TRACE("(%p,%p)\n", hemf, header);
2162 if(!hemf || !header)
2163 return InvalidParameter;
2165 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
2166 return GenericError;
2168 emfplusheader.Header.Type = 0;
2170 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
2172 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
2174 if ((emfplusheader.Header.Flags & 1) == 1)
2175 metafile_type = MetafileTypeEmfPlusDual;
2176 else
2177 metafile_type = MetafileTypeEmfPlusOnly;
2179 else
2180 metafile_type = MetafileTypeEmf;
2182 header->Type = metafile_type;
2183 header->Size = emfheader.nBytes;
2184 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
2185 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
2186 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
2187 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
2188 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
2189 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
2190 header->u.EmfHeader = emfheader;
2192 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
2194 header->Version = emfplusheader.Version;
2195 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
2196 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
2197 header->LogicalDpiX = emfplusheader.LogicalDpiX;
2198 header->LogicalDpiY = emfplusheader.LogicalDpiY;
2200 else
2202 header->Version = emfheader.nVersion;
2203 header->EmfPlusFlags = 0;
2204 header->EmfPlusHeaderSize = 0;
2205 header->LogicalDpiX = 0;
2206 header->LogicalDpiY = 0;
2209 return Ok;
2212 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
2213 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
2215 GpStatus status;
2216 GpMetafile *metafile;
2218 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
2220 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
2221 if (status == Ok)
2223 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2224 GdipDisposeImage(&metafile->image);
2226 return status;
2229 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
2230 MetafileHeader *header)
2232 GpStatus status;
2233 GpMetafile *metafile;
2235 TRACE("(%s,%p)\n", debugstr_w(filename), header);
2237 if (!filename || !header)
2238 return InvalidParameter;
2240 status = GdipCreateMetafileFromFile(filename, &metafile);
2241 if (status == Ok)
2243 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2244 GdipDisposeImage(&metafile->image);
2246 return status;
2249 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
2250 MetafileHeader *header)
2252 GpStatus status;
2253 GpMetafile *metafile;
2255 TRACE("(%p,%p)\n", stream, header);
2257 if (!stream || !header)
2258 return InvalidParameter;
2260 status = GdipCreateMetafileFromStream(stream, &metafile);
2261 if (status == Ok)
2263 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2264 GdipDisposeImage(&metafile->image);
2266 return status;
2269 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
2270 GpMetafile **metafile)
2272 GpStatus stat;
2273 MetafileHeader header;
2275 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
2277 if(!hemf || !metafile)
2278 return InvalidParameter;
2280 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
2281 if (stat != Ok)
2282 return stat;
2284 *metafile = heap_alloc_zero(sizeof(GpMetafile));
2285 if (!*metafile)
2286 return OutOfMemory;
2288 (*metafile)->image.type = ImageTypeMetafile;
2289 (*metafile)->image.format = ImageFormatEMF;
2290 (*metafile)->image.frame_count = 1;
2291 (*metafile)->image.xres = header.DpiX;
2292 (*metafile)->image.yres = header.DpiY;
2293 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
2294 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
2295 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
2296 / 2540.0 * header.DpiX;
2297 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
2298 / 2540.0 * header.DpiY;
2299 (*metafile)->unit = UnitPixel;
2300 (*metafile)->metafile_type = header.Type;
2301 (*metafile)->hemf = hemf;
2302 (*metafile)->preserve_hemf = !delete;
2303 list_init(&(*metafile)->containers);
2305 TRACE("<-- %p\n", *metafile);
2307 return Ok;
2310 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
2311 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2313 UINT read;
2314 BYTE *copy;
2315 HENHMETAFILE hemf;
2316 GpStatus retval = Ok;
2318 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
2320 if(!hwmf || !metafile)
2321 return InvalidParameter;
2323 *metafile = NULL;
2324 read = GetMetaFileBitsEx(hwmf, 0, NULL);
2325 if(!read)
2326 return GenericError;
2327 copy = heap_alloc_zero(read);
2328 GetMetaFileBitsEx(hwmf, read, copy);
2330 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
2331 heap_free(copy);
2333 /* FIXME: We should store and use hwmf instead of converting to hemf */
2334 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
2336 if (retval == Ok)
2338 if (placeable)
2340 (*metafile)->image.xres = (REAL)placeable->Inch;
2341 (*metafile)->image.yres = (REAL)placeable->Inch;
2342 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
2343 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
2344 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
2345 placeable->BoundingBox.Left);
2346 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
2347 placeable->BoundingBox.Top);
2348 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
2350 else
2351 (*metafile)->metafile_type = MetafileTypeWmf;
2352 (*metafile)->image.format = ImageFormatWMF;
2354 if (delete) DeleteMetaFile(hwmf);
2356 else
2357 DeleteEnhMetaFile(hemf);
2358 return retval;
2361 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
2362 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2364 HMETAFILE hmf;
2365 HENHMETAFILE emf;
2367 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
2369 hmf = GetMetaFileW(file);
2370 if(hmf)
2371 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
2373 emf = GetEnhMetaFileW(file);
2374 if(emf)
2375 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
2377 return GenericError;
2380 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
2381 GpMetafile **metafile)
2383 GpStatus status;
2384 IStream *stream;
2386 TRACE("(%p, %p)\n", file, metafile);
2388 if (!file || !metafile) return InvalidParameter;
2390 *metafile = NULL;
2392 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
2393 if (status == Ok)
2395 status = GdipCreateMetafileFromStream(stream, metafile);
2396 IStream_Release(stream);
2398 return status;
2401 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
2402 GpMetafile **metafile)
2404 GpStatus stat;
2406 TRACE("%p %p\n", stream, metafile);
2408 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
2409 if (stat != Ok) return stat;
2411 if ((*metafile)->image.type != ImageTypeMetafile)
2413 GdipDisposeImage(&(*metafile)->image);
2414 *metafile = NULL;
2415 return GenericError;
2418 return Ok;
2421 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
2422 UINT limitDpi)
2424 TRACE("(%p,%u)\n", metafile, limitDpi);
2426 return Ok;
2429 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
2430 GpMetafile* metafile, BOOL* succ, EmfType emfType,
2431 const WCHAR* description, GpMetafile** out_metafile)
2433 static int calls;
2435 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
2436 debugstr_w(description), out_metafile);
2438 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
2439 return InvalidParameter;
2441 if(succ)
2442 *succ = FALSE;
2443 *out_metafile = NULL;
2445 if(!(calls++))
2446 FIXME("not implemented\n");
2448 return NotImplemented;
2451 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
2452 LPBYTE pData16, INT iMapMode, INT eFlags)
2454 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
2455 return NotImplemented;
2458 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
2459 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
2460 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
2461 GpMetafile **metafile)
2463 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2464 frameUnit, debugstr_w(desc), metafile);
2466 return NotImplemented;
2469 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
2470 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
2471 GDIPCONST WCHAR *desc, GpMetafile **metafile)
2473 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2474 frameUnit, debugstr_w(desc), metafile);
2476 return NotImplemented;
2479 /*****************************************************************************
2480 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
2483 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
2484 GpMetafile* metafile, BOOL* conversionSuccess,
2485 const WCHAR* filename, EmfType emfType,
2486 const WCHAR* description, GpMetafile** out_metafile)
2488 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
2489 return NotImplemented;
2492 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
2494 LARGE_INTEGER zero;
2495 STATSTG statstg;
2496 GpStatus stat;
2497 HRESULT hr;
2499 *size = 0;
2501 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
2502 if (FAILED(hr)) return hresult_to_status(hr);
2504 stat = encode_image_png(image, *stream, NULL);
2505 if (stat != Ok)
2507 IStream_Release(*stream);
2508 return stat;
2511 hr = IStream_Stat(*stream, &statstg, 1);
2512 if (FAILED(hr))
2514 IStream_Release(*stream);
2515 return hresult_to_status(hr);
2517 *size = statstg.cbSize.u.LowPart;
2519 zero.QuadPart = 0;
2520 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
2521 if (FAILED(hr))
2523 IStream_Release(*stream);
2524 return hresult_to_status(hr);
2527 return Ok;
2530 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
2532 HRESULT hr;
2534 record->Width = 0;
2535 record->Height = 0;
2536 record->Stride = 0;
2537 record->PixelFormat = 0;
2538 record->Type = BitmapDataTypeCompressed;
2540 hr = IStream_Read(stream, record->BitmapData, size, NULL);
2541 if (FAILED(hr)) return hresult_to_status(hr);
2542 return Ok;
2545 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
2547 EmfPlusObject *object_record;
2548 GpStatus stat;
2549 DWORD size;
2551 *id = -1;
2553 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2554 return Ok;
2556 if (image->type == ImageTypeBitmap)
2558 IStream *stream;
2559 DWORD aligned_size;
2561 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
2562 if (stat != Ok) return stat;
2563 aligned_size = (size + 3) & ~3;
2565 stat = METAFILE_AllocateRecord(metafile,
2566 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
2567 (void**)&object_record);
2568 if (stat != Ok)
2570 IStream_Release(stream);
2571 return stat;
2573 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
2575 *id = METAFILE_AddObjectId(metafile);
2576 object_record->Header.Type = EmfPlusRecordTypeObject;
2577 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2578 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2579 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
2581 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
2582 IStream_Release(stream);
2583 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2584 return stat;
2586 else if (image->type == ImageTypeMetafile)
2588 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
2589 EmfPlusMetafile *metafile_record;
2591 if (!hemf) return InvalidParameter;
2593 size = GetEnhMetaFileBits(hemf, 0, NULL);
2594 if (!size) return GenericError;
2596 stat = METAFILE_AllocateRecord(metafile,
2597 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
2598 (void**)&object_record);
2599 if (stat != Ok) return stat;
2601 *id = METAFILE_AddObjectId(metafile);
2602 object_record->Header.Type = EmfPlusRecordTypeObject;
2603 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2604 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2605 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
2606 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
2607 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
2608 metafile_record->MetafileDataSize = size;
2609 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
2611 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2612 return GenericError;
2614 return Ok;
2616 else
2618 FIXME("not supported image type (%d)\n", image->type);
2619 return NotImplemented;
2623 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
2625 EmfPlusObject *object_record;
2626 EmfPlusImageAttributes *attrs_record;
2627 GpStatus stat;
2629 *id = -1;
2631 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2632 return Ok;
2634 if (!attrs)
2635 return Ok;
2637 stat = METAFILE_AllocateRecord(metafile,
2638 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
2639 (void**)&object_record);
2640 if (stat != Ok) return stat;
2642 *id = METAFILE_AddObjectId(metafile);
2643 object_record->Header.Type = EmfPlusRecordTypeObject;
2644 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
2645 attrs_record = &object_record->ObjectData.image_attributes;
2646 attrs_record->Version = VERSION_MAGIC2;
2647 attrs_record->Reserved1 = 0;
2648 attrs_record->WrapMode = attrs->wrap;
2649 attrs_record->ClampColor.Blue = attrs->outside_color & 0xff;
2650 attrs_record->ClampColor.Green = (attrs->outside_color >> 8) & 0xff;
2651 attrs_record->ClampColor.Red = (attrs->outside_color >> 16) & 0xff;
2652 attrs_record->ClampColor.Alpha = attrs->outside_color >> 24;
2653 attrs_record->ObjectClamp = attrs->clamp;
2654 attrs_record->Reserved2 = 0;
2655 return Ok;
2658 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
2659 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2660 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2661 DrawImageAbort callback, VOID *callbackData)
2663 EmfPlusDrawImagePoints *draw_image_record;
2664 DWORD image_id, attributes_id;
2665 GpStatus stat;
2667 if (count != 3) return InvalidParameter;
2669 if (metafile->metafile_type == MetafileTypeEmf)
2671 FIXME("MetafileTypeEmf metafiles not supported\n");
2672 return NotImplemented;
2674 else
2675 FIXME("semi-stub\n");
2677 if (!imageAttributes)
2679 stat = METAFILE_AddImageObject(metafile, image, &image_id);
2681 else if (image->type == ImageTypeBitmap)
2683 INT width = ((GpBitmap*)image)->width;
2684 INT height = ((GpBitmap*)image)->height;
2685 GpGraphics *graphics;
2686 GpBitmap *bitmap;
2688 stat = GdipCreateBitmapFromScan0(width, height,
2689 0, PixelFormat32bppARGB, NULL, &bitmap);
2690 if (stat != Ok) return stat;
2692 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
2693 if (stat != Ok)
2695 GdipDisposeImage((GpImage*)bitmap);
2696 return stat;
2699 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
2700 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
2701 GdipDeleteGraphics(graphics);
2702 if (stat != Ok)
2704 GdipDisposeImage((GpImage*)bitmap);
2705 return stat;
2708 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
2709 GdipDisposeImage((GpImage*)bitmap);
2711 else
2713 FIXME("imageAttributes not supported (image type %d)\n", image->type);
2714 return NotImplemented;
2716 if (stat != Ok) return stat;
2718 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
2719 if (stat != Ok) return stat;
2721 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
2722 if (stat != Ok) return stat;
2723 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
2724 draw_image_record->Header.Flags = image_id;
2725 draw_image_record->ImageAttributesID = attributes_id;
2726 draw_image_record->SrcUnit = UnitPixel;
2727 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
2728 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
2729 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
2730 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
2731 draw_image_record->count = 3;
2732 draw_image_record->PointData[0].pointF.X = points[0].X;
2733 draw_image_record->PointData[0].pointF.Y = points[0].Y;
2734 draw_image_record->PointData[1].pointF.X = points[1].X;
2735 draw_image_record->PointData[1].pointF.Y = points[1].Y;
2736 draw_image_record->PointData[2].pointF.X = points[2].X;
2737 draw_image_record->PointData[2].pointF.Y = points[2].Y;
2738 METAFILE_WriteRecords(metafile);
2739 return Ok;
2742 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
2744 EmfPlusRecordHeader *record;
2745 GpStatus stat;
2747 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2748 return Ok;
2750 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
2751 if (stat != Ok) return stat;
2753 record->Type = prop;
2754 record->Flags = val;
2756 METAFILE_WriteRecords(metafile);
2757 return Ok;
2760 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
2762 EmfPlusObject *object_record;
2763 GpStatus stat;
2764 DWORD size;
2766 *id = -1;
2767 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2768 return Ok;
2770 size = write_path_data(path, NULL);
2771 stat = METAFILE_AllocateRecord(metafile,
2772 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
2773 (void**)&object_record);
2774 if (stat != Ok) return stat;
2776 *id = METAFILE_AddObjectId(metafile);
2777 object_record->Header.Type = EmfPlusRecordTypeObject;
2778 object_record->Header.Flags = *id | ObjectTypePath << 8;
2779 write_path_data(path, &object_record->ObjectData.path);
2780 return Ok;
2783 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
2785 if (brush->bt == BrushTypeSolidColor)
2787 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.solid) + sizeof(EmfPlusSolidBrushData);
2788 return Ok;
2791 FIXME("unsupported brush type: %d\n", brush->bt);
2792 return NotImplemented;
2795 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
2797 if (brush->bt == BrushTypeSolidColor)
2799 GpSolidFill *solid = (GpSolidFill*)brush;
2801 data->Version = VERSION_MAGIC2;
2802 data->Type = solid->brush.bt;
2803 data->BrushData.solid.SolidColor.Blue = solid->color & 0xff;
2804 data->BrushData.solid.SolidColor.Green = (solid->color >> 8) & 0xff;
2805 data->BrushData.solid.SolidColor.Red = (solid->color >> 16) & 0xff;
2806 data->BrushData.solid.SolidColor.Alpha = solid->color >> 24;
2810 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
2812 DWORD i, data_flags, pen_data_size, brush_size;
2813 EmfPlusObject *object_record;
2814 EmfPlusPenData *pen_data;
2815 GpStatus stat;
2816 BOOL result;
2818 *id = -1;
2819 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2820 return Ok;
2822 data_flags = 0;
2823 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2825 GdipIsMatrixIdentity(&pen->transform, &result);
2826 if (!result)
2828 data_flags |= PenDataTransform;
2829 pen_data_size += sizeof(EmfPlusTransformMatrix);
2831 if (pen->startcap != LineCapFlat)
2833 data_flags |= PenDataStartCap;
2834 pen_data_size += sizeof(DWORD);
2836 if (pen->endcap != LineCapFlat)
2838 data_flags |= PenDataEndCap;
2839 pen_data_size += sizeof(DWORD);
2841 if (pen->join != LineJoinMiter)
2843 data_flags |= PenDataJoin;
2844 pen_data_size += sizeof(DWORD);
2846 if (pen->miterlimit != 10.0)
2848 data_flags |= PenDataMiterLimit;
2849 pen_data_size += sizeof(REAL);
2851 if (pen->style != GP_DEFAULT_PENSTYLE)
2853 data_flags |= PenDataLineStyle;
2854 pen_data_size += sizeof(DWORD);
2856 if (pen->dashcap != DashCapFlat)
2858 data_flags |= PenDataDashedLineCap;
2859 pen_data_size += sizeof(DWORD);
2861 data_flags |= PenDataDashedLineOffset;
2862 pen_data_size += sizeof(REAL);
2863 if (pen->numdashes)
2865 data_flags |= PenDataDashedLine;
2866 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
2868 if (pen->align != PenAlignmentCenter)
2870 data_flags |= PenDataNonCenter;
2871 pen_data_size += sizeof(DWORD);
2873 /* TODO: Add support for PenDataCompoundLine */
2874 if (pen->customstart)
2876 FIXME("ignoring custom start cup\n");
2878 if (pen->customend)
2880 FIXME("ignoring custom end cup\n");
2883 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
2884 if (stat != Ok) return stat;
2886 stat = METAFILE_AllocateRecord(metafile,
2887 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
2888 (void**)&object_record);
2889 if (stat != Ok) return stat;
2891 *id = METAFILE_AddObjectId(metafile);
2892 object_record->Header.Type = EmfPlusRecordTypeObject;
2893 object_record->Header.Flags = *id | ObjectTypePen << 8;
2894 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
2895 object_record->ObjectData.pen.Type = 0;
2897 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
2898 pen_data->PenDataFlags = data_flags;
2899 pen_data->PenUnit = pen->unit;
2900 pen_data->PenWidth = pen->width;
2902 i = 0;
2903 if (data_flags & PenDataTransform)
2905 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
2906 memcpy(m, &pen->transform, sizeof(*m));
2907 i += sizeof(EmfPlusTransformMatrix);
2909 if (data_flags & PenDataStartCap)
2911 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
2912 i += sizeof(DWORD);
2914 if (data_flags & PenDataEndCap)
2916 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
2917 i += sizeof(DWORD);
2919 if (data_flags & PenDataJoin)
2921 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
2922 i += sizeof(DWORD);
2924 if (data_flags & PenDataMiterLimit)
2926 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
2927 i += sizeof(REAL);
2929 if (data_flags & PenDataLineStyle)
2931 switch (pen->style & PS_STYLE_MASK)
2933 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
2934 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
2935 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
2936 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
2937 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
2938 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
2940 i += sizeof(DWORD);
2942 if (data_flags & PenDataDashedLineCap)
2944 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
2945 i += sizeof(DWORD);
2947 if (data_flags & PenDataDashedLineOffset)
2949 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
2950 i += sizeof(REAL);
2952 if (data_flags & PenDataDashedLine)
2954 int j;
2956 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
2957 i += sizeof(DWORD);
2959 for (j=0; j<pen->numdashes; j++)
2961 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
2962 i += sizeof(REAL);
2965 if (data_flags & PenDataNonCenter)
2967 *(REAL*)(pen_data->OptionalData + i) = pen->align;
2968 i += sizeof(DWORD);
2971 METAFILE_FillBrushData(pen->brush,
2972 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
2973 return Ok;
2976 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
2978 EmfPlusDrawPath *draw_path_record;
2979 DWORD path_id;
2980 DWORD pen_id;
2981 GpStatus stat;
2983 if (metafile->metafile_type == MetafileTypeEmf)
2985 FIXME("stub!\n");
2986 return NotImplemented;
2989 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
2990 if (stat != Ok) return stat;
2992 stat = METAFILE_AddPathObject(metafile, path, &path_id);
2993 if (stat != Ok) return stat;
2995 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
2996 if (stat != Ok) return stat;
2997 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
2998 draw_path_record->Header.Flags = path_id;
2999 draw_path_record->PenId = pen_id;
3001 METAFILE_WriteRecords(metafile);
3002 return Ok;
3005 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
3007 EmfPlusObject *object_record;
3008 GpStatus stat;
3009 DWORD size;
3011 *id = -1;
3012 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
3013 return Ok;
3015 stat = METAFILE_PrepareBrushData(brush, &size);
3016 if (stat != Ok) return stat;
3018 stat = METAFILE_AllocateRecord(metafile,
3019 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
3020 if (stat != Ok) return stat;
3022 *id = METAFILE_AddObjectId(metafile);
3023 object_record->Header.Type = EmfPlusRecordTypeObject;
3024 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
3025 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
3026 return Ok;
3029 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
3031 EmfPlusFillPath *fill_path_record;
3032 DWORD brush_id = -1, path_id;
3033 BOOL inline_color;
3034 GpStatus stat;
3036 if (metafile->metafile_type == MetafileTypeEmf)
3038 FIXME("stub!\n");
3039 return NotImplemented;
3042 inline_color = brush->bt == BrushTypeSolidColor;
3043 if (!inline_color)
3045 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
3046 if (stat != Ok) return stat;
3049 stat = METAFILE_AddPathObject(metafile, path, &path_id);
3050 if (stat != Ok) return stat;
3052 stat = METAFILE_AllocateRecord(metafile,
3053 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
3054 if (stat != Ok) return stat;
3055 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
3056 if (inline_color)
3058 fill_path_record->Header.Flags = 0x8000 | path_id;
3059 fill_path_record->data.Color.Blue = ((GpSolidFill*)brush)->color & 0xff;
3060 fill_path_record->data.Color.Green = (((GpSolidFill*)brush)->color >> 8) & 0xff;
3061 fill_path_record->data.Color.Red = (((GpSolidFill*)brush)->color >> 16) & 0xff;
3062 fill_path_record->data.Color.Alpha = ((GpSolidFill*)brush)->color >> 24;
3064 else
3066 fill_path_record->Header.Flags = path_id;
3067 fill_path_record->data.BrushId = brush_id;
3070 METAFILE_WriteRecords(metafile);
3071 return Ok;