gdiplus: Implement GdipEnumerateMetafileSrcRectDestPoints.
[wine] / dlls / 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 <stdarg.h>
20 #include <math.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "wine/unicode.h"
26
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "ocidl.h"
30 #include "olectl.h"
31 #include "ole2.h"
32
33 #include "winreg.h"
34 #include "shlwapi.h"
35
36 #include "gdiplus.h"
37 #include "gdiplus_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
42
43 typedef struct EmfPlusRecordHeader
44 {
45     WORD Type;
46     WORD Flags;
47     DWORD Size;
48     DWORD DataSize;
49 } EmfPlusRecordHeader;
50
51 typedef struct EmfPlusHeader
52 {
53     EmfPlusRecordHeader Header;
54     DWORD Version;
55     DWORD EmfPlusFlags;
56     DWORD LogicalDpiX;
57     DWORD LogicalDpiY;
58 } EmfPlusHeader;
59
60 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
61 {
62     DWORD size_needed;
63     EmfPlusRecordHeader *record;
64
65     if (!metafile->comment_data_size)
66     {
67         DWORD data_size = max(256, size * 2 + 4);
68         metafile->comment_data = GdipAlloc(data_size);
69
70         if (!metafile->comment_data)
71             return OutOfMemory;
72
73         memcpy(metafile->comment_data, "EMF+", 4);
74
75         metafile->comment_data_size = data_size;
76         metafile->comment_data_length = 4;
77     }
78
79     size_needed = size + metafile->comment_data_length;
80
81     if (size_needed > metafile->comment_data_size)
82     {
83         DWORD data_size = size_needed * 2;
84         BYTE *new_data = GdipAlloc(data_size);
85
86         if (!new_data)
87             return OutOfMemory;
88
89         memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
90
91         metafile->comment_data_size = data_size;
92         GdipFree(metafile->comment_data);
93         metafile->comment_data = new_data;
94     }
95
96     *result = metafile->comment_data + metafile->comment_data_length;
97     metafile->comment_data_length += size;
98
99     record = (EmfPlusRecordHeader*)*result;
100     record->Size = size;
101     record->DataSize = size - sizeof(EmfPlusRecordHeader);
102
103     return Ok;
104 }
105
106 static void METAFILE_WriteRecords(GpMetafile *metafile)
107 {
108     if (metafile->comment_data_length > 4)
109     {
110         GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
111         metafile->comment_data_length = 4;
112     }
113 }
114
115 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
116 {
117     GpStatus stat;
118
119     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
120     {
121         EmfPlusHeader *header;
122
123         stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
124         if (stat != Ok)
125             return stat;
126
127         header->Header.Type = EmfPlusRecordTypeHeader;
128
129         if (metafile->metafile_type == MetafileTypeEmfPlusDual)
130             header->Header.Flags = 1;
131         else
132             header->Header.Flags = 0;
133
134         header->Version = 0xDBC01002;
135
136         if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
137             header->EmfPlusFlags = 1;
138         else
139             header->EmfPlusFlags = 0;
140
141         header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
142         header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
143
144         METAFILE_WriteRecords(metafile);
145     }
146
147     return Ok;
148 }
149
150 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
151 {
152     GpStatus stat;
153
154     if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
155     {
156         EmfPlusRecordHeader *record;
157
158         stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
159         if (stat != Ok)
160             return stat;
161
162         record->Type = EmfPlusRecordTypeEndOfFile;
163         record->Flags = 0;
164
165         METAFILE_WriteRecords(metafile);
166     }
167
168     return Ok;
169 }
170
171 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
172                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
173 {
174     HDC record_dc;
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     switch (frameUnit)
191     {
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;
213     }
214
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;
219
220     record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
221
222     if (!record_dc)
223         return GenericError;
224
225     *metafile = GdipAlloc(sizeof(GpMetafile));
226     if(!*metafile)
227     {
228         DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
229         return OutOfMemory;
230     }
231
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;
247
248     stat = METAFILE_WriteHeader(*metafile, hdc);
249
250     if (stat != Ok)
251     {
252         DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
253         GdipFree(*metafile);
254         *metafile = NULL;
255         return OutOfMemory;
256     }
257
258     return stat;
259 }
260
261 /*****************************************************************************
262  * GdipRecordMetafileI [GDIPLUS.@]
263  */
264 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
265                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
266 {
267     GpRectF frameRectF, *pFrameRectF;
268
269     TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
270
271     if (frameRect)
272     {
273         frameRectF.X = frameRect->X;
274         frameRectF.Y = frameRect->Y;
275         frameRectF.Width = frameRect->Width;
276         frameRectF.Height = frameRect->Height;
277         pFrameRectF = &frameRectF;
278     }
279     else
280         pFrameRectF = NULL;
281
282     return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
283 }
284
285 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
286 {
287     GpStatus stat;
288
289     if (!metafile->record_dc || metafile->record_graphics)
290         return InvalidParameter;
291
292     stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
293
294     if (stat == Ok)
295         *result = metafile->record_graphics;
296
297     return stat;
298 }
299
300 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
301 {
302     GpStatus stat;
303
304     stat = METAFILE_WriteEndOfFile(metafile);
305     metafile->record_graphics = NULL;
306
307     metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
308     metafile->record_dc = NULL;
309
310     return stat;
311 }
312
313 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
314 {
315     TRACE("(%p,%p)\n", metafile, hEmf);
316
317     if (!metafile || !hEmf || !metafile->hemf)
318         return InvalidParameter;
319
320     *hEmf = metafile->hemf;
321     metafile->hemf = NULL;
322
323     return Ok;
324 }
325
326 struct enum_metafile_data
327 {
328     EnumerateMetafileProc callback;
329     void *callback_data;
330 };
331
332 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
333     int nObj, LPARAM lpData)
334 {
335     BOOL ret;
336     struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
337     const BYTE* pStr;
338
339     /* First check for an EMF+ record. */
340     if (lpEMFR->iType == EMR_GDICOMMENT)
341     {
342         const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
343
344         if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
345         {
346             int offset = 4;
347
348             while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
349             {
350                 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
351
352                 if (record->DataSize)
353                     pStr = (const BYTE*)(record+1);
354                 else
355                     pStr = NULL;
356
357                 ret = data->callback(record->Type, record->Flags, record->DataSize,
358                     pStr, data->callback_data);
359
360                 if (!ret)
361                     return 0;
362
363                 offset += record->Size;
364             }
365
366             return 1;
367         }
368     }
369
370     if (lpEMFR->nSize != 8)
371         pStr = (const BYTE*)lpEMFR->dParm;
372     else
373         pStr = NULL;
374
375     return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
376         pStr, data->callback_data);
377 }
378
379 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
380     GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
381     GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
382     VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
383 {
384     struct enum_metafile_data data;
385
386     TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
387         destPoints, count, srcRect, srcUnit, callback, callbackData,
388         imageAttributes);
389
390     if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
391         return InvalidParameter;
392
393     if (!metafile->hemf)
394         return InvalidParameter;
395
396     TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
397         debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
398         debugstr_pointf(&destPoints[2]));
399
400     data.callback = callback;
401     data.callback_data = callbackData;
402
403     EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
404
405     return Ok;
406 }