xref: /reactos/dll/win32/gdiplus/metafile.c (revision 6619d026)
1 /*
2  * Copyright (C) 2011 Vincent Povirk for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include <stdarg.h>
20 #include <math.h>
21 #include <assert.h>
22 
23 #define NONAMELESSUNION
24 
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "wine/unicode.h"
29 
30 #define COBJMACROS
31 #include "objbase.h"
32 #include "ocidl.h"
33 #include "olectl.h"
34 #include "ole2.h"
35 
36 #include "winreg.h"
37 #include "shlwapi.h"
38 
39 #include "gdiplus.h"
40 #include "gdiplus_private.h"
41 #include "wine/debug.h"
42 #include "wine/list.h"
43 
44 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
45 
46 HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
47 
48 typedef ARGB EmfPlusARGB;
49 
50 typedef struct EmfPlusRecordHeader
51 {
52     WORD Type;
53     WORD Flags;
54     DWORD Size;
55     DWORD DataSize;
56 } EmfPlusRecordHeader;
57 
58 typedef struct EmfPlusHeader
59 {
60     EmfPlusRecordHeader Header;
61     DWORD Version;
62     DWORD EmfPlusFlags;
63     DWORD LogicalDpiX;
64     DWORD LogicalDpiY;
65 } EmfPlusHeader;
66 
67 typedef struct EmfPlusClear
68 {
69     EmfPlusRecordHeader Header;
70     DWORD Color;
71 } EmfPlusClear;
72 
73 typedef struct EmfPlusFillRects
74 {
75     EmfPlusRecordHeader Header;
76     DWORD BrushID;
77     DWORD Count;
78 } EmfPlusFillRects;
79 
80 typedef struct EmfPlusSetClipRect
81 {
82     EmfPlusRecordHeader Header;
83     GpRectF ClipRect;
84 } EmfPlusSetClipRect;
85 
86 typedef struct EmfPlusSetPageTransform
87 {
88     EmfPlusRecordHeader Header;
89     REAL PageScale;
90 } EmfPlusSetPageTransform;
91 
92 typedef struct EmfPlusRect
93 {
94     SHORT X;
95     SHORT Y;
96     SHORT Width;
97     SHORT Height;
98 } EmfPlusRect;
99 
100 typedef struct EmfPlusSetWorldTransform
101 {
102     EmfPlusRecordHeader Header;
103     REAL MatrixData[6];
104 } EmfPlusSetWorldTransform;
105 
106 typedef struct EmfPlusScaleWorldTransform
107 {
108     EmfPlusRecordHeader Header;
109     REAL Sx;
110     REAL Sy;
111 } EmfPlusScaleWorldTransform;
112 
113 typedef struct EmfPlusMultiplyWorldTransform
114 {
115     EmfPlusRecordHeader Header;
116     REAL MatrixData[6];
117 } EmfPlusMultiplyWorldTransform;
118 
119 typedef struct EmfPlusRotateWorldTransform
120 {
121     EmfPlusRecordHeader Header;
122     REAL Angle;
123 } EmfPlusRotateWorldTransform;
124 
125 typedef struct EmfPlusTranslateWorldTransform
126 {
127     EmfPlusRecordHeader Header;
128     REAL dx;
129     REAL dy;
130 } EmfPlusTranslateWorldTransform;
131 
132 typedef struct EmfPlusBeginContainer
133 {
134     EmfPlusRecordHeader Header;
135     GpRectF DestRect;
136     GpRectF SrcRect;
137     DWORD StackIndex;
138 } EmfPlusBeginContainer;
139 
140 typedef struct EmfPlusContainerRecord
141 {
142     EmfPlusRecordHeader Header;
143     DWORD StackIndex;
144 } EmfPlusContainerRecord;
145 
146 enum container_type
147 {
148     BEGIN_CONTAINER,
149     SAVE_GRAPHICS
150 };
151 
152 typedef struct container
153 {
154     struct list entry;
155     DWORD id;
156     enum container_type type;
157     GraphicsContainer state;
158     GpMatrix world_transform;
159     GpUnit page_unit;
160     REAL page_scale;
161     GpRegion *clip;
162 } container;
163 
164 enum PenDataFlags
165 {
166     PenDataTransform        = 0x0001,
167     PenDataStartCap         = 0x0002,
168     PenDataEndCap           = 0x0004,
169     PenDataJoin             = 0x0008,
170     PenDataMiterLimit       = 0x0010,
171     PenDataLineStyle        = 0x0020,
172     PenDataDashedLineCap    = 0x0040,
173     PenDataDashedLineOffset = 0x0080,
174     PenDataDashedLine       = 0x0100,
175     PenDataNonCenter        = 0x0200,
176     PenDataCompoundLine     = 0x0400,
177     PenDataCustomStartCap   = 0x0800,
178     PenDataCustomEndCap     = 0x1000
179 };
180 
181 typedef struct EmfPlusTransformMatrix
182 {
183     REAL TransformMatrix[6];
184 } EmfPlusTransformMatrix;
185 
186 enum LineStyle
187 {
188     LineStyleSolid,
189     LineStyleDash,
190     LineStyleDot,
191     LineStyleDashDot,
192     LineStyleDashDotDot,
193     LineStyleCustom
194 };
195 
196 typedef struct EmfPlusDashedLineData
197 {
198     DWORD DashedLineDataSize;
199     BYTE data[1];
200 } EmfPlusDashedLineData;
201 
202 typedef struct EmfPlusCompoundLineData
203 {
204     DWORD CompoundLineDataSize;
205     BYTE data[1];
206 } EmfPlusCompoundLineData;
207 
208 typedef struct EmfPlusCustomStartCapData
209 {
210     DWORD CustomStartCapSize;
211     BYTE data[1];
212 } EmfPlusCustomStartCapData;
213 
214 typedef struct EmfPlusCustomEndCapData
215 {
216     DWORD CustomEndCapSize;
217     BYTE data[1];
218 } EmfPlusCustomEndCapData;
219 
220 typedef struct EmfPlusPenData
221 {
222     DWORD PenDataFlags;
223     DWORD PenUnit;
224     REAL PenWidth;
225     BYTE OptionalData[1];
226 } EmfPlusPenData;
227 
228 enum BrushDataFlags
229 {
230     BrushDataPath             = 1 << 0,
231     BrushDataTransform        = 1 << 1,
232     BrushDataPresetColors     = 1 << 2,
233     BrushDataBlendFactorsH    = 1 << 3,
234     BrushDataBlendFactorsV    = 1 << 4,
235     BrushDataFocusScales      = 1 << 6,
236     BrushDataIsGammaCorrected = 1 << 7,
237     BrushDataDoNotTransform   = 1 << 8,
238 };
239 
240 typedef struct EmfPlusSolidBrushData
241 {
242     EmfPlusARGB SolidColor;
243 } EmfPlusSolidBrushData;
244 
245 typedef struct EmfPlusHatchBrushData
246 {
247     DWORD HatchStyle;
248     EmfPlusARGB ForeColor;
249     EmfPlusARGB BackColor;
250 } EmfPlusHatchBrushData;
251 
252 typedef struct EmfPlusTextureBrushData
253 {
254     DWORD BrushDataFlags;
255     INT WrapMode;
256     BYTE OptionalData[1];
257 } EmfPlusTextureBrushData;
258 
259 typedef struct EmfPlusRectF
260 {
261     float X;
262     float Y;
263     float Width;
264     float Height;
265 } EmfPlusRectF;
266 
267 typedef struct EmfPlusLinearGradientBrushData
268 {
269     DWORD BrushDataFlags;
270     INT WrapMode;
271     EmfPlusRectF RectF;
272     EmfPlusARGB StartColor;
273     EmfPlusARGB EndColor;
274     DWORD Reserved1;
275     DWORD Reserved2;
276     BYTE OptionalData[1];
277 } EmfPlusLinearGradientBrushData;
278 
279 typedef struct EmfPlusBrush
280 {
281     DWORD Version;
282     DWORD Type;
283     union {
284         EmfPlusSolidBrushData solid;
285         EmfPlusHatchBrushData hatch;
286         EmfPlusTextureBrushData texture;
287         EmfPlusLinearGradientBrushData lineargradient;
288     } BrushData;
289 } EmfPlusBrush;
290 
291 typedef struct EmfPlusPen
292 {
293     DWORD Version;
294     DWORD Type;
295     /* EmfPlusPenData */
296     /* EmfPlusBrush */
297     BYTE data[1];
298 } EmfPlusPen;
299 
300 typedef struct EmfPlusPath
301 {
302     DWORD Version;
303     DWORD PathPointCount;
304     DWORD PathPointFlags;
305     /* PathPoints[] */
306     /* PathPointTypes[] */
307     /* AlignmentPadding */
308     BYTE data[1];
309 } EmfPlusPath;
310 
311 typedef struct EmfPlusRegionNodePath
312 {
313     DWORD RegionNodePathLength;
314     EmfPlusPath RegionNodePath;
315 } EmfPlusRegionNodePath;
316 
317 typedef struct EmfPlusRegion
318 {
319     DWORD Version;
320     DWORD RegionNodeCount;
321     BYTE RegionNode[1];
322 } EmfPlusRegion;
323 
324 typedef struct EmfPlusPalette
325 {
326     DWORD PaletteStyleFlags;
327     DWORD PaletteCount;
328     BYTE PaletteEntries[1];
329 } EmfPlusPalette;
330 
331 typedef enum
332 {
333     BitmapDataTypePixel,
334     BitmapDataTypeCompressed,
335 } BitmapDataType;
336 
337 typedef struct EmfPlusBitmap
338 {
339     DWORD Width;
340     DWORD Height;
341     DWORD Stride;
342     DWORD PixelFormat;
343     DWORD Type;
344     BYTE BitmapData[1];
345 } EmfPlusBitmap;
346 
347 typedef struct EmfPlusMetafile
348 {
349     DWORD Type;
350     DWORD MetafileDataSize;
351     BYTE MetafileData[1];
352 } EmfPlusMetafile;
353 
354 typedef enum ImageDataType
355 {
356     ImageDataTypeUnknown,
357     ImageDataTypeBitmap,
358     ImageDataTypeMetafile,
359 } ImageDataType;
360 
361 typedef struct EmfPlusImage
362 {
363     DWORD Version;
364     ImageDataType Type;
365     union
366     {
367         EmfPlusBitmap bitmap;
368         EmfPlusMetafile metafile;
369     } ImageData;
370 } EmfPlusImage;
371 
372 typedef struct EmfPlusImageAttributes
373 {
374     DWORD Version;
375     DWORD Reserved1;
376     DWORD WrapMode;
377     EmfPlusARGB ClampColor;
378     DWORD ObjectClamp;
379     DWORD Reserved2;
380 } EmfPlusImageAttributes;
381 
382 typedef struct EmfPlusObject
383 {
384     EmfPlusRecordHeader Header;
385     union
386     {
387         EmfPlusBrush brush;
388         EmfPlusPen pen;
389         EmfPlusPath path;
390         EmfPlusRegion region;
391         EmfPlusImage image;
392         EmfPlusImageAttributes image_attributes;
393     } ObjectData;
394 } EmfPlusObject;
395 
396 typedef struct EmfPlusPointR7
397 {
398     BYTE X;
399     BYTE Y;
400 } EmfPlusPointR7;
401 
402 typedef struct EmfPlusPoint
403 {
404     short X;
405     short Y;
406 } EmfPlusPoint;
407 
408 typedef struct EmfPlusPointF
409 {
410     float X;
411     float Y;
412 } EmfPlusPointF;
413 
414 typedef struct EmfPlusDrawImage
415 {
416     EmfPlusRecordHeader Header;
417     DWORD ImageAttributesID;
418     DWORD SrcUnit;
419     EmfPlusRectF SrcRect;
420     union
421     {
422         EmfPlusRect rect;
423         EmfPlusRectF rectF;
424     } RectData;
425 } EmfPlusDrawImage;
426 
427 typedef struct EmfPlusDrawImagePoints
428 {
429     EmfPlusRecordHeader Header;
430     DWORD ImageAttributesID;
431     DWORD SrcUnit;
432     EmfPlusRectF SrcRect;
433     DWORD count;
434     union
435     {
436         EmfPlusPointR7 pointsR[3];
437         EmfPlusPoint points[3];
438         EmfPlusPointF pointsF[3];
439     } PointData;
440 } EmfPlusDrawImagePoints;
441 
442 typedef struct EmfPlusDrawPath
443 {
444     EmfPlusRecordHeader Header;
445     DWORD PenId;
446 } EmfPlusDrawPath;
447 
448 typedef struct EmfPlusDrawArc
449 {
450     EmfPlusRecordHeader Header;
451     float StartAngle;
452     float SweepAngle;
453     union
454     {
455         EmfPlusRect rect;
456         EmfPlusRectF rectF;
457     } RectData;
458 } EmfPlusDrawArc;
459 
460 typedef struct EmfPlusDrawEllipse
461 {
462     EmfPlusRecordHeader Header;
463     union
464     {
465         EmfPlusRect rect;
466         EmfPlusRectF rectF;
467     } RectData;
468 } EmfPlusDrawEllipse;
469 
470 typedef struct EmfPlusDrawPie
471 {
472     EmfPlusRecordHeader Header;
473     float StartAngle;
474     float SweepAngle;
475     union
476     {
477         EmfPlusRect rect;
478         EmfPlusRectF rectF;
479     } RectData;
480 } EmfPlusDrawPie;
481 
482 typedef struct EmfPlusDrawRects
483 {
484     EmfPlusRecordHeader Header;
485     DWORD Count;
486     union
487     {
488         EmfPlusRect rect[1];
489         EmfPlusRectF rectF[1];
490     } RectData;
491 } EmfPlusDrawRects;
492 
493 typedef struct EmfPlusFillPath
494 {
495     EmfPlusRecordHeader Header;
496     union
497     {
498         DWORD BrushId;
499         EmfPlusARGB Color;
500     } data;
501 } EmfPlusFillPath;
502 
503 typedef struct EmfPlusFillClosedCurve
504 {
505     EmfPlusRecordHeader Header;
506     DWORD BrushId;
507     float Tension;
508     DWORD Count;
509     union
510     {
511         EmfPlusPointR7 pointsR[1];
512         EmfPlusPoint points[1];
513         EmfPlusPointF pointsF[1];
514     } PointData;
515 } EmfPlusFillClosedCurve;
516 
517 typedef struct EmfPlusFillEllipse
518 {
519     EmfPlusRecordHeader Header;
520     DWORD BrushId;
521     union
522     {
523         EmfPlusRect rect;
524         EmfPlusRectF rectF;
525     } RectData;
526 } EmfPlusFillEllipse;
527 
528 typedef struct EmfPlusFillPie
529 {
530     EmfPlusRecordHeader Header;
531     DWORD BrushId;
532     float StartAngle;
533     float SweepAngle;
534     union
535     {
536         EmfPlusRect rect;
537         EmfPlusRectF rectF;
538     } RectData;
539 } EmfPlusFillPie;
540 
541 typedef struct EmfPlusFont
542 {
543     DWORD Version;
544     float EmSize;
545     DWORD SizeUnit;
546     DWORD FontStyleFlags;
547     DWORD Reserved;
548     DWORD Length;
549     WCHAR FamilyName[1];
550 } EmfPlusFont;
551 
metafile_free_object_table_entry(GpMetafile * metafile,BYTE id)552 static void metafile_free_object_table_entry(GpMetafile *metafile, BYTE id)
553 {
554     struct emfplus_object *object = &metafile->objtable[id];
555 
556     switch (object->type)
557     {
558     case ObjectTypeInvalid:
559         break;
560     case ObjectTypeBrush:
561         GdipDeleteBrush(object->u.brush);
562         break;
563     case ObjectTypePen:
564         GdipDeletePen(object->u.pen);
565         break;
566     case ObjectTypePath:
567         GdipDeletePath(object->u.path);
568         break;
569     case ObjectTypeRegion:
570         GdipDeleteRegion(object->u.region);
571         break;
572     case ObjectTypeImage:
573         GdipDisposeImage(object->u.image);
574         break;
575     case ObjectTypeFont:
576         GdipDeleteFont(object->u.font);
577         break;
578     case ObjectTypeImageAttributes:
579         GdipDisposeImageAttributes(object->u.image_attributes);
580         break;
581     default:
582         FIXME("not implemented for object type %u.\n", object->type);
583         return;
584     }
585 
586     object->type = ObjectTypeInvalid;
587     object->u.object = NULL;
588 }
589 
METAFILE_Free(GpMetafile * metafile)590 void METAFILE_Free(GpMetafile *metafile)
591 {
592     unsigned int i;
593 
594     heap_free(metafile->comment_data);
595     DeleteEnhMetaFile(CloseEnhMetaFile(metafile->record_dc));
596     if (!metafile->preserve_hemf)
597         DeleteEnhMetaFile(metafile->hemf);
598     if (metafile->record_graphics)
599     {
600         WARN("metafile closed while recording\n");
601         /* not sure what to do here; for now just prevent the graphics from functioning or using this object */
602         metafile->record_graphics->image = NULL;
603         metafile->record_graphics->busy = TRUE;
604     }
605 
606     if (metafile->record_stream)
607         IStream_Release(metafile->record_stream);
608 
609     for (i = 0; i < ARRAY_SIZE(metafile->objtable); i++)
610         metafile_free_object_table_entry(metafile, i);
611 }
612 
METAFILE_AddObjectId(GpMetafile * metafile)613 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
614 {
615     return (metafile->next_object_id++) % EmfPlusObjectTableSize;
616 }
617 
METAFILE_AllocateRecord(GpMetafile * metafile,DWORD size,void ** result)618 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
619 {
620     DWORD size_needed;
621     EmfPlusRecordHeader *record;
622 
623     if (!metafile->comment_data_size)
624     {
625         DWORD data_size = max(256, size * 2 + 4);
626         metafile->comment_data = heap_alloc_zero(data_size);
627 
628         if (!metafile->comment_data)
629             return OutOfMemory;
630 
631         memcpy(metafile->comment_data, "EMF+", 4);
632 
633         metafile->comment_data_size = data_size;
634         metafile->comment_data_length = 4;
635     }
636 
637     size_needed = size + metafile->comment_data_length;
638 
639     if (size_needed > metafile->comment_data_size)
640     {
641         DWORD data_size = size_needed * 2;
642         BYTE *new_data = heap_alloc_zero(data_size);
643 
644         if (!new_data)
645             return OutOfMemory;
646 
647         memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
648 
649         metafile->comment_data_size = data_size;
650         heap_free(metafile->comment_data);
651         metafile->comment_data = new_data;
652     }
653 
654     *result = metafile->comment_data + metafile->comment_data_length;
655     metafile->comment_data_length += size;
656 
657     record = (EmfPlusRecordHeader*)*result;
658     record->Size = size;
659     record->DataSize = size - sizeof(EmfPlusRecordHeader);
660 
661     return Ok;
662 }
663 
METAFILE_RemoveLastRecord(GpMetafile * metafile,EmfPlusRecordHeader * record)664 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
665 {
666     assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
667     metafile->comment_data_length -=  record->Size;
668 }
669 
METAFILE_WriteRecords(GpMetafile * metafile)670 static void METAFILE_WriteRecords(GpMetafile *metafile)
671 {
672     if (metafile->comment_data_length > 4)
673     {
674         GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
675         metafile->comment_data_length = 4;
676     }
677 }
678 
METAFILE_WriteHeader(GpMetafile * metafile,HDC hdc)679 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
680 {
681     GpStatus stat;
682 
683     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
684     {
685         EmfPlusHeader *header;
686 
687         stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
688         if (stat != Ok)
689             return stat;
690 
691         header->Header.Type = EmfPlusRecordTypeHeader;
692 
693         if (metafile->metafile_type == MetafileTypeEmfPlusDual)
694             header->Header.Flags = 1;
695         else
696             header->Header.Flags = 0;
697 
698         header->Version = VERSION_MAGIC2;
699 
700         if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
701             header->EmfPlusFlags = 1;
702         else
703             header->EmfPlusFlags = 0;
704 
705         header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
706         header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
707 
708         METAFILE_WriteRecords(metafile);
709     }
710 
711     return Ok;
712 }
713 
METAFILE_WriteEndOfFile(GpMetafile * metafile)714 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
715 {
716     GpStatus stat;
717 
718     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
719     {
720         EmfPlusRecordHeader *record;
721 
722         stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
723         if (stat != Ok)
724             return stat;
725 
726         record->Type = EmfPlusRecordTypeEndOfFile;
727         record->Flags = 0;
728 
729         METAFILE_WriteRecords(metafile);
730     }
731 
732     return Ok;
733 }
734 
GdipRecordMetafile(HDC hdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * desc,GpMetafile ** metafile)735 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
736                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
737 {
738     HDC record_dc;
739     REAL dpix, dpiy;
740     REAL framerect_factor_x, framerect_factor_y;
741     RECT rc, *lprc;
742     GpStatus stat;
743 
744     TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
745 
746     if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
747         return InvalidParameter;
748 
749     dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
750     dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
751 
752     if (frameRect)
753     {
754         switch (frameUnit)
755         {
756         case MetafileFrameUnitPixel:
757             framerect_factor_x = 2540.0 / dpix;
758             framerect_factor_y = 2540.0 / dpiy;
759             break;
760         case MetafileFrameUnitPoint:
761             framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
762             break;
763         case MetafileFrameUnitInch:
764             framerect_factor_x = framerect_factor_y = 2540.0;
765             break;
766         case MetafileFrameUnitDocument:
767             framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
768             break;
769         case MetafileFrameUnitMillimeter:
770             framerect_factor_x = framerect_factor_y = 100.0;
771             break;
772         case MetafileFrameUnitGdi:
773             framerect_factor_x = framerect_factor_y = 1.0;
774             break;
775         default:
776             return InvalidParameter;
777         }
778 
779         rc.left = framerect_factor_x * frameRect->X;
780         rc.top = framerect_factor_y * frameRect->Y;
781         rc.right = rc.left + framerect_factor_x * frameRect->Width;
782         rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
783 
784         lprc = &rc;
785     }
786     else
787         lprc = NULL;
788 
789     record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
790 
791     if (!record_dc)
792         return GenericError;
793 
794     *metafile = heap_alloc_zero(sizeof(GpMetafile));
795     if(!*metafile)
796     {
797         DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
798         return OutOfMemory;
799     }
800 
801     (*metafile)->image.type = ImageTypeMetafile;
802     (*metafile)->image.flags   = ImageFlagsNone;
803     (*metafile)->image.palette = NULL;
804     (*metafile)->image.xres = dpix;
805     (*metafile)->image.yres = dpiy;
806     (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
807     (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
808     (*metafile)->unit = UnitPixel;
809     (*metafile)->metafile_type = type;
810     (*metafile)->record_dc = record_dc;
811     (*metafile)->comment_data = NULL;
812     (*metafile)->comment_data_size = 0;
813     (*metafile)->comment_data_length = 0;
814     (*metafile)->hemf = NULL;
815     list_init(&(*metafile)->containers);
816 
817     if (!frameRect)
818     {
819         (*metafile)->auto_frame = TRUE;
820         (*metafile)->auto_frame_min.X = 0;
821         (*metafile)->auto_frame_min.Y = 0;
822         (*metafile)->auto_frame_max.X = -1;
823         (*metafile)->auto_frame_max.Y = -1;
824     }
825 
826     stat = METAFILE_WriteHeader(*metafile, hdc);
827 
828     if (stat != Ok)
829     {
830         DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
831         heap_free(*metafile);
832         *metafile = NULL;
833         return OutOfMemory;
834     }
835 
836     return stat;
837 }
838 
839 /*****************************************************************************
840  * GdipRecordMetafileI [GDIPLUS.@]
841  */
GdipRecordMetafileI(HDC hdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * desc,GpMetafile ** metafile)842 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
843                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
844 {
845     GpRectF frameRectF, *pFrameRectF;
846 
847     TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
848 
849     if (frameRect)
850     {
851         frameRectF.X = frameRect->X;
852         frameRectF.Y = frameRect->Y;
853         frameRectF.Width = frameRect->Width;
854         frameRectF.Height = frameRect->Height;
855         pFrameRectF = &frameRectF;
856     }
857     else
858         pFrameRectF = NULL;
859 
860     return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
861 }
862 
GdipRecordMetafileStream(IStream * stream,HDC hdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * desc,GpMetafile ** metafile)863 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
864                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
865 {
866     GpStatus stat;
867 
868     TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
869 
870     if (!stream)
871         return InvalidParameter;
872 
873     stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
874 
875     if (stat == Ok)
876     {
877         (*metafile)->record_stream = stream;
878         IStream_AddRef(stream);
879     }
880 
881     return stat;
882 }
883 
METAFILE_AdjustFrame(GpMetafile * metafile,const GpPointF * points,UINT num_points)884 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
885     UINT num_points)
886 {
887     int i;
888 
889     if (!metafile->auto_frame || !num_points)
890         return;
891 
892     if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
893         metafile->auto_frame_max = metafile->auto_frame_min = points[0];
894 
895     for (i=0; i<num_points; i++)
896     {
897         if (points[i].X < metafile->auto_frame_min.X)
898             metafile->auto_frame_min.X = points[i].X;
899         if (points[i].X > metafile->auto_frame_max.X)
900             metafile->auto_frame_max.X = points[i].X;
901         if (points[i].Y < metafile->auto_frame_min.Y)
902             metafile->auto_frame_min.Y = points[i].Y;
903         if (points[i].Y > metafile->auto_frame_max.Y)
904             metafile->auto_frame_max.Y = points[i].Y;
905     }
906 }
907 
METAFILE_GetGraphicsContext(GpMetafile * metafile,GpGraphics ** result)908 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
909 {
910     GpStatus stat;
911 
912     if (!metafile->record_dc || metafile->record_graphics)
913         return InvalidParameter;
914 
915     stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
916 
917     if (stat == Ok)
918     {
919         *result = metafile->record_graphics;
920         metafile->record_graphics->xres = 96.0;
921         metafile->record_graphics->yres = 96.0;
922     }
923 
924     return stat;
925 }
926 
METAFILE_GetDC(GpMetafile * metafile,HDC * hdc)927 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
928 {
929     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
930     {
931         EmfPlusRecordHeader *record;
932         GpStatus stat;
933 
934         stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
935         if (stat != Ok)
936             return stat;
937 
938         record->Type = EmfPlusRecordTypeGetDC;
939         record->Flags = 0;
940 
941         METAFILE_WriteRecords(metafile);
942     }
943 
944     *hdc = metafile->record_dc;
945 
946     return Ok;
947 }
948 
METAFILE_GraphicsClear(GpMetafile * metafile,ARGB color)949 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
950 {
951     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
952     {
953         EmfPlusClear *record;
954         GpStatus stat;
955 
956         stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
957         if (stat != Ok)
958             return stat;
959 
960         record->Header.Type = EmfPlusRecordTypeClear;
961         record->Header.Flags = 0;
962         record->Color = color;
963 
964         METAFILE_WriteRecords(metafile);
965     }
966 
967     return Ok;
968 }
969 
is_integer_rect(const GpRectF * rect)970 static BOOL is_integer_rect(const GpRectF *rect)
971 {
972     SHORT x, y, width, height;
973     x = rect->X;
974     y = rect->Y;
975     width = rect->Width;
976     height = rect->Height;
977     if (rect->X != (REAL)x || rect->Y != (REAL)y ||
978         rect->Width != (REAL)width || rect->Height != (REAL)height)
979         return FALSE;
980     return TRUE;
981 }
982 
METAFILE_PrepareBrushData(GpBrush * brush,DWORD * size)983 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
984 {
985     switch (brush->bt)
986     {
987     case BrushTypeSolidColor:
988         *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusSolidBrushData);
989         break;
990     case BrushTypeHatchFill:
991         *size = FIELD_OFFSET(EmfPlusBrush, BrushData) + sizeof(EmfPlusHatchBrushData);
992         break;
993     default:
994         FIXME("unsupported brush type: %d\n", brush->bt);
995         return NotImplemented;
996     }
997 
998     return Ok;
999 }
1000 
METAFILE_FillBrushData(GpBrush * brush,EmfPlusBrush * data)1001 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
1002 {
1003     data->Version = VERSION_MAGIC2;
1004     data->Type = brush->bt;
1005 
1006     switch (brush->bt)
1007     {
1008     case BrushTypeSolidColor:
1009     {
1010         GpSolidFill *solid = (GpSolidFill *)brush;
1011         data->BrushData.solid.SolidColor = solid->color;
1012         break;
1013     }
1014     case BrushTypeHatchFill:
1015     {
1016         GpHatch *hatch = (GpHatch *)brush;
1017         data->BrushData.hatch.HatchStyle = hatch->hatchstyle;
1018         data->BrushData.hatch.ForeColor = hatch->forecol;
1019         data->BrushData.hatch.BackColor = hatch->backcol;
1020         break;
1021     }
1022     default:
1023         FIXME("unsupported brush type: %d\n", brush->bt);
1024     }
1025 }
1026 
METAFILE_AddBrushObject(GpMetafile * metafile,GpBrush * brush,DWORD * id)1027 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
1028 {
1029     EmfPlusObject *object_record;
1030     GpStatus stat;
1031     DWORD size;
1032 
1033     *id = -1;
1034     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1035         return Ok;
1036 
1037     stat = METAFILE_PrepareBrushData(brush, &size);
1038     if (stat != Ok) return stat;
1039 
1040     stat = METAFILE_AllocateRecord(metafile,
1041         FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
1042     if (stat != Ok) return stat;
1043 
1044     *id = METAFILE_AddObjectId(metafile);
1045     object_record->Header.Type = EmfPlusRecordTypeObject;
1046     object_record->Header.Flags = *id | ObjectTypeBrush << 8;
1047     METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
1048     return Ok;
1049 }
1050 
METAFILE_FillRectangles(GpMetafile * metafile,GpBrush * brush,GDIPCONST GpRectF * rects,INT count)1051 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
1052     GDIPCONST GpRectF* rects, INT count)
1053 {
1054     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1055     {
1056         EmfPlusFillRects *record;
1057         GpStatus stat;
1058         BOOL integer_rects = TRUE;
1059         int i;
1060         DWORD brushid;
1061         int flags = 0;
1062 
1063         if (brush->bt == BrushTypeSolidColor)
1064         {
1065             flags |= 0x8000;
1066             brushid = ((GpSolidFill*)brush)->color;
1067         }
1068         else
1069         {
1070             stat = METAFILE_AddBrushObject(metafile, brush, &brushid);
1071             if (stat != Ok)
1072                 return stat;
1073         }
1074 
1075         for (i=0; i<count; i++)
1076         {
1077             if (!is_integer_rect(&rects[i]))
1078             {
1079                 integer_rects = FALSE;
1080                 break;
1081             }
1082         }
1083 
1084         if (integer_rects)
1085             flags |= 0x4000;
1086 
1087         stat = METAFILE_AllocateRecord(metafile,
1088             sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
1089             (void**)&record);
1090         if (stat != Ok)
1091             return stat;
1092 
1093         record->Header.Type = EmfPlusRecordTypeFillRects;
1094         record->Header.Flags = flags;
1095         record->BrushID = brushid;
1096         record->Count = count;
1097 
1098         if (integer_rects)
1099         {
1100             EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
1101             for (i=0; i<count; i++)
1102             {
1103                 record_rects[i].X = (SHORT)rects[i].X;
1104                 record_rects[i].Y = (SHORT)rects[i].Y;
1105                 record_rects[i].Width = (SHORT)rects[i].Width;
1106                 record_rects[i].Height = (SHORT)rects[i].Height;
1107             }
1108         }
1109         else
1110             memcpy(record+1, rects, sizeof(GpRectF) * count);
1111 
1112         METAFILE_WriteRecords(metafile);
1113     }
1114 
1115     if (metafile->auto_frame)
1116     {
1117         GpPointF corners[4];
1118         int i;
1119 
1120         for (i=0; i<count; i++)
1121         {
1122             corners[0].X = rects[i].X;
1123             corners[0].Y = rects[i].Y;
1124             corners[1].X = rects[i].X + rects[i].Width;
1125             corners[1].Y = rects[i].Y;
1126             corners[2].X = rects[i].X;
1127             corners[2].Y = rects[i].Y + rects[i].Height;
1128             corners[3].X = rects[i].X + rects[i].Width;
1129             corners[3].Y = rects[i].Y + rects[i].Height;
1130 
1131             GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
1132                 CoordinateSpaceWorld, corners, 4);
1133 
1134             METAFILE_AdjustFrame(metafile, corners, 4);
1135         }
1136     }
1137 
1138     return Ok;
1139 }
1140 
METAFILE_SetClipRect(GpMetafile * metafile,REAL x,REAL y,REAL width,REAL height,CombineMode mode)1141 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
1142 {
1143     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1144     {
1145         EmfPlusSetClipRect *record;
1146         GpStatus stat;
1147 
1148         stat = METAFILE_AllocateRecord(metafile,
1149             sizeof(EmfPlusSetClipRect),
1150             (void**)&record);
1151         if (stat != Ok)
1152             return stat;
1153 
1154         record->Header.Type = EmfPlusRecordTypeSetClipRect;
1155         record->Header.Flags = (mode & 0xf) << 8;
1156         record->ClipRect.X = x;
1157         record->ClipRect.Y = y;
1158         record->ClipRect.Width = width;
1159         record->ClipRect.Height = height;
1160 
1161         METAFILE_WriteRecords(metafile);
1162     }
1163 
1164     return Ok;
1165 }
1166 
METAFILE_AddRegionObject(GpMetafile * metafile,GpRegion * region,DWORD * id)1167 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
1168 {
1169     EmfPlusObject *object_record;
1170     DWORD size;
1171     GpStatus stat;
1172 
1173     *id = -1;
1174     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
1175         return Ok;
1176 
1177     size = write_region_data(region, NULL);
1178     stat = METAFILE_AllocateRecord(metafile,
1179             FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
1180     if (stat != Ok) return stat;
1181 
1182     *id = METAFILE_AddObjectId(metafile);
1183     object_record->Header.Type = EmfPlusRecordTypeObject;
1184     object_record->Header.Flags = *id | ObjectTypeRegion << 8;
1185     write_region_data(region, &object_record->ObjectData.region);
1186     return Ok;
1187 }
1188 
METAFILE_SetClipRegion(GpMetafile * metafile,GpRegion * region,CombineMode mode)1189 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
1190 {
1191     EmfPlusRecordHeader *record;
1192     DWORD region_id;
1193     GpStatus stat;
1194 
1195     if (metafile->metafile_type == MetafileTypeEmf)
1196     {
1197         FIXME("stub!\n");
1198         return NotImplemented;
1199     }
1200 
1201     stat = METAFILE_AddRegionObject(metafile, region, &region_id);
1202     if (stat != Ok) return stat;
1203 
1204     stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1205     if (stat != Ok) return stat;
1206 
1207     record->Type = EmfPlusRecordTypeSetClipRegion;
1208     record->Flags = region_id | mode << 8;
1209 
1210     METAFILE_WriteRecords(metafile);
1211     return Ok;
1212 }
1213 
METAFILE_SetPageTransform(GpMetafile * metafile,GpUnit unit,REAL scale)1214 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
1215 {
1216     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1217     {
1218         EmfPlusSetPageTransform *record;
1219         GpStatus stat;
1220 
1221         stat = METAFILE_AllocateRecord(metafile,
1222             sizeof(EmfPlusSetPageTransform),
1223             (void**)&record);
1224         if (stat != Ok)
1225             return stat;
1226 
1227         record->Header.Type = EmfPlusRecordTypeSetPageTransform;
1228         record->Header.Flags = unit;
1229         record->PageScale = scale;
1230 
1231         METAFILE_WriteRecords(metafile);
1232     }
1233 
1234     return Ok;
1235 }
1236 
METAFILE_SetWorldTransform(GpMetafile * metafile,GDIPCONST GpMatrix * transform)1237 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
1238 {
1239     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1240     {
1241         EmfPlusSetWorldTransform *record;
1242         GpStatus stat;
1243 
1244         stat = METAFILE_AllocateRecord(metafile,
1245             sizeof(EmfPlusSetWorldTransform),
1246             (void**)&record);
1247         if (stat != Ok)
1248             return stat;
1249 
1250         record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
1251         record->Header.Flags = 0;
1252         memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
1253 
1254         METAFILE_WriteRecords(metafile);
1255     }
1256 
1257     return Ok;
1258 }
1259 
METAFILE_ScaleWorldTransform(GpMetafile * metafile,REAL sx,REAL sy,MatrixOrder order)1260 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
1261 {
1262     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1263     {
1264         EmfPlusScaleWorldTransform *record;
1265         GpStatus stat;
1266 
1267         stat = METAFILE_AllocateRecord(metafile,
1268             sizeof(EmfPlusScaleWorldTransform),
1269             (void**)&record);
1270         if (stat != Ok)
1271             return stat;
1272 
1273         record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
1274         record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1275         record->Sx = sx;
1276         record->Sy = sy;
1277 
1278         METAFILE_WriteRecords(metafile);
1279     }
1280 
1281     return Ok;
1282 }
1283 
METAFILE_MultiplyWorldTransform(GpMetafile * metafile,GDIPCONST GpMatrix * matrix,MatrixOrder order)1284 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
1285 {
1286     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1287     {
1288         EmfPlusMultiplyWorldTransform *record;
1289         GpStatus stat;
1290 
1291         stat = METAFILE_AllocateRecord(metafile,
1292             sizeof(EmfPlusMultiplyWorldTransform),
1293             (void**)&record);
1294         if (stat != Ok)
1295             return stat;
1296 
1297         record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
1298         record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1299         memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
1300 
1301         METAFILE_WriteRecords(metafile);
1302     }
1303 
1304     return Ok;
1305 }
1306 
METAFILE_RotateWorldTransform(GpMetafile * metafile,REAL angle,MatrixOrder order)1307 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
1308 {
1309     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1310     {
1311         EmfPlusRotateWorldTransform *record;
1312         GpStatus stat;
1313 
1314         stat = METAFILE_AllocateRecord(metafile,
1315             sizeof(EmfPlusRotateWorldTransform),
1316             (void**)&record);
1317         if (stat != Ok)
1318             return stat;
1319 
1320         record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
1321         record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1322         record->Angle = angle;
1323 
1324         METAFILE_WriteRecords(metafile);
1325     }
1326 
1327     return Ok;
1328 }
1329 
METAFILE_TranslateWorldTransform(GpMetafile * metafile,REAL dx,REAL dy,MatrixOrder order)1330 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1331 {
1332     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1333     {
1334         EmfPlusTranslateWorldTransform *record;
1335         GpStatus stat;
1336 
1337         stat = METAFILE_AllocateRecord(metafile,
1338             sizeof(EmfPlusTranslateWorldTransform),
1339             (void**)&record);
1340         if (stat != Ok)
1341             return stat;
1342 
1343         record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1344         record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1345         record->dx = dx;
1346         record->dy = dy;
1347 
1348         METAFILE_WriteRecords(metafile);
1349     }
1350 
1351     return Ok;
1352 }
1353 
METAFILE_ResetWorldTransform(GpMetafile * metafile)1354 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1355 {
1356     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1357     {
1358         EmfPlusRecordHeader *record;
1359         GpStatus stat;
1360 
1361         stat = METAFILE_AllocateRecord(metafile,
1362             sizeof(EmfPlusRecordHeader),
1363             (void**)&record);
1364         if (stat != Ok)
1365             return stat;
1366 
1367         record->Type = EmfPlusRecordTypeResetWorldTransform;
1368         record->Flags = 0;
1369 
1370         METAFILE_WriteRecords(metafile);
1371     }
1372 
1373     return Ok;
1374 }
1375 
METAFILE_BeginContainer(GpMetafile * metafile,GDIPCONST GpRectF * dstrect,GDIPCONST GpRectF * srcrect,GpUnit unit,DWORD StackIndex)1376 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1377     GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1378 {
1379     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1380     {
1381         EmfPlusBeginContainer *record;
1382         GpStatus stat;
1383 
1384         stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1385         if (stat != Ok)
1386             return stat;
1387 
1388         record->Header.Type = EmfPlusRecordTypeBeginContainer;
1389         record->Header.Flags = unit & 0xff;
1390         record->DestRect = *dstrect;
1391         record->SrcRect = *srcrect;
1392         record->StackIndex = StackIndex;
1393 
1394         METAFILE_WriteRecords(metafile);
1395     }
1396 
1397     return Ok;
1398 }
1399 
METAFILE_BeginContainerNoParams(GpMetafile * metafile,DWORD StackIndex)1400 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1401 {
1402     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1403     {
1404         EmfPlusContainerRecord *record;
1405         GpStatus stat;
1406 
1407         stat = METAFILE_AllocateRecord(metafile,
1408             sizeof(EmfPlusContainerRecord),
1409             (void**)&record);
1410         if (stat != Ok)
1411             return stat;
1412 
1413         record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1414         record->Header.Flags = 0;
1415         record->StackIndex = StackIndex;
1416 
1417         METAFILE_WriteRecords(metafile);
1418     }
1419 
1420     return Ok;
1421 }
1422 
METAFILE_EndContainer(GpMetafile * metafile,DWORD StackIndex)1423 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1424 {
1425     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1426     {
1427         EmfPlusContainerRecord *record;
1428         GpStatus stat;
1429 
1430         stat = METAFILE_AllocateRecord(metafile,
1431             sizeof(EmfPlusContainerRecord),
1432             (void**)&record);
1433         if (stat != Ok)
1434             return stat;
1435 
1436         record->Header.Type = EmfPlusRecordTypeEndContainer;
1437         record->Header.Flags = 0;
1438         record->StackIndex = StackIndex;
1439 
1440         METAFILE_WriteRecords(metafile);
1441     }
1442 
1443     return Ok;
1444 }
1445 
METAFILE_SaveGraphics(GpMetafile * metafile,DWORD StackIndex)1446 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1447 {
1448     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1449     {
1450         EmfPlusContainerRecord *record;
1451         GpStatus stat;
1452 
1453         stat = METAFILE_AllocateRecord(metafile,
1454             sizeof(EmfPlusContainerRecord),
1455             (void**)&record);
1456         if (stat != Ok)
1457             return stat;
1458 
1459         record->Header.Type = EmfPlusRecordTypeSave;
1460         record->Header.Flags = 0;
1461         record->StackIndex = StackIndex;
1462 
1463         METAFILE_WriteRecords(metafile);
1464     }
1465 
1466     return Ok;
1467 }
1468 
METAFILE_RestoreGraphics(GpMetafile * metafile,DWORD StackIndex)1469 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1470 {
1471     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1472     {
1473         EmfPlusContainerRecord *record;
1474         GpStatus stat;
1475 
1476         stat = METAFILE_AllocateRecord(metafile,
1477             sizeof(EmfPlusContainerRecord),
1478             (void**)&record);
1479         if (stat != Ok)
1480             return stat;
1481 
1482         record->Header.Type = EmfPlusRecordTypeRestore;
1483         record->Header.Flags = 0;
1484         record->StackIndex = StackIndex;
1485 
1486         METAFILE_WriteRecords(metafile);
1487     }
1488 
1489     return Ok;
1490 }
1491 
METAFILE_ReleaseDC(GpMetafile * metafile,HDC hdc)1492 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1493 {
1494     if (hdc != metafile->record_dc)
1495         return InvalidParameter;
1496 
1497     return Ok;
1498 }
1499 
METAFILE_GraphicsDeleted(GpMetafile * metafile)1500 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1501 {
1502     GpStatus stat;
1503 
1504     stat = METAFILE_WriteEndOfFile(metafile);
1505     metafile->record_graphics = NULL;
1506 
1507     metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1508     metafile->record_dc = NULL;
1509 
1510     heap_free(metafile->comment_data);
1511     metafile->comment_data = NULL;
1512     metafile->comment_data_size = 0;
1513 
1514     if (stat == Ok)
1515     {
1516         MetafileHeader header;
1517 
1518         stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1519         if (stat == Ok && metafile->auto_frame &&
1520             metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1521         {
1522             RECTL bounds_rc, gdi_bounds_rc;
1523             REAL x_scale = 2540.0 / header.DpiX;
1524             REAL y_scale = 2540.0 / header.DpiY;
1525             BYTE* buffer;
1526             UINT buffer_size;
1527 
1528             bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1529             bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1530             bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1531             bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1532 
1533             gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1534             if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1535             {
1536                 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1537                 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1538                 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1539                 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1540             }
1541 
1542             buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1543             buffer = heap_alloc(buffer_size);
1544             if (buffer)
1545             {
1546                 HENHMETAFILE new_hemf;
1547 
1548                 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1549 
1550                 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1551 
1552                 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1553 
1554                 if (new_hemf)
1555                 {
1556                     DeleteEnhMetaFile(metafile->hemf);
1557                     metafile->hemf = new_hemf;
1558                 }
1559                 else
1560                     stat = OutOfMemory;
1561 
1562                 heap_free(buffer);
1563             }
1564             else
1565                 stat = OutOfMemory;
1566 
1567             if (stat == Ok)
1568                 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1569         }
1570         if (stat == Ok)
1571         {
1572             metafile->bounds.X = header.X;
1573             metafile->bounds.Y = header.Y;
1574             metafile->bounds.Width = header.Width;
1575             metafile->bounds.Height = header.Height;
1576         }
1577     }
1578 
1579     if (stat == Ok && metafile->record_stream)
1580     {
1581         BYTE *buffer;
1582         UINT buffer_size;
1583 
1584         buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1585 
1586         buffer = heap_alloc(buffer_size);
1587         if (buffer)
1588         {
1589             HRESULT hr;
1590 
1591             GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1592 
1593             hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1594 
1595             if (FAILED(hr))
1596                 stat = hresult_to_status(hr);
1597 
1598             heap_free(buffer);
1599         }
1600         else
1601             stat = OutOfMemory;
1602     }
1603 
1604     if (metafile->record_stream)
1605     {
1606         IStream_Release(metafile->record_stream);
1607         metafile->record_stream = NULL;
1608     }
1609 
1610     return stat;
1611 }
1612 
GdipGetHemfFromMetafile(GpMetafile * metafile,HENHMETAFILE * hEmf)1613 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1614 {
1615     TRACE("(%p,%p)\n", metafile, hEmf);
1616 
1617     if (!metafile || !hEmf || !metafile->hemf)
1618         return InvalidParameter;
1619 
1620     *hEmf = metafile->hemf;
1621     metafile->hemf = NULL;
1622 
1623     return Ok;
1624 }
1625 
METAFILE_GetFinalGdiTransform(const GpMetafile * metafile,XFORM * result)1626 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1627 {
1628     const GpRectF *rect;
1629     const GpPointF *pt;
1630 
1631     /* This transforms metafile device space to output points. */
1632     rect = &metafile->src_rect;
1633     pt = metafile->playback_points;
1634     result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1635     result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1636     result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1637     result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1638     result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1639     result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1640 }
1641 
METAFILE_PlaybackUpdateGdiTransform(GpMetafile * metafile)1642 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1643 {
1644     XFORM combined, final;
1645 
1646     METAFILE_GetFinalGdiTransform(metafile, &final);
1647 
1648     CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1649 
1650     SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1651     SetWorldTransform(metafile->playback_dc, &combined);
1652 
1653     return Ok;
1654 }
1655 
METAFILE_PlaybackGetDC(GpMetafile * metafile)1656 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1657 {
1658     GpStatus stat = Ok;
1659 
1660     stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1661 
1662     if (stat == Ok)
1663     {
1664         static const XFORM identity = {1, 0, 0, 1, 0, 0};
1665 
1666         metafile->gdiworldtransform = identity;
1667         METAFILE_PlaybackUpdateGdiTransform(metafile);
1668     }
1669 
1670     return stat;
1671 }
1672 
METAFILE_PlaybackReleaseDC(GpMetafile * metafile)1673 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1674 {
1675     if (metafile->playback_dc)
1676     {
1677         GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1678         metafile->playback_dc = NULL;
1679     }
1680 }
1681 
METAFILE_PlaybackUpdateClip(GpMetafile * metafile)1682 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1683 {
1684     GpStatus stat;
1685     stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1686     if (stat == Ok)
1687         stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1688     return stat;
1689 }
1690 
METAFILE_PlaybackUpdateWorldTransform(GpMetafile * metafile)1691 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1692 {
1693     GpMatrix *real_transform;
1694     GpStatus stat;
1695 
1696     stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1697 
1698     if (stat == Ok)
1699     {
1700         REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1701 
1702         if (metafile->page_unit != UnitDisplay)
1703             scale *= metafile->page_scale;
1704 
1705         stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1706 
1707         if (stat == Ok)
1708             stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1709 
1710         if (stat == Ok)
1711             stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1712 
1713         GdipDeleteMatrix(real_transform);
1714     }
1715 
1716     return stat;
1717 }
1718 
metafile_set_object_table_entry(GpMetafile * metafile,BYTE id,BYTE type,void * object)1719 static void metafile_set_object_table_entry(GpMetafile *metafile, BYTE id, BYTE type, void *object)
1720 {
1721     metafile_free_object_table_entry(metafile, id);
1722     metafile->objtable[id].type = type;
1723     metafile->objtable[id].u.object = object;
1724 }
1725 
metafile_deserialize_image(const BYTE * record_data,UINT data_size,GpImage ** image)1726 static GpStatus metafile_deserialize_image(const BYTE *record_data, UINT data_size, GpImage **image)
1727 {
1728     EmfPlusImage *data = (EmfPlusImage *)record_data;
1729     GpStatus status;
1730 
1731     *image = NULL;
1732 
1733     if (data_size < FIELD_OFFSET(EmfPlusImage, ImageData))
1734         return InvalidParameter;
1735     data_size -= FIELD_OFFSET(EmfPlusImage, ImageData);
1736 
1737     switch (data->Type)
1738     {
1739     case ImageDataTypeBitmap:
1740     {
1741         EmfPlusBitmap *bitmapdata = &data->ImageData.bitmap;
1742 
1743         if (data_size <= FIELD_OFFSET(EmfPlusBitmap, BitmapData))
1744             return InvalidParameter;
1745         data_size -= FIELD_OFFSET(EmfPlusBitmap, BitmapData);
1746 
1747         switch (bitmapdata->Type)
1748         {
1749         case BitmapDataTypePixel:
1750         {
1751             ColorPalette *palette;
1752             BYTE *scan0;
1753 
1754             if (bitmapdata->PixelFormat & PixelFormatIndexed)
1755             {
1756                 EmfPlusPalette *palette_obj = (EmfPlusPalette *)bitmapdata->BitmapData;
1757                 UINT palette_size = FIELD_OFFSET(EmfPlusPalette, PaletteEntries);
1758 
1759                 if (data_size <= palette_size)
1760                     return InvalidParameter;
1761                 palette_size += palette_obj->PaletteCount * sizeof(EmfPlusARGB);
1762 
1763                 if (data_size < palette_size)
1764                     return InvalidParameter;
1765                 data_size -= palette_size;
1766 
1767                 palette = (ColorPalette *)bitmapdata->BitmapData;
1768                 scan0 = (BYTE *)bitmapdata->BitmapData + palette_size;
1769             }
1770             else
1771             {
1772                 palette = NULL;
1773                 scan0 = bitmapdata->BitmapData;
1774             }
1775 
1776             if (data_size < bitmapdata->Height * bitmapdata->Stride)
1777                 return InvalidParameter;
1778 
1779             status = GdipCreateBitmapFromScan0(bitmapdata->Width, bitmapdata->Height, bitmapdata->Stride,
1780                 bitmapdata->PixelFormat, scan0, (GpBitmap **)image);
1781             if (status == Ok && palette)
1782             {
1783                 status = GdipSetImagePalette(*image, palette);
1784                 if (status != Ok)
1785                 {
1786                     GdipDisposeImage(*image);
1787                     *image = NULL;
1788                 }
1789             }
1790             break;
1791         }
1792         case BitmapDataTypeCompressed:
1793         {
1794             IWICImagingFactory *factory;
1795             IWICStream *stream;
1796             HRESULT hr;
1797 
1798             if (WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &factory) != S_OK)
1799                 return GenericError;
1800 
1801             hr = IWICImagingFactory_CreateStream(factory, &stream);
1802             IWICImagingFactory_Release(factory);
1803             if (hr != S_OK)
1804                 return GenericError;
1805 
1806             if (IWICStream_InitializeFromMemory(stream, bitmapdata->BitmapData, data_size) == S_OK)
1807                 status = GdipCreateBitmapFromStream((IStream *)stream, (GpBitmap **)image);
1808             else
1809                 status = GenericError;
1810 
1811             IWICStream_Release(stream);
1812             break;
1813         }
1814         default:
1815             WARN("Invalid bitmap type %d.\n", bitmapdata->Type);
1816             return InvalidParameter;
1817         }
1818         break;
1819     }
1820     default:
1821         FIXME("image type %d not supported.\n", data->Type);
1822         return NotImplemented;
1823     }
1824 
1825     return status;
1826 }
1827 
metafile_deserialize_path(const BYTE * record_data,UINT data_size,GpPath ** path)1828 static GpStatus metafile_deserialize_path(const BYTE *record_data, UINT data_size, GpPath **path)
1829 {
1830     EmfPlusPath *data = (EmfPlusPath *)record_data;
1831     GpStatus status;
1832     BYTE *types;
1833     UINT size;
1834     DWORD i;
1835 
1836     *path = NULL;
1837 
1838     if (data_size <= FIELD_OFFSET(EmfPlusPath, data))
1839         return InvalidParameter;
1840     data_size -= FIELD_OFFSET(EmfPlusPath, data);
1841 
1842     if (data->PathPointFlags & 0x800) /* R */
1843     {
1844         FIXME("RLE encoded path data is not supported.\n");
1845         return NotImplemented;
1846     }
1847     else
1848     {
1849         if (data->PathPointFlags & 0x4000) /* C */
1850             size = sizeof(EmfPlusPoint);
1851         else
1852             size = sizeof(EmfPlusPointF);
1853         size += sizeof(BYTE); /* EmfPlusPathPointType */
1854         size *= data->PathPointCount;
1855     }
1856 
1857     if (data_size < size)
1858         return InvalidParameter;
1859 
1860     status = GdipCreatePath(FillModeAlternate, path);
1861     if (status != Ok)
1862         return status;
1863 
1864     (*path)->pathdata.Count = data->PathPointCount;
1865     (*path)->pathdata.Points = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Points));
1866     (*path)->pathdata.Types = GdipAlloc(data->PathPointCount * sizeof(*(*path)->pathdata.Types));
1867     (*path)->datalen = (*path)->pathdata.Count;
1868 
1869     if (!(*path)->pathdata.Points || !(*path)->pathdata.Types)
1870     {
1871         GdipDeletePath(*path);
1872         return OutOfMemory;
1873     }
1874 
1875     if (data->PathPointFlags & 0x4000) /* C */
1876     {
1877         EmfPlusPoint *points = (EmfPlusPoint *)data->data;
1878         for (i = 0; i < data->PathPointCount; i++)
1879         {
1880             (*path)->pathdata.Points[i].X = points[i].X;
1881             (*path)->pathdata.Points[i].Y = points[i].Y;
1882         }
1883         types = (BYTE *)(points + i);
1884     }
1885     else
1886     {
1887         EmfPlusPointF *points = (EmfPlusPointF *)data->data;
1888         memcpy((*path)->pathdata.Points, points, sizeof(*points) * data->PathPointCount);
1889         types = (BYTE *)(points + data->PathPointCount);
1890     }
1891 
1892     memcpy((*path)->pathdata.Types, types, sizeof(*types) * data->PathPointCount);
1893 
1894     return Ok;
1895 }
1896 
metafile_read_region_node(struct memory_buffer * mbuf,GpRegion * region,region_element * node,UINT * count)1897 static GpStatus metafile_read_region_node(struct memory_buffer *mbuf, GpRegion *region, region_element *node, UINT *count)
1898 {
1899     const DWORD *type;
1900     GpStatus status;
1901 
1902     type = buffer_read(mbuf, sizeof(*type));
1903     if (!type) return Ok;
1904 
1905     node->type = *type;
1906 
1907     switch (node->type)
1908     {
1909     case CombineModeReplace:
1910     case CombineModeIntersect:
1911     case CombineModeUnion:
1912     case CombineModeXor:
1913     case CombineModeExclude:
1914     case CombineModeComplement:
1915     {
1916         region_element *left, *right;
1917 
1918         left = heap_alloc_zero(sizeof(*left));
1919         if (!left)
1920             return OutOfMemory;
1921 
1922         right = heap_alloc_zero(sizeof(*right));
1923         if (!right)
1924         {
1925             heap_free(left);
1926             return OutOfMemory;
1927         }
1928 
1929         status = metafile_read_region_node(mbuf, region, left, count);
1930         if (status == Ok)
1931         {
1932             status = metafile_read_region_node(mbuf, region, right, count);
1933             if (status == Ok)
1934             {
1935                 node->elementdata.combine.left = left;
1936                 node->elementdata.combine.right = right;
1937                 region->num_children += 2;
1938                 return Ok;
1939             }
1940         }
1941 
1942         heap_free(left);
1943         heap_free(right);
1944         return status;
1945     }
1946     case RegionDataRect:
1947     {
1948         const EmfPlusRectF *rect;
1949 
1950         rect = buffer_read(mbuf, sizeof(*rect));
1951         if (!rect)
1952             return InvalidParameter;
1953 
1954         memcpy(&node->elementdata.rect, rect, sizeof(*rect));
1955         *count += 1;
1956         return Ok;
1957     }
1958     case RegionDataPath:
1959     {
1960         const BYTE *path_data;
1961         const UINT *data_size;
1962         GpPath *path;
1963 
1964         data_size = buffer_read(mbuf, FIELD_OFFSET(EmfPlusRegionNodePath, RegionNodePath));
1965         if (!data_size)
1966             return InvalidParameter;
1967 
1968         path_data = buffer_read(mbuf, *data_size);
1969         if (!path_data)
1970             return InvalidParameter;
1971 
1972         status = metafile_deserialize_path(path_data, *data_size, &path);
1973         if (status == Ok)
1974         {
1975             node->elementdata.path = path;
1976             *count += 1;
1977         }
1978         return Ok;
1979     }
1980     case RegionDataEmptyRect:
1981     case RegionDataInfiniteRect:
1982         *count += 1;
1983         return Ok;
1984     default:
1985         FIXME("element type %#x is not supported\n", *type);
1986         break;
1987     }
1988 
1989     return InvalidParameter;
1990 }
1991 
metafile_deserialize_region(const BYTE * record_data,UINT data_size,GpRegion ** region)1992 static GpStatus metafile_deserialize_region(const BYTE *record_data, UINT data_size, GpRegion **region)
1993 {
1994     struct memory_buffer mbuf;
1995     GpStatus status;
1996     UINT count;
1997 
1998     *region = NULL;
1999 
2000     init_memory_buffer(&mbuf, record_data, data_size);
2001 
2002     if (!buffer_read(&mbuf, FIELD_OFFSET(EmfPlusRegion, RegionNode)))
2003         return InvalidParameter;
2004 
2005     status = GdipCreateRegion(region);
2006     if (status != Ok)
2007         return status;
2008 
2009     count = 0;
2010     status = metafile_read_region_node(&mbuf, *region, &(*region)->node, &count);
2011     if (status == Ok && !count)
2012         status = InvalidParameter;
2013 
2014     if (status != Ok)
2015     {
2016         GdipDeleteRegion(*region);
2017         *region = NULL;
2018     }
2019 
2020     return status;
2021 }
2022 
metafile_deserialize_brush(const BYTE * record_data,UINT data_size,GpBrush ** brush)2023 static GpStatus metafile_deserialize_brush(const BYTE *record_data, UINT data_size, GpBrush **brush)
2024 {
2025     static const UINT header_size = FIELD_OFFSET(EmfPlusBrush, BrushData);
2026     EmfPlusBrush *data = (EmfPlusBrush *)record_data;
2027     EmfPlusTransformMatrix *transform = NULL;
2028     DWORD brushflags;
2029     GpStatus status;
2030     UINT offset;
2031 
2032     *brush = NULL;
2033 
2034     if (data_size < header_size)
2035         return InvalidParameter;
2036 
2037     switch (data->Type)
2038     {
2039     case BrushTypeSolidColor:
2040         if (data_size != header_size + sizeof(EmfPlusSolidBrushData))
2041             return InvalidParameter;
2042 
2043         status = GdipCreateSolidFill(data->BrushData.solid.SolidColor, (GpSolidFill **)brush);
2044         break;
2045     case BrushTypeHatchFill:
2046         if (data_size != header_size + sizeof(EmfPlusHatchBrushData))
2047             return InvalidParameter;
2048 
2049         status = GdipCreateHatchBrush(data->BrushData.hatch.HatchStyle, data->BrushData.hatch.ForeColor,
2050             data->BrushData.hatch.BackColor, (GpHatch **)brush);
2051         break;
2052     case BrushTypeTextureFill:
2053     {
2054         GpImage *image;
2055 
2056         offset = header_size + FIELD_OFFSET(EmfPlusTextureBrushData, OptionalData);
2057         if (data_size <= offset)
2058             return InvalidParameter;
2059 
2060         brushflags = data->BrushData.texture.BrushDataFlags;
2061         if (brushflags & BrushDataTransform)
2062         {
2063             if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2064                 return InvalidParameter;
2065             transform = (EmfPlusTransformMatrix *)(record_data + offset);
2066             offset += sizeof(EmfPlusTransformMatrix);
2067         }
2068 
2069         status = metafile_deserialize_image(record_data + offset, data_size - offset, &image);
2070         if (status != Ok)
2071             return status;
2072 
2073         status = GdipCreateTexture(image, data->BrushData.texture.WrapMode, (GpTexture **)brush);
2074         if (status == Ok && transform && !(brushflags & BrushDataDoNotTransform))
2075             GdipSetTextureTransform((GpTexture *)*brush, (const GpMatrix *)transform);
2076 
2077         GdipDisposeImage(image);
2078         break;
2079     }
2080     case BrushTypeLinearGradient:
2081     {
2082         GpLineGradient *gradient = NULL;
2083         GpPointF startpoint, endpoint;
2084         UINT position_count = 0;
2085 
2086         offset = header_size + FIELD_OFFSET(EmfPlusLinearGradientBrushData, OptionalData);
2087         if (data_size <= offset)
2088             return InvalidParameter;
2089 
2090         brushflags = data->BrushData.lineargradient.BrushDataFlags;
2091         if ((brushflags & BrushDataPresetColors) && (brushflags & (BrushDataBlendFactorsH | BrushDataBlendFactorsV)))
2092             return InvalidParameter;
2093 
2094         if (brushflags & BrushDataTransform)
2095         {
2096             if (data_size <= offset + sizeof(EmfPlusTransformMatrix))
2097                 return InvalidParameter;
2098             transform = (EmfPlusTransformMatrix *)(record_data + offset);
2099             offset += sizeof(EmfPlusTransformMatrix);
2100         }
2101 
2102         if (brushflags & (BrushDataPresetColors | BrushDataBlendFactorsH | BrushDataBlendFactorsV))
2103         {
2104             if (data_size <= offset + sizeof(DWORD)) /* Number of factors/preset colors. */
2105                 return InvalidParameter;
2106             position_count = *(DWORD *)(record_data + offset);
2107             offset += sizeof(DWORD);
2108         }
2109 
2110         if (brushflags & BrushDataPresetColors)
2111         {
2112             if (data_size != offset + position_count * (sizeof(float) + sizeof(EmfPlusARGB)))
2113                 return InvalidParameter;
2114         }
2115         else if (brushflags & BrushDataBlendFactorsH)
2116         {
2117             if (data_size != offset + position_count * 2 * sizeof(float))
2118                 return InvalidParameter;
2119         }
2120 
2121         startpoint.X = data->BrushData.lineargradient.RectF.X;
2122         startpoint.Y = data->BrushData.lineargradient.RectF.Y;
2123         endpoint.X = startpoint.X + data->BrushData.lineargradient.RectF.Width;
2124         endpoint.Y = startpoint.Y + data->BrushData.lineargradient.RectF.Height;
2125 
2126         status = GdipCreateLineBrush(&startpoint, &endpoint, data->BrushData.lineargradient.StartColor,
2127             data->BrushData.lineargradient.EndColor, data->BrushData.lineargradient.WrapMode, &gradient);
2128         if (status == Ok)
2129         {
2130             if (transform)
2131                 status = GdipSetLineTransform(gradient, (const GpMatrix *)transform);
2132 
2133             if (status == Ok)
2134             {
2135                 if (brushflags & BrushDataPresetColors)
2136                     status = GdipSetLinePresetBlend(gradient, (ARGB *)(record_data + offset +
2137                         position_count * sizeof(REAL)), (REAL *)(record_data + offset), position_count);
2138                 else if (brushflags & BrushDataBlendFactorsH)
2139                     status = GdipSetLineBlend(gradient, (REAL *)(record_data + offset + position_count * sizeof(REAL)),
2140                         (REAL *)(record_data + offset), position_count);
2141 
2142                 if (brushflags & BrushDataIsGammaCorrected)
2143                     FIXME("BrushDataIsGammaCorrected is not handled.\n");
2144             }
2145         }
2146 
2147         if (status == Ok)
2148             *brush = (GpBrush *)gradient;
2149         else
2150             GdipDeleteBrush((GpBrush *)gradient);
2151 
2152         break;
2153     }
2154     default:
2155         FIXME("brush type %u is not supported.\n", data->Type);
2156         return NotImplemented;
2157     }
2158 
2159     return status;
2160 }
2161 
metafile_get_pen_brush_data_offset(EmfPlusPen * data,UINT data_size,DWORD * ret)2162 static GpStatus metafile_get_pen_brush_data_offset(EmfPlusPen *data, UINT data_size, DWORD *ret)
2163 {
2164     EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2165     DWORD offset = FIELD_OFFSET(EmfPlusPen, data);
2166 
2167     if (data_size <= offset)
2168         return InvalidParameter;
2169 
2170     offset += FIELD_OFFSET(EmfPlusPenData, OptionalData);
2171     if (data_size <= offset)
2172         return InvalidParameter;
2173 
2174     if (pendata->PenDataFlags & PenDataTransform)
2175         offset += sizeof(EmfPlusTransformMatrix);
2176 
2177     if (pendata->PenDataFlags & PenDataStartCap)
2178         offset += sizeof(DWORD);
2179 
2180     if (pendata->PenDataFlags & PenDataEndCap)
2181         offset += sizeof(DWORD);
2182 
2183     if (pendata->PenDataFlags & PenDataJoin)
2184         offset += sizeof(DWORD);
2185 
2186     if (pendata->PenDataFlags & PenDataMiterLimit)
2187         offset += sizeof(REAL);
2188 
2189     if (pendata->PenDataFlags & PenDataLineStyle)
2190         offset += sizeof(DWORD);
2191 
2192     if (pendata->PenDataFlags & PenDataDashedLineCap)
2193         offset += sizeof(DWORD);
2194 
2195     if (pendata->PenDataFlags & PenDataDashedLineOffset)
2196         offset += sizeof(REAL);
2197 
2198     if (pendata->PenDataFlags & PenDataDashedLine)
2199     {
2200         EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)data + offset);
2201 
2202         offset += FIELD_OFFSET(EmfPlusDashedLineData, data);
2203         if (data_size <= offset)
2204             return InvalidParameter;
2205 
2206         offset += dashedline->DashedLineDataSize * sizeof(float);
2207     }
2208 
2209     if (pendata->PenDataFlags & PenDataNonCenter)
2210         offset += sizeof(DWORD);
2211 
2212     if (pendata->PenDataFlags & PenDataCompoundLine)
2213     {
2214         EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)data + offset);
2215 
2216         offset += FIELD_OFFSET(EmfPlusCompoundLineData, data);
2217         if (data_size <= offset)
2218             return InvalidParameter;
2219 
2220         offset += compoundline->CompoundLineDataSize * sizeof(float);
2221     }
2222 
2223     if (pendata->PenDataFlags & PenDataCustomStartCap)
2224     {
2225         EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)data + offset);
2226 
2227         offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data);
2228         if (data_size <= offset)
2229             return InvalidParameter;
2230 
2231         offset += startcap->CustomStartCapSize;
2232     }
2233 
2234     if (pendata->PenDataFlags & PenDataCustomEndCap)
2235     {
2236         EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)data + offset);
2237 
2238         offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data);
2239         if (data_size <= offset)
2240             return InvalidParameter;
2241 
2242         offset += endcap->CustomEndCapSize;
2243     }
2244 
2245     *ret = offset;
2246     return Ok;
2247 }
2248 
METAFILE_PlaybackObject(GpMetafile * metafile,UINT flags,UINT data_size,const BYTE * record_data)2249 static GpStatus METAFILE_PlaybackObject(GpMetafile *metafile, UINT flags, UINT data_size, const BYTE *record_data)
2250 {
2251     BYTE type = (flags >> 8) & 0xff;
2252     BYTE id = flags & 0xff;
2253     void *object = NULL;
2254     GpStatus status;
2255 
2256     if (type > ObjectTypeMax || id >= EmfPlusObjectTableSize)
2257         return InvalidParameter;
2258 
2259     switch (type)
2260     {
2261     case ObjectTypeBrush:
2262         status = metafile_deserialize_brush(record_data, data_size, (GpBrush **)&object);
2263         break;
2264     case ObjectTypePen:
2265     {
2266         EmfPlusPen *data = (EmfPlusPen *)record_data;
2267         EmfPlusPenData *pendata = (EmfPlusPenData *)data->data;
2268         GpBrush *brush;
2269         DWORD offset;
2270         GpPen *pen;
2271 
2272         status = metafile_get_pen_brush_data_offset(data, data_size, &offset);
2273         if (status != Ok)
2274             return status;
2275 
2276         status = metafile_deserialize_brush(record_data + offset, data_size - offset, &brush);
2277         if (status != Ok)
2278             return status;
2279 
2280         status = GdipCreatePen2(brush, pendata->PenWidth, pendata->PenUnit, &pen);
2281         GdipDeleteBrush(brush);
2282         if (status != Ok)
2283             return status;
2284 
2285         offset = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2286 
2287         if (pendata->PenDataFlags & PenDataTransform)
2288         {
2289             FIXME("PenDataTransform is not supported.\n");
2290             offset += sizeof(EmfPlusTransformMatrix);
2291         }
2292 
2293         if (pendata->PenDataFlags & PenDataStartCap)
2294         {
2295             if ((status = GdipSetPenStartCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2296                 goto penfailed;
2297             offset += sizeof(DWORD);
2298         }
2299 
2300         if (pendata->PenDataFlags & PenDataEndCap)
2301         {
2302             if ((status = GdipSetPenEndCap(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2303                 goto penfailed;
2304             offset += sizeof(DWORD);
2305         }
2306 
2307         if (pendata->PenDataFlags & PenDataJoin)
2308         {
2309             if ((status = GdipSetPenLineJoin(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2310                 goto penfailed;
2311             offset += sizeof(DWORD);
2312         }
2313 
2314         if (pendata->PenDataFlags & PenDataMiterLimit)
2315         {
2316             if ((status = GdipSetPenMiterLimit(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2317                 goto penfailed;
2318             offset += sizeof(REAL);
2319         }
2320 
2321         if (pendata->PenDataFlags & PenDataLineStyle)
2322         {
2323             if ((status = GdipSetPenDashStyle(pen, *(DWORD *)((BYTE *)pendata + offset))) != Ok)
2324                 goto penfailed;
2325             offset += sizeof(DWORD);
2326         }
2327 
2328         if (pendata->PenDataFlags & PenDataDashedLineCap)
2329         {
2330             FIXME("PenDataDashedLineCap is not supported.\n");
2331             offset += sizeof(DWORD);
2332         }
2333 
2334         if (pendata->PenDataFlags & PenDataDashedLineOffset)
2335         {
2336             if ((status = GdipSetPenDashOffset(pen, *(REAL *)((BYTE *)pendata + offset))) != Ok)
2337                 goto penfailed;
2338             offset += sizeof(REAL);
2339         }
2340 
2341         if (pendata->PenDataFlags & PenDataDashedLine)
2342         {
2343             EmfPlusDashedLineData *dashedline = (EmfPlusDashedLineData *)((BYTE *)pendata + offset);
2344             FIXME("PenDataDashedLine is not supported.\n");
2345             offset += FIELD_OFFSET(EmfPlusDashedLineData, data) + dashedline->DashedLineDataSize * sizeof(float);
2346         }
2347 
2348         if (pendata->PenDataFlags & PenDataNonCenter)
2349         {
2350             FIXME("PenDataNonCenter is not supported.\n");
2351             offset += sizeof(DWORD);
2352         }
2353 
2354         if (pendata->PenDataFlags & PenDataCompoundLine)
2355         {
2356             EmfPlusCompoundLineData *compoundline = (EmfPlusCompoundLineData *)((BYTE *)pendata + offset);
2357             FIXME("PenDataCompundLine is not supported.\n");
2358             offset += FIELD_OFFSET(EmfPlusCompoundLineData, data) + compoundline->CompoundLineDataSize * sizeof(float);
2359         }
2360 
2361         if (pendata->PenDataFlags & PenDataCustomStartCap)
2362         {
2363             EmfPlusCustomStartCapData *startcap = (EmfPlusCustomStartCapData *)((BYTE *)pendata + offset);
2364             FIXME("PenDataCustomStartCap is not supported.\n");
2365             offset += FIELD_OFFSET(EmfPlusCustomStartCapData, data) + startcap->CustomStartCapSize;
2366         }
2367 
2368         if (pendata->PenDataFlags & PenDataCustomEndCap)
2369         {
2370             EmfPlusCustomEndCapData *endcap = (EmfPlusCustomEndCapData *)((BYTE *)pendata + offset);
2371             FIXME("PenDataCustomEndCap is not supported.\n");
2372             offset += FIELD_OFFSET(EmfPlusCustomEndCapData, data) + endcap->CustomEndCapSize;
2373         }
2374 
2375         object = pen;
2376         break;
2377 
2378     penfailed:
2379         GdipDeletePen(pen);
2380         return status;
2381     }
2382     case ObjectTypePath:
2383         status = metafile_deserialize_path(record_data, data_size, (GpPath **)&object);
2384         break;
2385     case ObjectTypeRegion:
2386         status = metafile_deserialize_region(record_data, data_size, (GpRegion **)&object);
2387         break;
2388     case ObjectTypeImage:
2389         status = metafile_deserialize_image(record_data, data_size, (GpImage **)&object);
2390         break;
2391     case ObjectTypeFont:
2392     {
2393         EmfPlusFont *data = (EmfPlusFont *)record_data;
2394         GpFontFamily *family;
2395         WCHAR *familyname;
2396 
2397         if (data_size <= FIELD_OFFSET(EmfPlusFont, FamilyName))
2398             return InvalidParameter;
2399         data_size -= FIELD_OFFSET(EmfPlusFont, FamilyName);
2400 
2401         if (data_size < data->Length * sizeof(WCHAR))
2402             return InvalidParameter;
2403 
2404         if (!(familyname = GdipAlloc((data->Length + 1) * sizeof(*familyname))))
2405             return OutOfMemory;
2406 
2407         memcpy(familyname, data->FamilyName, data->Length * sizeof(*familyname));
2408         familyname[data->Length] = 0;
2409 
2410         status = GdipCreateFontFamilyFromName(familyname, NULL, &family);
2411         GdipFree(familyname);
2412         if (status != Ok)
2413             return InvalidParameter;
2414 
2415         status = GdipCreateFont(family, data->EmSize, data->FontStyleFlags, data->SizeUnit, (GpFont **)&object);
2416         GdipDeleteFontFamily(family);
2417         break;
2418     }
2419     case ObjectTypeImageAttributes:
2420     {
2421         EmfPlusImageAttributes *data = (EmfPlusImageAttributes *)record_data;
2422         GpImageAttributes *attributes = NULL;
2423 
2424         if (data_size != sizeof(*data))
2425             return InvalidParameter;
2426 
2427         if ((status = GdipCreateImageAttributes(&attributes)) != Ok)
2428             return status;
2429 
2430         status = GdipSetImageAttributesWrapMode(attributes, data->WrapMode, *(DWORD *)&data->ClampColor,
2431                 !!data->ObjectClamp);
2432         if (status == Ok)
2433             object = attributes;
2434         else
2435             GdipDisposeImageAttributes(attributes);
2436         break;
2437     }
2438     default:
2439         FIXME("not implemented for object type %d.\n", type);
2440         return NotImplemented;
2441     }
2442 
2443     if (status == Ok)
2444         metafile_set_object_table_entry(metafile, id, type, object);
2445 
2446     return status;
2447 }
2448 
metafile_set_clip_region(GpMetafile * metafile,GpRegion * region,CombineMode mode)2449 static GpStatus metafile_set_clip_region(GpMetafile *metafile, GpRegion *region, CombineMode mode)
2450 {
2451     GpMatrix world_to_device;
2452 
2453     get_graphics_transform(metafile->playback_graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
2454 
2455     GdipTransformRegion(region, &world_to_device);
2456     GdipCombineRegionRegion(metafile->clip, region, mode);
2457 
2458     return METAFILE_PlaybackUpdateClip(metafile);
2459 }
2460 
GdipPlayMetafileRecord(GDIPCONST GpMetafile * metafile,EmfPlusRecordType recordType,UINT flags,UINT dataSize,GDIPCONST BYTE * data)2461 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
2462     EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
2463 {
2464     GpStatus stat;
2465     GpMetafile *real_metafile = (GpMetafile*)metafile;
2466 
2467     TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
2468 
2469     if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
2470         return InvalidParameter;
2471 
2472     if (recordType >= 1 && recordType <= 0x7a)
2473     {
2474         /* regular EMF record */
2475         if (metafile->playback_dc)
2476         {
2477             switch (recordType)
2478             {
2479             case EMR_SETMAPMODE:
2480             case EMR_SAVEDC:
2481             case EMR_RESTOREDC:
2482             case EMR_SETWINDOWORGEX:
2483             case EMR_SETWINDOWEXTEX:
2484             case EMR_SETVIEWPORTORGEX:
2485             case EMR_SETVIEWPORTEXTEX:
2486             case EMR_SCALEVIEWPORTEXTEX:
2487             case EMR_SCALEWINDOWEXTEX:
2488             case EMR_MODIFYWORLDTRANSFORM:
2489                 FIXME("not implemented for record type %x\n", recordType);
2490                 break;
2491             case EMR_SETWORLDTRANSFORM:
2492             {
2493                 const XFORM* xform = (void*)data;
2494                 real_metafile->gdiworldtransform = *xform;
2495                 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
2496                 break;
2497             }
2498             case EMR_EXTSELECTCLIPRGN:
2499             {
2500                 DWORD rgndatasize = *(DWORD*)data;
2501                 DWORD mode = *(DWORD*)(data + 4);
2502                 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
2503                 HRGN hrgn = NULL;
2504 
2505                 if (dataSize > 8)
2506                 {
2507                     XFORM final;
2508 
2509                     METAFILE_GetFinalGdiTransform(metafile, &final);
2510 
2511                     hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
2512                 }
2513 
2514                 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
2515 
2516                 DeleteObject(hrgn);
2517 
2518                 return Ok;
2519             }
2520             default:
2521             {
2522                 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
2523 
2524                 if (record)
2525                 {
2526                     record->iType = recordType;
2527                     record->nSize = dataSize + 8;
2528                     memcpy(record->dParm, data, dataSize);
2529 
2530                     if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
2531                             record, metafile->handle_count) == 0)
2532                         ERR("PlayEnhMetaFileRecord failed\n");
2533 
2534                     heap_free(record);
2535                 }
2536                 else
2537                     return OutOfMemory;
2538 
2539                 break;
2540             }
2541             }
2542         }
2543     }
2544     else
2545     {
2546         EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
2547 
2548         METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
2549 
2550         switch(recordType)
2551         {
2552         case EmfPlusRecordTypeHeader:
2553         case EmfPlusRecordTypeEndOfFile:
2554             break;
2555         case EmfPlusRecordTypeGetDC:
2556             METAFILE_PlaybackGetDC((GpMetafile*)metafile);
2557             break;
2558         case EmfPlusRecordTypeClear:
2559         {
2560             EmfPlusClear *record = (EmfPlusClear*)header;
2561 
2562             if (dataSize != sizeof(record->Color))
2563                 return InvalidParameter;
2564 
2565             return GdipGraphicsClear(metafile->playback_graphics, record->Color);
2566         }
2567         case EmfPlusRecordTypeFillRects:
2568         {
2569             EmfPlusFillRects *record = (EmfPlusFillRects*)header;
2570             GpBrush *brush, *temp_brush=NULL;
2571             GpRectF *rects, *temp_rects=NULL;
2572 
2573             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
2574                 return InvalidParameter;
2575 
2576             if (flags & 0x4000)
2577             {
2578                 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
2579                     return InvalidParameter;
2580             }
2581             else
2582             {
2583                 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
2584                     return InvalidParameter;
2585             }
2586 
2587             if (flags & 0x8000)
2588             {
2589                 stat = GdipCreateSolidFill(record->BrushID, (GpSolidFill **)&temp_brush);
2590                 brush = temp_brush;
2591             }
2592             else
2593             {
2594                 if (record->BrushID >= EmfPlusObjectTableSize ||
2595                         real_metafile->objtable[record->BrushID].type != ObjectTypeBrush)
2596                     return InvalidParameter;
2597 
2598                 brush = real_metafile->objtable[record->BrushID].u.brush;
2599                 stat = Ok;
2600             }
2601 
2602             if (stat == Ok)
2603             {
2604                 if (flags & 0x4000)
2605                 {
2606                     EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
2607                     int i;
2608 
2609                     rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
2610                     if (rects)
2611                     {
2612                         for (i=0; i<record->Count; i++)
2613                         {
2614                             rects[i].X = int_rects[i].X;
2615                             rects[i].Y = int_rects[i].Y;
2616                             rects[i].Width = int_rects[i].Width;
2617                             rects[i].Height = int_rects[i].Height;
2618                         }
2619                     }
2620                     else
2621                         stat = OutOfMemory;
2622                 }
2623                 else
2624                     rects = (GpRectF*)(record+1);
2625             }
2626 
2627             if (stat == Ok)
2628             {
2629                 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
2630             }
2631 
2632             GdipDeleteBrush(temp_brush);
2633             heap_free(temp_rects);
2634 
2635             return stat;
2636         }
2637         case EmfPlusRecordTypeSetClipRect:
2638         {
2639             EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
2640             CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
2641             GpRegion *region;
2642 
2643             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
2644                 return InvalidParameter;
2645 
2646             stat = GdipCreateRegionRect(&record->ClipRect, &region);
2647 
2648             if (stat == Ok)
2649             {
2650                 stat = metafile_set_clip_region(real_metafile, region, mode);
2651                 GdipDeleteRegion(region);
2652             }
2653 
2654             return stat;
2655         }
2656         case EmfPlusRecordTypeSetClipRegion:
2657         {
2658             CombineMode mode = (flags >> 8) & 0xf;
2659             BYTE regionid = flags & 0xff;
2660             GpRegion *region;
2661 
2662             if (dataSize != 0)
2663                 return InvalidParameter;
2664 
2665             if (regionid >= EmfPlusObjectTableSize || real_metafile->objtable[regionid].type != ObjectTypeRegion)
2666                 return InvalidParameter;
2667 
2668             stat = GdipCloneRegion(real_metafile->objtable[regionid].u.region, &region);
2669             if (stat == Ok)
2670             {
2671                 stat = metafile_set_clip_region(real_metafile, region, mode);
2672                 GdipDeleteRegion(region);
2673             }
2674 
2675             return stat;
2676         }
2677         case EmfPlusRecordTypeSetClipPath:
2678         {
2679             CombineMode mode = (flags >> 8) & 0xf;
2680             BYTE pathid = flags & 0xff;
2681             GpRegion *region;
2682 
2683             if (dataSize != 0)
2684                 return InvalidParameter;
2685 
2686             if (pathid >= EmfPlusObjectTableSize || real_metafile->objtable[pathid].type != ObjectTypePath)
2687                 return InvalidParameter;
2688 
2689             stat = GdipCreateRegionPath(real_metafile->objtable[pathid].u.path, &region);
2690             if (stat == Ok)
2691             {
2692                 stat = metafile_set_clip_region(real_metafile, region, mode);
2693                 GdipDeleteRegion(region);
2694             }
2695 
2696             return stat;
2697         }
2698         case EmfPlusRecordTypeSetPageTransform:
2699         {
2700             EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
2701             GpUnit unit = (GpUnit)flags;
2702 
2703             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
2704                 return InvalidParameter;
2705 
2706             real_metafile->page_unit = unit;
2707             real_metafile->page_scale = record->PageScale;
2708 
2709             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2710         }
2711         case EmfPlusRecordTypeSetWorldTransform:
2712         {
2713             EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
2714 
2715             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
2716                 return InvalidParameter;
2717 
2718             memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
2719 
2720             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2721         }
2722         case EmfPlusRecordTypeScaleWorldTransform:
2723         {
2724             EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
2725             MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2726 
2727             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
2728                 return InvalidParameter;
2729 
2730             GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
2731 
2732             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2733         }
2734         case EmfPlusRecordTypeMultiplyWorldTransform:
2735         {
2736             EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
2737             MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2738             GpMatrix matrix;
2739 
2740             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
2741                 return InvalidParameter;
2742 
2743             memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
2744 
2745             GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
2746 
2747             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2748         }
2749         case EmfPlusRecordTypeRotateWorldTransform:
2750         {
2751             EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
2752             MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2753 
2754             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
2755                 return InvalidParameter;
2756 
2757             GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
2758 
2759             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2760         }
2761         case EmfPlusRecordTypeTranslateWorldTransform:
2762         {
2763             EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
2764             MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
2765 
2766             if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
2767                 return InvalidParameter;
2768 
2769             GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
2770 
2771             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2772         }
2773         case EmfPlusRecordTypeResetWorldTransform:
2774         {
2775             GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
2776 
2777             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2778         }
2779         case EmfPlusRecordTypeBeginContainer:
2780         {
2781             EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
2782             container* cont;
2783             GpUnit unit;
2784             REAL scale_x, scale_y;
2785             GpRectF scaled_srcrect;
2786             GpMatrix transform;
2787 
2788             cont = heap_alloc_zero(sizeof(*cont));
2789             if (!cont)
2790                 return OutOfMemory;
2791 
2792             stat = GdipCloneRegion(metafile->clip, &cont->clip);
2793             if (stat != Ok)
2794             {
2795                 heap_free(cont);
2796                 return stat;
2797             }
2798 
2799             stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2800 
2801             if (stat != Ok)
2802             {
2803                 GdipDeleteRegion(cont->clip);
2804                 heap_free(cont);
2805                 return stat;
2806             }
2807 
2808             cont->id = record->StackIndex;
2809             cont->type = BEGIN_CONTAINER;
2810             cont->world_transform = *metafile->world_transform;
2811             cont->page_unit = metafile->page_unit;
2812             cont->page_scale = metafile->page_scale;
2813             list_add_head(&real_metafile->containers, &cont->entry);
2814 
2815             unit = record->Header.Flags & 0xff;
2816 
2817             scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
2818             scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
2819 
2820             scaled_srcrect.X = scale_x * record->SrcRect.X;
2821             scaled_srcrect.Y = scale_y * record->SrcRect.Y;
2822             scaled_srcrect.Width = scale_x * record->SrcRect.Width;
2823             scaled_srcrect.Height = scale_y * record->SrcRect.Height;
2824 
2825             transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
2826             transform.matrix[1] = 0.0;
2827             transform.matrix[2] = 0.0;
2828             transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
2829             transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
2830             transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
2831 
2832             GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
2833 
2834             return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
2835         }
2836         case EmfPlusRecordTypeBeginContainerNoParams:
2837         case EmfPlusRecordTypeSave:
2838         {
2839             EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2840             container* cont;
2841 
2842             cont = heap_alloc_zero(sizeof(*cont));
2843             if (!cont)
2844                 return OutOfMemory;
2845 
2846             stat = GdipCloneRegion(metafile->clip, &cont->clip);
2847             if (stat != Ok)
2848             {
2849                 heap_free(cont);
2850                 return stat;
2851             }
2852 
2853             if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2854                 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
2855             else
2856                 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
2857 
2858             if (stat != Ok)
2859             {
2860                 GdipDeleteRegion(cont->clip);
2861                 heap_free(cont);
2862                 return stat;
2863             }
2864 
2865             cont->id = record->StackIndex;
2866             if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
2867                 cont->type = BEGIN_CONTAINER;
2868             else
2869                 cont->type = SAVE_GRAPHICS;
2870             cont->world_transform = *metafile->world_transform;
2871             cont->page_unit = metafile->page_unit;
2872             cont->page_scale = metafile->page_scale;
2873             list_add_head(&real_metafile->containers, &cont->entry);
2874 
2875             break;
2876         }
2877         case EmfPlusRecordTypeEndContainer:
2878         case EmfPlusRecordTypeRestore:
2879         {
2880             EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
2881             container* cont;
2882             enum container_type type;
2883             BOOL found=FALSE;
2884 
2885             if (recordType == EmfPlusRecordTypeEndContainer)
2886                 type = BEGIN_CONTAINER;
2887             else
2888                 type = SAVE_GRAPHICS;
2889 
2890             LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
2891             {
2892                 if (cont->id == record->StackIndex && cont->type == type)
2893                 {
2894                     found = TRUE;
2895                     break;
2896                 }
2897             }
2898 
2899             if (found)
2900             {
2901                 container* cont2;
2902 
2903                 /* pop any newer items on the stack */
2904                 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
2905                 {
2906                     list_remove(&cont2->entry);
2907                     GdipDeleteRegion(cont2->clip);
2908                     heap_free(cont2);
2909                 }
2910 
2911                 if (type == BEGIN_CONTAINER)
2912                     GdipEndContainer(real_metafile->playback_graphics, cont->state);
2913                 else
2914                     GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
2915 
2916                 *real_metafile->world_transform = cont->world_transform;
2917                 real_metafile->page_unit = cont->page_unit;
2918                 real_metafile->page_scale = cont->page_scale;
2919                 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
2920 
2921                 list_remove(&cont->entry);
2922                 GdipDeleteRegion(cont->clip);
2923                 heap_free(cont);
2924             }
2925 
2926             break;
2927         }
2928         case EmfPlusRecordTypeSetPixelOffsetMode:
2929         {
2930             return GdipSetPixelOffsetMode(real_metafile->playback_graphics, flags & 0xff);
2931         }
2932         case EmfPlusRecordTypeSetCompositingQuality:
2933         {
2934             return GdipSetCompositingQuality(real_metafile->playback_graphics, flags & 0xff);
2935         }
2936         case EmfPlusRecordTypeSetInterpolationMode:
2937         {
2938             return GdipSetInterpolationMode(real_metafile->playback_graphics, flags & 0xff);
2939         }
2940         case EmfPlusRecordTypeSetTextRenderingHint:
2941         {
2942             return GdipSetTextRenderingHint(real_metafile->playback_graphics, flags & 0xff);
2943         }
2944         case EmfPlusRecordTypeSetAntiAliasMode:
2945         {
2946             return GdipSetSmoothingMode(real_metafile->playback_graphics, (flags >> 1) & 0xff);
2947         }
2948         case EmfPlusRecordTypeSetCompositingMode:
2949         {
2950             return GdipSetCompositingMode(real_metafile->playback_graphics, flags & 0xff);
2951         }
2952         case EmfPlusRecordTypeObject:
2953         {
2954             return METAFILE_PlaybackObject(real_metafile, flags, dataSize, data);
2955         }
2956         case EmfPlusRecordTypeDrawImage:
2957         {
2958             EmfPlusDrawImage *draw = (EmfPlusDrawImage *)header;
2959             BYTE image = flags & 0xff;
2960             GpPointF points[3];
2961 
2962             if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
2963                 return InvalidParameter;
2964 
2965             if (dataSize != FIELD_OFFSET(EmfPlusDrawImage, RectData) - sizeof(EmfPlusRecordHeader) +
2966                     (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
2967                 return InvalidParameter;
2968 
2969             if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
2970                     real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
2971                 return InvalidParameter;
2972 
2973             if (flags & 0x4000) /* C */
2974             {
2975                 points[0].X = draw->RectData.rect.X;
2976                 points[0].Y = draw->RectData.rect.Y;
2977                 points[1].X = points[0].X + draw->RectData.rect.Width;
2978                 points[1].Y = points[0].Y;
2979                 points[2].X = points[1].X;
2980                 points[2].Y = points[1].Y + draw->RectData.rect.Height;
2981             }
2982             else
2983             {
2984                 points[0].X = draw->RectData.rectF.X;
2985                 points[0].Y = draw->RectData.rectF.Y;
2986                 points[1].X = points[0].X + draw->RectData.rectF.Width;
2987                 points[1].Y = points[0].Y;
2988                 points[2].X = points[1].X;
2989                 points[2].Y = points[1].Y + draw->RectData.rectF.Height;
2990             }
2991 
2992             return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
2993                 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
2994                 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
2995         }
2996         case EmfPlusRecordTypeDrawImagePoints:
2997         {
2998             EmfPlusDrawImagePoints *draw = (EmfPlusDrawImagePoints *)header;
2999             static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusDrawImagePoints, PointData) -
3000                 FIELD_OFFSET(EmfPlusDrawImagePoints, ImageAttributesID);
3001             BYTE image = flags & 0xff;
3002             GpPointF points[3];
3003             unsigned int i;
3004             UINT size;
3005 
3006             if (image >= EmfPlusObjectTableSize || real_metafile->objtable[image].type != ObjectTypeImage)
3007                 return InvalidParameter;
3008 
3009             if (dataSize <= fixed_part_size)
3010                 return InvalidParameter;
3011             dataSize -= fixed_part_size;
3012 
3013             if (draw->ImageAttributesID >= EmfPlusObjectTableSize ||
3014                     real_metafile->objtable[draw->ImageAttributesID].type != ObjectTypeImageAttributes)
3015                 return InvalidParameter;
3016 
3017             if (draw->count != 3)
3018                 return InvalidParameter;
3019 
3020             if ((flags >> 13) & 1) /* E */
3021                 FIXME("image effects are not supported.\n");
3022 
3023             if ((flags >> 11) & 1) /* P */
3024                 size = sizeof(EmfPlusPointR7) * draw->count;
3025             else if ((flags >> 14) & 1) /* C */
3026                 size = sizeof(EmfPlusPoint) * draw->count;
3027             else
3028                 size = sizeof(EmfPlusPointF) * draw->count;
3029 
3030             if (dataSize != size)
3031                 return InvalidParameter;
3032 
3033             if ((flags >> 11) & 1) /* P */
3034             {
3035                 points[0].X = draw->PointData.pointsR[0].X;
3036                 points[0].Y = draw->PointData.pointsR[0].Y;
3037                 for (i = 1; i < 3; i++)
3038                 {
3039                     points[i].X = points[i-1].X + draw->PointData.pointsR[i].X;
3040                     points[i].Y = points[i-1].Y + draw->PointData.pointsR[i].Y;
3041                 }
3042             }
3043             else if ((flags >> 14) & 1) /* C */
3044             {
3045                 for (i = 0; i < 3; i++)
3046                 {
3047                     points[i].X = draw->PointData.points[i].X;
3048                     points[i].Y = draw->PointData.points[i].Y;
3049                 }
3050             }
3051             else
3052                 memcpy(points, draw->PointData.pointsF, sizeof(points));
3053 
3054             return GdipDrawImagePointsRect(real_metafile->playback_graphics, real_metafile->objtable[image].u.image,
3055                 points, 3, draw->SrcRect.X, draw->SrcRect.Y, draw->SrcRect.Width, draw->SrcRect.Height, draw->SrcUnit,
3056                 real_metafile->objtable[draw->ImageAttributesID].u.image_attributes, NULL, NULL);
3057         }
3058         case EmfPlusRecordTypeFillPath:
3059         {
3060             EmfPlusFillPath *fill = (EmfPlusFillPath *)header;
3061             GpSolidFill *solidfill = NULL;
3062             BYTE path = flags & 0xff;
3063             GpBrush *brush;
3064 
3065             if (path >= EmfPlusObjectTableSize || real_metafile->objtable[path].type != ObjectTypePath)
3066                 return InvalidParameter;
3067 
3068             if (dataSize != sizeof(fill->data.BrushId))
3069                 return InvalidParameter;
3070 
3071             if (flags & 0x8000)
3072             {
3073                 stat = GdipCreateSolidFill(fill->data.Color, (GpSolidFill **)&solidfill);
3074                 if (stat != Ok)
3075                     return stat;
3076                 brush = (GpBrush *)solidfill;
3077             }
3078             else
3079             {
3080                 if (fill->data.BrushId >= EmfPlusObjectTableSize ||
3081                         real_metafile->objtable[fill->data.BrushId].type != ObjectTypeBrush)
3082                     return InvalidParameter;
3083 
3084                 brush = real_metafile->objtable[fill->data.BrushId].u.brush;
3085             }
3086 
3087             stat = GdipFillPath(real_metafile->playback_graphics, brush, real_metafile->objtable[path].u.path);
3088             GdipDeleteBrush((GpBrush *)solidfill);
3089             return stat;
3090         }
3091         case EmfPlusRecordTypeFillClosedCurve:
3092         {
3093             static const UINT fixed_part_size = FIELD_OFFSET(EmfPlusFillClosedCurve, PointData) -
3094                 sizeof(EmfPlusRecordHeader);
3095             EmfPlusFillClosedCurve *fill = (EmfPlusFillClosedCurve *)header;
3096             GpSolidFill *solidfill = NULL;
3097             GpFillMode mode;
3098             GpBrush *brush;
3099             UINT size, i;
3100 
3101             if (dataSize <= fixed_part_size)
3102                 return InvalidParameter;
3103 
3104             if (fill->Count == 0)
3105                 return InvalidParameter;
3106 
3107             if (flags & 0x800) /* P */
3108                 size = (fixed_part_size + sizeof(EmfPlusPointR7) * fill->Count + 3) & ~3;
3109             else if (flags & 0x4000) /* C */
3110                 size = fixed_part_size + sizeof(EmfPlusPoint) * fill->Count;
3111             else
3112                 size = fixed_part_size + sizeof(EmfPlusPointF) * fill->Count;
3113 
3114             if (dataSize != size)
3115                 return InvalidParameter;
3116 
3117             mode = flags & 0x200 ? FillModeWinding : FillModeAlternate; /* W */
3118 
3119             if (flags & 0x8000) /* S */
3120             {
3121                 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3122                 if (stat != Ok)
3123                     return stat;
3124                 brush = (GpBrush *)solidfill;
3125             }
3126             else
3127             {
3128                 if (fill->BrushId >= EmfPlusObjectTableSize ||
3129                         real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3130                     return InvalidParameter;
3131 
3132                 brush = real_metafile->objtable[fill->BrushId].u.brush;
3133             }
3134 
3135             if (flags & (0x800 | 0x4000))
3136             {
3137                 GpPointF *points = GdipAlloc(fill->Count * sizeof(*points));
3138                 if (points)
3139                 {
3140                     if (flags & 0x800) /* P */
3141                     {
3142                         for (i = 1; i < fill->Count; i++)
3143                         {
3144                             points[i].X = points[i - 1].X + fill->PointData.pointsR[i].X;
3145                             points[i].Y = points[i - 1].Y + fill->PointData.pointsR[i].Y;
3146                         }
3147                     }
3148                     else
3149                     {
3150                         for (i = 0; i < fill->Count; i++)
3151                         {
3152                             points[i].X = fill->PointData.points[i].X;
3153                             points[i].Y = fill->PointData.points[i].Y;
3154                         }
3155                     }
3156 
3157                     stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3158                         points, fill->Count, fill->Tension, mode);
3159                     GdipFree(points);
3160                 }
3161                 else
3162                     stat = OutOfMemory;
3163             }
3164             else
3165                 stat = GdipFillClosedCurve2(real_metafile->playback_graphics, brush,
3166                     (const GpPointF *)fill->PointData.pointsF, fill->Count, fill->Tension, mode);
3167 
3168             GdipDeleteBrush((GpBrush *)solidfill);
3169             return stat;
3170         }
3171         case EmfPlusRecordTypeFillEllipse:
3172         {
3173             EmfPlusFillEllipse *fill = (EmfPlusFillEllipse *)header;
3174             GpSolidFill *solidfill = NULL;
3175             GpBrush *brush;
3176 
3177             if (dataSize <= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader))
3178                 return InvalidParameter;
3179             dataSize -= FIELD_OFFSET(EmfPlusFillEllipse, RectData) - sizeof(EmfPlusRecordHeader);
3180 
3181             if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3182                 return InvalidParameter;
3183 
3184             if (flags & 0x8000)
3185             {
3186                 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3187                 if (stat != Ok)
3188                     return stat;
3189                 brush = (GpBrush *)solidfill;
3190             }
3191             else
3192             {
3193                 if (fill->BrushId >= EmfPlusObjectTableSize ||
3194                         real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3195                     return InvalidParameter;
3196 
3197                 brush = real_metafile->objtable[fill->BrushId].u.brush;
3198             }
3199 
3200             if (flags & 0x4000)
3201                 stat = GdipFillEllipseI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3202                     fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height);
3203             else
3204                 stat = GdipFillEllipse(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3205                     fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height);
3206 
3207             GdipDeleteBrush((GpBrush *)solidfill);
3208             return stat;
3209         }
3210         case EmfPlusRecordTypeFillPie:
3211         {
3212             EmfPlusFillPie *fill = (EmfPlusFillPie *)header;
3213             GpSolidFill *solidfill = NULL;
3214             GpBrush *brush;
3215 
3216             if (dataSize <= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader))
3217                 return InvalidParameter;
3218             dataSize -= FIELD_OFFSET(EmfPlusFillPie, RectData) - sizeof(EmfPlusRecordHeader);
3219 
3220             if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3221                 return InvalidParameter;
3222 
3223             if (flags & 0x8000) /* S */
3224             {
3225                 stat = GdipCreateSolidFill(fill->BrushId, (GpSolidFill **)&solidfill);
3226                 if (stat != Ok)
3227                     return stat;
3228                 brush = (GpBrush *)solidfill;
3229             }
3230             else
3231             {
3232                 if (fill->BrushId >= EmfPlusObjectTableSize ||
3233                         real_metafile->objtable[fill->BrushId].type != ObjectTypeBrush)
3234                     return InvalidParameter;
3235 
3236                 brush = real_metafile->objtable[fill->BrushId].u.brush;
3237             }
3238 
3239             if (flags & 0x4000) /* C */
3240                 stat = GdipFillPieI(real_metafile->playback_graphics, brush, fill->RectData.rect.X,
3241                     fill->RectData.rect.Y, fill->RectData.rect.Width, fill->RectData.rect.Height,
3242                     fill->StartAngle, fill->SweepAngle);
3243             else
3244                 stat = GdipFillPie(real_metafile->playback_graphics, brush, fill->RectData.rectF.X,
3245                     fill->RectData.rectF.Y, fill->RectData.rectF.Width, fill->RectData.rectF.Height,
3246                     fill->StartAngle, fill->SweepAngle);
3247 
3248             GdipDeleteBrush((GpBrush *)solidfill);
3249             return stat;
3250         }
3251         case EmfPlusRecordTypeDrawPath:
3252         {
3253             EmfPlusDrawPath *draw = (EmfPlusDrawPath *)header;
3254             BYTE path = flags & 0xff;
3255 
3256             if (dataSize != sizeof(draw->PenId))
3257                 return InvalidParameter;
3258 
3259             if (path >= EmfPlusObjectTableSize || draw->PenId >= EmfPlusObjectTableSize)
3260                 return InvalidParameter;
3261 
3262             if (real_metafile->objtable[path].type != ObjectTypePath ||
3263                     real_metafile->objtable[draw->PenId].type != ObjectTypePen)
3264                 return InvalidParameter;
3265 
3266             return GdipDrawPath(real_metafile->playback_graphics, real_metafile->objtable[draw->PenId].u.pen,
3267                 real_metafile->objtable[path].u.path);
3268         }
3269         case EmfPlusRecordTypeDrawArc:
3270         {
3271             EmfPlusDrawArc *draw = (EmfPlusDrawArc *)header;
3272             BYTE pen = flags & 0xff;
3273 
3274             if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3275                 return InvalidParameter;
3276 
3277             if (dataSize != FIELD_OFFSET(EmfPlusDrawArc, RectData) - sizeof(EmfPlusRecordHeader) +
3278                     (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3279                 return InvalidParameter;
3280 
3281             if (flags & 0x4000) /* C */
3282                 return GdipDrawArcI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3283                     draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3284                     draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3285             else
3286                 return GdipDrawArc(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3287                     draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3288                     draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3289         }
3290         case EmfPlusRecordTypeDrawEllipse:
3291         {
3292             EmfPlusDrawEllipse *draw = (EmfPlusDrawEllipse *)header;
3293             BYTE pen = flags & 0xff;
3294 
3295             if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3296                 return InvalidParameter;
3297 
3298             if (dataSize != (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3299                 return InvalidParameter;
3300 
3301             if (flags & 0x4000) /* C */
3302                 return GdipDrawEllipseI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3303                     draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3304                     draw->RectData.rect.Height);
3305             else
3306                 return GdipDrawEllipse(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3307                     draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3308                     draw->RectData.rectF.Height);
3309         }
3310         case EmfPlusRecordTypeDrawPie:
3311         {
3312             EmfPlusDrawPie *draw = (EmfPlusDrawPie *)header;
3313             BYTE pen = flags & 0xff;
3314 
3315             if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3316                 return InvalidParameter;
3317 
3318             if (dataSize != FIELD_OFFSET(EmfPlusDrawPie, RectData) - sizeof(EmfPlusRecordHeader) +
3319                     (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3320                 return InvalidParameter;
3321 
3322             if (flags & 0x4000) /* C */
3323                 return GdipDrawPieI(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3324                     draw->RectData.rect.X, draw->RectData.rect.Y, draw->RectData.rect.Width,
3325                     draw->RectData.rect.Height, draw->StartAngle, draw->SweepAngle);
3326             else
3327                 return GdipDrawPie(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3328                     draw->RectData.rectF.X, draw->RectData.rectF.Y, draw->RectData.rectF.Width,
3329                     draw->RectData.rectF.Height, draw->StartAngle, draw->SweepAngle);
3330         }
3331         case EmfPlusRecordTypeDrawRects:
3332         {
3333             EmfPlusDrawRects *draw = (EmfPlusDrawRects *)header;
3334             BYTE pen = flags & 0xff;
3335             GpRectF *rects = NULL;
3336 
3337             if (pen >= EmfPlusObjectTableSize || real_metafile->objtable[pen].type != ObjectTypePen)
3338                 return InvalidParameter;
3339 
3340             if (dataSize <= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader))
3341                 return InvalidParameter;
3342             dataSize -= FIELD_OFFSET(EmfPlusDrawRects, RectData) - sizeof(EmfPlusRecordHeader);
3343 
3344             if (dataSize != draw->Count * (flags & 0x4000 ? sizeof(EmfPlusRect) : sizeof(EmfPlusRectF)))
3345                 return InvalidParameter;
3346 
3347             if (flags & 0x4000)
3348             {
3349                 DWORD i;
3350 
3351                 rects = GdipAlloc(draw->Count * sizeof(*rects));
3352                 if (!rects)
3353                     return OutOfMemory;
3354 
3355                 for (i = 0; i < draw->Count; i++)
3356                 {
3357                     rects[i].X = draw->RectData.rect[i].X;
3358                     rects[i].Y = draw->RectData.rect[i].Y;
3359                     rects[i].Width = draw->RectData.rect[i].Width;
3360                     rects[i].Height = draw->RectData.rect[i].Height;
3361                 }
3362             }
3363 
3364             stat = GdipDrawRectangles(real_metafile->playback_graphics, real_metafile->objtable[pen].u.pen,
3365                     rects ? rects : (GpRectF *)draw->RectData.rectF, draw->Count);
3366             GdipFree(rects);
3367             return stat;
3368         }
3369         default:
3370             FIXME("Not implemented for record type %x\n", recordType);
3371             return NotImplemented;
3372         }
3373     }
3374 
3375     return Ok;
3376 }
3377 
3378 struct enum_metafile_data
3379 {
3380     EnumerateMetafileProc callback;
3381     void *callback_data;
3382     GpMetafile *metafile;
3383 };
3384 
enum_metafile_proc(HDC hDC,HANDLETABLE * lpHTable,const ENHMETARECORD * lpEMFR,int nObj,LPARAM lpData)3385 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3386     int nObj, LPARAM lpData)
3387 {
3388     BOOL ret;
3389     struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
3390     const BYTE* pStr;
3391 
3392     data->metafile->handle_table = lpHTable;
3393     data->metafile->handle_count = nObj;
3394 
3395     /* First check for an EMF+ record. */
3396     if (lpEMFR->iType == EMR_GDICOMMENT)
3397     {
3398         const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3399 
3400         if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3401         {
3402             int offset = 4;
3403 
3404             while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
3405             {
3406                 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
3407 
3408                 if (record->DataSize)
3409                     pStr = (const BYTE*)(record+1);
3410                 else
3411                     pStr = NULL;
3412 
3413                 ret = data->callback(record->Type, record->Flags, record->DataSize,
3414                     pStr, data->callback_data);
3415 
3416                 if (!ret)
3417                     return 0;
3418 
3419                 offset += record->Size;
3420             }
3421 
3422             return 1;
3423         }
3424     }
3425 
3426     if (lpEMFR->nSize != 8)
3427         pStr = (const BYTE*)lpEMFR->dParm;
3428     else
3429         pStr = NULL;
3430 
3431     return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
3432         pStr, data->callback_data);
3433 }
3434 
GdipEnumerateMetafileSrcRectDestPoints(GpGraphics * graphics,GDIPCONST GpMetafile * metafile,GDIPCONST GpPointF * destPoints,INT count,GDIPCONST GpRectF * srcRect,Unit srcUnit,EnumerateMetafileProc callback,VOID * callbackData,GDIPCONST GpImageAttributes * imageAttributes)3435 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
3436     GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
3437     GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
3438     VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
3439 {
3440     struct enum_metafile_data data;
3441     GpStatus stat;
3442     GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
3443     GraphicsContainer state;
3444     GpPath *dst_path;
3445 
3446     TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
3447         destPoints, count, srcRect, srcUnit, callback, callbackData,
3448         imageAttributes);
3449 
3450     if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
3451         return InvalidParameter;
3452 
3453     if (!metafile->hemf)
3454         return InvalidParameter;
3455 
3456     if (metafile->playback_graphics)
3457         return ObjectBusy;
3458 
3459     TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
3460         debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
3461         debugstr_pointf(&destPoints[2]));
3462 
3463     data.callback = callback;
3464     data.callback_data = callbackData;
3465     data.metafile = real_metafile;
3466 
3467     real_metafile->playback_graphics = graphics;
3468     real_metafile->playback_dc = NULL;
3469     real_metafile->src_rect = *srcRect;
3470 
3471     memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
3472     stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
3473 
3474     if (stat == Ok)
3475         stat = GdipBeginContainer2(graphics, &state);
3476 
3477     if (stat == Ok)
3478     {
3479         stat = GdipSetPageScale(graphics, 1.0);
3480 
3481         if (stat == Ok)
3482             stat = GdipSetPageUnit(graphics, UnitPixel);
3483 
3484         if (stat == Ok)
3485             stat = GdipResetWorldTransform(graphics);
3486 
3487         if (stat == Ok)
3488             stat = GdipCreateRegion(&real_metafile->base_clip);
3489 
3490         if (stat == Ok)
3491             stat = GdipGetClip(graphics, real_metafile->base_clip);
3492 
3493         if (stat == Ok)
3494             stat = GdipCreateRegion(&real_metafile->clip);
3495 
3496         if (stat == Ok)
3497             stat = GdipCreatePath(FillModeAlternate, &dst_path);
3498 
3499         if (stat == Ok)
3500         {
3501             GpPointF clip_points[4];
3502 
3503             clip_points[0] = real_metafile->playback_points[0];
3504             clip_points[1] = real_metafile->playback_points[1];
3505             clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
3506                 - real_metafile->playback_points[0].X;
3507             clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
3508                 - real_metafile->playback_points[0].Y;
3509             clip_points[3] = real_metafile->playback_points[2];
3510 
3511             stat = GdipAddPathPolygon(dst_path, clip_points, 4);
3512 
3513             if (stat == Ok)
3514                 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
3515 
3516             GdipDeletePath(dst_path);
3517         }
3518 
3519         if (stat == Ok)
3520             stat = GdipCreateMatrix(&real_metafile->world_transform);
3521 
3522         if (stat == Ok)
3523         {
3524             real_metafile->page_unit = UnitDisplay;
3525             real_metafile->page_scale = 1.0;
3526             stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
3527         }
3528 
3529         if (stat == Ok)
3530         {
3531             stat = METAFILE_PlaybackUpdateClip(real_metafile);
3532         }
3533 
3534         if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
3535             metafile->metafile_type == MetafileTypeWmfPlaceable ||
3536             metafile->metafile_type == MetafileTypeWmf))
3537             stat = METAFILE_PlaybackGetDC(real_metafile);
3538 
3539         if (stat == Ok)
3540             EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
3541 
3542         METAFILE_PlaybackReleaseDC(real_metafile);
3543 
3544         GdipDeleteMatrix(real_metafile->world_transform);
3545         real_metafile->world_transform = NULL;
3546 
3547         GdipDeleteRegion(real_metafile->base_clip);
3548         real_metafile->base_clip = NULL;
3549 
3550         GdipDeleteRegion(real_metafile->clip);
3551         real_metafile->clip = NULL;
3552 
3553         while (list_head(&real_metafile->containers))
3554         {
3555             container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
3556             list_remove(&cont->entry);
3557             GdipDeleteRegion(cont->clip);
3558             heap_free(cont);
3559         }
3560 
3561         GdipEndContainer(graphics, state);
3562     }
3563 
3564     real_metafile->playback_graphics = NULL;
3565 
3566     return stat;
3567 }
3568 
GdipEnumerateMetafileDestRect(GpGraphics * graphics,GDIPCONST GpMetafile * metafile,GDIPCONST GpRectF * dest,EnumerateMetafileProc callback,VOID * cb_data,GDIPCONST GpImageAttributes * attrs)3569 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
3570     GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
3571     EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3572 {
3573     GpPointF points[3];
3574 
3575     if (!graphics || !metafile || !dest) return InvalidParameter;
3576 
3577     points[0].X = points[2].X = dest->X;
3578     points[0].Y = points[1].Y = dest->Y;
3579     points[1].X = dest->X + dest->Width;
3580     points[2].Y = dest->Y + dest->Height;
3581 
3582     return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
3583         &metafile->bounds, metafile->unit, callback, cb_data, attrs);
3584 }
3585 
GdipEnumerateMetafileDestRectI(GpGraphics * graphics,GDIPCONST GpMetafile * metafile,GDIPCONST GpRect * dest,EnumerateMetafileProc callback,VOID * cb_data,GDIPCONST GpImageAttributes * attrs)3586 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
3587     GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
3588     EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3589 {
3590     GpRectF destf;
3591 
3592     if (!graphics || !metafile || !dest) return InvalidParameter;
3593 
3594     destf.X = dest->X;
3595     destf.Y = dest->Y;
3596     destf.Width = dest->Width;
3597     destf.Height = dest->Height;
3598 
3599     return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3600 }
3601 
GdipEnumerateMetafileDestPoint(GpGraphics * graphics,GDIPCONST GpMetafile * metafile,GDIPCONST GpPointF * dest,EnumerateMetafileProc callback,VOID * cb_data,GDIPCONST GpImageAttributes * attrs)3602 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
3603     GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
3604     EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3605 {
3606     GpRectF destf;
3607 
3608     if (!graphics || !metafile || !dest) return InvalidParameter;
3609 
3610     destf.X = dest->X;
3611     destf.Y = dest->Y;
3612     destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
3613     destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
3614 
3615     return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
3616 }
3617 
GdipEnumerateMetafileDestPointI(GpGraphics * graphics,GDIPCONST GpMetafile * metafile,GDIPCONST GpPoint * dest,EnumerateMetafileProc callback,VOID * cb_data,GDIPCONST GpImageAttributes * attrs)3618 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
3619     GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
3620     EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
3621 {
3622     GpPointF ptf;
3623 
3624     if (!graphics || !metafile || !dest) return InvalidParameter;
3625 
3626     ptf.X = dest->X;
3627     ptf.Y = dest->Y;
3628 
3629     return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
3630 }
3631 
GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,MetafileHeader * header)3632 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
3633     MetafileHeader * header)
3634 {
3635     GpStatus status;
3636 
3637     TRACE("(%p, %p)\n", metafile, header);
3638 
3639     if(!metafile || !header)
3640         return InvalidParameter;
3641 
3642     if (metafile->hemf)
3643     {
3644         status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
3645         if (status != Ok) return status;
3646     }
3647     else
3648     {
3649         memset(header, 0, sizeof(*header));
3650         header->Version = VERSION_MAGIC2;
3651     }
3652 
3653     header->Type = metafile->metafile_type;
3654     header->DpiX = metafile->image.xres;
3655     header->DpiY = metafile->image.yres;
3656     header->Width = gdip_round(metafile->bounds.Width);
3657     header->Height = gdip_round(metafile->bounds.Height);
3658 
3659     return Ok;
3660 }
3661 
get_emfplus_header_proc(HDC hDC,HANDLETABLE * lpHTable,const ENHMETARECORD * lpEMFR,int nObj,LPARAM lpData)3662 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
3663     int nObj, LPARAM lpData)
3664 {
3665     EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
3666 
3667     if (lpEMFR->iType == EMR_GDICOMMENT)
3668     {
3669         const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
3670 
3671         if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
3672         {
3673             const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
3674 
3675             if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
3676                 header->Type == EmfPlusRecordTypeHeader)
3677             {
3678                 memcpy(dst_header, header, sizeof(*dst_header));
3679             }
3680         }
3681     }
3682     else if (lpEMFR->iType == EMR_HEADER)
3683         return TRUE;
3684 
3685     return FALSE;
3686 }
3687 
GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,MetafileHeader * header)3688 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
3689     MetafileHeader *header)
3690 {
3691     ENHMETAHEADER3 emfheader;
3692     EmfPlusHeader emfplusheader;
3693     MetafileType metafile_type;
3694 
3695     TRACE("(%p,%p)\n", hemf, header);
3696 
3697     if(!hemf || !header)
3698         return InvalidParameter;
3699 
3700     if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
3701         return GenericError;
3702 
3703     emfplusheader.Header.Type = 0;
3704 
3705     EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
3706 
3707     if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
3708     {
3709         if ((emfplusheader.Header.Flags & 1) == 1)
3710             metafile_type = MetafileTypeEmfPlusDual;
3711         else
3712             metafile_type = MetafileTypeEmfPlusOnly;
3713     }
3714     else
3715         metafile_type = MetafileTypeEmf;
3716 
3717     header->Type = metafile_type;
3718     header->Size = emfheader.nBytes;
3719     header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
3720     header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
3721     header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
3722     header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
3723     header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
3724     header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
3725     header->u.EmfHeader = emfheader;
3726 
3727     if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
3728     {
3729         header->Version = emfplusheader.Version;
3730         header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
3731         header->EmfPlusHeaderSize = emfplusheader.Header.Size;
3732         header->LogicalDpiX = emfplusheader.LogicalDpiX;
3733         header->LogicalDpiY = emfplusheader.LogicalDpiY;
3734     }
3735     else
3736     {
3737         header->Version = emfheader.nVersion;
3738         header->EmfPlusFlags = 0;
3739         header->EmfPlusHeaderSize = 0;
3740         header->LogicalDpiX = 0;
3741         header->LogicalDpiY = 0;
3742     }
3743 
3744     return Ok;
3745 }
3746 
GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,GDIPCONST WmfPlaceableFileHeader * placeable,MetafileHeader * header)3747 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
3748     GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
3749 {
3750     GpStatus status;
3751     GpMetafile *metafile;
3752 
3753     TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
3754 
3755     status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
3756     if (status == Ok)
3757     {
3758         status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3759         GdipDisposeImage(&metafile->image);
3760     }
3761     return status;
3762 }
3763 
GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR * filename,MetafileHeader * header)3764 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
3765     MetafileHeader *header)
3766 {
3767     GpStatus status;
3768     GpMetafile *metafile;
3769 
3770     TRACE("(%s,%p)\n", debugstr_w(filename), header);
3771 
3772     if (!filename || !header)
3773         return InvalidParameter;
3774 
3775     status = GdipCreateMetafileFromFile(filename, &metafile);
3776     if (status == Ok)
3777     {
3778         status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3779         GdipDisposeImage(&metafile->image);
3780     }
3781     return status;
3782 }
3783 
GdipGetMetafileHeaderFromStream(IStream * stream,MetafileHeader * header)3784 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
3785     MetafileHeader *header)
3786 {
3787     GpStatus status;
3788     GpMetafile *metafile;
3789 
3790     TRACE("(%p,%p)\n", stream, header);
3791 
3792     if (!stream || !header)
3793         return InvalidParameter;
3794 
3795     status = GdipCreateMetafileFromStream(stream, &metafile);
3796     if (status == Ok)
3797     {
3798         status = GdipGetMetafileHeaderFromMetafile(metafile, header);
3799         GdipDisposeImage(&metafile->image);
3800     }
3801     return status;
3802 }
3803 
GdipCreateMetafileFromEmf(HENHMETAFILE hemf,BOOL delete,GpMetafile ** metafile)3804 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
3805     GpMetafile **metafile)
3806 {
3807     GpStatus stat;
3808     MetafileHeader header;
3809 
3810     TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
3811 
3812     if(!hemf || !metafile)
3813         return InvalidParameter;
3814 
3815     stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
3816     if (stat != Ok)
3817         return stat;
3818 
3819     *metafile = heap_alloc_zero(sizeof(GpMetafile));
3820     if (!*metafile)
3821         return OutOfMemory;
3822 
3823     (*metafile)->image.type = ImageTypeMetafile;
3824     (*metafile)->image.format = ImageFormatEMF;
3825     (*metafile)->image.frame_count = 1;
3826     (*metafile)->image.xres = header.DpiX;
3827     (*metafile)->image.yres = header.DpiY;
3828     (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
3829     (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
3830     (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
3831                                 / 2540.0 * header.DpiX;
3832     (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
3833                                  / 2540.0 * header.DpiY;
3834     (*metafile)->unit = UnitPixel;
3835     (*metafile)->metafile_type = header.Type;
3836     (*metafile)->hemf = hemf;
3837     (*metafile)->preserve_hemf = !delete;
3838     list_init(&(*metafile)->containers);
3839 
3840     TRACE("<-- %p\n", *metafile);
3841 
3842     return Ok;
3843 }
3844 
GdipCreateMetafileFromWmf(HMETAFILE hwmf,BOOL delete,GDIPCONST WmfPlaceableFileHeader * placeable,GpMetafile ** metafile)3845 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
3846     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3847 {
3848     UINT read;
3849     BYTE *copy;
3850     HENHMETAFILE hemf;
3851     GpStatus retval = Ok;
3852 
3853     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
3854 
3855     if(!hwmf || !metafile)
3856         return InvalidParameter;
3857 
3858     *metafile = NULL;
3859     read = GetMetaFileBitsEx(hwmf, 0, NULL);
3860     if(!read)
3861         return GenericError;
3862     copy = heap_alloc_zero(read);
3863     GetMetaFileBitsEx(hwmf, read, copy);
3864 
3865     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
3866     heap_free(copy);
3867 
3868     /* FIXME: We should store and use hwmf instead of converting to hemf */
3869     retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
3870 
3871     if (retval == Ok)
3872     {
3873         if (placeable)
3874         {
3875             (*metafile)->image.xres = (REAL)placeable->Inch;
3876             (*metafile)->image.yres = (REAL)placeable->Inch;
3877             (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
3878             (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
3879             (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
3880                                                placeable->BoundingBox.Left);
3881             (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
3882                                                 placeable->BoundingBox.Top);
3883             (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
3884         }
3885         else
3886             (*metafile)->metafile_type = MetafileTypeWmf;
3887         (*metafile)->image.format = ImageFormatWMF;
3888 
3889         if (delete) DeleteMetaFile(hwmf);
3890     }
3891     else
3892         DeleteEnhMetaFile(hemf);
3893     return retval;
3894 }
3895 
GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR * file,GDIPCONST WmfPlaceableFileHeader * placeable,GpMetafile ** metafile)3896 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
3897     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
3898 {
3899     HMETAFILE hmf;
3900     HENHMETAFILE emf;
3901 
3902     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
3903 
3904     hmf = GetMetaFileW(file);
3905     if(hmf)
3906         return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
3907 
3908     emf = GetEnhMetaFileW(file);
3909     if(emf)
3910         return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
3911 
3912     return GenericError;
3913 }
3914 
GdipCreateMetafileFromFile(GDIPCONST WCHAR * file,GpMetafile ** metafile)3915 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
3916     GpMetafile **metafile)
3917 {
3918     GpStatus status;
3919     IStream *stream;
3920 
3921     TRACE("(%p, %p)\n", file, metafile);
3922 
3923     if (!file || !metafile) return InvalidParameter;
3924 
3925     *metafile = NULL;
3926 
3927     status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
3928     if (status == Ok)
3929     {
3930         status = GdipCreateMetafileFromStream(stream, metafile);
3931         IStream_Release(stream);
3932     }
3933     return status;
3934 }
3935 
GdipCreateMetafileFromStream(IStream * stream,GpMetafile ** metafile)3936 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
3937     GpMetafile **metafile)
3938 {
3939     GpStatus stat;
3940 
3941     TRACE("%p %p\n", stream, metafile);
3942 
3943     stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
3944     if (stat != Ok) return stat;
3945 
3946     if ((*metafile)->image.type != ImageTypeMetafile)
3947     {
3948         GdipDisposeImage(&(*metafile)->image);
3949         *metafile = NULL;
3950         return GenericError;
3951     }
3952 
3953     return Ok;
3954 }
3955 
GdipSetMetafileDownLevelRasterizationLimit(GpMetafile * metafile,UINT limitDpi)3956 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3957     UINT limitDpi)
3958 {
3959     TRACE("(%p,%u)\n", metafile, limitDpi);
3960 
3961     return Ok;
3962 }
3963 
GdipConvertToEmfPlus(const GpGraphics * ref,GpMetafile * metafile,BOOL * succ,EmfType emfType,const WCHAR * description,GpMetafile ** out_metafile)3964 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
3965     GpMetafile* metafile, BOOL* succ, EmfType emfType,
3966     const WCHAR* description, GpMetafile** out_metafile)
3967 {
3968     static int calls;
3969 
3970     TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
3971         debugstr_w(description), out_metafile);
3972 
3973     if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
3974         return InvalidParameter;
3975 
3976     if(succ)
3977         *succ = FALSE;
3978     *out_metafile = NULL;
3979 
3980     if(!(calls++))
3981         FIXME("not implemented\n");
3982 
3983     return NotImplemented;
3984 }
3985 
GdipEmfToWmfBits(HENHMETAFILE hemf,UINT cbData16,LPBYTE pData16,INT iMapMode,INT eFlags)3986 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
3987     LPBYTE pData16, INT iMapMode, INT eFlags)
3988 {
3989     FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
3990     return NotImplemented;
3991 }
3992 
GdipRecordMetafileFileName(GDIPCONST WCHAR * fileName,HDC hdc,EmfType type,GDIPCONST GpRectF * pFrameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * desc,GpMetafile ** metafile)3993 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
3994                             HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
3995                             MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
3996                             GpMetafile **metafile)
3997 {
3998     FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
3999                                  frameUnit, debugstr_w(desc), metafile);
4000 
4001     return NotImplemented;
4002 }
4003 
GdipRecordMetafileFileNameI(GDIPCONST WCHAR * fileName,HDC hdc,EmfType type,GDIPCONST GpRect * pFrameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * desc,GpMetafile ** metafile)4004 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
4005                             GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
4006                             GDIPCONST WCHAR *desc, GpMetafile **metafile)
4007 {
4008     FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
4009                                  frameUnit, debugstr_w(desc), metafile);
4010 
4011     return NotImplemented;
4012 }
4013 
4014 /*****************************************************************************
4015  * GdipConvertToEmfPlusToFile [GDIPLUS.@]
4016  */
4017 
GdipConvertToEmfPlusToFile(const GpGraphics * refGraphics,GpMetafile * metafile,BOOL * conversionSuccess,const WCHAR * filename,EmfType emfType,const WCHAR * description,GpMetafile ** out_metafile)4018 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
4019                                                GpMetafile* metafile, BOOL* conversionSuccess,
4020                                                const WCHAR* filename, EmfType emfType,
4021                                                const WCHAR* description, GpMetafile** out_metafile)
4022 {
4023     FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
4024     return NotImplemented;
4025 }
4026 
METAFILE_CreateCompressedImageStream(GpImage * image,IStream ** stream,DWORD * size)4027 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
4028 {
4029     LARGE_INTEGER zero;
4030     STATSTG statstg;
4031     GpStatus stat;
4032     HRESULT hr;
4033 
4034     *size = 0;
4035 
4036     hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
4037     if (FAILED(hr)) return hresult_to_status(hr);
4038 
4039     stat = encode_image_png(image, *stream, NULL);
4040     if (stat != Ok)
4041     {
4042         IStream_Release(*stream);
4043         return stat;
4044     }
4045 
4046     hr = IStream_Stat(*stream, &statstg, 1);
4047     if (FAILED(hr))
4048     {
4049         IStream_Release(*stream);
4050         return hresult_to_status(hr);
4051     }
4052     *size = statstg.cbSize.u.LowPart;
4053 
4054     zero.QuadPart = 0;
4055     hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
4056     if (FAILED(hr))
4057     {
4058         IStream_Release(*stream);
4059         return hresult_to_status(hr);
4060     }
4061 
4062     return Ok;
4063 }
4064 
METAFILE_FillEmfPlusBitmap(EmfPlusBitmap * record,IStream * stream,DWORD size)4065 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
4066 {
4067     HRESULT hr;
4068 
4069     record->Width = 0;
4070     record->Height = 0;
4071     record->Stride = 0;
4072     record->PixelFormat = 0;
4073     record->Type = BitmapDataTypeCompressed;
4074 
4075     hr = IStream_Read(stream, record->BitmapData, size, NULL);
4076     if (FAILED(hr)) return hresult_to_status(hr);
4077     return Ok;
4078 }
4079 
METAFILE_AddImageObject(GpMetafile * metafile,GpImage * image,DWORD * id)4080 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
4081 {
4082     EmfPlusObject *object_record;
4083     GpStatus stat;
4084     DWORD size;
4085 
4086     *id = -1;
4087 
4088     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4089         return Ok;
4090 
4091     if (image->type == ImageTypeBitmap)
4092     {
4093         IStream *stream;
4094         DWORD aligned_size;
4095 
4096         stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
4097         if (stat != Ok) return stat;
4098         aligned_size = (size + 3) & ~3;
4099 
4100         stat = METAFILE_AllocateRecord(metafile,
4101                 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
4102                 (void**)&object_record);
4103         if (stat != Ok)
4104         {
4105             IStream_Release(stream);
4106             return stat;
4107         }
4108         memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
4109 
4110         *id = METAFILE_AddObjectId(metafile);
4111         object_record->Header.Type = EmfPlusRecordTypeObject;
4112         object_record->Header.Flags = *id | ObjectTypeImage << 8;
4113         object_record->ObjectData.image.Version = VERSION_MAGIC2;
4114         object_record->ObjectData.image.Type = ImageDataTypeBitmap;
4115 
4116         stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
4117         IStream_Release(stream);
4118         if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4119         return stat;
4120     }
4121     else if (image->type == ImageTypeMetafile)
4122     {
4123         HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
4124         EmfPlusMetafile *metafile_record;
4125 
4126         if (!hemf) return InvalidParameter;
4127 
4128         size = GetEnhMetaFileBits(hemf, 0, NULL);
4129         if (!size) return GenericError;
4130 
4131         stat  = METAFILE_AllocateRecord(metafile,
4132                 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
4133                 (void**)&object_record);
4134         if (stat != Ok) return stat;
4135 
4136         *id = METAFILE_AddObjectId(metafile);
4137         object_record->Header.Type = EmfPlusRecordTypeObject;
4138         object_record->Header.Flags = *id | ObjectTypeImage << 8;
4139         object_record->ObjectData.image.Version = VERSION_MAGIC2;
4140         object_record->ObjectData.image.Type = ImageDataTypeMetafile;
4141         metafile_record = &object_record->ObjectData.image.ImageData.metafile;
4142         metafile_record->Type = ((GpMetafile*)image)->metafile_type;
4143         metafile_record->MetafileDataSize = size;
4144         if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
4145         {
4146             METAFILE_RemoveLastRecord(metafile, &object_record->Header);
4147             return GenericError;
4148         }
4149         return Ok;
4150     }
4151     else
4152     {
4153         FIXME("not supported image type (%d)\n", image->type);
4154         return NotImplemented;
4155     }
4156 }
4157 
METAFILE_AddImageAttributesObject(GpMetafile * metafile,const GpImageAttributes * attrs,DWORD * id)4158 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
4159 {
4160     EmfPlusObject *object_record;
4161     EmfPlusImageAttributes *attrs_record;
4162     GpStatus stat;
4163 
4164     *id = -1;
4165 
4166     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4167         return Ok;
4168 
4169     if (!attrs)
4170         return Ok;
4171 
4172     stat = METAFILE_AllocateRecord(metafile,
4173             FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
4174             (void**)&object_record);
4175     if (stat != Ok) return stat;
4176 
4177     *id = METAFILE_AddObjectId(metafile);
4178     object_record->Header.Type = EmfPlusRecordTypeObject;
4179     object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
4180     attrs_record = &object_record->ObjectData.image_attributes;
4181     attrs_record->Version = VERSION_MAGIC2;
4182     attrs_record->Reserved1 = 0;
4183     attrs_record->WrapMode = attrs->wrap;
4184     attrs_record->ClampColor = attrs->outside_color;
4185     attrs_record->ObjectClamp = attrs->clamp;
4186     attrs_record->Reserved2 = 0;
4187     return Ok;
4188 }
4189 
METAFILE_DrawImagePointsRect(GpMetafile * metafile,GpImage * image,GDIPCONST GpPointF * points,INT count,REAL srcx,REAL srcy,REAL srcwidth,REAL srcheight,GpUnit srcUnit,GDIPCONST GpImageAttributes * imageAttributes,DrawImageAbort callback,VOID * callbackData)4190 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
4191      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
4192      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
4193      DrawImageAbort callback, VOID *callbackData)
4194 {
4195     EmfPlusDrawImagePoints *draw_image_record;
4196     DWORD image_id, attributes_id;
4197     GpStatus stat;
4198 
4199     if (count != 3) return InvalidParameter;
4200 
4201     if (metafile->metafile_type == MetafileTypeEmf)
4202     {
4203         FIXME("MetafileTypeEmf metafiles not supported\n");
4204         return NotImplemented;
4205     }
4206     else
4207         FIXME("semi-stub\n");
4208 
4209     if (!imageAttributes)
4210     {
4211         stat = METAFILE_AddImageObject(metafile, image, &image_id);
4212     }
4213     else if (image->type == ImageTypeBitmap)
4214     {
4215         INT width = ((GpBitmap*)image)->width;
4216         INT height = ((GpBitmap*)image)->height;
4217         GpGraphics *graphics;
4218         GpBitmap *bitmap;
4219 
4220         stat = GdipCreateBitmapFromScan0(width, height,
4221                 0, PixelFormat32bppARGB, NULL, &bitmap);
4222         if (stat != Ok) return stat;
4223 
4224         stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
4225         if (stat != Ok)
4226         {
4227             GdipDisposeImage((GpImage*)bitmap);
4228             return stat;
4229         }
4230 
4231         stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
4232                 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
4233         GdipDeleteGraphics(graphics);
4234         if (stat != Ok)
4235         {
4236             GdipDisposeImage((GpImage*)bitmap);
4237             return stat;
4238         }
4239 
4240         stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
4241         GdipDisposeImage((GpImage*)bitmap);
4242     }
4243     else
4244     {
4245         FIXME("imageAttributes not supported (image type %d)\n", image->type);
4246         return NotImplemented;
4247     }
4248     if (stat != Ok) return stat;
4249 
4250     stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
4251     if (stat != Ok) return stat;
4252 
4253     stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
4254     if (stat != Ok) return stat;
4255     draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
4256     draw_image_record->Header.Flags = image_id;
4257     draw_image_record->ImageAttributesID = attributes_id;
4258     draw_image_record->SrcUnit = UnitPixel;
4259     draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
4260     draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
4261     draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
4262     draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
4263     draw_image_record->count = 3;
4264     memcpy(draw_image_record->PointData.pointsF, points, 3 * sizeof(*points));
4265     METAFILE_WriteRecords(metafile);
4266     return Ok;
4267 }
4268 
METAFILE_AddSimpleProperty(GpMetafile * metafile,SHORT prop,SHORT val)4269 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
4270 {
4271     EmfPlusRecordHeader *record;
4272     GpStatus stat;
4273 
4274     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4275         return Ok;
4276 
4277     stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
4278     if (stat != Ok) return stat;
4279 
4280     record->Type = prop;
4281     record->Flags = val;
4282 
4283     METAFILE_WriteRecords(metafile);
4284     return Ok;
4285 }
4286 
METAFILE_AddPathObject(GpMetafile * metafile,GpPath * path,DWORD * id)4287 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
4288 {
4289     EmfPlusObject *object_record;
4290     GpStatus stat;
4291     DWORD size;
4292 
4293     *id = -1;
4294     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4295         return Ok;
4296 
4297     size = write_path_data(path, NULL);
4298     stat = METAFILE_AllocateRecord(metafile,
4299             FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
4300             (void**)&object_record);
4301     if (stat != Ok) return stat;
4302 
4303     *id = METAFILE_AddObjectId(metafile);
4304     object_record->Header.Type = EmfPlusRecordTypeObject;
4305     object_record->Header.Flags = *id | ObjectTypePath << 8;
4306     write_path_data(path, &object_record->ObjectData.path);
4307     return Ok;
4308 }
4309 
METAFILE_AddPenObject(GpMetafile * metafile,GpPen * pen,DWORD * id)4310 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
4311 {
4312     DWORD i, data_flags, pen_data_size, brush_size;
4313     EmfPlusObject *object_record;
4314     EmfPlusPenData *pen_data;
4315     GpStatus stat;
4316     BOOL result;
4317 
4318     *id = -1;
4319     if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
4320         return Ok;
4321 
4322     data_flags = 0;
4323     pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
4324 
4325     GdipIsMatrixIdentity(&pen->transform, &result);
4326     if (!result)
4327     {
4328         data_flags |= PenDataTransform;
4329         pen_data_size += sizeof(EmfPlusTransformMatrix);
4330     }
4331     if (pen->startcap != LineCapFlat)
4332     {
4333         data_flags |= PenDataStartCap;
4334         pen_data_size += sizeof(DWORD);
4335     }
4336     if (pen->endcap != LineCapFlat)
4337     {
4338         data_flags |= PenDataEndCap;
4339         pen_data_size += sizeof(DWORD);
4340     }
4341     if (pen->join != LineJoinMiter)
4342     {
4343         data_flags |= PenDataJoin;
4344         pen_data_size += sizeof(DWORD);
4345     }
4346     if (pen->miterlimit != 10.0)
4347     {
4348         data_flags |= PenDataMiterLimit;
4349         pen_data_size += sizeof(REAL);
4350     }
4351     if (pen->style != GP_DEFAULT_PENSTYLE)
4352     {
4353         data_flags |= PenDataLineStyle;
4354         pen_data_size += sizeof(DWORD);
4355     }
4356     if (pen->dashcap != DashCapFlat)
4357     {
4358         data_flags |= PenDataDashedLineCap;
4359         pen_data_size += sizeof(DWORD);
4360     }
4361     data_flags |= PenDataDashedLineOffset;
4362     pen_data_size += sizeof(REAL);
4363     if (pen->numdashes)
4364     {
4365         data_flags |= PenDataDashedLine;
4366         pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
4367     }
4368     if (pen->align != PenAlignmentCenter)
4369     {
4370         data_flags |= PenDataNonCenter;
4371         pen_data_size += sizeof(DWORD);
4372     }
4373     /* TODO: Add support for PenDataCompoundLine */
4374     if (pen->customstart)
4375     {
4376         FIXME("ignoring custom start cup\n");
4377     }
4378     if (pen->customend)
4379     {
4380         FIXME("ignoring custom end cup\n");
4381     }
4382 
4383     stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
4384     if (stat != Ok) return stat;
4385 
4386     stat = METAFILE_AllocateRecord(metafile,
4387             FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
4388             (void**)&object_record);
4389     if (stat != Ok) return stat;
4390 
4391     *id = METAFILE_AddObjectId(metafile);
4392     object_record->Header.Type = EmfPlusRecordTypeObject;
4393     object_record->Header.Flags = *id | ObjectTypePen << 8;
4394     object_record->ObjectData.pen.Version = VERSION_MAGIC2;
4395     object_record->ObjectData.pen.Type = 0;
4396 
4397     pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
4398     pen_data->PenDataFlags = data_flags;
4399     pen_data->PenUnit = pen->unit;
4400     pen_data->PenWidth = pen->width;
4401 
4402     i = 0;
4403     if (data_flags & PenDataTransform)
4404     {
4405         EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
4406         memcpy(m, &pen->transform, sizeof(*m));
4407         i += sizeof(EmfPlusTransformMatrix);
4408     }
4409     if (data_flags & PenDataStartCap)
4410     {
4411         *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
4412         i += sizeof(DWORD);
4413     }
4414     if (data_flags & PenDataEndCap)
4415     {
4416         *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
4417         i += sizeof(DWORD);
4418     }
4419     if (data_flags & PenDataJoin)
4420     {
4421         *(DWORD*)(pen_data->OptionalData + i) = pen->join;
4422         i += sizeof(DWORD);
4423     }
4424     if (data_flags & PenDataMiterLimit)
4425     {
4426         *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
4427         i += sizeof(REAL);
4428     }
4429     if (data_flags & PenDataLineStyle)
4430     {
4431         switch (pen->style & PS_STYLE_MASK)
4432         {
4433         case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
4434         case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
4435         case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
4436         case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
4437         case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
4438         default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
4439         }
4440         i += sizeof(DWORD);
4441     }
4442     if (data_flags & PenDataDashedLineCap)
4443     {
4444         *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
4445         i += sizeof(DWORD);
4446     }
4447     if (data_flags & PenDataDashedLineOffset)
4448     {
4449         *(REAL*)(pen_data->OptionalData + i) = pen->offset;
4450         i += sizeof(REAL);
4451     }
4452     if (data_flags & PenDataDashedLine)
4453     {
4454         int j;
4455 
4456         *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
4457         i += sizeof(DWORD);
4458 
4459         for (j=0; j<pen->numdashes; j++)
4460         {
4461             *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
4462             i += sizeof(REAL);
4463         }
4464     }
4465     if (data_flags & PenDataNonCenter)
4466     {
4467         *(REAL*)(pen_data->OptionalData + i) = pen->align;
4468         i += sizeof(DWORD);
4469     }
4470 
4471     METAFILE_FillBrushData(pen->brush,
4472             (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
4473     return Ok;
4474 }
4475 
METAFILE_DrawPath(GpMetafile * metafile,GpPen * pen,GpPath * path)4476 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
4477 {
4478     EmfPlusDrawPath *draw_path_record;
4479     DWORD path_id;
4480     DWORD pen_id;
4481     GpStatus stat;
4482 
4483     if (metafile->metafile_type == MetafileTypeEmf)
4484     {
4485         FIXME("stub!\n");
4486         return NotImplemented;
4487     }
4488 
4489     stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
4490     if (stat != Ok) return stat;
4491 
4492     stat = METAFILE_AddPathObject(metafile, path, &path_id);
4493     if (stat != Ok) return stat;
4494 
4495     stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
4496     if (stat != Ok) return stat;
4497     draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
4498     draw_path_record->Header.Flags = path_id;
4499     draw_path_record->PenId = pen_id;
4500 
4501     METAFILE_WriteRecords(metafile);
4502     return Ok;
4503 }
4504 
METAFILE_FillPath(GpMetafile * metafile,GpBrush * brush,GpPath * path)4505 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
4506 {
4507     EmfPlusFillPath *fill_path_record;
4508     DWORD brush_id = -1, path_id;
4509     BOOL inline_color;
4510     GpStatus stat;
4511 
4512     if (metafile->metafile_type == MetafileTypeEmf)
4513     {
4514         FIXME("stub!\n");
4515         return NotImplemented;
4516     }
4517 
4518     inline_color = brush->bt == BrushTypeSolidColor;
4519     if (!inline_color)
4520     {
4521         stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
4522         if (stat != Ok) return stat;
4523     }
4524 
4525     stat = METAFILE_AddPathObject(metafile, path, &path_id);
4526     if (stat != Ok) return stat;
4527 
4528     stat = METAFILE_AllocateRecord(metafile,
4529             sizeof(EmfPlusFillPath), (void**)&fill_path_record);
4530     if (stat != Ok) return stat;
4531     fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
4532     if (inline_color)
4533     {
4534         fill_path_record->Header.Flags = 0x8000 | path_id;
4535         fill_path_record->data.Color = ((GpSolidFill *)brush)->color;
4536     }
4537     else
4538     {
4539         fill_path_record->Header.Flags = path_id;
4540         fill_path_record->data.BrushId = brush_id;
4541     }
4542 
4543     METAFILE_WriteRecords(metafile);
4544     return Ok;
4545 }
4546