gdiplus: Add basic metafile recording support.
[wine.git] / dlls / gdiplus / metafile.c
blob99b16213a94edc59d7c71fd1d33553481e8e1c49
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 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
62 DWORD size_needed;
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)
71 return OutOfMemory;
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);
86 if (!new_data)
87 return OutOfMemory;
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;
100 record->Size = size;
101 record->DataSize = size - sizeof(EmfPlusRecordHeader);
103 return Ok;
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)
117 GpStatus stat;
119 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
121 EmfPlusHeader *header;
123 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
124 if (stat != Ok)
125 return stat;
127 header->Header.Type = EmfPlusRecordTypeHeader;
129 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
130 header->Header.Flags = 1;
131 else
132 header->Header.Flags = 0;
134 header->Version = 0xDBC01002;
136 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
137 header->EmfPlusFlags = 1;
138 else
139 header->EmfPlusFlags = 0;
141 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
142 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
144 METAFILE_WriteRecords(metafile);
147 return Ok;
150 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
152 GpStatus stat;
154 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
156 EmfPlusRecordHeader *record;
158 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
159 if (stat != Ok)
160 return stat;
162 record->Type = EmfPlusRecordTypeEndOfFile;
163 record->Flags = 0;
165 METAFILE_WriteRecords(metafile);
168 return Ok;
171 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
172 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
174 HDC record_dc;
175 REAL framerect_factor_x, framerect_factor_y;
176 RECT rc;
177 GpStatus stat;
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;
184 if (!frameRect)
186 FIXME("not implemented for NULL rect\n");
187 return NotImplemented;
190 switch (frameUnit)
192 case MetafileFrameUnitPixel:
193 framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX);
194 framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY);
195 break;
196 case MetafileFrameUnitPoint:
197 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
198 break;
199 case MetafileFrameUnitInch:
200 framerect_factor_x = framerect_factor_y = 2540.0;
201 break;
202 case MetafileFrameUnitDocument:
203 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
204 break;
205 case MetafileFrameUnitMillimeter:
206 framerect_factor_x = framerect_factor_y = 100.0;
207 break;
208 case MetafileFrameUnitGdi:
209 framerect_factor_x = framerect_factor_y = 1.0;
210 break;
211 default:
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);
222 if (!record_dc)
223 return GenericError;
225 *metafile = GdipAlloc(sizeof(GpMetafile));
226 if(!*metafile)
228 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
229 return OutOfMemory;
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);
250 if (stat != Ok)
252 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
253 GdipFree(*metafile);
254 *metafile = NULL;
255 return OutOfMemory;
258 return stat;
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);
271 if (frameRect)
273 frameRectF.X = frameRect->X;
274 frameRectF.Y = frameRect->Y;
275 frameRectF.Width = frameRect->Width;
276 frameRectF.Height = frameRect->Height;
277 pFrameRectF = &frameRectF;
279 else
280 pFrameRectF = NULL;
282 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
285 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
287 GpStatus stat;
289 if (!metafile->record_dc || metafile->record_graphics)
290 return InvalidParameter;
292 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
294 if (stat == Ok)
295 *result = metafile->record_graphics;
297 return stat;
300 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
302 GpStatus stat;
304 stat = METAFILE_WriteEndOfFile(metafile);
305 metafile->record_graphics = NULL;
307 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
308 metafile->record_dc = NULL;
310 return stat;
313 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
315 TRACE("(%p,%p)\n", metafile, hEmf);
317 if (!metafile || !hEmf || !metafile->hemf)
318 return InvalidParameter;
320 *hEmf = metafile->hemf;
321 metafile->hemf = NULL;
323 return Ok;