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