* Sync up to trunk HEAD (r62975).
[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 "gdiplus_private.h"
20
21 typedef struct EmfPlusRecordHeader
22 {
23 WORD Type;
24 WORD Flags;
25 DWORD Size;
26 DWORD DataSize;
27 } EmfPlusRecordHeader;
28
29 typedef struct EmfPlusHeader
30 {
31 EmfPlusRecordHeader Header;
32 DWORD Version;
33 DWORD EmfPlusFlags;
34 DWORD LogicalDpiX;
35 DWORD LogicalDpiY;
36 } EmfPlusHeader;
37
38 typedef struct EmfPlusFillRects
39 {
40 EmfPlusRecordHeader Header;
41 DWORD BrushID;
42 DWORD Count;
43 } EmfPlusFillRects;
44
45 typedef struct EmfPlusSetPageTransform
46 {
47 EmfPlusRecordHeader Header;
48 REAL PageScale;
49 } EmfPlusSetPageTransform;
50
51 typedef struct EmfPlusRect
52 {
53 SHORT X;
54 SHORT Y;
55 SHORT Width;
56 SHORT Height;
57 } EmfPlusRect;
58
59 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
60 {
61 DWORD size_needed;
62 EmfPlusRecordHeader *record;
63
64 if (!metafile->comment_data_size)
65 {
66 DWORD data_size = max(256, size * 2 + 4);
67 metafile->comment_data = GdipAlloc(data_size);
68
69 if (!metafile->comment_data)
70 return OutOfMemory;
71
72 memcpy(metafile->comment_data, "EMF+", 4);
73
74 metafile->comment_data_size = data_size;
75 metafile->comment_data_length = 4;
76 }
77
78 size_needed = size + metafile->comment_data_length;
79
80 if (size_needed > metafile->comment_data_size)
81 {
82 DWORD data_size = size_needed * 2;
83 BYTE *new_data = GdipAlloc(data_size);
84
85 if (!new_data)
86 return OutOfMemory;
87
88 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
89
90 metafile->comment_data_size = data_size;
91 GdipFree(metafile->comment_data);
92 metafile->comment_data = new_data;
93 }
94
95 *result = metafile->comment_data + metafile->comment_data_length;
96 metafile->comment_data_length += size;
97
98 record = (EmfPlusRecordHeader*)*result;
99 record->Size = size;
100 record->DataSize = size - sizeof(EmfPlusRecordHeader);
101
102 return Ok;
103 }
104
105 static void METAFILE_WriteRecords(GpMetafile *metafile)
106 {
107 if (metafile->comment_data_length > 4)
108 {
109 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
110 metafile->comment_data_length = 4;
111 }
112 }
113
114 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
115 {
116 GpStatus stat;
117
118 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
119 {
120 EmfPlusHeader *header;
121
122 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
123 if (stat != Ok)
124 return stat;
125
126 header->Header.Type = EmfPlusRecordTypeHeader;
127
128 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
129 header->Header.Flags = 1;
130 else
131 header->Header.Flags = 0;
132
133 header->Version = 0xDBC01002;
134
135 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
136 header->EmfPlusFlags = 1;
137 else
138 header->EmfPlusFlags = 0;
139
140 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
141 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
142
143 METAFILE_WriteRecords(metafile);
144 }
145
146 return Ok;
147 }
148
149 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
150 {
151 GpStatus stat;
152
153 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
154 {
155 EmfPlusRecordHeader *record;
156
157 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
158 if (stat != Ok)
159 return stat;
160
161 record->Type = EmfPlusRecordTypeEndOfFile;
162 record->Flags = 0;
163
164 METAFILE_WriteRecords(metafile);
165 }
166
167 return Ok;
168 }
169
170 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
171 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
172 {
173 HDC record_dc;
174 REAL framerect_factor_x, framerect_factor_y;
175 RECT rc;
176 GpStatus stat;
177
178 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
179
180 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
181 return InvalidParameter;
182
183 if (!frameRect)
184 {
185 FIXME("not implemented for NULL rect\n");
186 return NotImplemented;
187 }
188
189 switch (frameUnit)
190 {
191 case MetafileFrameUnitPixel:
192 framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX);
193 framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY);
194 break;
195 case MetafileFrameUnitPoint:
196 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
197 break;
198 case MetafileFrameUnitInch:
199 framerect_factor_x = framerect_factor_y = 2540.0;
200 break;
201 case MetafileFrameUnitDocument:
202 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
203 break;
204 case MetafileFrameUnitMillimeter:
205 framerect_factor_x = framerect_factor_y = 100.0;
206 break;
207 case MetafileFrameUnitGdi:
208 framerect_factor_x = framerect_factor_y = 1.0;
209 break;
210 default:
211 return InvalidParameter;
212 }
213
214 rc.left = framerect_factor_x * frameRect->X;
215 rc.top = framerect_factor_y * frameRect->Y;
216 rc.right = rc.left + framerect_factor_x * frameRect->Width;
217 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
218
219 record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
220
221 if (!record_dc)
222 return GenericError;
223
224 *metafile = GdipAlloc(sizeof(GpMetafile));
225 if(!*metafile)
226 {
227 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
228 return OutOfMemory;
229 }
230
231 (*metafile)->image.type = ImageTypeMetafile;
232 (*metafile)->image.picture = NULL;
233 (*metafile)->image.flags = ImageFlagsNone;
234 (*metafile)->image.palette = NULL;
235 (*metafile)->image.xres = 72.0;
236 (*metafile)->image.yres = 72.0;
237 (*metafile)->bounds = *frameRect;
238 (*metafile)->unit = frameUnit;
239 (*metafile)->metafile_type = type;
240 (*metafile)->record_dc = record_dc;
241 (*metafile)->comment_data = NULL;
242 (*metafile)->comment_data_size = 0;
243 (*metafile)->comment_data_length = 0;
244 (*metafile)->hemf = NULL;
245
246 stat = METAFILE_WriteHeader(*metafile, hdc);
247
248 if (stat != Ok)
249 {
250 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
251 GdipFree(*metafile);
252 *metafile = NULL;
253 return OutOfMemory;
254 }
255
256 return stat;
257 }
258
259 /*****************************************************************************
260 * GdipRecordMetafileI [GDIPLUS.@]
261 */
262 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
263 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
264 {
265 GpRectF frameRectF, *pFrameRectF;
266
267 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
268
269 if (frameRect)
270 {
271 frameRectF.X = frameRect->X;
272 frameRectF.Y = frameRect->Y;
273 frameRectF.Width = frameRect->Width;
274 frameRectF.Height = frameRect->Height;
275 pFrameRectF = &frameRectF;
276 }
277 else
278 pFrameRectF = NULL;
279
280 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
281 }
282
283 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
284 {
285 GpStatus stat;
286
287 if (!metafile->record_dc || metafile->record_graphics)
288 return InvalidParameter;
289
290 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
291
292 if (stat == Ok)
293 {
294 *result = metafile->record_graphics;
295 metafile->record_graphics->xres = 96.0;
296 metafile->record_graphics->yres = 96.0;
297 }
298
299 return stat;
300 }
301
302 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
303 {
304 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
305 {
306 EmfPlusRecordHeader *record;
307 GpStatus stat;
308
309 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
310 if (stat != Ok)
311 return stat;
312
313 record->Type = EmfPlusRecordTypeGetDC;
314 record->Flags = 0;
315
316 METAFILE_WriteRecords(metafile);
317 }
318
319 *hdc = metafile->record_dc;
320
321 return Ok;
322 }
323
324 static BOOL is_integer_rect(const GpRectF *rect)
325 {
326 SHORT x, y, width, height;
327 x = rect->X;
328 y = rect->Y;
329 width = rect->Width;
330 height = rect->Height;
331 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
332 rect->Width != (REAL)width || rect->Height != (REAL)height)
333 return FALSE;
334 return TRUE;
335 }
336
337 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
338 GDIPCONST GpRectF* rects, INT count)
339 {
340 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
341 {
342 EmfPlusFillRects *record;
343 GpStatus stat;
344 BOOL integer_rects = TRUE;
345 int i;
346 DWORD brushid;
347 int flags = 0;
348
349 if (brush->bt == BrushTypeSolidColor)
350 {
351 flags |= 0x8000;
352 brushid = ((GpSolidFill*)brush)->color;
353 }
354 else
355 {
356 FIXME("brush serialization not implemented\n");
357 return NotImplemented;
358 }
359
360 for (i=0; i<count; i++)
361 {
362 if (!is_integer_rect(&rects[i]))
363 {
364 integer_rects = FALSE;
365 break;
366 }
367 }
368
369 if (integer_rects)
370 flags |= 0x4000;
371
372 stat = METAFILE_AllocateRecord(metafile,
373 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
374 (void**)&record);
375 if (stat != Ok)
376 return stat;
377
378 record->Header.Type = EmfPlusRecordTypeFillRects;
379 record->Header.Flags = flags;
380 record->BrushID = brushid;
381 record->Count = count;
382
383 if (integer_rects)
384 {
385 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
386 for (i=0; i<count; i++)
387 {
388 record_rects[i].X = (SHORT)rects[i].X;
389 record_rects[i].Y = (SHORT)rects[i].Y;
390 record_rects[i].Width = (SHORT)rects[i].Width;
391 record_rects[i].Height = (SHORT)rects[i].Height;
392 }
393 }
394 else
395 memcpy(record+1, rects, sizeof(GpRectF) * count);
396
397 METAFILE_WriteRecords(metafile);
398 }
399
400 return Ok;
401 }
402
403 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
404 {
405 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
406 {
407 EmfPlusSetPageTransform *record;
408 GpStatus stat;
409
410 stat = METAFILE_AllocateRecord(metafile,
411 sizeof(EmfPlusSetPageTransform),
412 (void**)&record);
413 if (stat != Ok)
414 return stat;
415
416 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
417 record->Header.Flags = unit;
418 record->PageScale = scale;
419
420 METAFILE_WriteRecords(metafile);
421 }
422
423 return Ok;
424 }
425
426 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
427 {
428 if (hdc != metafile->record_dc)
429 return InvalidParameter;
430
431 return Ok;
432 }
433
434 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
435 {
436 GpStatus stat;
437
438 stat = METAFILE_WriteEndOfFile(metafile);
439 metafile->record_graphics = NULL;
440
441 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
442 metafile->record_dc = NULL;
443
444 GdipFree(metafile->comment_data);
445 metafile->comment_data = NULL;
446 metafile->comment_data_size = 0;
447
448 return stat;
449 }
450
451 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
452 {
453 TRACE("(%p,%p)\n", metafile, hEmf);
454
455 if (!metafile || !hEmf || !metafile->hemf)
456 return InvalidParameter;
457
458 *hEmf = metafile->hemf;
459 metafile->hemf = NULL;
460
461 return Ok;
462 }
463
464 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
465 {
466 GpStatus stat = Ok;
467
468 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
469
470 if (stat == Ok)
471 {
472 /* The result of GdipGetDC always expects device co-ordinates, but the
473 * device co-ordinates of the source metafile do not correspond to
474 * device co-ordinates of the destination. Therefore, we set up the DC
475 * so that the metafile's bounds map to the destination points where we
476 * are drawing this metafile. */
477 SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
478
479 SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
480 SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
481
482 SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
483 SetViewportExtEx(metafile->playback_dc,
484 metafile->playback_points[1].X - metafile->playback_points[0].X,
485 metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
486 }
487
488 return stat;
489 }
490
491 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
492 {
493 if (metafile->playback_dc)
494 {
495 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
496 metafile->playback_dc = NULL;
497 }
498 }
499
500 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
501 {
502 GpMatrix *real_transform;
503 GpStatus stat;
504
505 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
506
507 if (stat == Ok)
508 {
509 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
510
511 if (metafile->page_unit != UnitDisplay)
512 scale *= metafile->page_scale;
513
514 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
515
516 if (stat == Ok)
517 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
518
519 if (stat == Ok)
520 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
521
522 GdipDeleteMatrix(real_transform);
523 }
524
525 return stat;
526 }
527
528 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
529 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
530 {
531 GpStatus stat;
532 GpMetafile *real_metafile = (GpMetafile*)metafile;
533
534 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
535
536 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
537 return InvalidParameter;
538
539 if (recordType >= 1 && recordType <= 0x7a)
540 {
541 /* regular EMF record */
542 if (metafile->playback_dc)
543 {
544 ENHMETARECORD *record;
545
546 record = GdipAlloc(dataSize + 8);
547
548 if (record)
549 {
550 record->iType = recordType;
551 record->nSize = dataSize + 8;
552 memcpy(record->dParm, data, dataSize);
553
554 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
555 record, metafile->handle_count);
556
557 GdipFree(record);
558 }
559 else
560 return OutOfMemory;
561 }
562 }
563 else
564 {
565 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
566
567 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
568
569 switch(recordType)
570 {
571 case EmfPlusRecordTypeHeader:
572 case EmfPlusRecordTypeEndOfFile:
573 break;
574 case EmfPlusRecordTypeGetDC:
575 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
576 break;
577 case EmfPlusRecordTypeFillRects:
578 {
579 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
580 GpBrush *brush, *temp_brush=NULL;
581 GpRectF *rects, *temp_rects=NULL;
582
583 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
584 return InvalidParameter;
585
586 if (flags & 0x4000)
587 {
588 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
589 return InvalidParameter;
590 }
591 else
592 {
593 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
594 return InvalidParameter;
595 }
596
597 if (flags & 0x8000)
598 {
599 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
600 brush = temp_brush;
601 }
602 else
603 {
604 FIXME("brush deserialization not implemented\n");
605 return NotImplemented;
606 }
607
608 if (stat == Ok)
609 {
610 if (flags & 0x4000)
611 {
612 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
613 int i;
614
615 rects = temp_rects = GdipAlloc(sizeof(GpRectF) * record->Count);
616 if (rects)
617 {
618 for (i=0; i<record->Count; i++)
619 {
620 rects[i].X = int_rects[i].X;
621 rects[i].Y = int_rects[i].Y;
622 rects[i].Width = int_rects[i].Width;
623 rects[i].Height = int_rects[i].Height;
624 }
625 }
626 else
627 stat = OutOfMemory;
628 }
629 else
630 rects = (GpRectF*)(record+1);
631 }
632
633 if (stat == Ok)
634 {
635 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
636 }
637
638 GdipDeleteBrush(temp_brush);
639 GdipFree(temp_rects);
640
641 return stat;
642 }
643 case EmfPlusRecordTypeSetPageTransform:
644 {
645 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
646 GpUnit unit = (GpUnit)flags;
647
648 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
649 return InvalidParameter;
650
651 real_metafile->page_unit = unit;
652 real_metafile->page_scale = record->PageScale;
653
654 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
655 }
656 default:
657 FIXME("Not implemented for record type %x\n", recordType);
658 return NotImplemented;
659 }
660 }
661
662 return Ok;
663 }
664
665 struct enum_metafile_data
666 {
667 EnumerateMetafileProc callback;
668 void *callback_data;
669 GpMetafile *metafile;
670 };
671
672 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
673 int nObj, LPARAM lpData)
674 {
675 BOOL ret;
676 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
677 const BYTE* pStr;
678
679 data->metafile->handle_table = lpHTable;
680 data->metafile->handle_count = nObj;
681
682 /* First check for an EMF+ record. */
683 if (lpEMFR->iType == EMR_GDICOMMENT)
684 {
685 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
686
687 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
688 {
689 int offset = 4;
690
691 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
692 {
693 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
694
695 if (record->DataSize)
696 pStr = (const BYTE*)(record+1);
697 else
698 pStr = NULL;
699
700 ret = data->callback(record->Type, record->Flags, record->DataSize,
701 pStr, data->callback_data);
702
703 if (!ret)
704 return 0;
705
706 offset += record->Size;
707 }
708
709 return 1;
710 }
711 }
712
713 if (lpEMFR->nSize != 8)
714 pStr = (const BYTE*)lpEMFR->dParm;
715 else
716 pStr = NULL;
717
718 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
719 pStr, data->callback_data);
720 }
721
722 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
723 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
724 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
725 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
726 {
727 struct enum_metafile_data data;
728 GpStatus stat;
729 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
730 GraphicsContainer state;
731
732 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
733 destPoints, count, srcRect, srcUnit, callback, callbackData,
734 imageAttributes);
735
736 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
737 return InvalidParameter;
738
739 if (!metafile->hemf)
740 return InvalidParameter;
741
742 if (metafile->playback_graphics)
743 return ObjectBusy;
744
745 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
746 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
747 debugstr_pointf(&destPoints[2]));
748
749 data.callback = callback;
750 data.callback_data = callbackData;
751 data.metafile = real_metafile;
752
753 real_metafile->playback_graphics = graphics;
754 real_metafile->playback_dc = NULL;
755 real_metafile->src_rect = *srcRect;
756
757 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
758 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
759
760 if (stat == Ok)
761 stat = GdipBeginContainer2(graphics, &state);
762
763 if (stat == Ok)
764 {
765 stat = GdipSetPageScale(graphics, 1.0);
766
767 if (stat == Ok)
768 stat = GdipSetPageUnit(graphics, UnitPixel);
769
770 if (stat == Ok)
771 stat = GdipCreateMatrix(&real_metafile->world_transform);
772
773 if (stat == Ok)
774 {
775 real_metafile->page_unit = UnitDisplay;
776 real_metafile->page_scale = 1.0;
777 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
778 }
779
780 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
781 metafile->metafile_type == MetafileTypeWmfPlaceable ||
782 metafile->metafile_type == MetafileTypeWmf))
783 stat = METAFILE_PlaybackGetDC(real_metafile);
784
785 if (stat == Ok)
786 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
787
788 METAFILE_PlaybackReleaseDC(real_metafile);
789
790 GdipDeleteMatrix(real_metafile->world_transform);
791 real_metafile->world_transform = NULL;
792
793 GdipEndContainer(graphics, state);
794 }
795
796 real_metafile->playback_graphics = NULL;
797
798 return stat;
799 }
800
801 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
802 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
803 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
804 {
805 GpPointF points[3];
806
807 if (!graphics || !metafile || !dest) return InvalidParameter;
808
809 points[0].X = points[2].X = dest->X;
810 points[0].Y = points[1].Y = dest->Y;
811 points[1].X = dest->X + dest->Width;
812 points[2].Y = dest->Y + dest->Height;
813
814 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
815 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
816 }
817
818 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
819 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
820 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
821 {
822 GpRectF destf;
823
824 if (!graphics || !metafile || !dest) return InvalidParameter;
825
826 destf.X = dest->X;
827 destf.Y = dest->Y;
828 destf.Width = dest->Width;
829 destf.Height = dest->Height;
830
831 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
832 }
833
834 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
835 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
836 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
837 {
838 GpRectF destf;
839
840 if (!graphics || !metafile || !dest) return InvalidParameter;
841
842 destf.X = dest->X;
843 destf.Y = dest->Y;
844 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
845 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
846
847 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
848 }
849
850 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
851 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
852 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
853 {
854 GpPointF ptf;
855
856 if (!graphics || !metafile || !dest) return InvalidParameter;
857
858 ptf.X = dest->X;
859 ptf.Y = dest->Y;
860
861 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
862 }
863
864 static int CALLBACK get_metafile_type_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
865 int nObj, LPARAM lpData)
866 {
867 MetafileType *result = (MetafileType*)lpData;
868
869 if (lpEMFR->iType == EMR_GDICOMMENT)
870 {
871 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
872
873 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
874 {
875 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
876
877 if (4 + sizeof(EmfPlusRecordHeader) <= comment->cbData &&
878 header->Type == EmfPlusRecordTypeHeader)
879 {
880 if ((header->Flags & 1) == 1)
881 *result = MetafileTypeEmfPlusDual;
882 else
883 *result = MetafileTypeEmfPlusOnly;
884 }
885 }
886 else
887 *result = MetafileTypeEmf;
888 }
889 else
890 *result = MetafileTypeEmf;
891
892 return FALSE;
893 }
894
895 static MetafileType METAFILE_GetEmfType(HENHMETAFILE hemf)
896 {
897 MetafileType result = MetafileTypeInvalid;
898 EnumEnhMetaFile(NULL, hemf, get_metafile_type_proc, &result, NULL);
899 return result;
900 }
901
902 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
903 MetafileHeader * header)
904 {
905 static int calls;
906
907 TRACE("(%p, %p)\n", metafile, header);
908
909 if(!metafile || !header)
910 return InvalidParameter;
911
912 if(!(calls++))
913 FIXME("not implemented\n");
914
915 memset(header, 0, sizeof(MetafileHeader));
916
917 return Ok;
918 }
919
920 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hEmf,
921 MetafileHeader *header)
922 {
923 static int calls;
924
925 if(!hEmf || !header)
926 return InvalidParameter;
927
928 if(!(calls++))
929 FIXME("not implemented\n");
930
931 memset(header, 0, sizeof(MetafileHeader));
932
933 return Ok;
934 }
935
936 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
937 MetafileHeader *header)
938 {
939 static int calls;
940
941 TRACE("(%s,%p)\n", debugstr_w(filename), header);
942
943 if(!filename || !header)
944 return InvalidParameter;
945
946 if(!(calls++))
947 FIXME("not implemented\n");
948
949 memset(header, 0, sizeof(MetafileHeader));
950
951 return Ok;
952 }
953
954 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
955 MetafileHeader *header)
956 {
957 static int calls;
958
959 TRACE("(%p,%p)\n", stream, header);
960
961 if(!stream || !header)
962 return InvalidParameter;
963
964 if(!(calls++))
965 FIXME("not implemented\n");
966
967 memset(header, 0, sizeof(MetafileHeader));
968
969 return Ok;
970 }
971
972 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
973 GpMetafile **metafile)
974 {
975 ENHMETAHEADER header;
976 MetafileType metafile_type;
977
978 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
979
980 if(!hemf || !metafile)
981 return InvalidParameter;
982
983 if (GetEnhMetaFileHeader(hemf, sizeof(header), &header) == 0)
984 return GenericError;
985
986 metafile_type = METAFILE_GetEmfType(hemf);
987
988 if (metafile_type == MetafileTypeInvalid)
989 return GenericError;
990
991 *metafile = GdipAlloc(sizeof(GpMetafile));
992 if (!*metafile)
993 return OutOfMemory;
994
995 (*metafile)->image.type = ImageTypeMetafile;
996 (*metafile)->image.format = ImageFormatEMF;
997 (*metafile)->image.frame_count = 1;
998 (*metafile)->image.xres = (REAL)header.szlDevice.cx;
999 (*metafile)->image.yres = (REAL)header.szlDevice.cy;
1000 (*metafile)->bounds.X = (REAL)header.rclBounds.left;
1001 (*metafile)->bounds.Y = (REAL)header.rclBounds.top;
1002 (*metafile)->bounds.Width = (REAL)(header.rclBounds.right - header.rclBounds.left);
1003 (*metafile)->bounds.Height = (REAL)(header.rclBounds.bottom - header.rclBounds.top);
1004 (*metafile)->unit = UnitPixel;
1005 (*metafile)->metafile_type = metafile_type;
1006 (*metafile)->hemf = hemf;
1007 (*metafile)->preserve_hemf = !delete;
1008
1009 TRACE("<-- %p\n", *metafile);
1010
1011 return Ok;
1012 }
1013
1014 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
1015 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1016 {
1017 UINT read;
1018 BYTE *copy;
1019 HENHMETAFILE hemf;
1020 GpStatus retval = Ok;
1021
1022 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
1023
1024 if(!hwmf || !metafile || !placeable)
1025 return InvalidParameter;
1026
1027 *metafile = NULL;
1028 read = GetMetaFileBitsEx(hwmf, 0, NULL);
1029 if(!read)
1030 return GenericError;
1031 copy = GdipAlloc(read);
1032 GetMetaFileBitsEx(hwmf, read, copy);
1033
1034 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1035 GdipFree(copy);
1036
1037 /* FIXME: We should store and use hwmf instead of converting to hemf */
1038 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
1039
1040 if (retval == Ok)
1041 {
1042 (*metafile)->image.xres = (REAL)placeable->Inch;
1043 (*metafile)->image.yres = (REAL)placeable->Inch;
1044 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
1045 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
1046 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
1047 placeable->BoundingBox.Left);
1048 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
1049 placeable->BoundingBox.Top);
1050 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
1051 (*metafile)->image.format = ImageFormatWMF;
1052
1053 if (delete) DeleteMetaFile(hwmf);
1054 }
1055 else
1056 DeleteEnhMetaFile(hemf);
1057 return retval;
1058 }
1059
1060 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1061 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1062 {
1063 HMETAFILE hmf = GetMetaFileW(file);
1064
1065 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1066
1067 if(!hmf) return InvalidParameter;
1068
1069 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1070 }
1071
1072 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
1073 GpMetafile **metafile)
1074 {
1075 FIXME("(%p, %p): stub\n", file, metafile);
1076 return NotImplemented;
1077 }
1078
1079 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
1080 GpMetafile **metafile)
1081 {
1082 FIXME("(%p, %p): stub\n", stream, metafile);
1083 return NotImplemented;
1084 }
1085
1086 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
1087 UINT limitDpi)
1088 {
1089 static int calls;
1090
1091 TRACE("(%p,%u)\n", metafile, limitDpi);
1092
1093 if(!(calls++))
1094 FIXME("not implemented\n");
1095
1096 return NotImplemented;
1097 }
1098
1099 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
1100 GpMetafile* metafile, BOOL* succ, EmfType emfType,
1101 const WCHAR* description, GpMetafile** out_metafile)
1102 {
1103 static int calls;
1104
1105 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
1106 debugstr_w(description), out_metafile);
1107
1108 if(!ref || !metafile || !out_metafile)
1109 return InvalidParameter;
1110
1111 *succ = FALSE;
1112 *out_metafile = NULL;
1113
1114 if(!(calls++))
1115 FIXME("not implemented\n");
1116
1117 return NotImplemented;
1118 }
1119
1120 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
1121 LPBYTE pData16, INT iMapMode, INT eFlags)
1122 {
1123 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
1124 return NotImplemented;
1125 }
1126
1127 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
1128 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
1129 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
1130 GpMetafile **metafile)
1131 {
1132 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1133 frameUnit, debugstr_w(desc), metafile);
1134
1135 return NotImplemented;
1136 }
1137
1138 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
1139 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
1140 GDIPCONST WCHAR *desc, GpMetafile **metafile)
1141 {
1142 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1143 frameUnit, debugstr_w(desc), metafile);
1144
1145 return NotImplemented;
1146 }
1147
1148 /*****************************************************************************
1149 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
1150 */
1151
1152 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
1153 GpMetafile* metafile, BOOL* conversionSuccess,
1154 const WCHAR* filename, EmfType emfType,
1155 const WCHAR* description, GpMetafile** out_metafile)
1156 {
1157 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
1158 return NotImplemented;
1159 }