[GDIPLUS] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / reactos / 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 "gdiplus_private.h"
20
21 #include <assert.h>
22 #include <ole2.h>
23
24 typedef struct EmfPlusARGB
25 {
26 BYTE Blue;
27 BYTE Green;
28 BYTE Red;
29 BYTE Alpha;
30 } EmfPlusARGB;
31
32 typedef struct EmfPlusRecordHeader
33 {
34 WORD Type;
35 WORD Flags;
36 DWORD Size;
37 DWORD DataSize;
38 } EmfPlusRecordHeader;
39
40 typedef struct EmfPlusHeader
41 {
42 EmfPlusRecordHeader Header;
43 DWORD Version;
44 DWORD EmfPlusFlags;
45 DWORD LogicalDpiX;
46 DWORD LogicalDpiY;
47 } EmfPlusHeader;
48
49 typedef struct EmfPlusClear
50 {
51 EmfPlusRecordHeader Header;
52 DWORD Color;
53 } EmfPlusClear;
54
55 typedef struct EmfPlusFillRects
56 {
57 EmfPlusRecordHeader Header;
58 DWORD BrushID;
59 DWORD Count;
60 } EmfPlusFillRects;
61
62 typedef struct EmfPlusSetClipRect
63 {
64 EmfPlusRecordHeader Header;
65 GpRectF ClipRect;
66 } EmfPlusSetClipRect;
67
68 typedef struct EmfPlusSetPageTransform
69 {
70 EmfPlusRecordHeader Header;
71 REAL PageScale;
72 } EmfPlusSetPageTransform;
73
74 typedef struct EmfPlusRect
75 {
76 SHORT X;
77 SHORT Y;
78 SHORT Width;
79 SHORT Height;
80 } EmfPlusRect;
81
82 typedef struct EmfPlusSetWorldTransform
83 {
84 EmfPlusRecordHeader Header;
85 REAL MatrixData[6];
86 } EmfPlusSetWorldTransform;
87
88 typedef struct EmfPlusScaleWorldTransform
89 {
90 EmfPlusRecordHeader Header;
91 REAL Sx;
92 REAL Sy;
93 } EmfPlusScaleWorldTransform;
94
95 typedef struct EmfPlusMultiplyWorldTransform
96 {
97 EmfPlusRecordHeader Header;
98 REAL MatrixData[6];
99 } EmfPlusMultiplyWorldTransform;
100
101 typedef struct EmfPlusRotateWorldTransform
102 {
103 EmfPlusRecordHeader Header;
104 REAL Angle;
105 } EmfPlusRotateWorldTransform;
106
107 typedef struct EmfPlusTranslateWorldTransform
108 {
109 EmfPlusRecordHeader Header;
110 REAL dx;
111 REAL dy;
112 } EmfPlusTranslateWorldTransform;
113
114 typedef struct EmfPlusBeginContainer
115 {
116 EmfPlusRecordHeader Header;
117 GpRectF DestRect;
118 GpRectF SrcRect;
119 DWORD StackIndex;
120 } EmfPlusBeginContainer;
121
122 typedef struct EmfPlusContainerRecord
123 {
124 EmfPlusRecordHeader Header;
125 DWORD StackIndex;
126 } EmfPlusContainerRecord;
127
128 enum container_type
129 {
130 BEGIN_CONTAINER,
131 SAVE_GRAPHICS
132 };
133
134 typedef struct container
135 {
136 struct list entry;
137 DWORD id;
138 enum container_type type;
139 GraphicsContainer state;
140 GpMatrix world_transform;
141 GpUnit page_unit;
142 REAL page_scale;
143 GpRegion *clip;
144 } container;
145
146 enum PenDataFlags
147 {
148 PenDataTransform = 0x0001,
149 PenDataStartCap = 0x0002,
150 PenDataEndCap = 0x0004,
151 PenDataJoin = 0x0008,
152 PenDataMiterLimit = 0x0010,
153 PenDataLineStyle = 0x0020,
154 PenDataDashedLineCap = 0x0040,
155 PenDataDashedLineOffset = 0x0080,
156 PenDataDashedLine = 0x0100,
157 PenDataNonCenter = 0x0200,
158 PenDataCompoundLine = 0x0400,
159 PenDataCustomStartCap = 0x0800,
160 PenDataCustomEndCap = 0x1000
161 };
162
163 typedef struct EmfPlusTransformMatrix
164 {
165 REAL TransformMatrix[6];
166 } EmfPlusTransformMatrix;
167
168 enum LineStyle
169 {
170 LineStyleSolid,
171 LineStyleDash,
172 LineStyleDot,
173 LineStyleDashDot,
174 LineStyleDashDotDot,
175 LineStyleCustom
176 };
177
178 typedef struct EmfPlusPenData
179 {
180 DWORD PenDataFlags;
181 DWORD PenUnit;
182 REAL PenWidth;
183 BYTE OptionalData[1];
184 } EmfPlusPenData;
185
186 typedef struct EmfPlusSolidBrushData
187 {
188 EmfPlusARGB SolidColor;
189 } EmfPlusSolidBrushData;
190
191 typedef struct EmfPlusBrush
192 {
193 DWORD Version;
194 DWORD Type;
195 union {
196 EmfPlusSolidBrushData solid;
197 } BrushData;
198 } EmfPlusBrush;
199
200 typedef struct EmfPlusPen
201 {
202 DWORD Version;
203 DWORD Type;
204 /* EmfPlusPenData */
205 /* EmfPlusBrush */
206 BYTE data[1];
207 } EmfPlusPen;
208
209 typedef struct EmfPlusPath
210 {
211 DWORD Version;
212 DWORD PathPointCount;
213 DWORD PathPointFlags;
214 /* PathPoints[] */
215 /* PathPointTypes[] */
216 /* AlignmentPadding */
217 BYTE data[1];
218 } EmfPlusPath;
219
220 typedef struct EmfPlusRegion
221 {
222 DWORD Version;
223 DWORD RegionNodeCount;
224 BYTE RegionNode[1];
225 } EmfPlusRegion;
226
227 typedef enum
228 {
229 BitmapDataTypePixel,
230 BitmapDataTypeCompressed,
231 } BitmapDataType;
232
233 typedef struct EmfPlusBitmap
234 {
235 DWORD Width;
236 DWORD Height;
237 DWORD Stride;
238 DWORD PixelFormat;
239 DWORD Type;
240 BYTE BitmapData[1];
241 } EmfPlusBitmap;
242
243 typedef struct EmfPlusMetafile
244 {
245 DWORD Type;
246 DWORD MetafileDataSize;
247 BYTE MetafileData[1];
248 } EmfPlusMetafile;
249
250 typedef enum ImageDataType
251 {
252 ImageDataTypeUnknown,
253 ImageDataTypeBitmap,
254 ImageDataTypeMetafile,
255 } ImageDataType;
256
257 typedef struct EmfPlusImage
258 {
259 DWORD Version;
260 ImageDataType Type;
261 union
262 {
263 EmfPlusBitmap bitmap;
264 EmfPlusMetafile metafile;
265 } ImageData;
266 } EmfPlusImage;
267
268 typedef struct EmfPlusImageAttributes
269 {
270 DWORD Version;
271 DWORD Reserved1;
272 DWORD WrapMode;
273 EmfPlusARGB ClampColor;
274 DWORD ObjectClamp;
275 DWORD Reserved2;
276 } EmfPlusImageAttributes;
277
278 typedef enum ObjectType
279 {
280 ObjectTypeInvalid,
281 ObjectTypeBrush,
282 ObjectTypePen,
283 ObjectTypePath,
284 ObjectTypeRegion,
285 ObjectTypeImage,
286 ObjectTypeFont,
287 ObjectTypeStringFormat,
288 ObjectTypeImageAttributes,
289 ObjectTypeCustomLineCap,
290 } ObjectType;
291
292 typedef struct EmfPlusObject
293 {
294 EmfPlusRecordHeader Header;
295 union
296 {
297 EmfPlusBrush brush;
298 EmfPlusPen pen;
299 EmfPlusPath path;
300 EmfPlusRegion region;
301 EmfPlusImage image;
302 EmfPlusImageAttributes image_attributes;
303 } ObjectData;
304 } EmfPlusObject;
305
306 typedef struct EmfPlusRectF
307 {
308 float X;
309 float Y;
310 float Width;
311 float Height;
312 } EmfPlusRectF;
313
314 typedef struct EmfPlusPointF
315 {
316 float X;
317 float Y;
318 } EmfPlusPointF;
319
320 typedef struct EmfPlusDrawImagePoints
321 {
322 EmfPlusRecordHeader Header;
323 DWORD ImageAttributesID;
324 DWORD SrcUnit;
325 EmfPlusRectF SrcRect;
326 DWORD count;
327 union
328 {
329 /*EmfPlusPointR pointR;
330 EmfPlusPoint point;*/
331 EmfPlusPointF pointF;
332 } PointData[3];
333 } EmfPlusDrawImagePoints;
334
335 typedef struct EmfPlusDrawPath
336 {
337 EmfPlusRecordHeader Header;
338 DWORD PenId;
339 } EmfPlusDrawPath;
340
341 typedef struct EmfPlusFillPath
342 {
343 EmfPlusRecordHeader Header;
344 union
345 {
346 DWORD BrushId;
347 EmfPlusARGB Color;
348 } data;
349 } EmfPlusFillPath;
350
351 static DWORD METAFILE_AddObjectId(GpMetafile *metafile)
352 {
353 return (metafile->next_object_id++) % 64;
354 }
355
356 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
357 {
358 DWORD size_needed;
359 EmfPlusRecordHeader *record;
360
361 if (!metafile->comment_data_size)
362 {
363 DWORD data_size = max(256, size * 2 + 4);
364 metafile->comment_data = heap_alloc_zero(data_size);
365
366 if (!metafile->comment_data)
367 return OutOfMemory;
368
369 memcpy(metafile->comment_data, "EMF+", 4);
370
371 metafile->comment_data_size = data_size;
372 metafile->comment_data_length = 4;
373 }
374
375 size_needed = size + metafile->comment_data_length;
376
377 if (size_needed > metafile->comment_data_size)
378 {
379 DWORD data_size = size_needed * 2;
380 BYTE *new_data = heap_alloc_zero(data_size);
381
382 if (!new_data)
383 return OutOfMemory;
384
385 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
386
387 metafile->comment_data_size = data_size;
388 heap_free(metafile->comment_data);
389 metafile->comment_data = new_data;
390 }
391
392 *result = metafile->comment_data + metafile->comment_data_length;
393 metafile->comment_data_length += size;
394
395 record = (EmfPlusRecordHeader*)*result;
396 record->Size = size;
397 record->DataSize = size - sizeof(EmfPlusRecordHeader);
398
399 return Ok;
400 }
401
402 static void METAFILE_RemoveLastRecord(GpMetafile *metafile, EmfPlusRecordHeader *record)
403 {
404 assert(metafile->comment_data + metafile->comment_data_length == (BYTE*)record + record->Size);
405 metafile->comment_data_length -= record->Size;
406 }
407
408 static void METAFILE_WriteRecords(GpMetafile *metafile)
409 {
410 if (metafile->comment_data_length > 4)
411 {
412 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
413 metafile->comment_data_length = 4;
414 }
415 }
416
417 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
418 {
419 GpStatus stat;
420
421 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
422 {
423 EmfPlusHeader *header;
424
425 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
426 if (stat != Ok)
427 return stat;
428
429 header->Header.Type = EmfPlusRecordTypeHeader;
430
431 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
432 header->Header.Flags = 1;
433 else
434 header->Header.Flags = 0;
435
436 header->Version = VERSION_MAGIC2;
437
438 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
439 header->EmfPlusFlags = 1;
440 else
441 header->EmfPlusFlags = 0;
442
443 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
444 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
445
446 METAFILE_WriteRecords(metafile);
447 }
448
449 return Ok;
450 }
451
452 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
453 {
454 GpStatus stat;
455
456 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
457 {
458 EmfPlusRecordHeader *record;
459
460 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
461 if (stat != Ok)
462 return stat;
463
464 record->Type = EmfPlusRecordTypeEndOfFile;
465 record->Flags = 0;
466
467 METAFILE_WriteRecords(metafile);
468 }
469
470 return Ok;
471 }
472
473 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
474 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
475 {
476 HDC record_dc;
477 REAL dpix, dpiy;
478 REAL framerect_factor_x, framerect_factor_y;
479 RECT rc, *lprc;
480 GpStatus stat;
481
482 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
483
484 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
485 return InvalidParameter;
486
487 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
488 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
489
490 if (frameRect)
491 {
492 switch (frameUnit)
493 {
494 case MetafileFrameUnitPixel:
495 framerect_factor_x = 2540.0 / dpix;
496 framerect_factor_y = 2540.0 / dpiy;
497 break;
498 case MetafileFrameUnitPoint:
499 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
500 break;
501 case MetafileFrameUnitInch:
502 framerect_factor_x = framerect_factor_y = 2540.0;
503 break;
504 case MetafileFrameUnitDocument:
505 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
506 break;
507 case MetafileFrameUnitMillimeter:
508 framerect_factor_x = framerect_factor_y = 100.0;
509 break;
510 case MetafileFrameUnitGdi:
511 framerect_factor_x = framerect_factor_y = 1.0;
512 break;
513 default:
514 return InvalidParameter;
515 }
516
517 rc.left = framerect_factor_x * frameRect->X;
518 rc.top = framerect_factor_y * frameRect->Y;
519 rc.right = rc.left + framerect_factor_x * frameRect->Width;
520 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
521
522 lprc = &rc;
523 }
524 else
525 lprc = NULL;
526
527 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
528
529 if (!record_dc)
530 return GenericError;
531
532 *metafile = heap_alloc_zero(sizeof(GpMetafile));
533 if(!*metafile)
534 {
535 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
536 return OutOfMemory;
537 }
538
539 (*metafile)->image.type = ImageTypeMetafile;
540 (*metafile)->image.flags = ImageFlagsNone;
541 (*metafile)->image.palette = NULL;
542 (*metafile)->image.xres = dpix;
543 (*metafile)->image.yres = dpiy;
544 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
545 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
546 (*metafile)->unit = UnitPixel;
547 (*metafile)->metafile_type = type;
548 (*metafile)->record_dc = record_dc;
549 (*metafile)->comment_data = NULL;
550 (*metafile)->comment_data_size = 0;
551 (*metafile)->comment_data_length = 0;
552 (*metafile)->hemf = NULL;
553 list_init(&(*metafile)->containers);
554
555 if (!frameRect)
556 {
557 (*metafile)->auto_frame = TRUE;
558 (*metafile)->auto_frame_min.X = 0;
559 (*metafile)->auto_frame_min.Y = 0;
560 (*metafile)->auto_frame_max.X = -1;
561 (*metafile)->auto_frame_max.Y = -1;
562 }
563
564 stat = METAFILE_WriteHeader(*metafile, hdc);
565
566 if (stat != Ok)
567 {
568 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
569 heap_free(*metafile);
570 *metafile = NULL;
571 return OutOfMemory;
572 }
573
574 return stat;
575 }
576
577 /*****************************************************************************
578 * GdipRecordMetafileI [GDIPLUS.@]
579 */
580 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
581 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
582 {
583 GpRectF frameRectF, *pFrameRectF;
584
585 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
586
587 if (frameRect)
588 {
589 frameRectF.X = frameRect->X;
590 frameRectF.Y = frameRect->Y;
591 frameRectF.Width = frameRect->Width;
592 frameRectF.Height = frameRect->Height;
593 pFrameRectF = &frameRectF;
594 }
595 else
596 pFrameRectF = NULL;
597
598 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
599 }
600
601 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
602 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
603 {
604 GpStatus stat;
605
606 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
607
608 if (!stream)
609 return InvalidParameter;
610
611 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
612
613 if (stat == Ok)
614 {
615 (*metafile)->record_stream = stream;
616 IStream_AddRef(stream);
617 }
618
619 return stat;
620 }
621
622 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
623 UINT num_points)
624 {
625 int i;
626
627 if (!metafile->auto_frame || !num_points)
628 return;
629
630 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
631 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
632
633 for (i=0; i<num_points; i++)
634 {
635 if (points[i].X < metafile->auto_frame_min.X)
636 metafile->auto_frame_min.X = points[i].X;
637 if (points[i].X > metafile->auto_frame_max.X)
638 metafile->auto_frame_max.X = points[i].X;
639 if (points[i].Y < metafile->auto_frame_min.Y)
640 metafile->auto_frame_min.Y = points[i].Y;
641 if (points[i].Y > metafile->auto_frame_max.Y)
642 metafile->auto_frame_max.Y = points[i].Y;
643 }
644 }
645
646 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
647 {
648 GpStatus stat;
649
650 if (!metafile->record_dc || metafile->record_graphics)
651 return InvalidParameter;
652
653 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
654
655 if (stat == Ok)
656 {
657 *result = metafile->record_graphics;
658 metafile->record_graphics->xres = 96.0;
659 metafile->record_graphics->yres = 96.0;
660 }
661
662 return stat;
663 }
664
665 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
666 {
667 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
668 {
669 EmfPlusRecordHeader *record;
670 GpStatus stat;
671
672 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
673 if (stat != Ok)
674 return stat;
675
676 record->Type = EmfPlusRecordTypeGetDC;
677 record->Flags = 0;
678
679 METAFILE_WriteRecords(metafile);
680 }
681
682 *hdc = metafile->record_dc;
683
684 return Ok;
685 }
686
687 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
688 {
689 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
690 {
691 EmfPlusClear *record;
692 GpStatus stat;
693
694 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
695 if (stat != Ok)
696 return stat;
697
698 record->Header.Type = EmfPlusRecordTypeClear;
699 record->Header.Flags = 0;
700 record->Color = color;
701
702 METAFILE_WriteRecords(metafile);
703 }
704
705 return Ok;
706 }
707
708 static BOOL is_integer_rect(const GpRectF *rect)
709 {
710 SHORT x, y, width, height;
711 x = rect->X;
712 y = rect->Y;
713 width = rect->Width;
714 height = rect->Height;
715 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
716 rect->Width != (REAL)width || rect->Height != (REAL)height)
717 return FALSE;
718 return TRUE;
719 }
720
721 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
722 GDIPCONST GpRectF* rects, INT count)
723 {
724 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
725 {
726 EmfPlusFillRects *record;
727 GpStatus stat;
728 BOOL integer_rects = TRUE;
729 int i;
730 DWORD brushid;
731 int flags = 0;
732
733 if (brush->bt == BrushTypeSolidColor)
734 {
735 flags |= 0x8000;
736 brushid = ((GpSolidFill*)brush)->color;
737 }
738 else
739 {
740 FIXME("brush serialization not implemented\n");
741 return NotImplemented;
742 }
743
744 for (i=0; i<count; i++)
745 {
746 if (!is_integer_rect(&rects[i]))
747 {
748 integer_rects = FALSE;
749 break;
750 }
751 }
752
753 if (integer_rects)
754 flags |= 0x4000;
755
756 stat = METAFILE_AllocateRecord(metafile,
757 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
758 (void**)&record);
759 if (stat != Ok)
760 return stat;
761
762 record->Header.Type = EmfPlusRecordTypeFillRects;
763 record->Header.Flags = flags;
764 record->BrushID = brushid;
765 record->Count = count;
766
767 if (integer_rects)
768 {
769 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
770 for (i=0; i<count; i++)
771 {
772 record_rects[i].X = (SHORT)rects[i].X;
773 record_rects[i].Y = (SHORT)rects[i].Y;
774 record_rects[i].Width = (SHORT)rects[i].Width;
775 record_rects[i].Height = (SHORT)rects[i].Height;
776 }
777 }
778 else
779 memcpy(record+1, rects, sizeof(GpRectF) * count);
780
781 METAFILE_WriteRecords(metafile);
782 }
783
784 if (metafile->auto_frame)
785 {
786 GpPointF corners[4];
787 int i;
788
789 for (i=0; i<count; i++)
790 {
791 corners[0].X = rects[i].X;
792 corners[0].Y = rects[i].Y;
793 corners[1].X = rects[i].X + rects[i].Width;
794 corners[1].Y = rects[i].Y;
795 corners[2].X = rects[i].X;
796 corners[2].Y = rects[i].Y + rects[i].Height;
797 corners[3].X = rects[i].X + rects[i].Width;
798 corners[3].Y = rects[i].Y + rects[i].Height;
799
800 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
801 CoordinateSpaceWorld, corners, 4);
802
803 METAFILE_AdjustFrame(metafile, corners, 4);
804 }
805 }
806
807 return Ok;
808 }
809
810 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
811 {
812 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
813 {
814 EmfPlusSetClipRect *record;
815 GpStatus stat;
816
817 stat = METAFILE_AllocateRecord(metafile,
818 sizeof(EmfPlusSetClipRect),
819 (void**)&record);
820 if (stat != Ok)
821 return stat;
822
823 record->Header.Type = EmfPlusRecordTypeSetClipRect;
824 record->Header.Flags = (mode & 0xf) << 8;
825 record->ClipRect.X = x;
826 record->ClipRect.Y = y;
827 record->ClipRect.Width = width;
828 record->ClipRect.Height = height;
829
830 METAFILE_WriteRecords(metafile);
831 }
832
833 return Ok;
834 }
835
836 static GpStatus METAFILE_AddRegionObject(GpMetafile *metafile, GpRegion *region, DWORD *id)
837 {
838 EmfPlusObject *object_record;
839 DWORD size;
840 GpStatus stat;
841
842 *id = -1;
843 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
844 return Ok;
845
846 size = write_region_data(region, NULL);
847 stat = METAFILE_AllocateRecord(metafile,
848 FIELD_OFFSET(EmfPlusObject, ObjectData.region) + size, (void**)&object_record);
849 if (stat != Ok) return stat;
850
851 *id = METAFILE_AddObjectId(metafile);
852 object_record->Header.Type = EmfPlusRecordTypeObject;
853 object_record->Header.Flags = *id | ObjectTypeRegion << 8;
854 write_region_data(region, &object_record->ObjectData.region);
855 return Ok;
856 }
857
858 GpStatus METAFILE_SetClipRegion(GpMetafile* metafile, GpRegion* region, CombineMode mode)
859 {
860 EmfPlusRecordHeader *record;
861 DWORD region_id;
862 GpStatus stat;
863
864 if (metafile->metafile_type == MetafileTypeEmf)
865 {
866 FIXME("stub!\n");
867 return NotImplemented;
868 }
869
870 stat = METAFILE_AddRegionObject(metafile, region, &region_id);
871 if (stat != Ok) return stat;
872
873 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
874 if (stat != Ok) return stat;
875
876 record->Type = EmfPlusRecordTypeSetClipRegion;
877 record->Flags = region_id | mode << 8;
878
879 METAFILE_WriteRecords(metafile);
880 return Ok;
881 }
882
883 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
884 {
885 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
886 {
887 EmfPlusSetPageTransform *record;
888 GpStatus stat;
889
890 stat = METAFILE_AllocateRecord(metafile,
891 sizeof(EmfPlusSetPageTransform),
892 (void**)&record);
893 if (stat != Ok)
894 return stat;
895
896 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
897 record->Header.Flags = unit;
898 record->PageScale = scale;
899
900 METAFILE_WriteRecords(metafile);
901 }
902
903 return Ok;
904 }
905
906 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
907 {
908 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
909 {
910 EmfPlusSetWorldTransform *record;
911 GpStatus stat;
912
913 stat = METAFILE_AllocateRecord(metafile,
914 sizeof(EmfPlusSetWorldTransform),
915 (void**)&record);
916 if (stat != Ok)
917 return stat;
918
919 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
920 record->Header.Flags = 0;
921 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
922
923 METAFILE_WriteRecords(metafile);
924 }
925
926 return Ok;
927 }
928
929 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
930 {
931 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
932 {
933 EmfPlusScaleWorldTransform *record;
934 GpStatus stat;
935
936 stat = METAFILE_AllocateRecord(metafile,
937 sizeof(EmfPlusScaleWorldTransform),
938 (void**)&record);
939 if (stat != Ok)
940 return stat;
941
942 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
943 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
944 record->Sx = sx;
945 record->Sy = sy;
946
947 METAFILE_WriteRecords(metafile);
948 }
949
950 return Ok;
951 }
952
953 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
954 {
955 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
956 {
957 EmfPlusMultiplyWorldTransform *record;
958 GpStatus stat;
959
960 stat = METAFILE_AllocateRecord(metafile,
961 sizeof(EmfPlusMultiplyWorldTransform),
962 (void**)&record);
963 if (stat != Ok)
964 return stat;
965
966 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
967 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
968 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
969
970 METAFILE_WriteRecords(metafile);
971 }
972
973 return Ok;
974 }
975
976 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
977 {
978 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
979 {
980 EmfPlusRotateWorldTransform *record;
981 GpStatus stat;
982
983 stat = METAFILE_AllocateRecord(metafile,
984 sizeof(EmfPlusRotateWorldTransform),
985 (void**)&record);
986 if (stat != Ok)
987 return stat;
988
989 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
990 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
991 record->Angle = angle;
992
993 METAFILE_WriteRecords(metafile);
994 }
995
996 return Ok;
997 }
998
999 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
1000 {
1001 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1002 {
1003 EmfPlusTranslateWorldTransform *record;
1004 GpStatus stat;
1005
1006 stat = METAFILE_AllocateRecord(metafile,
1007 sizeof(EmfPlusTranslateWorldTransform),
1008 (void**)&record);
1009 if (stat != Ok)
1010 return stat;
1011
1012 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
1013 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
1014 record->dx = dx;
1015 record->dy = dy;
1016
1017 METAFILE_WriteRecords(metafile);
1018 }
1019
1020 return Ok;
1021 }
1022
1023 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
1024 {
1025 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1026 {
1027 EmfPlusRecordHeader *record;
1028 GpStatus stat;
1029
1030 stat = METAFILE_AllocateRecord(metafile,
1031 sizeof(EmfPlusRecordHeader),
1032 (void**)&record);
1033 if (stat != Ok)
1034 return stat;
1035
1036 record->Type = EmfPlusRecordTypeResetWorldTransform;
1037 record->Flags = 0;
1038
1039 METAFILE_WriteRecords(metafile);
1040 }
1041
1042 return Ok;
1043 }
1044
1045 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
1046 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
1047 {
1048 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1049 {
1050 EmfPlusBeginContainer *record;
1051 GpStatus stat;
1052
1053 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
1054 if (stat != Ok)
1055 return stat;
1056
1057 record->Header.Type = EmfPlusRecordTypeBeginContainer;
1058 record->Header.Flags = unit & 0xff;
1059 record->DestRect = *dstrect;
1060 record->SrcRect = *srcrect;
1061 record->StackIndex = StackIndex;
1062
1063 METAFILE_WriteRecords(metafile);
1064 }
1065
1066 return Ok;
1067 }
1068
1069 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
1070 {
1071 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1072 {
1073 EmfPlusContainerRecord *record;
1074 GpStatus stat;
1075
1076 stat = METAFILE_AllocateRecord(metafile,
1077 sizeof(EmfPlusContainerRecord),
1078 (void**)&record);
1079 if (stat != Ok)
1080 return stat;
1081
1082 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
1083 record->Header.Flags = 0;
1084 record->StackIndex = StackIndex;
1085
1086 METAFILE_WriteRecords(metafile);
1087 }
1088
1089 return Ok;
1090 }
1091
1092 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
1093 {
1094 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1095 {
1096 EmfPlusContainerRecord *record;
1097 GpStatus stat;
1098
1099 stat = METAFILE_AllocateRecord(metafile,
1100 sizeof(EmfPlusContainerRecord),
1101 (void**)&record);
1102 if (stat != Ok)
1103 return stat;
1104
1105 record->Header.Type = EmfPlusRecordTypeEndContainer;
1106 record->Header.Flags = 0;
1107 record->StackIndex = StackIndex;
1108
1109 METAFILE_WriteRecords(metafile);
1110 }
1111
1112 return Ok;
1113 }
1114
1115 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
1116 {
1117 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1118 {
1119 EmfPlusContainerRecord *record;
1120 GpStatus stat;
1121
1122 stat = METAFILE_AllocateRecord(metafile,
1123 sizeof(EmfPlusContainerRecord),
1124 (void**)&record);
1125 if (stat != Ok)
1126 return stat;
1127
1128 record->Header.Type = EmfPlusRecordTypeSave;
1129 record->Header.Flags = 0;
1130 record->StackIndex = StackIndex;
1131
1132 METAFILE_WriteRecords(metafile);
1133 }
1134
1135 return Ok;
1136 }
1137
1138 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
1139 {
1140 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
1141 {
1142 EmfPlusContainerRecord *record;
1143 GpStatus stat;
1144
1145 stat = METAFILE_AllocateRecord(metafile,
1146 sizeof(EmfPlusContainerRecord),
1147 (void**)&record);
1148 if (stat != Ok)
1149 return stat;
1150
1151 record->Header.Type = EmfPlusRecordTypeRestore;
1152 record->Header.Flags = 0;
1153 record->StackIndex = StackIndex;
1154
1155 METAFILE_WriteRecords(metafile);
1156 }
1157
1158 return Ok;
1159 }
1160
1161 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
1162 {
1163 if (hdc != metafile->record_dc)
1164 return InvalidParameter;
1165
1166 return Ok;
1167 }
1168
1169 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
1170 {
1171 GpStatus stat;
1172
1173 stat = METAFILE_WriteEndOfFile(metafile);
1174 metafile->record_graphics = NULL;
1175
1176 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
1177 metafile->record_dc = NULL;
1178
1179 heap_free(metafile->comment_data);
1180 metafile->comment_data = NULL;
1181 metafile->comment_data_size = 0;
1182
1183 if (stat == Ok)
1184 {
1185 MetafileHeader header;
1186
1187 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1188 if (stat == Ok && metafile->auto_frame &&
1189 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
1190 {
1191 RECTL bounds_rc, gdi_bounds_rc;
1192 REAL x_scale = 2540.0 / header.DpiX;
1193 REAL y_scale = 2540.0 / header.DpiY;
1194 BYTE* buffer;
1195 UINT buffer_size;
1196
1197 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
1198 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
1199 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
1200 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
1201
1202 gdi_bounds_rc = header.u.EmfHeader.rclBounds;
1203 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
1204 {
1205 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
1206 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
1207 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
1208 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
1209 }
1210
1211 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1212 buffer = heap_alloc(buffer_size);
1213 if (buffer)
1214 {
1215 HENHMETAFILE new_hemf;
1216
1217 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1218
1219 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
1220
1221 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
1222
1223 if (new_hemf)
1224 {
1225 DeleteEnhMetaFile(metafile->hemf);
1226 metafile->hemf = new_hemf;
1227 }
1228 else
1229 stat = OutOfMemory;
1230
1231 heap_free(buffer);
1232 }
1233 else
1234 stat = OutOfMemory;
1235
1236 if (stat == Ok)
1237 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
1238 }
1239 if (stat == Ok)
1240 {
1241 metafile->bounds.X = header.X;
1242 metafile->bounds.Y = header.Y;
1243 metafile->bounds.Width = header.Width;
1244 metafile->bounds.Height = header.Height;
1245 }
1246 }
1247
1248 if (stat == Ok && metafile->record_stream)
1249 {
1250 BYTE *buffer;
1251 UINT buffer_size;
1252
1253 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1254
1255 buffer = heap_alloc(buffer_size);
1256 if (buffer)
1257 {
1258 HRESULT hr;
1259
1260 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1261
1262 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1263
1264 if (FAILED(hr))
1265 stat = hresult_to_status(hr);
1266
1267 heap_free(buffer);
1268 }
1269 else
1270 stat = OutOfMemory;
1271 }
1272
1273 if (metafile->record_stream)
1274 {
1275 IStream_Release(metafile->record_stream);
1276 metafile->record_stream = NULL;
1277 }
1278
1279 return stat;
1280 }
1281
1282 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1283 {
1284 TRACE("(%p,%p)\n", metafile, hEmf);
1285
1286 if (!metafile || !hEmf || !metafile->hemf)
1287 return InvalidParameter;
1288
1289 *hEmf = metafile->hemf;
1290 metafile->hemf = NULL;
1291
1292 return Ok;
1293 }
1294
1295 static void METAFILE_GetFinalGdiTransform(const GpMetafile *metafile, XFORM *result)
1296 {
1297 const GpRectF *rect;
1298 const GpPointF *pt;
1299
1300 /* This transforms metafile device space to output points. */
1301 rect = &metafile->src_rect;
1302 pt = metafile->playback_points;
1303 result->eM11 = (pt[1].X - pt[0].X) / rect->Width;
1304 result->eM21 = (pt[2].X - pt[0].X) / rect->Height;
1305 result->eDx = pt[0].X - result->eM11 * rect->X - result->eM21 * rect->Y;
1306 result->eM12 = (pt[1].Y - pt[0].Y) / rect->Width;
1307 result->eM22 = (pt[2].Y - pt[0].Y) / rect->Height;
1308 result->eDy = pt[0].Y - result->eM12 * rect->X - result->eM22 * rect->Y;
1309 }
1310
1311 static GpStatus METAFILE_PlaybackUpdateGdiTransform(GpMetafile *metafile)
1312 {
1313 XFORM combined, final;
1314
1315 METAFILE_GetFinalGdiTransform(metafile, &final);
1316
1317 CombineTransform(&combined, &metafile->gdiworldtransform, &final);
1318
1319 SetGraphicsMode(metafile->playback_dc, GM_ADVANCED);
1320 SetWorldTransform(metafile->playback_dc, &combined);
1321
1322 return Ok;
1323 }
1324
1325 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1326 {
1327 GpStatus stat = Ok;
1328
1329 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1330
1331 if (stat == Ok)
1332 {
1333 static const XFORM identity = {1, 0, 0, 1, 0, 0};
1334
1335 metafile->gdiworldtransform = identity;
1336 METAFILE_PlaybackUpdateGdiTransform(metafile);
1337 }
1338
1339 return stat;
1340 }
1341
1342 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1343 {
1344 if (metafile->playback_dc)
1345 {
1346 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1347 metafile->playback_dc = NULL;
1348 }
1349 }
1350
1351 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1352 {
1353 GpStatus stat;
1354 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1355 if (stat == Ok)
1356 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1357 return stat;
1358 }
1359
1360 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1361 {
1362 GpMatrix *real_transform;
1363 GpStatus stat;
1364
1365 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1366
1367 if (stat == Ok)
1368 {
1369 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1370
1371 if (metafile->page_unit != UnitDisplay)
1372 scale *= metafile->page_scale;
1373
1374 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1375
1376 if (stat == Ok)
1377 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1378
1379 if (stat == Ok)
1380 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1381
1382 GdipDeleteMatrix(real_transform);
1383 }
1384
1385 return stat;
1386 }
1387
1388 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
1389 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
1390 {
1391 GpStatus stat;
1392 GpMetafile *real_metafile = (GpMetafile*)metafile;
1393
1394 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
1395
1396 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
1397 return InvalidParameter;
1398
1399 if (recordType >= 1 && recordType <= 0x7a)
1400 {
1401 /* regular EMF record */
1402 if (metafile->playback_dc)
1403 {
1404 switch (recordType)
1405 {
1406 case EMR_SETMAPMODE:
1407 case EMR_SAVEDC:
1408 case EMR_RESTOREDC:
1409 case EMR_SETWINDOWORGEX:
1410 case EMR_SETWINDOWEXTEX:
1411 case EMR_SETVIEWPORTORGEX:
1412 case EMR_SETVIEWPORTEXTEX:
1413 case EMR_SCALEVIEWPORTEXTEX:
1414 case EMR_SCALEWINDOWEXTEX:
1415 case EMR_MODIFYWORLDTRANSFORM:
1416 FIXME("not implemented for record type %x\n", recordType);
1417 break;
1418 case EMR_SETWORLDTRANSFORM:
1419 {
1420 const XFORM* xform = (void*)data;
1421 real_metafile->gdiworldtransform = *xform;
1422 METAFILE_PlaybackUpdateGdiTransform(real_metafile);
1423 break;
1424 }
1425 case EMR_EXTSELECTCLIPRGN:
1426 {
1427 DWORD rgndatasize = *(DWORD*)data;
1428 DWORD mode = *(DWORD*)(data + 4);
1429 const RGNDATA *rgndata = (const RGNDATA*)(data + 8);
1430 HRGN hrgn = NULL;
1431
1432 if (dataSize > 8)
1433 {
1434 XFORM final;
1435
1436 METAFILE_GetFinalGdiTransform(metafile, &final);
1437
1438 hrgn = ExtCreateRegion(&final, rgndatasize, rgndata);
1439 }
1440
1441 ExtSelectClipRgn(metafile->playback_dc, hrgn, mode);
1442
1443 DeleteObject(hrgn);
1444
1445 return Ok;
1446 }
1447 default:
1448 {
1449 ENHMETARECORD *record = heap_alloc_zero(dataSize + 8);
1450
1451 if (record)
1452 {
1453 record->iType = recordType;
1454 record->nSize = dataSize + 8;
1455 memcpy(record->dParm, data, dataSize);
1456
1457 if(PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
1458 record, metafile->handle_count) == 0)
1459 ERR("PlayEnhMetaFileRecord failed\n");
1460
1461 heap_free(record);
1462 }
1463 else
1464 return OutOfMemory;
1465
1466 break;
1467 }
1468 }
1469 }
1470 }
1471 else
1472 {
1473 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
1474
1475 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
1476
1477 switch(recordType)
1478 {
1479 case EmfPlusRecordTypeHeader:
1480 case EmfPlusRecordTypeEndOfFile:
1481 break;
1482 case EmfPlusRecordTypeGetDC:
1483 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
1484 break;
1485 case EmfPlusRecordTypeClear:
1486 {
1487 EmfPlusClear *record = (EmfPlusClear*)header;
1488
1489 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
1490 }
1491 case EmfPlusRecordTypeFillRects:
1492 {
1493 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
1494 GpBrush *brush, *temp_brush=NULL;
1495 GpRectF *rects, *temp_rects=NULL;
1496
1497 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
1498 return InvalidParameter;
1499
1500 if (flags & 0x4000)
1501 {
1502 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
1503 return InvalidParameter;
1504 }
1505 else
1506 {
1507 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
1508 return InvalidParameter;
1509 }
1510
1511 if (flags & 0x8000)
1512 {
1513 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
1514 brush = temp_brush;
1515 }
1516 else
1517 {
1518 FIXME("brush deserialization not implemented\n");
1519 return NotImplemented;
1520 }
1521
1522 if (stat == Ok)
1523 {
1524 if (flags & 0x4000)
1525 {
1526 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
1527 int i;
1528
1529 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
1530 if (rects)
1531 {
1532 for (i=0; i<record->Count; i++)
1533 {
1534 rects[i].X = int_rects[i].X;
1535 rects[i].Y = int_rects[i].Y;
1536 rects[i].Width = int_rects[i].Width;
1537 rects[i].Height = int_rects[i].Height;
1538 }
1539 }
1540 else
1541 stat = OutOfMemory;
1542 }
1543 else
1544 rects = (GpRectF*)(record+1);
1545 }
1546
1547 if (stat == Ok)
1548 {
1549 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
1550 }
1551
1552 GdipDeleteBrush(temp_brush);
1553 heap_free(temp_rects);
1554
1555 return stat;
1556 }
1557 case EmfPlusRecordTypeSetClipRect:
1558 {
1559 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
1560 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
1561 GpRegion *region;
1562 GpMatrix world_to_device;
1563
1564 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
1565 return InvalidParameter;
1566
1567 stat = GdipCreateRegionRect(&record->ClipRect, &region);
1568
1569 if (stat == Ok)
1570 {
1571 get_graphics_transform(real_metafile->playback_graphics,
1572 CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
1573
1574 GdipTransformRegion(region, &world_to_device);
1575
1576 GdipCombineRegionRegion(real_metafile->clip, region, mode);
1577
1578 GdipDeleteRegion(region);
1579 }
1580
1581 return METAFILE_PlaybackUpdateClip(real_metafile);
1582 }
1583 case EmfPlusRecordTypeSetPageTransform:
1584 {
1585 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
1586 GpUnit unit = (GpUnit)flags;
1587
1588 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
1589 return InvalidParameter;
1590
1591 real_metafile->page_unit = unit;
1592 real_metafile->page_scale = record->PageScale;
1593
1594 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1595 }
1596 case EmfPlusRecordTypeSetWorldTransform:
1597 {
1598 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
1599
1600 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
1601 return InvalidParameter;
1602
1603 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
1604
1605 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1606 }
1607 case EmfPlusRecordTypeScaleWorldTransform:
1608 {
1609 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
1610 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1611
1612 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
1613 return InvalidParameter;
1614
1615 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
1616
1617 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1618 }
1619 case EmfPlusRecordTypeMultiplyWorldTransform:
1620 {
1621 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
1622 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1623 GpMatrix matrix;
1624
1625 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
1626 return InvalidParameter;
1627
1628 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
1629
1630 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
1631
1632 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1633 }
1634 case EmfPlusRecordTypeRotateWorldTransform:
1635 {
1636 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
1637 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1638
1639 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
1640 return InvalidParameter;
1641
1642 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
1643
1644 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1645 }
1646 case EmfPlusRecordTypeTranslateWorldTransform:
1647 {
1648 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
1649 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1650
1651 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
1652 return InvalidParameter;
1653
1654 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
1655
1656 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1657 }
1658 case EmfPlusRecordTypeResetWorldTransform:
1659 {
1660 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1661
1662 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1663 }
1664 case EmfPlusRecordTypeBeginContainer:
1665 {
1666 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
1667 container* cont;
1668 GpUnit unit;
1669 REAL scale_x, scale_y;
1670 GpRectF scaled_srcrect;
1671 GpMatrix transform;
1672
1673 cont = heap_alloc_zero(sizeof(*cont));
1674 if (!cont)
1675 return OutOfMemory;
1676
1677 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1678 if (stat != Ok)
1679 {
1680 heap_free(cont);
1681 return stat;
1682 }
1683
1684 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1685
1686 if (stat != Ok)
1687 {
1688 GdipDeleteRegion(cont->clip);
1689 heap_free(cont);
1690 return stat;
1691 }
1692
1693 cont->id = record->StackIndex;
1694 cont->type = BEGIN_CONTAINER;
1695 cont->world_transform = *metafile->world_transform;
1696 cont->page_unit = metafile->page_unit;
1697 cont->page_scale = metafile->page_scale;
1698 list_add_head(&real_metafile->containers, &cont->entry);
1699
1700 unit = record->Header.Flags & 0xff;
1701
1702 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
1703 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
1704
1705 scaled_srcrect.X = scale_x * record->SrcRect.X;
1706 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
1707 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
1708 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
1709
1710 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
1711 transform.matrix[1] = 0.0;
1712 transform.matrix[2] = 0.0;
1713 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
1714 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
1715 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
1716
1717 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
1718
1719 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1720 }
1721 case EmfPlusRecordTypeBeginContainerNoParams:
1722 case EmfPlusRecordTypeSave:
1723 {
1724 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1725 container* cont;
1726
1727 cont = heap_alloc_zero(sizeof(*cont));
1728 if (!cont)
1729 return OutOfMemory;
1730
1731 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1732 if (stat != Ok)
1733 {
1734 heap_free(cont);
1735 return stat;
1736 }
1737
1738 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1739 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1740 else
1741 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
1742
1743 if (stat != Ok)
1744 {
1745 GdipDeleteRegion(cont->clip);
1746 heap_free(cont);
1747 return stat;
1748 }
1749
1750 cont->id = record->StackIndex;
1751 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1752 cont->type = BEGIN_CONTAINER;
1753 else
1754 cont->type = SAVE_GRAPHICS;
1755 cont->world_transform = *metafile->world_transform;
1756 cont->page_unit = metafile->page_unit;
1757 cont->page_scale = metafile->page_scale;
1758 list_add_head(&real_metafile->containers, &cont->entry);
1759
1760 break;
1761 }
1762 case EmfPlusRecordTypeEndContainer:
1763 case EmfPlusRecordTypeRestore:
1764 {
1765 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1766 container* cont;
1767 enum container_type type;
1768 BOOL found=FALSE;
1769
1770 if (recordType == EmfPlusRecordTypeEndContainer)
1771 type = BEGIN_CONTAINER;
1772 else
1773 type = SAVE_GRAPHICS;
1774
1775 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
1776 {
1777 if (cont->id == record->StackIndex && cont->type == type)
1778 {
1779 found = TRUE;
1780 break;
1781 }
1782 }
1783
1784 if (found)
1785 {
1786 container* cont2;
1787
1788 /* pop any newer items on the stack */
1789 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
1790 {
1791 list_remove(&cont2->entry);
1792 GdipDeleteRegion(cont2->clip);
1793 heap_free(cont2);
1794 }
1795
1796 if (type == BEGIN_CONTAINER)
1797 GdipEndContainer(real_metafile->playback_graphics, cont->state);
1798 else
1799 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
1800
1801 *real_metafile->world_transform = cont->world_transform;
1802 real_metafile->page_unit = cont->page_unit;
1803 real_metafile->page_scale = cont->page_scale;
1804 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
1805
1806 list_remove(&cont->entry);
1807 GdipDeleteRegion(cont->clip);
1808 heap_free(cont);
1809 }
1810
1811 break;
1812 }
1813 default:
1814 FIXME("Not implemented for record type %x\n", recordType);
1815 return NotImplemented;
1816 }
1817 }
1818
1819 return Ok;
1820 }
1821
1822 struct enum_metafile_data
1823 {
1824 EnumerateMetafileProc callback;
1825 void *callback_data;
1826 GpMetafile *metafile;
1827 };
1828
1829 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1830 int nObj, LPARAM lpData)
1831 {
1832 BOOL ret;
1833 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
1834 const BYTE* pStr;
1835
1836 data->metafile->handle_table = lpHTable;
1837 data->metafile->handle_count = nObj;
1838
1839 /* First check for an EMF+ record. */
1840 if (lpEMFR->iType == EMR_GDICOMMENT)
1841 {
1842 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1843
1844 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1845 {
1846 int offset = 4;
1847
1848 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
1849 {
1850 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
1851
1852 if (record->DataSize)
1853 pStr = (const BYTE*)(record+1);
1854 else
1855 pStr = NULL;
1856
1857 ret = data->callback(record->Type, record->Flags, record->DataSize,
1858 pStr, data->callback_data);
1859
1860 if (!ret)
1861 return 0;
1862
1863 offset += record->Size;
1864 }
1865
1866 return 1;
1867 }
1868 }
1869
1870 if (lpEMFR->nSize != 8)
1871 pStr = (const BYTE*)lpEMFR->dParm;
1872 else
1873 pStr = NULL;
1874
1875 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
1876 pStr, data->callback_data);
1877 }
1878
1879 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
1880 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
1881 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
1882 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
1883 {
1884 struct enum_metafile_data data;
1885 GpStatus stat;
1886 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
1887 GraphicsContainer state;
1888 GpPath *dst_path;
1889
1890 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
1891 destPoints, count, srcRect, srcUnit, callback, callbackData,
1892 imageAttributes);
1893
1894 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
1895 return InvalidParameter;
1896
1897 if (!metafile->hemf)
1898 return InvalidParameter;
1899
1900 if (metafile->playback_graphics)
1901 return ObjectBusy;
1902
1903 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
1904 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
1905 debugstr_pointf(&destPoints[2]));
1906
1907 data.callback = callback;
1908 data.callback_data = callbackData;
1909 data.metafile = real_metafile;
1910
1911 real_metafile->playback_graphics = graphics;
1912 real_metafile->playback_dc = NULL;
1913 real_metafile->src_rect = *srcRect;
1914
1915 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
1916 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
1917
1918 if (stat == Ok)
1919 stat = GdipBeginContainer2(graphics, &state);
1920
1921 if (stat == Ok)
1922 {
1923 stat = GdipSetPageScale(graphics, 1.0);
1924
1925 if (stat == Ok)
1926 stat = GdipSetPageUnit(graphics, UnitPixel);
1927
1928 if (stat == Ok)
1929 stat = GdipResetWorldTransform(graphics);
1930
1931 if (stat == Ok)
1932 stat = GdipCreateRegion(&real_metafile->base_clip);
1933
1934 if (stat == Ok)
1935 stat = GdipGetClip(graphics, real_metafile->base_clip);
1936
1937 if (stat == Ok)
1938 stat = GdipCreateRegion(&real_metafile->clip);
1939
1940 if (stat == Ok)
1941 stat = GdipCreatePath(FillModeAlternate, &dst_path);
1942
1943 if (stat == Ok)
1944 {
1945 GpPointF clip_points[4];
1946
1947 clip_points[0] = real_metafile->playback_points[0];
1948 clip_points[1] = real_metafile->playback_points[1];
1949 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
1950 - real_metafile->playback_points[0].X;
1951 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
1952 - real_metafile->playback_points[0].Y;
1953 clip_points[3] = real_metafile->playback_points[2];
1954
1955 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
1956
1957 if (stat == Ok)
1958 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
1959
1960 GdipDeletePath(dst_path);
1961 }
1962
1963 if (stat == Ok)
1964 stat = GdipCreateMatrix(&real_metafile->world_transform);
1965
1966 if (stat == Ok)
1967 {
1968 real_metafile->page_unit = UnitDisplay;
1969 real_metafile->page_scale = 1.0;
1970 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1971 }
1972
1973 if (stat == Ok)
1974 {
1975 stat = METAFILE_PlaybackUpdateClip(real_metafile);
1976 }
1977
1978 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
1979 metafile->metafile_type == MetafileTypeWmfPlaceable ||
1980 metafile->metafile_type == MetafileTypeWmf))
1981 stat = METAFILE_PlaybackGetDC(real_metafile);
1982
1983 if (stat == Ok)
1984 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
1985
1986 METAFILE_PlaybackReleaseDC(real_metafile);
1987
1988 GdipDeleteMatrix(real_metafile->world_transform);
1989 real_metafile->world_transform = NULL;
1990
1991 GdipDeleteRegion(real_metafile->base_clip);
1992 real_metafile->base_clip = NULL;
1993
1994 GdipDeleteRegion(real_metafile->clip);
1995 real_metafile->clip = NULL;
1996
1997 while (list_head(&real_metafile->containers))
1998 {
1999 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
2000 list_remove(&cont->entry);
2001 GdipDeleteRegion(cont->clip);
2002 heap_free(cont);
2003 }
2004
2005 GdipEndContainer(graphics, state);
2006 }
2007
2008 real_metafile->playback_graphics = NULL;
2009
2010 return stat;
2011 }
2012
2013 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
2014 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
2015 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2016 {
2017 GpPointF points[3];
2018
2019 if (!graphics || !metafile || !dest) return InvalidParameter;
2020
2021 points[0].X = points[2].X = dest->X;
2022 points[0].Y = points[1].Y = dest->Y;
2023 points[1].X = dest->X + dest->Width;
2024 points[2].Y = dest->Y + dest->Height;
2025
2026 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
2027 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
2028 }
2029
2030 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
2031 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
2032 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2033 {
2034 GpRectF destf;
2035
2036 if (!graphics || !metafile || !dest) return InvalidParameter;
2037
2038 destf.X = dest->X;
2039 destf.Y = dest->Y;
2040 destf.Width = dest->Width;
2041 destf.Height = dest->Height;
2042
2043 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2044 }
2045
2046 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
2047 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
2048 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2049 {
2050 GpRectF destf;
2051
2052 if (!graphics || !metafile || !dest) return InvalidParameter;
2053
2054 destf.X = dest->X;
2055 destf.Y = dest->Y;
2056 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
2057 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
2058
2059 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
2060 }
2061
2062 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
2063 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
2064 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
2065 {
2066 GpPointF ptf;
2067
2068 if (!graphics || !metafile || !dest) return InvalidParameter;
2069
2070 ptf.X = dest->X;
2071 ptf.Y = dest->Y;
2072
2073 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
2074 }
2075
2076 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
2077 MetafileHeader * header)
2078 {
2079 GpStatus status;
2080
2081 TRACE("(%p, %p)\n", metafile, header);
2082
2083 if(!metafile || !header)
2084 return InvalidParameter;
2085
2086 if (metafile->hemf)
2087 {
2088 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
2089 if (status != Ok) return status;
2090 }
2091 else
2092 {
2093 memset(header, 0, sizeof(*header));
2094 header->Version = VERSION_MAGIC2;
2095 }
2096
2097 header->Type = metafile->metafile_type;
2098 header->DpiX = metafile->image.xres;
2099 header->DpiY = metafile->image.yres;
2100 header->Width = gdip_round(metafile->bounds.Width);
2101 header->Height = gdip_round(metafile->bounds.Height);
2102
2103 return Ok;
2104 }
2105
2106 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
2107 int nObj, LPARAM lpData)
2108 {
2109 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
2110
2111 if (lpEMFR->iType == EMR_GDICOMMENT)
2112 {
2113 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
2114
2115 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
2116 {
2117 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
2118
2119 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
2120 header->Type == EmfPlusRecordTypeHeader)
2121 {
2122 memcpy(dst_header, header, sizeof(*dst_header));
2123 }
2124 }
2125 }
2126 else if (lpEMFR->iType == EMR_HEADER)
2127 return TRUE;
2128
2129 return FALSE;
2130 }
2131
2132 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
2133 MetafileHeader *header)
2134 {
2135 ENHMETAHEADER3 emfheader;
2136 EmfPlusHeader emfplusheader;
2137 MetafileType metafile_type;
2138
2139 TRACE("(%p,%p)\n", hemf, header);
2140
2141 if(!hemf || !header)
2142 return InvalidParameter;
2143
2144 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
2145 return GenericError;
2146
2147 emfplusheader.Header.Type = 0;
2148
2149 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
2150
2151 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
2152 {
2153 if ((emfplusheader.Header.Flags & 1) == 1)
2154 metafile_type = MetafileTypeEmfPlusDual;
2155 else
2156 metafile_type = MetafileTypeEmfPlusOnly;
2157 }
2158 else
2159 metafile_type = MetafileTypeEmf;
2160
2161 header->Type = metafile_type;
2162 header->Size = emfheader.nBytes;
2163 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
2164 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
2165 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
2166 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
2167 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
2168 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
2169 header->u.EmfHeader = emfheader;
2170
2171 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
2172 {
2173 header->Version = emfplusheader.Version;
2174 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
2175 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
2176 header->LogicalDpiX = emfplusheader.LogicalDpiX;
2177 header->LogicalDpiY = emfplusheader.LogicalDpiY;
2178 }
2179 else
2180 {
2181 header->Version = emfheader.nVersion;
2182 header->EmfPlusFlags = 0;
2183 header->EmfPlusHeaderSize = 0;
2184 header->LogicalDpiX = 0;
2185 header->LogicalDpiY = 0;
2186 }
2187
2188 return Ok;
2189 }
2190
2191 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
2192 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
2193 {
2194 GpStatus status;
2195 GpMetafile *metafile;
2196
2197 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
2198
2199 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
2200 if (status == Ok)
2201 {
2202 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2203 GdipDisposeImage(&metafile->image);
2204 }
2205 return status;
2206 }
2207
2208 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
2209 MetafileHeader *header)
2210 {
2211 GpStatus status;
2212 GpMetafile *metafile;
2213
2214 TRACE("(%s,%p)\n", debugstr_w(filename), header);
2215
2216 if (!filename || !header)
2217 return InvalidParameter;
2218
2219 status = GdipCreateMetafileFromFile(filename, &metafile);
2220 if (status == Ok)
2221 {
2222 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2223 GdipDisposeImage(&metafile->image);
2224 }
2225 return status;
2226 }
2227
2228 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
2229 MetafileHeader *header)
2230 {
2231 GpStatus status;
2232 GpMetafile *metafile;
2233
2234 TRACE("(%p,%p)\n", stream, header);
2235
2236 if (!stream || !header)
2237 return InvalidParameter;
2238
2239 status = GdipCreateMetafileFromStream(stream, &metafile);
2240 if (status == Ok)
2241 {
2242 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
2243 GdipDisposeImage(&metafile->image);
2244 }
2245 return status;
2246 }
2247
2248 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
2249 GpMetafile **metafile)
2250 {
2251 GpStatus stat;
2252 MetafileHeader header;
2253
2254 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
2255
2256 if(!hemf || !metafile)
2257 return InvalidParameter;
2258
2259 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
2260 if (stat != Ok)
2261 return stat;
2262
2263 *metafile = heap_alloc_zero(sizeof(GpMetafile));
2264 if (!*metafile)
2265 return OutOfMemory;
2266
2267 (*metafile)->image.type = ImageTypeMetafile;
2268 (*metafile)->image.format = ImageFormatEMF;
2269 (*metafile)->image.frame_count = 1;
2270 (*metafile)->image.xres = header.DpiX;
2271 (*metafile)->image.yres = header.DpiY;
2272 (*metafile)->bounds.X = (REAL)header.u.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
2273 (*metafile)->bounds.Y = (REAL)header.u.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
2274 (*metafile)->bounds.Width = (REAL)(header.u.EmfHeader.rclFrame.right - header.u.EmfHeader.rclFrame.left)
2275 / 2540.0 * header.DpiX;
2276 (*metafile)->bounds.Height = (REAL)(header.u.EmfHeader.rclFrame.bottom - header.u.EmfHeader.rclFrame.top)
2277 / 2540.0 * header.DpiY;
2278 (*metafile)->unit = UnitPixel;
2279 (*metafile)->metafile_type = header.Type;
2280 (*metafile)->hemf = hemf;
2281 (*metafile)->preserve_hemf = !delete;
2282 list_init(&(*metafile)->containers);
2283
2284 TRACE("<-- %p\n", *metafile);
2285
2286 return Ok;
2287 }
2288
2289 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
2290 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2291 {
2292 UINT read;
2293 BYTE *copy;
2294 HENHMETAFILE hemf;
2295 GpStatus retval = Ok;
2296
2297 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
2298
2299 if(!hwmf || !metafile)
2300 return InvalidParameter;
2301
2302 *metafile = NULL;
2303 read = GetMetaFileBitsEx(hwmf, 0, NULL);
2304 if(!read)
2305 return GenericError;
2306 copy = heap_alloc_zero(read);
2307 GetMetaFileBitsEx(hwmf, read, copy);
2308
2309 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
2310 heap_free(copy);
2311
2312 /* FIXME: We should store and use hwmf instead of converting to hemf */
2313 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
2314
2315 if (retval == Ok)
2316 {
2317 if (placeable)
2318 {
2319 (*metafile)->image.xres = (REAL)placeable->Inch;
2320 (*metafile)->image.yres = (REAL)placeable->Inch;
2321 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
2322 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
2323 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
2324 placeable->BoundingBox.Left);
2325 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
2326 placeable->BoundingBox.Top);
2327 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
2328 }
2329 else
2330 (*metafile)->metafile_type = MetafileTypeWmf;
2331 (*metafile)->image.format = ImageFormatWMF;
2332
2333 if (delete) DeleteMetaFile(hwmf);
2334 }
2335 else
2336 DeleteEnhMetaFile(hemf);
2337 return retval;
2338 }
2339
2340 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
2341 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2342 {
2343 HMETAFILE hmf;
2344 HENHMETAFILE emf;
2345
2346 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
2347
2348 hmf = GetMetaFileW(file);
2349 if(hmf)
2350 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
2351
2352 emf = GetEnhMetaFileW(file);
2353 if(emf)
2354 return GdipCreateMetafileFromEmf(emf, TRUE, metafile);
2355
2356 return GenericError;
2357 }
2358
2359 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
2360 GpMetafile **metafile)
2361 {
2362 GpStatus status;
2363 IStream *stream;
2364
2365 TRACE("(%p, %p)\n", file, metafile);
2366
2367 if (!file || !metafile) return InvalidParameter;
2368
2369 *metafile = NULL;
2370
2371 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
2372 if (status == Ok)
2373 {
2374 status = GdipCreateMetafileFromStream(stream, metafile);
2375 IStream_Release(stream);
2376 }
2377 return status;
2378 }
2379
2380 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
2381 GpMetafile **metafile)
2382 {
2383 GpStatus stat;
2384
2385 TRACE("%p %p\n", stream, metafile);
2386
2387 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
2388 if (stat != Ok) return stat;
2389
2390 if ((*metafile)->image.type != ImageTypeMetafile)
2391 {
2392 GdipDisposeImage(&(*metafile)->image);
2393 *metafile = NULL;
2394 return GenericError;
2395 }
2396
2397 return Ok;
2398 }
2399
2400 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
2401 UINT limitDpi)
2402 {
2403 TRACE("(%p,%u)\n", metafile, limitDpi);
2404
2405 return Ok;
2406 }
2407
2408 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
2409 GpMetafile* metafile, BOOL* succ, EmfType emfType,
2410 const WCHAR* description, GpMetafile** out_metafile)
2411 {
2412 static int calls;
2413
2414 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
2415 debugstr_w(description), out_metafile);
2416
2417 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
2418 return InvalidParameter;
2419
2420 if(succ)
2421 *succ = FALSE;
2422 *out_metafile = NULL;
2423
2424 if(!(calls++))
2425 FIXME("not implemented\n");
2426
2427 return NotImplemented;
2428 }
2429
2430 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
2431 LPBYTE pData16, INT iMapMode, INT eFlags)
2432 {
2433 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
2434 return NotImplemented;
2435 }
2436
2437 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
2438 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
2439 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
2440 GpMetafile **metafile)
2441 {
2442 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2443 frameUnit, debugstr_w(desc), metafile);
2444
2445 return NotImplemented;
2446 }
2447
2448 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
2449 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
2450 GDIPCONST WCHAR *desc, GpMetafile **metafile)
2451 {
2452 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2453 frameUnit, debugstr_w(desc), metafile);
2454
2455 return NotImplemented;
2456 }
2457
2458 /*****************************************************************************
2459 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
2460 */
2461
2462 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
2463 GpMetafile* metafile, BOOL* conversionSuccess,
2464 const WCHAR* filename, EmfType emfType,
2465 const WCHAR* description, GpMetafile** out_metafile)
2466 {
2467 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
2468 return NotImplemented;
2469 }
2470
2471 static GpStatus METAFILE_CreateCompressedImageStream(GpImage *image, IStream **stream, DWORD *size)
2472 {
2473 LARGE_INTEGER zero;
2474 STATSTG statstg;
2475 GpStatus stat;
2476 HRESULT hr;
2477
2478 *size = 0;
2479
2480 hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
2481 if (FAILED(hr)) return hresult_to_status(hr);
2482
2483 stat = encode_image_png(image, *stream, NULL);
2484 if (stat != Ok)
2485 {
2486 IStream_Release(*stream);
2487 return stat;
2488 }
2489
2490 hr = IStream_Stat(*stream, &statstg, 1);
2491 if (FAILED(hr))
2492 {
2493 IStream_Release(*stream);
2494 return hresult_to_status(hr);
2495 }
2496 *size = statstg.cbSize.u.LowPart;
2497
2498 zero.QuadPart = 0;
2499 hr = IStream_Seek(*stream, zero, STREAM_SEEK_SET, NULL);
2500 if (FAILED(hr))
2501 {
2502 IStream_Release(*stream);
2503 return hresult_to_status(hr);
2504 }
2505
2506 return Ok;
2507 }
2508
2509 static GpStatus METAFILE_FillEmfPlusBitmap(EmfPlusBitmap *record, IStream *stream, DWORD size)
2510 {
2511 HRESULT hr;
2512
2513 record->Width = 0;
2514 record->Height = 0;
2515 record->Stride = 0;
2516 record->PixelFormat = 0;
2517 record->Type = BitmapDataTypeCompressed;
2518
2519 hr = IStream_Read(stream, record->BitmapData, size, NULL);
2520 if (FAILED(hr)) return hresult_to_status(hr);
2521 return Ok;
2522 }
2523
2524 static GpStatus METAFILE_AddImageObject(GpMetafile *metafile, GpImage *image, DWORD *id)
2525 {
2526 EmfPlusObject *object_record;
2527 GpStatus stat;
2528 DWORD size;
2529
2530 *id = -1;
2531
2532 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2533 return Ok;
2534
2535 if (image->type == ImageTypeBitmap)
2536 {
2537 IStream *stream;
2538 DWORD aligned_size;
2539
2540 stat = METAFILE_CreateCompressedImageStream(image, &stream, &size);
2541 if (stat != Ok) return stat;
2542 aligned_size = (size + 3) & ~3;
2543
2544 stat = METAFILE_AllocateRecord(metafile,
2545 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.bitmap.BitmapData[aligned_size]),
2546 (void**)&object_record);
2547 if (stat != Ok)
2548 {
2549 IStream_Release(stream);
2550 return stat;
2551 }
2552 memset(object_record->ObjectData.image.ImageData.bitmap.BitmapData + size, 0, aligned_size - size);
2553
2554 *id = METAFILE_AddObjectId(metafile);
2555 object_record->Header.Type = EmfPlusRecordTypeObject;
2556 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2557 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2558 object_record->ObjectData.image.Type = ImageDataTypeBitmap;
2559
2560 stat = METAFILE_FillEmfPlusBitmap(&object_record->ObjectData.image.ImageData.bitmap, stream, size);
2561 IStream_Release(stream);
2562 if (stat != Ok) METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2563 return stat;
2564 }
2565 else if (image->type == ImageTypeMetafile)
2566 {
2567 HENHMETAFILE hemf = ((GpMetafile*)image)->hemf;
2568 EmfPlusMetafile *metafile_record;
2569
2570 if (!hemf) return InvalidParameter;
2571
2572 size = GetEnhMetaFileBits(hemf, 0, NULL);
2573 if (!size) return GenericError;
2574
2575 stat = METAFILE_AllocateRecord(metafile,
2576 FIELD_OFFSET(EmfPlusObject, ObjectData.image.ImageData.metafile.MetafileData[size]),
2577 (void**)&object_record);
2578 if (stat != Ok) return stat;
2579
2580 *id = METAFILE_AddObjectId(metafile);
2581 object_record->Header.Type = EmfPlusRecordTypeObject;
2582 object_record->Header.Flags = *id | ObjectTypeImage << 8;
2583 object_record->ObjectData.image.Version = VERSION_MAGIC2;
2584 object_record->ObjectData.image.Type = ImageDataTypeMetafile;
2585 metafile_record = &object_record->ObjectData.image.ImageData.metafile;
2586 metafile_record->Type = ((GpMetafile*)image)->metafile_type;
2587 metafile_record->MetafileDataSize = size;
2588 if (GetEnhMetaFileBits(hemf, size, metafile_record->MetafileData) != size)
2589 {
2590 METAFILE_RemoveLastRecord(metafile, &object_record->Header);
2591 return GenericError;
2592 }
2593 return Ok;
2594 }
2595 else
2596 {
2597 FIXME("not supported image type (%d)\n", image->type);
2598 return NotImplemented;
2599 }
2600 }
2601
2602 static GpStatus METAFILE_AddImageAttributesObject(GpMetafile *metafile, const GpImageAttributes *attrs, DWORD *id)
2603 {
2604 EmfPlusObject *object_record;
2605 EmfPlusImageAttributes *attrs_record;
2606 GpStatus stat;
2607
2608 *id = -1;
2609
2610 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2611 return Ok;
2612
2613 if (!attrs)
2614 return Ok;
2615
2616 stat = METAFILE_AllocateRecord(metafile,
2617 FIELD_OFFSET(EmfPlusObject, ObjectData.image_attributes) + sizeof(EmfPlusImageAttributes),
2618 (void**)&object_record);
2619 if (stat != Ok) return stat;
2620
2621 *id = METAFILE_AddObjectId(metafile);
2622 object_record->Header.Type = EmfPlusRecordTypeObject;
2623 object_record->Header.Flags = *id | (ObjectTypeImageAttributes << 8);
2624 attrs_record = &object_record->ObjectData.image_attributes;
2625 attrs_record->Version = VERSION_MAGIC2;
2626 attrs_record->Reserved1 = 0;
2627 attrs_record->WrapMode = attrs->wrap;
2628 attrs_record->ClampColor.Blue = attrs->outside_color & 0xff;
2629 attrs_record->ClampColor.Green = (attrs->outside_color >> 8) & 0xff;
2630 attrs_record->ClampColor.Red = (attrs->outside_color >> 16) & 0xff;
2631 attrs_record->ClampColor.Alpha = attrs->outside_color >> 24;
2632 attrs_record->ObjectClamp = attrs->clamp;
2633 attrs_record->Reserved2 = 0;
2634 return Ok;
2635 }
2636
2637 GpStatus METAFILE_DrawImagePointsRect(GpMetafile *metafile, GpImage *image,
2638 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2639 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2640 DrawImageAbort callback, VOID *callbackData)
2641 {
2642 EmfPlusDrawImagePoints *draw_image_record;
2643 DWORD image_id, attributes_id;
2644 GpStatus stat;
2645
2646 if (count != 3) return InvalidParameter;
2647
2648 if (metafile->metafile_type == MetafileTypeEmf)
2649 {
2650 FIXME("MetafileTypeEmf metafiles not supported\n");
2651 return NotImplemented;
2652 }
2653 else
2654 FIXME("semi-stub\n");
2655
2656 if (!imageAttributes)
2657 {
2658 stat = METAFILE_AddImageObject(metafile, image, &image_id);
2659 }
2660 else if (image->type == ImageTypeBitmap)
2661 {
2662 INT width = ((GpBitmap*)image)->width;
2663 INT height = ((GpBitmap*)image)->height;
2664 GpGraphics *graphics;
2665 GpBitmap *bitmap;
2666
2667 stat = GdipCreateBitmapFromScan0(width, height,
2668 0, PixelFormat32bppARGB, NULL, &bitmap);
2669 if (stat != Ok) return stat;
2670
2671 stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
2672 if (stat != Ok)
2673 {
2674 GdipDisposeImage((GpImage*)bitmap);
2675 return stat;
2676 }
2677
2678 stat = GdipDrawImageRectRectI(graphics, image, 0, 0, width, height,
2679 0, 0, width, height, UnitPixel, imageAttributes, NULL, NULL);
2680 GdipDeleteGraphics(graphics);
2681 if (stat != Ok)
2682 {
2683 GdipDisposeImage((GpImage*)bitmap);
2684 return stat;
2685 }
2686
2687 stat = METAFILE_AddImageObject(metafile, (GpImage*)bitmap, &image_id);
2688 GdipDisposeImage((GpImage*)bitmap);
2689 }
2690 else
2691 {
2692 FIXME("imageAttributes not supported (image type %d)\n", image->type);
2693 return NotImplemented;
2694 }
2695 if (stat != Ok) return stat;
2696
2697 stat = METAFILE_AddImageAttributesObject(metafile, imageAttributes, &attributes_id);
2698 if (stat != Ok) return stat;
2699
2700 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawImagePoints), (void**)&draw_image_record);
2701 if (stat != Ok) return stat;
2702 draw_image_record->Header.Type = EmfPlusRecordTypeDrawImagePoints;
2703 draw_image_record->Header.Flags = image_id;
2704 draw_image_record->ImageAttributesID = attributes_id;
2705 draw_image_record->SrcUnit = UnitPixel;
2706 draw_image_record->SrcRect.X = units_to_pixels(srcx, srcUnit, metafile->image.xres);
2707 draw_image_record->SrcRect.Y = units_to_pixels(srcy, srcUnit, metafile->image.yres);
2708 draw_image_record->SrcRect.Width = units_to_pixels(srcwidth, srcUnit, metafile->image.xres);
2709 draw_image_record->SrcRect.Height = units_to_pixels(srcheight, srcUnit, metafile->image.yres);
2710 draw_image_record->count = 3;
2711 draw_image_record->PointData[0].pointF.X = points[0].X;
2712 draw_image_record->PointData[0].pointF.Y = points[0].Y;
2713 draw_image_record->PointData[1].pointF.X = points[1].X;
2714 draw_image_record->PointData[1].pointF.Y = points[1].Y;
2715 draw_image_record->PointData[2].pointF.X = points[2].X;
2716 draw_image_record->PointData[2].pointF.Y = points[2].Y;
2717 METAFILE_WriteRecords(metafile);
2718 return Ok;
2719 }
2720
2721 GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val)
2722 {
2723 EmfPlusRecordHeader *record;
2724 GpStatus stat;
2725
2726 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2727 return Ok;
2728
2729 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
2730 if (stat != Ok) return stat;
2731
2732 record->Type = prop;
2733 record->Flags = val;
2734
2735 METAFILE_WriteRecords(metafile);
2736 return Ok;
2737 }
2738
2739 static GpStatus METAFILE_AddPathObject(GpMetafile *metafile, GpPath *path, DWORD *id)
2740 {
2741 EmfPlusObject *object_record;
2742 GpStatus stat;
2743 DWORD size;
2744
2745 *id = -1;
2746 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2747 return Ok;
2748
2749 size = write_path_data(path, NULL);
2750 stat = METAFILE_AllocateRecord(metafile,
2751 FIELD_OFFSET(EmfPlusObject, ObjectData.path) + size,
2752 (void**)&object_record);
2753 if (stat != Ok) return stat;
2754
2755 *id = METAFILE_AddObjectId(metafile);
2756 object_record->Header.Type = EmfPlusRecordTypeObject;
2757 object_record->Header.Flags = *id | ObjectTypePath << 8;
2758 write_path_data(path, &object_record->ObjectData.path);
2759 return Ok;
2760 }
2761
2762 static GpStatus METAFILE_PrepareBrushData(GpBrush *brush, DWORD *size)
2763 {
2764 if (brush->bt == BrushTypeSolidColor)
2765 {
2766 *size = FIELD_OFFSET(EmfPlusBrush, BrushData.solid) + sizeof(EmfPlusSolidBrushData);
2767 return Ok;
2768 }
2769
2770 FIXME("unsupported brush type: %d\n", brush->bt);
2771 return NotImplemented;
2772 }
2773
2774 static void METAFILE_FillBrushData(GpBrush *brush, EmfPlusBrush *data)
2775 {
2776 if (brush->bt == BrushTypeSolidColor)
2777 {
2778 GpSolidFill *solid = (GpSolidFill*)brush;
2779
2780 data->Version = VERSION_MAGIC2;
2781 data->Type = solid->brush.bt;
2782 data->BrushData.solid.SolidColor.Blue = solid->color & 0xff;
2783 data->BrushData.solid.SolidColor.Green = (solid->color >> 8) & 0xff;
2784 data->BrushData.solid.SolidColor.Red = (solid->color >> 16) & 0xff;
2785 data->BrushData.solid.SolidColor.Alpha = solid->color >> 24;
2786 }
2787 }
2788
2789 static GpStatus METAFILE_AddPenObject(GpMetafile *metafile, GpPen *pen, DWORD *id)
2790 {
2791 DWORD i, data_flags, pen_data_size, brush_size;
2792 EmfPlusObject *object_record;
2793 EmfPlusPenData *pen_data;
2794 GpStatus stat;
2795 BOOL result;
2796
2797 *id = -1;
2798 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2799 return Ok;
2800
2801 data_flags = 0;
2802 pen_data_size = FIELD_OFFSET(EmfPlusPenData, OptionalData);
2803
2804 GdipIsMatrixIdentity(&pen->transform, &result);
2805 if (!result)
2806 {
2807 data_flags |= PenDataTransform;
2808 pen_data_size += sizeof(EmfPlusTransformMatrix);
2809 }
2810 if (pen->startcap != LineCapFlat)
2811 {
2812 data_flags |= PenDataStartCap;
2813 pen_data_size += sizeof(DWORD);
2814 }
2815 if (pen->endcap != LineCapFlat)
2816 {
2817 data_flags |= PenDataEndCap;
2818 pen_data_size += sizeof(DWORD);
2819 }
2820 if (pen->join != LineJoinMiter)
2821 {
2822 data_flags |= PenDataJoin;
2823 pen_data_size += sizeof(DWORD);
2824 }
2825 if (pen->miterlimit != 10.0)
2826 {
2827 data_flags |= PenDataMiterLimit;
2828 pen_data_size += sizeof(REAL);
2829 }
2830 if (pen->style != GP_DEFAULT_PENSTYLE)
2831 {
2832 data_flags |= PenDataLineStyle;
2833 pen_data_size += sizeof(DWORD);
2834 }
2835 if (pen->dashcap != DashCapFlat)
2836 {
2837 data_flags |= PenDataDashedLineCap;
2838 pen_data_size += sizeof(DWORD);
2839 }
2840 data_flags |= PenDataDashedLineOffset;
2841 pen_data_size += sizeof(REAL);
2842 if (pen->numdashes)
2843 {
2844 data_flags |= PenDataDashedLine;
2845 pen_data_size += sizeof(DWORD) + pen->numdashes*sizeof(REAL);
2846 }
2847 if (pen->align != PenAlignmentCenter)
2848 {
2849 data_flags |= PenDataNonCenter;
2850 pen_data_size += sizeof(DWORD);
2851 }
2852 /* TODO: Add support for PenDataCompoundLine */
2853 if (pen->customstart)
2854 {
2855 FIXME("ignoring custom start cup\n");
2856 }
2857 if (pen->customend)
2858 {
2859 FIXME("ignoring custom end cup\n");
2860 }
2861
2862 stat = METAFILE_PrepareBrushData(pen->brush, &brush_size);
2863 if (stat != Ok) return stat;
2864
2865 stat = METAFILE_AllocateRecord(metafile,
2866 FIELD_OFFSET(EmfPlusObject, ObjectData.pen.data) + pen_data_size + brush_size,
2867 (void**)&object_record);
2868 if (stat != Ok) return stat;
2869
2870 *id = METAFILE_AddObjectId(metafile);
2871 object_record->Header.Type = EmfPlusRecordTypeObject;
2872 object_record->Header.Flags = *id | ObjectTypePen << 8;
2873 object_record->ObjectData.pen.Version = VERSION_MAGIC2;
2874 object_record->ObjectData.pen.Type = 0;
2875
2876 pen_data = (EmfPlusPenData*)object_record->ObjectData.pen.data;
2877 pen_data->PenDataFlags = data_flags;
2878 pen_data->PenUnit = pen->unit;
2879 pen_data->PenWidth = pen->width;
2880
2881 i = 0;
2882 if (data_flags & PenDataTransform)
2883 {
2884 EmfPlusTransformMatrix *m = (EmfPlusTransformMatrix*)(pen_data->OptionalData + i);
2885 memcpy(m, &pen->transform, sizeof(*m));
2886 i += sizeof(EmfPlusTransformMatrix);
2887 }
2888 if (data_flags & PenDataStartCap)
2889 {
2890 *(DWORD*)(pen_data->OptionalData + i) = pen->startcap;
2891 i += sizeof(DWORD);
2892 }
2893 if (data_flags & PenDataEndCap)
2894 {
2895 *(DWORD*)(pen_data->OptionalData + i) = pen->endcap;
2896 i += sizeof(DWORD);
2897 }
2898 if (data_flags & PenDataJoin)
2899 {
2900 *(DWORD*)(pen_data->OptionalData + i) = pen->join;
2901 i += sizeof(DWORD);
2902 }
2903 if (data_flags & PenDataMiterLimit)
2904 {
2905 *(REAL*)(pen_data->OptionalData + i) = pen->miterlimit;
2906 i += sizeof(REAL);
2907 }
2908 if (data_flags & PenDataLineStyle)
2909 {
2910 switch (pen->style & PS_STYLE_MASK)
2911 {
2912 case PS_SOLID: *(DWORD*)(pen_data->OptionalData + i) = LineStyleSolid; break;
2913 case PS_DASH: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDash; break;
2914 case PS_DOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDot; break;
2915 case PS_DASHDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDot; break;
2916 case PS_DASHDOTDOT: *(DWORD*)(pen_data->OptionalData + i) = LineStyleDashDotDot; break;
2917 default: *(DWORD*)(pen_data->OptionalData + i) = LineStyleCustom; break;
2918 }
2919 i += sizeof(DWORD);
2920 }
2921 if (data_flags & PenDataDashedLineCap)
2922 {
2923 *(DWORD*)(pen_data->OptionalData + i) = pen->dashcap;
2924 i += sizeof(DWORD);
2925 }
2926 if (data_flags & PenDataDashedLineOffset)
2927 {
2928 *(REAL*)(pen_data->OptionalData + i) = pen->offset;
2929 i += sizeof(REAL);
2930 }
2931 if (data_flags & PenDataDashedLine)
2932 {
2933 int j;
2934
2935 *(DWORD*)(pen_data->OptionalData + i) = pen->numdashes;
2936 i += sizeof(DWORD);
2937
2938 for (j=0; j<pen->numdashes; j++)
2939 {
2940 *(REAL*)(pen_data->OptionalData + i) = pen->dashes[j];
2941 i += sizeof(REAL);
2942 }
2943 }
2944 if (data_flags & PenDataNonCenter)
2945 {
2946 *(REAL*)(pen_data->OptionalData + i) = pen->align;
2947 i += sizeof(DWORD);
2948 }
2949
2950 METAFILE_FillBrushData(pen->brush,
2951 (EmfPlusBrush*)(object_record->ObjectData.pen.data + pen_data_size));
2952 return Ok;
2953 }
2954
2955 GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path)
2956 {
2957 EmfPlusDrawPath *draw_path_record;
2958 DWORD path_id;
2959 DWORD pen_id;
2960 GpStatus stat;
2961
2962 if (metafile->metafile_type == MetafileTypeEmf)
2963 {
2964 FIXME("stub!\n");
2965 return NotImplemented;
2966 }
2967
2968 stat = METAFILE_AddPenObject(metafile, pen, &pen_id);
2969 if (stat != Ok) return stat;
2970
2971 stat = METAFILE_AddPathObject(metafile, path, &path_id);
2972 if (stat != Ok) return stat;
2973
2974 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusDrawPath), (void**)&draw_path_record);
2975 if (stat != Ok) return stat;
2976 draw_path_record->Header.Type = EmfPlusRecordTypeDrawPath;
2977 draw_path_record->Header.Flags = path_id;
2978 draw_path_record->PenId = pen_id;
2979
2980 METAFILE_WriteRecords(metafile);
2981 return Ok;
2982 }
2983
2984 static GpStatus METAFILE_AddBrushObject(GpMetafile *metafile, GpBrush *brush, DWORD *id)
2985 {
2986 EmfPlusObject *object_record;
2987 GpStatus stat;
2988 DWORD size;
2989
2990 *id = -1;
2991 if (metafile->metafile_type != MetafileTypeEmfPlusOnly && metafile->metafile_type != MetafileTypeEmfPlusDual)
2992 return Ok;
2993
2994 stat = METAFILE_PrepareBrushData(brush, &size);
2995 if (stat != Ok) return stat;
2996
2997 stat = METAFILE_AllocateRecord(metafile,
2998 FIELD_OFFSET(EmfPlusObject, ObjectData) + size, (void**)&object_record);
2999 if (stat != Ok) return stat;
3000
3001 *id = METAFILE_AddObjectId(metafile);
3002 object_record->Header.Type = EmfPlusRecordTypeObject;
3003 object_record->Header.Flags = *id | ObjectTypeBrush << 8;
3004 METAFILE_FillBrushData(brush, &object_record->ObjectData.brush);
3005 return Ok;
3006 }
3007
3008 GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
3009 {
3010 EmfPlusFillPath *fill_path_record;
3011 DWORD brush_id = -1, path_id;
3012 BOOL inline_color;
3013 GpStatus stat;
3014
3015 if (metafile->metafile_type == MetafileTypeEmf)
3016 {
3017 FIXME("stub!\n");
3018 return NotImplemented;
3019 }
3020
3021 inline_color = brush->bt == BrushTypeSolidColor;
3022 if (!inline_color)
3023 {
3024 stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
3025 if (stat != Ok) return stat;
3026 }
3027
3028 stat = METAFILE_AddPathObject(metafile, path, &path_id);
3029 if (stat != Ok) return stat;
3030
3031 stat = METAFILE_AllocateRecord(metafile,
3032 sizeof(EmfPlusFillPath), (void**)&fill_path_record);
3033 if (stat != Ok) return stat;
3034 fill_path_record->Header.Type = EmfPlusRecordTypeFillPath;
3035 if (inline_color)
3036 {
3037 fill_path_record->Header.Flags = 0x8000 | path_id;
3038 fill_path_record->data.Color.Blue = ((GpSolidFill*)brush)->color & 0xff;
3039 fill_path_record->data.Color.Green = (((GpSolidFill*)brush)->color >> 8) & 0xff;
3040 fill_path_record->data.Color.Red = (((GpSolidFill*)brush)->color >> 16) & 0xff;
3041 fill_path_record->data.Color.Alpha = ((GpSolidFill*)brush)->color >> 24;
3042 }
3043 else
3044 {
3045 fill_path_record->Header.Flags = path_id;
3046 fill_path_record->data.BrushId = brush_id;
3047 }
3048
3049 METAFILE_WriteRecords(metafile);
3050 return Ok;
3051 }