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
25 #include "wine/unicode.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
49 } EmfPlusRecordHeader
;
51 typedef struct EmfPlusHeader
53 EmfPlusRecordHeader Header
;
60 static GpStatus
METAFILE_AllocateRecord(GpMetafile
*metafile
, DWORD size
, void **result
)
63 EmfPlusRecordHeader
*record
;
65 if (!metafile
->comment_data_size
)
67 DWORD data_size
= max(256, size
* 2 + 4);
68 metafile
->comment_data
= GdipAlloc(data_size
);
70 if (!metafile
->comment_data
)
73 memcpy(metafile
->comment_data
, "EMF+", 4);
75 metafile
->comment_data_size
= data_size
;
76 metafile
->comment_data_length
= 4;
79 size_needed
= size
+ metafile
->comment_data_length
;
81 if (size_needed
> metafile
->comment_data_size
)
83 DWORD data_size
= size_needed
* 2;
84 BYTE
*new_data
= GdipAlloc(data_size
);
89 memcpy(new_data
, metafile
->comment_data
, metafile
->comment_data_length
);
91 metafile
->comment_data_size
= data_size
;
92 GdipFree(metafile
->comment_data
);
93 metafile
->comment_data
= new_data
;
96 *result
= metafile
->comment_data
+ metafile
->comment_data_length
;
97 metafile
->comment_data_length
+= size
;
99 record
= (EmfPlusRecordHeader
*)*result
;
101 record
->DataSize
= size
- sizeof(EmfPlusRecordHeader
);
106 static void METAFILE_WriteRecords(GpMetafile
*metafile
)
108 if (metafile
->comment_data_length
> 4)
110 GdiComment(metafile
->record_dc
, metafile
->comment_data_length
, metafile
->comment_data
);
111 metafile
->comment_data_length
= 4;
115 static GpStatus
METAFILE_WriteHeader(GpMetafile
*metafile
, HDC hdc
)
119 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
121 EmfPlusHeader
*header
;
123 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusHeader
), (void**)&header
);
127 header
->Header
.Type
= EmfPlusRecordTypeHeader
;
129 if (metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
130 header
->Header
.Flags
= 1;
132 header
->Header
.Flags
= 0;
134 header
->Version
= 0xDBC01002;
136 if (GetDeviceCaps(hdc
, TECHNOLOGY
) == DT_RASDISPLAY
)
137 header
->EmfPlusFlags
= 1;
139 header
->EmfPlusFlags
= 0;
141 header
->LogicalDpiX
= GetDeviceCaps(hdc
, LOGPIXELSX
);
142 header
->LogicalDpiY
= GetDeviceCaps(hdc
, LOGPIXELSY
);
144 METAFILE_WriteRecords(metafile
);
150 static GpStatus
METAFILE_WriteEndOfFile(GpMetafile
*metafile
)
154 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
156 EmfPlusRecordHeader
*record
;
158 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusRecordHeader
), (void**)&record
);
162 record
->Type
= EmfPlusRecordTypeEndOfFile
;
165 METAFILE_WriteRecords(metafile
);
171 GpStatus WINGDIPAPI
GdipRecordMetafile(HDC hdc
, EmfType type
, GDIPCONST GpRectF
*frameRect
,
172 MetafileFrameUnit frameUnit
, GDIPCONST WCHAR
*desc
, GpMetafile
**metafile
)
175 REAL framerect_factor_x
, framerect_factor_y
;
179 TRACE("(%p %d %p %d %p %p)\n", hdc
, type
, frameRect
, frameUnit
, desc
, metafile
);
181 if (!hdc
|| type
< EmfTypeEmfOnly
|| type
> EmfTypeEmfPlusDual
|| !metafile
)
182 return InvalidParameter
;
186 FIXME("not implemented for NULL rect\n");
187 return NotImplemented
;
192 case MetafileFrameUnitPixel
:
193 framerect_factor_x
= 2540.0 / GetDeviceCaps(hdc
, LOGPIXELSX
);
194 framerect_factor_y
= 2540.0 / GetDeviceCaps(hdc
, LOGPIXELSY
);
196 case MetafileFrameUnitPoint
:
197 framerect_factor_x
= framerect_factor_y
= 2540.0 / 72.0;
199 case MetafileFrameUnitInch
:
200 framerect_factor_x
= framerect_factor_y
= 2540.0;
202 case MetafileFrameUnitDocument
:
203 framerect_factor_x
= framerect_factor_y
= 2540.0 / 300.0;
205 case MetafileFrameUnitMillimeter
:
206 framerect_factor_x
= framerect_factor_y
= 100.0;
208 case MetafileFrameUnitGdi
:
209 framerect_factor_x
= framerect_factor_y
= 1.0;
212 return InvalidParameter
;
215 rc
.left
= framerect_factor_x
* frameRect
->X
;
216 rc
.top
= framerect_factor_y
* frameRect
->Y
;
217 rc
.right
= rc
.left
+ framerect_factor_x
* frameRect
->Width
;
218 rc
.bottom
= rc
.top
+ framerect_factor_y
* frameRect
->Height
;
220 record_dc
= CreateEnhMetaFileW(hdc
, NULL
, &rc
, desc
);
225 *metafile
= GdipAlloc(sizeof(GpMetafile
));
228 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc
));
232 (*metafile
)->image
.type
= ImageTypeMetafile
;
233 (*metafile
)->image
.picture
= NULL
;
234 (*metafile
)->image
.flags
= ImageFlagsNone
;
235 (*metafile
)->image
.palette_flags
= 0;
236 (*metafile
)->image
.palette_count
= 0;
237 (*metafile
)->image
.palette_size
= 0;
238 (*metafile
)->image
.palette_entries
= NULL
;
239 (*metafile
)->bounds
= *frameRect
;
240 (*metafile
)->unit
= frameUnit
;
241 (*metafile
)->metafile_type
= type
;
242 (*metafile
)->record_dc
= record_dc
;
243 (*metafile
)->comment_data
= NULL
;
244 (*metafile
)->comment_data_size
= 0;
245 (*metafile
)->comment_data_length
= 0;
246 (*metafile
)->hemf
= NULL
;
248 stat
= METAFILE_WriteHeader(*metafile
, hdc
);
252 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc
));
261 /*****************************************************************************
262 * GdipRecordMetafileI [GDIPLUS.@]
264 GpStatus WINGDIPAPI
GdipRecordMetafileI(HDC hdc
, EmfType type
, GDIPCONST GpRect
*frameRect
,
265 MetafileFrameUnit frameUnit
, GDIPCONST WCHAR
*desc
, GpMetafile
**metafile
)
267 GpRectF frameRectF
, *pFrameRectF
;
269 TRACE("(%p %d %p %d %p %p)\n", hdc
, type
, frameRect
, frameUnit
, desc
, metafile
);
273 frameRectF
.X
= frameRect
->X
;
274 frameRectF
.Y
= frameRect
->Y
;
275 frameRectF
.Width
= frameRect
->Width
;
276 frameRectF
.Height
= frameRect
->Height
;
277 pFrameRectF
= &frameRectF
;
282 return GdipRecordMetafile(hdc
, type
, pFrameRectF
, frameUnit
, desc
, metafile
);
285 GpStatus
METAFILE_GetGraphicsContext(GpMetafile
* metafile
, GpGraphics
**result
)
289 if (!metafile
->record_dc
|| metafile
->record_graphics
)
290 return InvalidParameter
;
292 stat
= graphics_from_image((GpImage
*)metafile
, &metafile
->record_graphics
);
295 *result
= metafile
->record_graphics
;
300 GpStatus
METAFILE_GetDC(GpMetafile
* metafile
, HDC
*hdc
)
302 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
304 EmfPlusRecordHeader
*record
;
307 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusRecordHeader
), (void**)&record
);
311 record
->Type
= EmfPlusRecordTypeGetDC
;
314 METAFILE_WriteRecords(metafile
);
317 *hdc
= metafile
->record_dc
;
322 GpStatus
METAFILE_ReleaseDC(GpMetafile
* metafile
, HDC hdc
)
324 if (hdc
!= metafile
->record_dc
)
325 return InvalidParameter
;
330 GpStatus
METAFILE_GraphicsDeleted(GpMetafile
* metafile
)
334 stat
= METAFILE_WriteEndOfFile(metafile
);
335 metafile
->record_graphics
= NULL
;
337 metafile
->hemf
= CloseEnhMetaFile(metafile
->record_dc
);
338 metafile
->record_dc
= NULL
;
340 GdipFree(metafile
->comment_data
);
341 metafile
->comment_data
= NULL
;
342 metafile
->comment_data_size
= 0;
347 GpStatus WINGDIPAPI
GdipGetHemfFromMetafile(GpMetafile
*metafile
, HENHMETAFILE
*hEmf
)
349 TRACE("(%p,%p)\n", metafile
, hEmf
);
351 if (!metafile
|| !hEmf
|| !metafile
->hemf
)
352 return InvalidParameter
;
354 *hEmf
= metafile
->hemf
;
355 metafile
->hemf
= NULL
;
360 static GpStatus
METAFILE_PlaybackGetDC(GpMetafile
*metafile
)
364 stat
= GdipGetDC(metafile
->playback_graphics
, &metafile
->playback_dc
);
368 /* The result of GdipGetDC always expects device co-ordinates, but the
369 * device co-ordinates of the source metafile do not correspond to
370 * device co-ordinates of the destination. Therefore, we set up the DC
371 * so that the metafile's bounds map to the destination points where we
372 * are drawing this metafile. */
373 SetMapMode(metafile
->playback_dc
, MM_ANISOTROPIC
);
375 SetWindowOrgEx(metafile
->playback_dc
, metafile
->bounds
.X
, metafile
->bounds
.Y
, NULL
);
376 SetWindowExtEx(metafile
->playback_dc
, metafile
->bounds
.Width
, metafile
->bounds
.Height
, NULL
);
378 SetViewportOrgEx(metafile
->playback_dc
, metafile
->playback_points
[0].X
, metafile
->playback_points
[0].Y
, NULL
);
379 SetViewportExtEx(metafile
->playback_dc
,
380 metafile
->playback_points
[1].X
- metafile
->playback_points
[0].X
,
381 metafile
->playback_points
[2].Y
- metafile
->playback_points
[0].Y
, NULL
);
387 static void METAFILE_PlaybackReleaseDC(GpMetafile
*metafile
)
389 if (metafile
->playback_dc
)
391 GdipReleaseDC(metafile
->playback_graphics
, metafile
->playback_dc
);
392 metafile
->playback_dc
= NULL
;
396 GpStatus WINGDIPAPI
GdipPlayMetafileRecord(GDIPCONST GpMetafile
*metafile
,
397 EmfPlusRecordType recordType
, UINT flags
, UINT dataSize
, GDIPCONST BYTE
*data
)
399 TRACE("(%p,%x,%x,%d,%p)\n", metafile
, recordType
, flags
, dataSize
, data
);
401 if (!metafile
|| (dataSize
&& !data
) || !metafile
->playback_graphics
)
402 return InvalidParameter
;
404 if (recordType
>= 1 && recordType
<= 0x7a)
406 /* regular EMF record */
407 if (metafile
->playback_dc
)
409 ENHMETARECORD
*record
;
411 record
= GdipAlloc(dataSize
+ 8);
415 record
->iType
= recordType
;
416 record
->nSize
= dataSize
;
417 memcpy(record
->dParm
, data
, dataSize
);
419 PlayEnhMetaFileRecord(metafile
->playback_dc
, metafile
->handle_table
,
420 record
, metafile
->handle_count
);
430 METAFILE_PlaybackReleaseDC((GpMetafile
*)metafile
);
434 case EmfPlusRecordTypeHeader
:
435 case EmfPlusRecordTypeEndOfFile
:
437 case EmfPlusRecordTypeGetDC
:
438 METAFILE_PlaybackGetDC((GpMetafile
*)metafile
);
441 FIXME("Not implemented for record type %x\n", recordType
);
442 return NotImplemented
;
449 struct enum_metafile_data
451 EnumerateMetafileProc callback
;
453 GpMetafile
*metafile
;
456 static int CALLBACK
enum_metafile_proc(HDC hDC
, HANDLETABLE
*lpHTable
, const ENHMETARECORD
*lpEMFR
,
457 int nObj
, LPARAM lpData
)
460 struct enum_metafile_data
*data
= (struct enum_metafile_data
*)lpData
;
463 data
->metafile
->handle_table
= lpHTable
;
464 data
->metafile
->handle_count
= nObj
;
466 /* First check for an EMF+ record. */
467 if (lpEMFR
->iType
== EMR_GDICOMMENT
)
469 const EMRGDICOMMENT
*comment
= (const EMRGDICOMMENT
*)lpEMFR
;
471 if (comment
->cbData
>= 4 && memcmp(comment
->Data
, "EMF+", 4) == 0)
475 while (offset
+ sizeof(EmfPlusRecordHeader
) <= comment
->cbData
)
477 const EmfPlusRecordHeader
*record
= (const EmfPlusRecordHeader
*)&comment
->Data
[offset
];
479 if (record
->DataSize
)
480 pStr
= (const BYTE
*)(record
+1);
484 ret
= data
->callback(record
->Type
, record
->Flags
, record
->DataSize
,
485 pStr
, data
->callback_data
);
490 offset
+= record
->Size
;
497 if (lpEMFR
->nSize
!= 8)
498 pStr
= (const BYTE
*)lpEMFR
->dParm
;
502 return data
->callback(lpEMFR
->iType
, 0, lpEMFR
->nSize
-8,
503 pStr
, data
->callback_data
);
506 GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestPoints(GpGraphics
*graphics
,
507 GDIPCONST GpMetafile
*metafile
, GDIPCONST GpPointF
*destPoints
, INT count
,
508 GDIPCONST GpRectF
*srcRect
, Unit srcUnit
, EnumerateMetafileProc callback
,
509 VOID
*callbackData
, GDIPCONST GpImageAttributes
*imageAttributes
)
511 struct enum_metafile_data data
;
513 GpMetafile
*real_metafile
= (GpMetafile
*)metafile
; /* whoever made this const was joking */
515 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics
, metafile
,
516 destPoints
, count
, srcRect
, srcUnit
, callback
, callbackData
,
519 if (!graphics
|| !metafile
|| !destPoints
|| count
!= 3 || !srcRect
)
520 return InvalidParameter
;
523 return InvalidParameter
;
525 if (metafile
->playback_graphics
)
528 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect
), srcUnit
,
529 debugstr_pointf(&destPoints
[0]), debugstr_pointf(&destPoints
[1]),
530 debugstr_pointf(&destPoints
[2]));
532 data
.callback
= callback
;
533 data
.callback_data
= callbackData
;
534 data
.metafile
= real_metafile
;
536 real_metafile
->playback_graphics
= graphics
;
537 real_metafile
->playback_dc
= NULL
;
539 memcpy(real_metafile
->playback_points
, destPoints
, sizeof(PointF
) * 3);
540 stat
= GdipTransformPoints(graphics
, CoordinateSpaceDevice
, CoordinateSpaceWorld
, real_metafile
->playback_points
, 3);
542 if (stat
== Ok
&& metafile
->metafile_type
== MetafileTypeEmf
)
543 stat
= METAFILE_PlaybackGetDC((GpMetafile
*)metafile
);
546 EnumEnhMetaFile(0, metafile
->hemf
, enum_metafile_proc
, &data
, NULL
);
548 METAFILE_PlaybackReleaseDC((GpMetafile
*)metafile
);
550 real_metafile
->playback_graphics
= NULL
;