TESTING -- override pthreads to fix gstreamer v5
[wine/multimedia.git] / dlls / gdiplus / metafile.c
blob922c101d8c4b3c33cf6c4668913e03b179bff83e
1 /*
2 * Copyright (C) 2011 Vincent Povirk for CodeWeavers
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.
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.
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
19 #include <stdarg.h>
20 #include <math.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "wine/unicode.h"
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "ocidl.h"
30 #include "olectl.h"
31 #include "ole2.h"
33 #include "winreg.h"
34 #include "shlwapi.h"
36 #include "gdiplus.h"
37 #include "gdiplus_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43 typedef struct EmfPlusRecordHeader
45 WORD Type;
46 WORD Flags;
47 DWORD Size;
48 DWORD DataSize;
49 } EmfPlusRecordHeader;
51 typedef struct EmfPlusHeader
53 EmfPlusRecordHeader Header;
54 DWORD Version;
55 DWORD EmfPlusFlags;
56 DWORD LogicalDpiX;
57 DWORD LogicalDpiY;
58 } EmfPlusHeader;
60 typedef struct EmfPlusFillRects
62 EmfPlusRecordHeader Header;
63 DWORD BrushID;
64 DWORD Count;
65 } EmfPlusFillRects;
67 typedef struct EmfPlusSetPageTransform
69 EmfPlusRecordHeader Header;
70 REAL PageScale;
71 } EmfPlusSetPageTransform;
73 typedef struct EmfPlusRect
75 SHORT X;
76 SHORT Y;
77 SHORT Width;
78 SHORT Height;
79 } EmfPlusRect;
81 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
83 DWORD size_needed;
84 EmfPlusRecordHeader *record;
86 if (!metafile->comment_data_size)
88 DWORD data_size = max(256, size * 2 + 4);
89 metafile->comment_data = heap_alloc_zero(data_size);
91 if (!metafile->comment_data)
92 return OutOfMemory;
94 memcpy(metafile->comment_data, "EMF+", 4);
96 metafile->comment_data_size = data_size;
97 metafile->comment_data_length = 4;
100 size_needed = size + metafile->comment_data_length;
102 if (size_needed > metafile->comment_data_size)
104 DWORD data_size = size_needed * 2;
105 BYTE *new_data = heap_alloc_zero(data_size);
107 if (!new_data)
108 return OutOfMemory;
110 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
112 metafile->comment_data_size = data_size;
113 heap_free(metafile->comment_data);
114 metafile->comment_data = new_data;
117 *result = metafile->comment_data + metafile->comment_data_length;
118 metafile->comment_data_length += size;
120 record = (EmfPlusRecordHeader*)*result;
121 record->Size = size;
122 record->DataSize = size - sizeof(EmfPlusRecordHeader);
124 return Ok;
127 static void METAFILE_WriteRecords(GpMetafile *metafile)
129 if (metafile->comment_data_length > 4)
131 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
132 metafile->comment_data_length = 4;
136 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
138 GpStatus stat;
140 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
142 EmfPlusHeader *header;
144 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
145 if (stat != Ok)
146 return stat;
148 header->Header.Type = EmfPlusRecordTypeHeader;
150 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
151 header->Header.Flags = 1;
152 else
153 header->Header.Flags = 0;
155 header->Version = 0xDBC01002;
157 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
158 header->EmfPlusFlags = 1;
159 else
160 header->EmfPlusFlags = 0;
162 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
163 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
165 METAFILE_WriteRecords(metafile);
168 return Ok;
171 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
173 GpStatus stat;
175 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
177 EmfPlusRecordHeader *record;
179 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
180 if (stat != Ok)
181 return stat;
183 record->Type = EmfPlusRecordTypeEndOfFile;
184 record->Flags = 0;
186 METAFILE_WriteRecords(metafile);
189 return Ok;
192 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
193 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
195 HDC record_dc;
196 REAL dpix, dpiy;
197 REAL framerect_factor_x, framerect_factor_y;
198 RECT rc;
199 GpStatus stat;
201 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
203 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
204 return InvalidParameter;
206 if (!frameRect)
208 FIXME("not implemented for NULL rect\n");
209 return NotImplemented;
212 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
213 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
215 switch (frameUnit)
217 case MetafileFrameUnitPixel:
218 framerect_factor_x = 2540.0 / dpix;
219 framerect_factor_y = 2540.0 / dpiy;
220 break;
221 case MetafileFrameUnitPoint:
222 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
223 break;
224 case MetafileFrameUnitInch:
225 framerect_factor_x = framerect_factor_y = 2540.0;
226 break;
227 case MetafileFrameUnitDocument:
228 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
229 break;
230 case MetafileFrameUnitMillimeter:
231 framerect_factor_x = framerect_factor_y = 100.0;
232 break;
233 case MetafileFrameUnitGdi:
234 framerect_factor_x = framerect_factor_y = 1.0;
235 break;
236 default:
237 return InvalidParameter;
240 rc.left = framerect_factor_x * frameRect->X;
241 rc.top = framerect_factor_y * frameRect->Y;
242 rc.right = rc.left + framerect_factor_x * frameRect->Width;
243 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
245 record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
247 if (!record_dc)
248 return GenericError;
250 *metafile = heap_alloc_zero(sizeof(GpMetafile));
251 if(!*metafile)
253 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
254 return OutOfMemory;
257 (*metafile)->image.type = ImageTypeMetafile;
258 (*metafile)->image.picture = NULL;
259 (*metafile)->image.flags = ImageFlagsNone;
260 (*metafile)->image.palette = NULL;
261 (*metafile)->image.xres = dpix;
262 (*metafile)->image.yres = dpiy;
263 (*metafile)->bounds = *frameRect;
264 (*metafile)->unit = frameUnit;
265 (*metafile)->metafile_type = type;
266 (*metafile)->record_dc = record_dc;
267 (*metafile)->comment_data = NULL;
268 (*metafile)->comment_data_size = 0;
269 (*metafile)->comment_data_length = 0;
270 (*metafile)->hemf = NULL;
272 stat = METAFILE_WriteHeader(*metafile, hdc);
274 if (stat != Ok)
276 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
277 heap_free(*metafile);
278 *metafile = NULL;
279 return OutOfMemory;
282 return stat;
285 /*****************************************************************************
286 * GdipRecordMetafileI [GDIPLUS.@]
288 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
289 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
291 GpRectF frameRectF, *pFrameRectF;
293 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
295 if (frameRect)
297 frameRectF.X = frameRect->X;
298 frameRectF.Y = frameRect->Y;
299 frameRectF.Width = frameRect->Width;
300 frameRectF.Height = frameRect->Height;
301 pFrameRectF = &frameRectF;
303 else
304 pFrameRectF = NULL;
306 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
309 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
311 GpStatus stat;
313 if (!metafile->record_dc || metafile->record_graphics)
314 return InvalidParameter;
316 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
318 if (stat == Ok)
320 *result = metafile->record_graphics;
321 metafile->record_graphics->xres = 96.0;
322 metafile->record_graphics->yres = 96.0;
325 return stat;
328 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
330 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
332 EmfPlusRecordHeader *record;
333 GpStatus stat;
335 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
336 if (stat != Ok)
337 return stat;
339 record->Type = EmfPlusRecordTypeGetDC;
340 record->Flags = 0;
342 METAFILE_WriteRecords(metafile);
345 *hdc = metafile->record_dc;
347 return Ok;
350 static BOOL is_integer_rect(const GpRectF *rect)
352 SHORT x, y, width, height;
353 x = rect->X;
354 y = rect->Y;
355 width = rect->Width;
356 height = rect->Height;
357 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
358 rect->Width != (REAL)width || rect->Height != (REAL)height)
359 return FALSE;
360 return TRUE;
363 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
364 GDIPCONST GpRectF* rects, INT count)
366 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
368 EmfPlusFillRects *record;
369 GpStatus stat;
370 BOOL integer_rects = TRUE;
371 int i;
372 DWORD brushid;
373 int flags = 0;
375 if (brush->bt == BrushTypeSolidColor)
377 flags |= 0x8000;
378 brushid = ((GpSolidFill*)brush)->color;
380 else
382 FIXME("brush serialization not implemented\n");
383 return NotImplemented;
386 for (i=0; i<count; i++)
388 if (!is_integer_rect(&rects[i]))
390 integer_rects = FALSE;
391 break;
395 if (integer_rects)
396 flags |= 0x4000;
398 stat = METAFILE_AllocateRecord(metafile,
399 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
400 (void**)&record);
401 if (stat != Ok)
402 return stat;
404 record->Header.Type = EmfPlusRecordTypeFillRects;
405 record->Header.Flags = flags;
406 record->BrushID = brushid;
407 record->Count = count;
409 if (integer_rects)
411 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
412 for (i=0; i<count; i++)
414 record_rects[i].X = (SHORT)rects[i].X;
415 record_rects[i].Y = (SHORT)rects[i].Y;
416 record_rects[i].Width = (SHORT)rects[i].Width;
417 record_rects[i].Height = (SHORT)rects[i].Height;
420 else
421 memcpy(record+1, rects, sizeof(GpRectF) * count);
423 METAFILE_WriteRecords(metafile);
426 return Ok;
429 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
431 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
433 EmfPlusSetPageTransform *record;
434 GpStatus stat;
436 stat = METAFILE_AllocateRecord(metafile,
437 sizeof(EmfPlusSetPageTransform),
438 (void**)&record);
439 if (stat != Ok)
440 return stat;
442 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
443 record->Header.Flags = unit;
444 record->PageScale = scale;
446 METAFILE_WriteRecords(metafile);
449 return Ok;
452 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
454 if (hdc != metafile->record_dc)
455 return InvalidParameter;
457 return Ok;
460 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
462 GpStatus stat;
464 stat = METAFILE_WriteEndOfFile(metafile);
465 metafile->record_graphics = NULL;
467 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
468 metafile->record_dc = NULL;
470 heap_free(metafile->comment_data);
471 metafile->comment_data = NULL;
472 metafile->comment_data_size = 0;
474 return stat;
477 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
479 TRACE("(%p,%p)\n", metafile, hEmf);
481 if (!metafile || !hEmf || !metafile->hemf)
482 return InvalidParameter;
484 *hEmf = metafile->hemf;
485 metafile->hemf = NULL;
487 return Ok;
490 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
492 GpStatus stat = Ok;
494 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
496 if (stat == Ok)
498 /* The result of GdipGetDC always expects device co-ordinates, but the
499 * device co-ordinates of the source metafile do not correspond to
500 * device co-ordinates of the destination. Therefore, we set up the DC
501 * so that the metafile's bounds map to the destination points where we
502 * are drawing this metafile. */
503 SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
505 SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
506 SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
508 SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
509 SetViewportExtEx(metafile->playback_dc,
510 metafile->playback_points[1].X - metafile->playback_points[0].X,
511 metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
514 return stat;
517 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
519 if (metafile->playback_dc)
521 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
522 metafile->playback_dc = NULL;
526 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
528 GpMatrix *real_transform;
529 GpStatus stat;
531 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
533 if (stat == Ok)
535 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
537 if (metafile->page_unit != UnitDisplay)
538 scale *= metafile->page_scale;
540 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
542 if (stat == Ok)
543 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
545 if (stat == Ok)
546 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
548 GdipDeleteMatrix(real_transform);
551 return stat;
554 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
555 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
557 GpStatus stat;
558 GpMetafile *real_metafile = (GpMetafile*)metafile;
560 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
562 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
563 return InvalidParameter;
565 if (recordType >= 1 && recordType <= 0x7a)
567 /* regular EMF record */
568 if (metafile->playback_dc)
570 ENHMETARECORD *record;
572 record = heap_alloc_zero(dataSize + 8);
574 if (record)
576 record->iType = recordType;
577 record->nSize = dataSize + 8;
578 memcpy(record->dParm, data, dataSize);
580 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
581 record, metafile->handle_count);
583 heap_free(record);
585 else
586 return OutOfMemory;
589 else
591 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
593 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
595 switch(recordType)
597 case EmfPlusRecordTypeHeader:
598 case EmfPlusRecordTypeEndOfFile:
599 break;
600 case EmfPlusRecordTypeGetDC:
601 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
602 break;
603 case EmfPlusRecordTypeFillRects:
605 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
606 GpBrush *brush, *temp_brush=NULL;
607 GpRectF *rects, *temp_rects=NULL;
609 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
610 return InvalidParameter;
612 if (flags & 0x4000)
614 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
615 return InvalidParameter;
617 else
619 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
620 return InvalidParameter;
623 if (flags & 0x8000)
625 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
626 brush = temp_brush;
628 else
630 FIXME("brush deserialization not implemented\n");
631 return NotImplemented;
634 if (stat == Ok)
636 if (flags & 0x4000)
638 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
639 int i;
641 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
642 if (rects)
644 for (i=0; i<record->Count; i++)
646 rects[i].X = int_rects[i].X;
647 rects[i].Y = int_rects[i].Y;
648 rects[i].Width = int_rects[i].Width;
649 rects[i].Height = int_rects[i].Height;
652 else
653 stat = OutOfMemory;
655 else
656 rects = (GpRectF*)(record+1);
659 if (stat == Ok)
661 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
664 GdipDeleteBrush(temp_brush);
665 heap_free(temp_rects);
667 return stat;
669 case EmfPlusRecordTypeSetPageTransform:
671 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
672 GpUnit unit = (GpUnit)flags;
674 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
675 return InvalidParameter;
677 real_metafile->page_unit = unit;
678 real_metafile->page_scale = record->PageScale;
680 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
682 default:
683 FIXME("Not implemented for record type %x\n", recordType);
684 return NotImplemented;
688 return Ok;
691 struct enum_metafile_data
693 EnumerateMetafileProc callback;
694 void *callback_data;
695 GpMetafile *metafile;
698 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
699 int nObj, LPARAM lpData)
701 BOOL ret;
702 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
703 const BYTE* pStr;
705 data->metafile->handle_table = lpHTable;
706 data->metafile->handle_count = nObj;
708 /* First check for an EMF+ record. */
709 if (lpEMFR->iType == EMR_GDICOMMENT)
711 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
713 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
715 int offset = 4;
717 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
719 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
721 if (record->DataSize)
722 pStr = (const BYTE*)(record+1);
723 else
724 pStr = NULL;
726 ret = data->callback(record->Type, record->Flags, record->DataSize,
727 pStr, data->callback_data);
729 if (!ret)
730 return 0;
732 offset += record->Size;
735 return 1;
739 if (lpEMFR->nSize != 8)
740 pStr = (const BYTE*)lpEMFR->dParm;
741 else
742 pStr = NULL;
744 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
745 pStr, data->callback_data);
748 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
749 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
750 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
751 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
753 struct enum_metafile_data data;
754 GpStatus stat;
755 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
756 GraphicsContainer state;
758 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
759 destPoints, count, srcRect, srcUnit, callback, callbackData,
760 imageAttributes);
762 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
763 return InvalidParameter;
765 if (!metafile->hemf)
766 return InvalidParameter;
768 if (metafile->playback_graphics)
769 return ObjectBusy;
771 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
772 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
773 debugstr_pointf(&destPoints[2]));
775 data.callback = callback;
776 data.callback_data = callbackData;
777 data.metafile = real_metafile;
779 real_metafile->playback_graphics = graphics;
780 real_metafile->playback_dc = NULL;
781 real_metafile->src_rect = *srcRect;
783 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
784 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
786 if (stat == Ok)
787 stat = GdipBeginContainer2(graphics, &state);
789 if (stat == Ok)
791 stat = GdipSetPageScale(graphics, 1.0);
793 if (stat == Ok)
794 stat = GdipSetPageUnit(graphics, UnitPixel);
796 if (stat == Ok)
797 stat = GdipCreateMatrix(&real_metafile->world_transform);
799 if (stat == Ok)
801 real_metafile->page_unit = UnitDisplay;
802 real_metafile->page_scale = 1.0;
803 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
806 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
807 metafile->metafile_type == MetafileTypeWmfPlaceable ||
808 metafile->metafile_type == MetafileTypeWmf))
809 stat = METAFILE_PlaybackGetDC(real_metafile);
811 if (stat == Ok)
812 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
814 METAFILE_PlaybackReleaseDC(real_metafile);
816 GdipDeleteMatrix(real_metafile->world_transform);
817 real_metafile->world_transform = NULL;
819 GdipEndContainer(graphics, state);
822 real_metafile->playback_graphics = NULL;
824 return stat;
827 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
828 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
829 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
831 GpPointF points[3];
833 if (!graphics || !metafile || !dest) return InvalidParameter;
835 points[0].X = points[2].X = dest->X;
836 points[0].Y = points[1].Y = dest->Y;
837 points[1].X = dest->X + dest->Width;
838 points[2].Y = dest->Y + dest->Height;
840 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
841 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
844 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
845 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
846 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
848 GpRectF destf;
850 if (!graphics || !metafile || !dest) return InvalidParameter;
852 destf.X = dest->X;
853 destf.Y = dest->Y;
854 destf.Width = dest->Width;
855 destf.Height = dest->Height;
857 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
860 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
861 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
862 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
864 GpRectF destf;
866 if (!graphics || !metafile || !dest) return InvalidParameter;
868 destf.X = dest->X;
869 destf.Y = dest->Y;
870 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
871 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
873 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
876 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
877 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
878 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
880 GpPointF ptf;
882 if (!graphics || !metafile || !dest) return InvalidParameter;
884 ptf.X = dest->X;
885 ptf.Y = dest->Y;
887 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
890 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
891 MetafileHeader * header)
893 static int calls;
895 TRACE("(%p, %p)\n", metafile, header);
897 if(!metafile || !header)
898 return InvalidParameter;
900 if(!(calls++))
901 FIXME("not implemented\n");
903 memset(header, 0, sizeof(MetafileHeader));
905 return Ok;
908 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
909 int nObj, LPARAM lpData)
911 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
913 if (lpEMFR->iType == EMR_GDICOMMENT)
915 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
917 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
919 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
921 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
922 header->Type == EmfPlusRecordTypeHeader)
924 memcpy(dst_header, header, sizeof(*dst_header));
928 else if (lpEMFR->iType == EMR_HEADER)
929 return TRUE;
931 return FALSE;
934 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
935 MetafileHeader *header)
937 ENHMETAHEADER3 emfheader;
938 EmfPlusHeader emfplusheader;
939 MetafileType metafile_type;
941 TRACE("(%p,%p)\n", hemf, header);
943 if(!hemf || !header)
944 return InvalidParameter;
946 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
947 return GenericError;
949 emfplusheader.Header.Type = 0;
951 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
953 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
955 if ((emfplusheader.Header.Flags & 1) == 1)
956 metafile_type = MetafileTypeEmfPlusDual;
957 else
958 metafile_type = MetafileTypeEmfPlusOnly;
960 else
961 metafile_type = MetafileTypeEmf;
963 header->Type = metafile_type;
964 header->Size = emfheader.nBytes;
965 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
966 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
967 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
968 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
969 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
970 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
971 header->EmfHeader = emfheader;
973 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
975 header->Version = emfplusheader.Version;
976 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
977 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
978 header->LogicalDpiX = emfplusheader.LogicalDpiX;
979 header->LogicalDpiY = emfplusheader.LogicalDpiY;
981 else
983 header->Version = emfheader.nVersion;
984 header->EmfPlusFlags = 0;
985 header->EmfPlusHeaderSize = 0;
986 header->LogicalDpiX = 0;
987 header->LogicalDpiY = 0;
990 return Ok;
993 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
994 MetafileHeader *header)
996 static int calls;
998 TRACE("(%s,%p)\n", debugstr_w(filename), header);
1000 if(!filename || !header)
1001 return InvalidParameter;
1003 if(!(calls++))
1004 FIXME("not implemented\n");
1006 memset(header, 0, sizeof(MetafileHeader));
1008 return Ok;
1011 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
1012 MetafileHeader *header)
1014 static int calls;
1016 TRACE("(%p,%p)\n", stream, header);
1018 if(!stream || !header)
1019 return InvalidParameter;
1021 if(!(calls++))
1022 FIXME("not implemented\n");
1024 memset(header, 0, sizeof(MetafileHeader));
1026 return Ok;
1029 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
1030 GpMetafile **metafile)
1032 GpStatus stat;
1033 MetafileHeader header;
1035 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
1037 if(!hemf || !metafile)
1038 return InvalidParameter;
1040 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
1041 if (stat != Ok)
1042 return stat;
1044 *metafile = heap_alloc_zero(sizeof(GpMetafile));
1045 if (!*metafile)
1046 return OutOfMemory;
1048 (*metafile)->image.type = ImageTypeMetafile;
1049 (*metafile)->image.format = ImageFormatEMF;
1050 (*metafile)->image.frame_count = 1;
1051 (*metafile)->image.xres = header.DpiX;
1052 (*metafile)->image.yres = header.DpiY;
1053 (*metafile)->bounds.X = (REAL)header.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
1054 (*metafile)->bounds.Y = (REAL)header.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
1055 (*metafile)->bounds.Width = (REAL)(header.EmfHeader.rclFrame.right - header.EmfHeader.rclFrame.left)
1056 / 2540.0 * header.DpiX;
1057 (*metafile)->bounds.Height = (REAL)(header.EmfHeader.rclFrame.bottom - header.EmfHeader.rclFrame.top)
1058 / 2540.0 * header.DpiY;
1059 (*metafile)->unit = UnitPixel;
1060 (*metafile)->metafile_type = header.Type;
1061 (*metafile)->hemf = hemf;
1062 (*metafile)->preserve_hemf = !delete;
1064 TRACE("<-- %p\n", *metafile);
1066 return Ok;
1069 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
1070 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1072 UINT read;
1073 BYTE *copy;
1074 HENHMETAFILE hemf;
1075 GpStatus retval = Ok;
1077 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
1079 if(!hwmf || !metafile)
1080 return InvalidParameter;
1082 *metafile = NULL;
1083 read = GetMetaFileBitsEx(hwmf, 0, NULL);
1084 if(!read)
1085 return GenericError;
1086 copy = heap_alloc_zero(read);
1087 GetMetaFileBitsEx(hwmf, read, copy);
1089 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1090 heap_free(copy);
1092 /* FIXME: We should store and use hwmf instead of converting to hemf */
1093 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
1095 if (retval == Ok)
1097 if (placeable)
1099 (*metafile)->image.xres = (REAL)placeable->Inch;
1100 (*metafile)->image.yres = (REAL)placeable->Inch;
1101 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
1102 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
1103 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
1104 placeable->BoundingBox.Left);
1105 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
1106 placeable->BoundingBox.Top);
1107 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
1109 else
1110 (*metafile)->metafile_type = MetafileTypeWmf;
1111 (*metafile)->image.format = ImageFormatWMF;
1113 if (delete) DeleteMetaFile(hwmf);
1115 else
1116 DeleteEnhMetaFile(hemf);
1117 return retval;
1120 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1121 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1123 HMETAFILE hmf = GetMetaFileW(file);
1125 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1127 if(!hmf) return InvalidParameter;
1129 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1132 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
1133 GpMetafile **metafile)
1135 FIXME("(%p, %p): stub\n", file, metafile);
1136 return NotImplemented;
1139 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
1140 GpMetafile **metafile)
1142 FIXME("(%p, %p): stub\n", stream, metafile);
1143 return NotImplemented;
1146 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
1147 UINT limitDpi)
1149 TRACE("(%p,%u)\n", metafile, limitDpi);
1151 return Ok;
1154 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
1155 GpMetafile* metafile, BOOL* succ, EmfType emfType,
1156 const WCHAR* description, GpMetafile** out_metafile)
1158 static int calls;
1160 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
1161 debugstr_w(description), out_metafile);
1163 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
1164 return InvalidParameter;
1166 if(succ)
1167 *succ = FALSE;
1168 *out_metafile = NULL;
1170 if(!(calls++))
1171 FIXME("not implemented\n");
1173 return NotImplemented;
1176 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
1177 LPBYTE pData16, INT iMapMode, INT eFlags)
1179 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
1180 return NotImplemented;
1183 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
1184 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
1185 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
1186 GpMetafile **metafile)
1188 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1189 frameUnit, debugstr_w(desc), metafile);
1191 return NotImplemented;
1194 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
1195 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
1196 GDIPCONST WCHAR *desc, GpMetafile **metafile)
1198 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
1199 frameUnit, debugstr_w(desc), metafile);
1201 return NotImplemented;
1204 /*****************************************************************************
1205 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
1208 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
1209 GpMetafile* metafile, BOOL* conversionSuccess,
1210 const WCHAR* filename, EmfType emfType,
1211 const WCHAR* description, GpMetafile** out_metafile)
1213 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
1214 return NotImplemented;