Merge PR #283 "[USBPORT] Transaction Translator (TT) support bringup"
[reactos.git] / dll / win32 / gdiplus / metafile.c
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
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
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 < sizeof(metafile->objtable)/sizeof(metafile->objtable[0]); i++)
610 metafile_free_object_table_entry(metafile, i);
611 }
612
613 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
614 {
615 return (metafile->next_object_id++) % EmfPlusObjectTableSize;
616 }
617
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
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
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
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
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
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 */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1492 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1493 {
1494 if (hdc != metafile->record_dc)
1495 return InvalidParameter;
1496
1497 return Ok;
1498 }
1499
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
3956 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3957 UINT limitDpi)
3958 {
3959 TRACE("(%p,%u)\n", metafile, limitDpi);
3960
3961 return Ok;
3962 }
3963
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }