1 /*
2 * Copyright (C) 2007 Novell, Inc (http://www.novell.com)
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8 * subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in all copies or substantial
11 * portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
14 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
16 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
17 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 *
19 * Authors:
20 * Sebastien Pouliot <sebastien@ximian.com>
21 */
22
23 #include "metafile-private.h"
24 #include "solidbrush-private.h"
25 #include "general-private.h"
26 #include "graphics.h"
27 #include "graphics-path-private.h"
28 #include "hatchbrush-private.h"
29 #include "pen.h"
30
31 //#define DEBUG_METAFILE
32
33 /* http://wvware.sourceforge.net/caolan/SaveDC.html */
34 GpStatus
gdip_metafile_SaveDC(MetafilePlayContext * context)35 gdip_metafile_SaveDC (MetafilePlayContext *context)
36 {
37 #ifdef DEBUG_METAFILE
38 printf ("SaveDC");
39 #endif
40 /* TODO */
41 return Ok;
42 }
43
44 /* http://wvware.sourceforge.net/caolan/SetBkMode.html */
45 GpStatus
gdip_metafile_SetBkMode(MetafilePlayContext * context,DWORD bkMode)46 gdip_metafile_SetBkMode (MetafilePlayContext *context, DWORD bkMode)
47 {
48 #ifdef DEBUG_METAFILE
49 printf ("SetBkMode %d", bkMode);
50 #endif
51 /* TODO - currently unused elsewhere */
52 context->bk_mode = bkMode;
53 return Ok;
54 }
55
56 /* http://wvware.sourceforge.net/caolan/SetMapMode.html */
57 GpStatus
gdip_metafile_SetMapMode(MetafilePlayContext * context,DWORD mode)58 gdip_metafile_SetMapMode (MetafilePlayContext *context, DWORD mode)
59 {
60 GpStatus status;
61 float scale;
62 #ifdef DEBUG_METAFILE
63 printf ("SetMapMode %d", mode);
64 #endif
65 context->map_mode = mode;
66
67 switch (mode) {
68 case MM_HIENGLISH:
69 /* 1 logical unit == 0.001 inch */
70 scale = gdip_get_display_dpi () * 0.001;
71 break;
72 case MM_LOENGLISH:
73 /* 1 logical unit == 0.01 inch */
74 scale = gdip_get_display_dpi () * 0.01;
75 break;
76 case MM_HIMETRIC:
77 /* 1 logical unit == 0.01 mm */
78 scale = gdip_get_display_dpi () / (MM_PER_INCH * 100);
79 break;
80 case MM_LOMETRIC:
81 /* 1 logical unit == 0.1 mm */
82 scale = gdip_get_display_dpi () / (MM_PER_INCH * 10);
83 break;
84 case MM_TWIPS:
85 /* 1 logical point == 1/1440 inch (1/20 of a "old" printer point ;-) */
86 scale = gdip_get_display_dpi () / 1440;
87 break;
88 case MM_TEXT:
89 default:
90 /* 1 logical unit == 1 pixel */
91 scale = 1.0f;
92 context->map_mode = MM_TEXT;
93 break;
94 case MM_ISOTROPIC:
95 case MM_ANISOTROPIC:
96 /* SetWindowExt will calculate the correct ratio */
97 return Ok;
98 }
99
100 /* this isn't cumulative (and we get a lot of "junk" calls) */
101 GdipSetWorldTransform (context->graphics, &context->matrix);
102 status = GdipScaleWorldTransform (context->graphics, scale, scale, MatrixOrderPrepend);
103 #ifdef DEBUG_METAFILE_2
104 printf ("\n\tGdipScaleWorldTransform sx %g, sy %g (status %d)", scale, scale, status);
105 #endif
106 return status;
107 }
108
109 /* http://wvware.sourceforge.net/caolan/SetROP2.html */
110 GpStatus
gdip_metafile_SetROP2(MetafilePlayContext * context,DWORD rop)111 gdip_metafile_SetROP2 (MetafilePlayContext *context, DWORD rop)
112 {
113 #ifdef DEBUG_METAFILE
114 printf ("SetROP2 %d", rop);
115 #endif
116 /* TODO */
117 return Ok;
118 }
119
120 /* http://www.ousob.com/ng/win2api/ng30859.php */
121 GpStatus
gdip_metafile_SetRelabs(MetafilePlayContext * context,DWORD mode)122 gdip_metafile_SetRelabs (MetafilePlayContext *context, DWORD mode)
123 {
124 #ifdef DEBUG_METAFILE
125 printf ("SetRelabs %d", mode);
126 #endif
127 /* TODO */
128 return Ok;
129 }
130
131 /* http://wvware.sourceforge.net/caolan/SetPolyFillMode.html */
132 GpStatus
gdip_metafile_SetPolyFillMode(MetafilePlayContext * context,DWORD fillmode)133 gdip_metafile_SetPolyFillMode (MetafilePlayContext *context, DWORD fillmode)
134 {
135 #ifdef DEBUG_METAFILE
136 printf ("SetPolyFillMode %d", fillmode);
137 #endif
138 switch (fillmode) {
139 case WINDING:
140 context->fill_mode = FillModeWinding;
141 break;
142 default:
143 g_warning ("Unknown fillmode %d, assuming ALTERNATE", fillmode);
144 /* fall through */
145 case ALTERNATE:
146 context->fill_mode = FillModeAlternate;
147 break;
148 }
149 return Ok;
150 }
151
152 /* http://wvware.sourceforge.net/caolan/SetStretchBltMode.html */
153 GpStatus
gdip_metafile_SetStretchBltMode(MetafilePlayContext * context,int iStretchMode)154 gdip_metafile_SetStretchBltMode (MetafilePlayContext *context, int iStretchMode)
155 {
156 #ifdef DEBUG_METAFILE
157 printf ("SetStretchBltMode %d", iStretchMode);
158 #endif
159 /* TODO */
160 return Ok;
161 }
162
163 /* http://wvware.sourceforge.net/caolan/RestoreDC.html */
164 GpStatus
gdip_metafile_RestoreDC(MetafilePlayContext * context)165 gdip_metafile_RestoreDC (MetafilePlayContext *context)
166 {
167 #ifdef DEBUG_METAFILE
168 printf ("RestoreDC");
169 #endif
170 /* TODO */
171 return Ok;
172 }
173
174 /* http://wvware.sourceforge.net/caolan/SelectObject.html */
175 GpStatus
gdip_metafile_SelectObject(MetafilePlayContext * context,DWORD slot)176 gdip_metafile_SelectObject (MetafilePlayContext *context, DWORD slot)
177 {
178 // EMF have some stock objects that do not requires creation
179 if (slot & ENHMETA_STOCK_OBJECT) {
180 #ifdef DEBUG_METAFILE
181 printf ("SelectObject stock #%X", slot);
182 #endif
183 switch (slot - ENHMETA_STOCK_OBJECT) {
184 case WHITE_BRUSH:
185 case LTGRAY_BRUSH:
186 case GRAY_BRUSH:
187 case DKGRAY_BRUSH:
188 case BLACK_BRUSH:
189 case NULL_BRUSH:
190 context->selected_brush = slot;
191 break;
192 case WHITE_PEN:
193 case BLACK_PEN:
194 case NULL_PEN:
195 context->selected_pen = slot;
196 break;
197 case OEM_FIXED_FONT:
198 case ANSI_FIXED_FONT:
199 case ANSI_VAR_FONT:
200 case SYSTEM_FONT:
201 case DEVICE_DEFAULT_FONT:
202 case SYSTEM_FIXED_FONT:
203 context->selected_font = slot;
204 break;
205 case DEFAULT_PALETTE:
206 context->selected_palette = slot;
207 break;
208 default:
209 return InvalidParameter;
210 }
211 return Ok;
212 }
213
214 #ifdef DEBUG_METAFILE
215 printf ("SelectObject %d (out of %d slots)", slot, context->objects_count);
216 #endif
217 if (slot >= context->objects_count) {
218 g_warning ("SelectObject %d, invalid slot number.", slot);
219 return InvalidParameter;
220 }
221
222 /* FIXME - what's first ? the created object or what's in the slot ? */
223 switch (context->created.type) {
224 case METAOBJECT_TYPE_EMPTY:
225 /* if nothing is "created" (and not yet selected into a slot) then we "reselect" the object */
226 switch (context->objects [slot].type) {
227 case METAOBJECT_TYPE_EMPTY:
228 g_warning ("SelectObject %d, no created object, slot empty.", slot);
229 break;
230 case METAOBJECT_TYPE_PEN:
231 context->selected_pen = slot;
232 break;
233 case METAOBJECT_TYPE_BRUSH:
234 context->selected_brush = slot;
235 break;
236 }
237 return Ok;
238 case METAOBJECT_TYPE_PEN:
239 context->selected_pen = slot;
240 break;
241 case METAOBJECT_TYPE_BRUSH:
242 context->selected_brush = slot;
243 break;
244 }
245
246 context->objects [slot].type = context->created.type;
247 context->objects [slot].ptr = context->created.ptr;
248
249 context->created.type = METAOBJECT_TYPE_EMPTY;
250 context->created.ptr = NULL;
251 return Ok;
252 }
253
254 GpStatus
gdip_metafile_ModifyWorldTransform(MetafilePlayContext * context,XFORM * lpXform,DWORD iMode)255 gdip_metafile_ModifyWorldTransform (MetafilePlayContext *context, XFORM *lpXform, DWORD iMode)
256 {
257 GpMatrix matrix;
258 GpMatrixOrder order;
259
260 switch (iMode) {
261 case MWT_IDENTITY:
262 /* This is a reset and it ignores lpXform in this case */
263 /* we can't use GdipResetWorldTransform as we still want the original parameters */
264 return GdipSetWorldTransform (context->graphics, &context->matrix);
265 case MWT_LEFTMULTIPLY:
266 order = MatrixOrderPrepend;
267 break;
268 case MWT_RIGHTMULTIPLY:
269 order = MatrixOrderPrepend;
270 break;
271 default:
272 return InvalidParameter;
273 }
274
275 if (!lpXform)
276 return InvalidParameter;
277 matrix.xx = lpXform->eM11;
278 matrix.yx = lpXform->eM12;
279 matrix.xy = lpXform->eM21;
280 matrix.yy = lpXform->eM22;
281 matrix.x0 = lpXform->eDx;
282 matrix.y0 = lpXform->eDy;
283 return GdipMultiplyWorldTransform (context->graphics, &matrix, order);
284 }
285
286
287 /* http://wvware.sourceforge.net/caolan/SetTextAlign.html */
288 GpStatus
gdip_metafile_SetTextAlign(MetafilePlayContext * context,DWORD textalign)289 gdip_metafile_SetTextAlign (MetafilePlayContext *context, DWORD textalign)
290 {
291 #ifdef DEBUG_METAFILE
292 printf ("SetTextAlign %d", textalign);
293 #endif
294 /* TODO */
295 return Ok;
296 }
297
298 /* http://wvware.sourceforge.net/caolan/DeleteObject.html */
299 GpStatus
gdip_metafile_DeleteObject(MetafilePlayContext * context,DWORD slot)300 gdip_metafile_DeleteObject (MetafilePlayContext *context, DWORD slot)
301 {
302 GpStatus status = Ok;
303 MetaObject *obj;
304
305 if (slot >= context->objects_count) {
306 g_warning ("DeleteObject failure");
307 return InvalidParameter;
308 }
309
310 obj = &context->objects [slot];
311 switch (obj->type) {
312 case METAOBJECT_TYPE_PEN:
313 status = GdipDeletePen ((GpPen*)obj->ptr);
314 break;
315 case METAOBJECT_TYPE_BRUSH:
316 status = GdipDeleteBrush ((GpBrush*)obj->ptr);
317 break;
318 case METAOBJECT_TYPE_EMPTY:
319 break;
320 }
321
322 #ifdef DEBUG_METAFILE
323 printf ("DeleteObject %d (type %d, ptr %p, status %d)", slot, obj->type, obj->ptr, status);
324 #endif
325 obj->type = METAOBJECT_TYPE_EMPTY;
326 obj->ptr = NULL;
327 return status;
328 }
329
330 /* http://wvware.sourceforge.net/caolan/SetBkColor.html */
331 GpStatus
gdip_metafile_SetBkColor(MetafilePlayContext * context,DWORD color)332 gdip_metafile_SetBkColor (MetafilePlayContext *context, DWORD color)
333 {
334 #ifdef DEBUG_METAFILE
335 printf ("SetBkColor %X", color);
336 #endif
337 /* TODO - currently unused elsewhere */
338 context->bk_color = color;
339 return Ok;
340 }
341
342 /* http://wvware.sourceforge.net/caolan/SetWindowOrg.html */
343 GpStatus
gdip_metafile_SetWindowOrg(MetafilePlayContext * context,int x,int y)344 gdip_metafile_SetWindowOrg (MetafilePlayContext *context, int x, int y)
345 {
346 #ifdef DEBUG_METAFILE
347 printf ("SetWindowOrg X: %d, Y: %d", x, y);
348 #endif
349 /* TODO */
350 return Ok;
351 }
352
353 /* http://wvware.sourceforge.net/caolan/SetWindowExt.html */
354 GpStatus
gdip_metafile_SetWindowExt(MetafilePlayContext * context,int height,int width)355 gdip_metafile_SetWindowExt (MetafilePlayContext *context, int height, int width)
356 {
357 GpStatus status;
358 float sx, sy;
359
360 #ifdef DEBUG_METAFILE
361 printf ("SetWindowExt height %d, width %d", height, width);
362 #endif
363 switch (context->map_mode) {
364 case MM_ISOTROPIC:
365 sx = (float)context->metafile->metafile_header.Width / width;
366 sy = (float)context->metafile->metafile_header.Height / height;
367 /* keeps ratio to 1:1 */
368 if (sx < sy)
369 sy = sx;
370 break;
371 case MM_ANISOTROPIC:
372 sx = (float)context->metafile->metafile_header.Width / width;
373 sy = (float)context->metafile->metafile_header.Height / height;
374 break;
375 default:
376 /* most cases are handled by SetMapMode */
377 return Ok;
378 }
379
380 /* this isn't cumulative (and we get a lot of "junk" calls) */
381 GdipSetWorldTransform (context->graphics, &context->matrix);
382 status = GdipScaleWorldTransform (context->graphics, sx, sy, MatrixOrderPrepend);
383 #ifdef DEBUG_METAFILE_2
384 printf ("\n\tGdipScaleWorldTransform sx %g, sy %g (status %d)", sx, sy, status);
385 #endif
386 return status;
387 }
388
389 /* http://wvware.sourceforge.net/caolan/LineTo.html */
390 GpStatus
gdip_metafile_LineTo(MetafilePlayContext * context,int x,int y)391 gdip_metafile_LineTo (MetafilePlayContext *context, int x, int y)
392 {
393 GpStatus status;
394 #ifdef DEBUG_METAFILE
395 printf ("LineTo %s%d, %d", context->use_path ? "Path " : " ", x, y);
396 #endif
397 if (context->use_path) {
398 status = GdipAddPathLine (context->path, context->current_x, context->current_y, x, y);
399 } else {
400 GpPen *pen = gdip_metafile_GetSelectedPen (context);
401 status = GdipDrawLine (context->graphics, pen, context->current_x, context->current_y, x, y);
402 }
403 context->current_x = x;
404 context->current_y = y;
405 return status;
406 }
407
408 /* http://wvware.sourceforge.net/caolan/MoveTo.html */
409 GpStatus
gdip_metafile_MoveTo(MetafilePlayContext * context,int x,int y)410 gdip_metafile_MoveTo (MetafilePlayContext *context, int x, int y)
411 {
412 #ifdef DEBUG_METAFILE
413 printf ("MoveTo X: %d, Y: %d", x, y);
414 #endif
415 /* seems to always be called before Arc but, according to documentation,
416 Arc doesn't depend on the current cursor position */
417 context->current_x = x;
418 context->current_y = y;
419 return Ok;
420 }
421
422 GpStatus
gdip_metafile_SetMiterLimit(MetafilePlayContext * context,float eNewLimit,float * peOldLimit)423 gdip_metafile_SetMiterLimit (MetafilePlayContext *context, float eNewLimit, float *peOldLimit)
424 {
425 if (peOldLimit)
426 *peOldLimit = context->miter_limit;
427 /* GDI+ keeps the miter limit with it's pen, not the context, definition */
428 context->miter_limit = eNewLimit;
429 return Ok;
430 }
431
432 /* http://wvware.sourceforge.net/caolan/CreatePenIndirect.html */
433 GpStatus
gdip_metafile_CreatePenIndirect(MetafilePlayContext * context,DWORD style,DWORD width,DWORD color)434 gdip_metafile_CreatePenIndirect (MetafilePlayContext *context, DWORD style, DWORD width, DWORD color)
435 {
436 LOGBRUSH brush;
437 brush.lbStyle = 0;
438 brush.lbColor = color;
439 brush.lbHatch = 0;
440 return gdip_metafile_ExtCreatePen (context, style, width, &brush, 0, NULL);
441 }
442
443 GpStatus
gdip_metafile_ExtCreatePen(MetafilePlayContext * context,DWORD dwPenStyle,DWORD dwWidth,CONST LOGBRUSH * lplb,DWORD dwStyleCount,CONST DWORD * lpStyle)444 gdip_metafile_ExtCreatePen (MetafilePlayContext *context, DWORD dwPenStyle, DWORD dwWidth, CONST LOGBRUSH *lplb,
445 DWORD dwStyleCount, CONST DWORD *lpStyle)
446 {
447 /* TODO - there's more cases to consider */
448 GpStatus status;
449 GpPen *pen = NULL;
450 GpLineCap line_cap = LineCapRound;
451 GpLineJoin line_join = LineJoinRound;
452 int s = dwPenStyle & PS_STYLE_MASK;
453 DWORD color = lplb->lbColor;
454
455 #ifdef DEBUG_METAFILE
456 printf ("ExtCreatePenIndirect style %d, width %d, color %X", dwPenStyle, dwWidth, color);
457 #endif
458 if (s == PS_NULL)
459 color &= 0x00FFFFFF;
460 else
461 color |= 0xFF000000;
462
463 status = GdipCreatePen1 (color, dwWidth, UnitPixel, &pen);
464 if (status != Ok)
465 return status;
466
467 /* The style is always solid for width > 1 */
468 if (dwWidth > 1) {
469 switch (s) {
470 default:
471 g_warning ("Invalid pen style %d, style & PS_STYLE_MASK %d", dwPenStyle, s);
472 /* fall through */
473 case PS_NULL:
474 case PS_SOLID:
475 break;
476 case PS_DASH:
477 case PS_DOT:
478 case PS_DASHDOT:
479 case PS_DASHDOTDOT:
480 status = GdipSetPenDashStyle (pen, s);
481 if (status != Ok) {
482 GdipDeletePen (pen);
483 return status;
484 }
485 break;
486 }
487 }
488
489 /* at this stage we got a pen, so we won't abort drawing on it's style */
490 s = (dwPenStyle & PS_TYPE_MASK);
491 if (s == PS_GEOMETRIC) {
492 s = (dwPenStyle & PS_ENDCAP_MASK);
493 switch (s) {
494 default:
495 g_warning ("Invalid pen endcap, style %d, (style & PS_ENDCAP_MASK) %d", dwPenStyle, s);
496 /* fall through */
497 case PS_ENDCAP_ROUND:
498 line_cap = LineCapRound;
499 break;
500 case PS_ENDCAP_SQUARE:
501 line_cap = LineCapSquare;
502 break;
503 case PS_ENDCAP_FLAT:
504 line_cap = LineCapFlat;
505 break;
506 }
507 GdipSetPenStartCap (pen, line_cap);
508 GdipSetPenEndCap (pen, line_cap);
509
510 s = (dwPenStyle & PS_JOIN_MASK);
511 switch (s) {
512 default:
513 g_warning ("Invalid pen join, style %d, (style & PS_JOIN_MASK) %d", dwPenStyle, s);
514 /* fall through */
515 case PS_JOIN_ROUND:
516 line_join = LineJoinRound;
517 break;
518 case PS_JOIN_BEVEL:
519 line_join = LineJoinBevel;
520 break;
521 case PS_JOIN_MITER:
522 line_join = LineJoinMiter;
523 break;
524 }
525 GdipSetPenLineJoin (pen, line_join);
526 }
527
528 context->created.type = METAOBJECT_TYPE_PEN;
529 context->created.ptr = pen;
530 return Ok;
531 }
532
533 /* http://wvware.sourceforge.net/caolan/CreateBrushIndirect.html */
534 GpStatus
gdip_metafile_CreateBrushIndirect(MetafilePlayContext * context,DWORD style,DWORD color,DWORD hatch)535 gdip_metafile_CreateBrushIndirect (MetafilePlayContext *context, DWORD style, DWORD color, DWORD hatch)
536 {
537 GpStatus status = Ok;
538 void *brush;
539
540 switch (style) {
541 case BS_SOLID:
542 color |= 0xFF000000;
543 status = GdipCreateSolidFill (color, (GpSolidFill **) &brush);
544 break;
545 case BS_NULL:
546 color &= 0x00FFFFFF;
547 status = GdipCreateSolidFill (color, (GpSolidFill **) &brush);
548 break;
549 case BS_HATCHED:
550 color |= 0xFF000000;
551 status = GdipCreateHatchBrush (hatch, color, 0xFFFFFFFF, (GpHatch **) &brush);
552 break;
553 default:
554 g_warning ("gdip_metafile_CreateBrushIndirect unimplemented style %d", style);
555 status = GdipCreateSolidFill (color, (GpSolidFill **) &brush);
556 break;
557 }
558
559 #ifdef DEBUG_METAFILE
560 printf ("CreateBrushIndirect style %d, color %X, hatch %d (%p, status %d)", style, color, hatch, brush, status);
561 #endif
562 context->created.type = METAOBJECT_TYPE_BRUSH;
563 context->created.ptr = brush;
564 return status;
565 }
566
567 /* http://wvware.sourceforge.net/caolan/Arc.html */
568 GpStatus
gdip_metafile_Arc(MetafilePlayContext * context,int left,int top,int right,int bottom,int xstart,int ystart,int xend,int yend)569 gdip_metafile_Arc (MetafilePlayContext *context, int left, int top, int right, int bottom,
570 int xstart, int ystart, int xend, int yend)
571 {
572 #ifdef DEBUG_METAFILE
573 printf ("Arc left %d, top %d, right %d, bottom %d, xstart %d, ystart %d, xend %d, yend %d",
574 left, top, right, bottom, xstart, ystart, xend, yend);
575 #endif
576
577 /* Don't draw if the width or height are empty. */
578 if ((right - left <= 0) || (bottom - top <= 0))
579 return Ok;
580
581 return GdipDrawArc (context->graphics, gdip_metafile_GetSelectedPen (context), left, top,
582 (right - left), (bottom - top), atan2 (ystart, xstart), atan2 (yend, xend));
583 }
584
585 /* http://wvware.sourceforge.net/caolan/Rectangle.html */
586 GpStatus
gdip_metafile_Rectangle(MetafilePlayContext * context,int bottomRect,int rightRect,int topRect,int leftRect)587 gdip_metafile_Rectangle (MetafilePlayContext *context, int bottomRect, int rightRect, int topRect, int leftRect)
588 {
589 GpStatus status;
590 int x = min (leftRect, rightRect);
591 int y = min (topRect, bottomRect);
592 int width = abs (rightRect - leftRect);
593 int height = abs (bottomRect - topRect);
594
595 #ifdef DEBUG_METAFILE
596 printf ("Rectangle bottom %d, right %d, top %d, left %d",
597 bottomRect, rightRect, topRect, leftRect);
598 #endif
599
600 status = GdipFillRectangleI (context->graphics, gdip_metafile_GetSelectedBrush (context), x, y, width, height);
601 if (status != Ok)
602 return status;
603
604 return GdipDrawRectangleI (context->graphics, gdip_metafile_GetSelectedPen (context), x, y, width, height);
605 }
606
607 GpStatus
gdip_metafile_SetPixel(MetafilePlayContext * context,DWORD color,int x,int y)608 gdip_metafile_SetPixel (MetafilePlayContext *context, DWORD color, int x, int y)
609 {
610 color |= 0xFF000000;
611
612 GpBrush *fill;
613 GpStatus status = GdipCreateSolidFill (color, (GpSolidFill **) &fill);
614 if (status != Ok)
615 return status;
616
617 status = GdipFillRectangle (context->graphics, fill, x, y, 1, 1);
618 GdipDeleteBrush (fill);
619 return status;
620 }
621
622
623 /* http://wvware.sourceforge.net/caolan/ora-wmf.html */
624 GpStatus
gdip_metafile_StretchDIBits(MetafilePlayContext * context,int XDest,int YDest,int nDestWidth,int nDestHeight,int XSrc,int YSrc,int nSrcWidth,int nSrcHeight,CONST void * lpBits,CONST BITMAPINFO * lpBitsInfo,UINT iUsage,DWORD dwRop)625 gdip_metafile_StretchDIBits (MetafilePlayContext *context, int XDest, int YDest, int nDestWidth, int nDestHeight,
626 int XSrc, int YSrc, int nSrcWidth, int nSrcHeight, CONST void *lpBits, CONST BITMAPINFO *lpBitsInfo,
627 UINT iUsage, DWORD dwRop)
628 {
629 GpStatus status = OutOfMemory;
630 GpImage *image = NULL;
631 MemorySource ms;
632 #ifdef DEBUG_METAFILE
633 printf ("StretchDIBits\n\t[XDest %d, YDest %d, nDestWidth %d, nDestHeight %d]", XDest, YDest, nDestWidth, nDestHeight);
634 printf ("\n\t[XSrc %d, YSrc %d, nSrcWidth %d, nSrcHeight %d]", XSrc, YSrc, nSrcWidth, nSrcHeight);
635 printf ("\n\tlpBits %p, lpBitsInfo %p, iUsage %d, dwRop %d", lpBits, lpBitsInfo, iUsage, dwRop);
636 printf ("\n\tBITMAPINFO\n\t\tSize: %d", lpBitsInfo->bmiHeader.biSize);
637 printf ("\n\t\tWidth: %d", lpBitsInfo->bmiHeader.biWidth);
638 printf ("\n\t\tHeight: %d", lpBitsInfo->bmiHeader.biHeight);
639 printf ("\n\t\tPlanes: %d", lpBitsInfo->bmiHeader.biPlanes);
640 printf ("\n\t\tBitCount: %d", lpBitsInfo->bmiHeader.biBitCount);
641 printf ("\n\t\tCompression: %d", lpBitsInfo->bmiHeader.biCompression);
642 printf ("\n\t\tSizeImage: %d", lpBitsInfo->bmiHeader.biSizeImage);
643 printf ("\n\t\tXPelsPerMeter: %d", lpBitsInfo->bmiHeader.biXPelsPerMeter);
644 printf ("\n\t\tYPelsPerMeter: %d", lpBitsInfo->bmiHeader.biXPelsPerMeter);
645 printf ("\n\t\tClrUsed: %d", lpBitsInfo->bmiHeader.biClrUsed);
646 printf ("\n\t\tClrImportant: %d", lpBitsInfo->bmiHeader.biClrImportant);
647 #endif
648 ms.ptr = (BYTE*)lpBitsInfo;
649 if (lpBitsInfo->bmiHeader.biCompression == BI_RGB) {
650 // Per the spec, if compression is RGB ImageSize must be ignored (and it should be zero anyway)
651 // and calculated according to the following formula.
652 ms.size = (floor ((lpBitsInfo->bmiHeader.biWidth * lpBitsInfo->bmiHeader.biPlanes *
653 lpBitsInfo->bmiHeader.biBitCount + 31) / 32) * 4) * abs (lpBitsInfo->bmiHeader.biHeight);
654 } else {
655 ms.size = lpBitsInfo->bmiHeader.biSizeImage;
656 }
657 ms.pos = 0;
658 status = gdip_read_bmp_image (&ms, &image, Memory);
659 if (status == Ok) {
660 status = GdipDrawImageRectRect (context->graphics, image, XDest, YDest,
661 nDestWidth, nDestHeight, XSrc, YSrc, nSrcWidth, nSrcHeight, UnitPixel, NULL, NULL, NULL);
662 }
663 if (image)
664 GdipDisposeImage (image);
665 return status;
666 }
667
668 /*
669 * Return the selected GDI+ Pen to draw on the metafile or NULL if none is selected/valid.
670 */
671 GpPen*
gdip_metafile_GetSelectedPen(MetafilePlayContext * context)672 gdip_metafile_GetSelectedPen (MetafilePlayContext *context)
673 {
674 GpPen *pen;
675
676 // EMF have some stock objects that do not requires explicit creation
677 if (context->selected_pen & ENHMETA_STOCK_OBJECT) {
678 switch (context->selected_pen - ENHMETA_STOCK_OBJECT) {
679 case WHITE_PEN:
680 if (!context->stock_pen_white) {
681 if (GdipCreatePen1 (0xFFFFFFFF, 0, UnitPixel, &context->stock_pen_white) != Ok)
682 return NULL;
683 }
684 pen = context->stock_pen_white;
685 break;
686 case BLACK_PEN:
687 if (!context->stock_pen_black) {
688 if (GdipCreatePen1 (0xFF000000, 0, UnitPixel, &context->stock_pen_black) != Ok)
689 return NULL;
690 }
691 pen = context->stock_pen_black;
692 break;
693 case NULL_PEN:
694 if (!context->stock_pen_null) {
695 if (GdipCreatePen1 (0x00000000, 0, UnitPixel, &context->stock_pen_null) != Ok)
696 return NULL;
697 }
698 pen = context->stock_pen_null;
699 break;
700 default:
701 return NULL;
702 }
703 } else {
704 if ((context->selected_pen < 0) || (context->selected_pen >= context->objects_count)) {
705 g_warning ("Invalid pen handle %d [0..%d[", context->selected_pen, context->objects_count);
706 return NULL;
707 }
708
709 if (context->objects [context->selected_pen].type != METAOBJECT_TYPE_PEN) {
710 g_warning ("Wrong object type %d, expected pen (%d)", context->objects [context->selected_pen].type,
711 METAOBJECT_TYPE_PEN);
712 return NULL;
713 }
714 #ifdef DEBUG_METAFILE
715 printf ("\tusing pen #%d (%p)", context->selected_pen, context->objects [context->selected_pen].ptr);
716 #endif
717 pen = (GpPen*) context->objects [context->selected_pen].ptr;
718 }
719
720 /* miter limit was global (i.e. context not pen specific) in GDI */
721 GdipSetPenMiterLimit (pen, context->miter_limit);
722 return pen;
723 }
724
725 /*
726 * Return the selected GDI+ Brush to fill on the metafile or NULL if none is selected/valid.
727 */
728 GpBrush*
gdip_metafile_GetSelectedBrush(MetafilePlayContext * context)729 gdip_metafile_GetSelectedBrush (MetafilePlayContext *context)
730 {
731 // EMF have some stock objects that do not requires explicit creation
732 if (context->selected_brush & ENHMETA_STOCK_OBJECT) {
733 switch (context->selected_brush - ENHMETA_STOCK_OBJECT) {
734 case WHITE_BRUSH:
735 if (!context->stock_brush_white) {
736 if (GdipCreateSolidFill (0xFFFFFFFF, &context->stock_brush_white) != Ok)
737 return NULL;
738 }
739 return (GpBrush*)context->stock_brush_white;
740 case LTGRAY_BRUSH:
741 if (!context->stock_brush_ltgray) {
742 if (GdipCreateSolidFill (0xFFBBBBBB, &context->stock_brush_ltgray) != Ok)
743 return NULL;
744 }
745 return (GpBrush*)context->stock_brush_ltgray;
746 case GRAY_BRUSH:
747 if (!context->stock_brush_gray) {
748 if (GdipCreateSolidFill (0xFF888888, &context->stock_brush_gray) != Ok)
749 return NULL;
750 }
751 return (GpBrush*)context->stock_brush_gray;
752 case DKGRAY_BRUSH:
753 if (!context->stock_brush_dkgray) {
754 if (GdipCreateSolidFill (0xFF444444, &context->stock_brush_dkgray) != Ok)
755 return NULL;
756 }
757 return (GpBrush*)context->stock_brush_dkgray;
758 case BLACK_BRUSH:
759 if (!context->stock_brush_black) {
760 if (GdipCreateSolidFill (0xFF000000, &context->stock_brush_black) != Ok)
761 return NULL;
762 }
763 return (GpBrush*)context->stock_brush_black;
764 case NULL_BRUSH:
765 if (!context->stock_brush_null) {
766 if (GdipCreateSolidFill (0x00000000, &context->stock_brush_null) != Ok)
767 return NULL;
768 }
769 return (GpBrush*)context->stock_brush_null;
770 default:
771 return NULL;
772 }
773 }
774
775 if ((context->selected_brush < 0) || (context->selected_brush >= context->objects_count)) {
776 g_warning ("Invalid brush handle %d [0..%d[", context->selected_brush, context->objects_count);
777 return NULL;
778 }
779
780 if (context->objects [context->selected_brush].type != METAOBJECT_TYPE_BRUSH) {
781 g_warning ("Wrong object type %d, expected brush (%d)", context->objects [context->selected_brush].type,
782 METAOBJECT_TYPE_BRUSH);
783 return NULL;
784 }
785
786 #ifdef DEBUG_METAFILE
787 printf ("\tusing brush #%d (%p)", context->selected_brush, context->objects [context->selected_brush].ptr);
788 #endif
789 return (GpBrush*) context->objects [context->selected_brush].ptr;
790 }
791
792 GpStatus
gdip_metafile_PolyBezier(MetafilePlayContext * context,GpPointF * points,int count)793 gdip_metafile_PolyBezier (MetafilePlayContext *context, GpPointF *points, int count)
794 {
795 GpStatus status;
796 #ifdef DEBUG_METAFILE
797 printf ("PolyBezier %s count %d", context->use_path ? "Path " : " ", count);
798 #endif
799 if (context->use_path) {
800 status = GdipAddPathBeziers (context->path, points, count);
801 } else {
802 GpPen *pen = gdip_metafile_GetSelectedPen (context);
803 return GdipDrawCurve (context->graphics, pen, points, count);
804 }
805 return status;
806 }
807
808 GpStatus
gdip_metafile_Polygon(MetafilePlayContext * context,GpPointF * points,int count)809 gdip_metafile_Polygon (MetafilePlayContext *context, GpPointF *points, int count)
810 {
811 #ifdef DEBUG_METAFILE
812 printf ("Polygon %s count %d", context->use_path ? "Path " : " ", count);
813 #endif
814 GpBrush *brush = gdip_metafile_GetSelectedBrush (context);
815 GpStatus status = GdipFillPolygon (context->graphics, brush, points, count, context->fill_mode);
816 if (status == Ok) {
817 GpPen *pen = gdip_metafile_GetSelectedPen (context);
818 status = GdipDrawPolygon (context->graphics, pen, points, count);
819 }
820 return status;
821 }
822
823 GpStatus
gdip_metafile_BeginPath(MetafilePlayContext * context)824 gdip_metafile_BeginPath (MetafilePlayContext *context)
825 {
826 GpStatus status;
827
828 if (context->path) {
829 GdipDeletePath (context->path);
830 context->path = NULL;
831 }
832 context->use_path = TRUE;
833 status = GdipCreatePath (0, &context->path);
834 #ifdef DEBUG_METAFILE
835 printf ("BeginPath %p", context->path);
836 #endif
837 return status;
838 }
839
840 GpStatus
gdip_metafile_EndPath(MetafilePlayContext * context)841 gdip_metafile_EndPath (MetafilePlayContext *context)
842 {
843 #ifdef DEBUG_METAFILE
844 printf ("EndPath %p", context->path);
845 #endif
846 context->use_path = FALSE;
847 return Ok;
848 }
849
850 GpStatus
gdip_metafile_CloseFigure(MetafilePlayContext * context)851 gdip_metafile_CloseFigure (MetafilePlayContext *context)
852 {
853 #ifdef DEBUG_METAFILE
854 printf ("CloseFigure %p", context->path);
855 #endif
856 return GdipClosePathFigures (context->path);
857 }
858
859 GpStatus
gdip_metafile_FillPath(MetafilePlayContext * context)860 gdip_metafile_FillPath (MetafilePlayContext *context)
861 {
862 #ifdef DEBUG_METAFILE
863 printf ("FillPath %p", context->path);
864 #endif
865 GpBrush* brush = gdip_metafile_GetSelectedBrush (context);
866 /* end path if required */
867 if (context->use_path)
868 gdip_metafile_EndPath (context);
869 return GdipFillPath (context->graphics, brush, context->path);
870 }
871
872 GpStatus
gdip_metafile_StrokePath(MetafilePlayContext * context)873 gdip_metafile_StrokePath (MetafilePlayContext *context)
874 {
875 #ifdef DEBUG_METAFILE
876 printf ("StrokePath %p", context->path);
877 #endif
878 GpPen *pen = gdip_metafile_GetSelectedPen (context);
879 /* end path if required */
880 if (context->use_path)
881 gdip_metafile_EndPath (context);
882 return GdipDrawPath (context->graphics, pen, context->path);
883 }
884
885 GpStatus
gdip_metafile_StrokeAndFillPath(MetafilePlayContext * context)886 gdip_metafile_StrokeAndFillPath (MetafilePlayContext *context)
887 {
888 GpBrush *brush;
889 GpStatus status;
890 #ifdef DEBUG_METAFILE
891 printf ("StrokeAndFillPath %p", context->path);
892 #endif
893 /* end path if required */
894 if (context->use_path)
895 gdip_metafile_EndPath (context);
896
897 brush = gdip_metafile_GetSelectedBrush (context);
898 status = GdipFillPath (context->graphics, brush, context->path);
899 if (status == Ok) {
900 GpPen *pen = gdip_metafile_GetSelectedPen (context);
901 status = GdipDrawPath (context->graphics, pen, context->path);
902 }
903 return status;
904 }
905
906
907 static GpMetafile*
gdip_metafile_create()908 gdip_metafile_create ()
909 {
910 GpMetafile* mf = (GpMetafile*) GdipAlloc (sizeof (GpMetafile));
911 if (mf) {
912 memset (&mf->base, 0, sizeof (GpImage));
913
914 mf->base.type = ImageTypeMetafile;
915 mf->delete = FALSE;
916 mf->data = NULL;
917 mf->length = 0;
918 mf->recording = FALSE;
919 mf->fp = NULL;
920 mf->stream = NULL;
921 }
922 return mf;
923 }
924
925 GpStatus
gdip_metafile_clone(GpMetafile * metafile,GpMetafile ** clonedmetafile)926 gdip_metafile_clone (GpMetafile *metafile, GpMetafile **clonedmetafile)
927 {
928 GpStatus status;
929 GpMetafile *mf = gdip_metafile_create ();
930 GpImage *base;
931 if (!mf)
932 return OutOfMemory;
933
934 status = gdip_bitmap_clone (&metafile->base, &base);
935 if (status != Ok) {
936 GdipFree (mf);
937 return status;
938 }
939
940 status = gdip_bitmap_setactive (base, NULL, 0);
941 if (status != Ok) {
942 GdipFree (mf);
943 GdipFree (base);
944 return status;
945 }
946
947 mf->base = *base;
948
949 memcpy (&mf->metafile_header, &metafile->metafile_header, sizeof (MetafileHeader));
950 if (metafile->length > 0) {
951 mf->data = GdipAlloc (metafile->length);
952 if (!mf->data) {
953 GdipFree (mf);
954 return OutOfMemory;
955 }
956
957 memcpy (mf->data, metafile->data, metafile->length);
958 mf->length = metafile->length;
959 }
960
961 *clonedmetafile = mf;
962 return Ok;
963 }
964
965 GpStatus
gdip_metafile_dispose(GpMetafile * metafile)966 gdip_metafile_dispose (GpMetafile *metafile)
967 {
968 if (!metafile)
969 return InvalidParameter;
970
971 /* TODO deal with "delete" flag */
972 metafile->length = 0;
973 if (metafile->data) {
974 GdipFree (metafile->data);
975 metafile->data = NULL;
976 }
977
978 if (metafile->recording)
979 gdip_metafile_stop_recording (metafile);
980
981 GdipFree (metafile);
982 return Ok;
983 }
984
985 GpStatus
gdip_get_bitmap_from_metafile(GpMetafile * metafile,INT width,INT height,GpImage ** thumbnail)986 gdip_get_bitmap_from_metafile (GpMetafile *metafile, INT width, INT height, GpImage **thumbnail)
987 {
988 if (width <= 0 || height <= 0) {
989 switch (metafile->metafile_header.Type) {
990 case MetafileTypeWmfPlaceable:
991 case MetafileTypeWmf: {
992 width = iround (metafile->metafile_header.Width / 1000.0f * gdip_get_display_dpi());
993 height = iround (metafile->metafile_header.Height / 1000.0f * gdip_get_display_dpi());
994 break;
995 }
996 case MetafileTypeEmf:
997 case MetafileTypeEmfPlusOnly:
998 case MetafileTypeEmfPlusDual: {
999 width = metafile->metafile_header.Width;
1000 height = metafile->metafile_header.Height;
1001 break;
1002 }
1003 default:
1004 return GenericError;
1005 }
1006 }
1007
1008 return GdipGetImageThumbnail ((GpImage *) metafile, width, height, thumbnail, NULL, NULL);
1009 }
1010
1011 GpStatus
gdip_metafile_stop_recording(GpMetafile * metafile)1012 gdip_metafile_stop_recording (GpMetafile *metafile)
1013 {
1014 /* TODO save current stuff */
1015
1016 if (metafile->fp) {
1017 fclose (metafile->fp);
1018 metafile->fp = NULL;
1019 }
1020 if (metafile->stream) {
1021 /* it's not ours to close, just forget about it */
1022 metafile->stream = NULL;
1023 }
1024 /* we cannot open a new graphics instance on this metafile - recording is over */
1025 metafile->recording = FALSE;
1026 return Ok;
1027 }
1028
1029 MetafilePlayContext*
gdip_metafile_play_setup(GpMetafile * metafile,GpGraphics * graphics,int x,int y,int width,int height)1030 gdip_metafile_play_setup (GpMetafile *metafile, GpGraphics *graphics, int x, int y, int width, int height)
1031 {
1032 int i;
1033 MetaObject *obj;
1034 MetafilePlayContext *context;
1035 float scaleX;
1036 float scaleY;
1037 float translateX;
1038 float translateY;
1039 /* metafiles always render as 32bppRgb */
1040
1041 if (!metafile || !graphics)
1042 return NULL;
1043
1044 context = GdipAlloc (sizeof (MetafilePlayContext));
1045 if (!context) {
1046 return NULL;
1047 }
1048
1049 context->metafile = metafile;
1050 context->graphics = graphics;
1051 context->use_path = FALSE;
1052 context->path = NULL;
1053
1054 /* keep a copy for clean up */
1055 GdipGetWorldTransform (graphics, &context->initial);
1056 #ifdef DEBUG_METAFILE
1057 MetafileHeader *mh = &metafile->metafile_header;
1058 GpMatrix *m = &context->initial;
1059 g_warning ("\nMetafileHeader X %d, Y %d, W %d, H %d", mh->X, mh->Y, mh->Width, mh->Height);
1060 g_warning ("\n\tinitial matrix %g %g %g %g %g %g", m->xx, m->yx, m->xy, m->yy, m->x0, m->y0);
1061 #endif
1062 context->x = x;
1063 context->y = y;
1064 context->width = width;
1065 context->height = height;
1066 /* and keep an adjusted copy for providing "resets" */
1067 scaleX = (float) width / (float) metafile->metafile_header.Width;
1068 scaleY = (float) height / (float) metafile->metafile_header.Height;
1069 GdipScaleWorldTransform (graphics, scaleX, scaleY, MatrixOrderPrepend);
1070 translateX = -metafile->metafile_header.X + x / scaleX;
1071 translateY = -metafile->metafile_header.Y + y / scaleY;
1072 GdipTranslateWorldTransform (graphics, translateX, translateY, MatrixOrderPrepend);
1073 GdipGetWorldTransform (graphics, &context->matrix);
1074
1075 /* defaults */
1076 context->fill_mode = FillModeAlternate;
1077 switch (context->metafile->metafile_header.Type) {
1078 case MetafileTypeWmfPlaceable:
1079 case MetafileTypeWmf:
1080 gdip_metafile_SetMapMode (context, MM_TWIPS);
1081 break;
1082 case MetafileTypeEmf:
1083 case MetafileTypeEmfPlusOnly:
1084 case MetafileTypeEmfPlusDual:
1085 gdip_metafile_SetMapMode (context, MM_TEXT);
1086 break;
1087 default:
1088 GdipFree (context);
1089 return NULL;
1090 }
1091
1092 context->miter_limit = 10.0f;
1093 context->selected_pen = ENHMETA_STOCK_OBJECT + BLACK_PEN;
1094 context->selected_brush = ENHMETA_STOCK_OBJECT + WHITE_BRUSH;
1095 context->selected_font = -1;
1096 context->selected_palette = -1;
1097
1098 /* Create* functions store the object here */
1099 context->created.type = METAOBJECT_TYPE_EMPTY;
1100 context->created.ptr = NULL;
1101
1102 /* stock objects */
1103 context->stock_pen_white = NULL;
1104 context->stock_pen_black = NULL;
1105 context->stock_pen_null = NULL;
1106 context->stock_brush_white = NULL;
1107 context->stock_brush_ltgray = NULL;
1108 context->stock_brush_gray = NULL;
1109 context->stock_brush_dkgray = NULL;
1110 context->stock_brush_black = NULL;
1111 context->stock_brush_null = NULL;
1112
1113 /* SelectObject | DeleteObject works on this array */
1114 switch (context->metafile->metafile_header.Type) {
1115 case MetafileTypeWmfPlaceable:
1116 case MetafileTypeWmf:
1117 context->objects_count = metafile->metafile_header.Header.Wmf.mtNoObjects;
1118 break;
1119 case MetafileTypeEmf:
1120 case MetafileTypeEmfPlusOnly:
1121 case MetafileTypeEmfPlusDual:
1122 context->objects_count = metafile->metafile_header.Header.Emf.nHandles + 1; /* 0 is reserved */
1123 break;
1124 default:
1125 GdipFree (context);
1126 return NULL;
1127 }
1128 context->objects = (MetaObject*) GdipAlloc (context->objects_count * sizeof (MetaObject));
1129 if (!context->objects) {
1130 GdipFree (context);
1131 return NULL;
1132 }
1133 obj = context->objects;
1134 for (i = 0; i < context->objects_count; i++) {
1135 obj->type = METAOBJECT_TYPE_EMPTY;
1136 obj->ptr = NULL;
1137 obj++;
1138 }
1139
1140 return context;
1141 }
1142
1143 GpStatus
gdip_metafile_play(MetafilePlayContext * context)1144 gdip_metafile_play (MetafilePlayContext *context)
1145 {
1146 if (!context || !context->metafile)
1147 return InvalidParameter;
1148
1149 switch (context->metafile->metafile_header.Type) {
1150 case MetafileTypeWmfPlaceable:
1151 case MetafileTypeWmf:
1152 return gdip_metafile_play_wmf (context);
1153 case MetafileTypeEmf:
1154 return gdip_metafile_play_emf (context);
1155 case MetafileTypeEmfPlusOnly:
1156 case MetafileTypeEmfPlusDual:
1157 return gdip_metafile_play_emf (context);
1158 default:
1159 g_warning ("Invalid metafile format %d", context->metafile->metafile_header.Type);
1160 break;
1161 }
1162 return NotImplemented;
1163 }
1164
1165 GpStatus
gdip_metafile_play_cleanup(MetafilePlayContext * context)1166 gdip_metafile_play_cleanup (MetafilePlayContext *context)
1167 {
1168 if (!context)
1169 return InvalidParameter;
1170
1171 GdipSetWorldTransform (context->graphics, &context->initial);
1172 context->graphics = NULL;
1173 if (context->path) {
1174 GdipDeletePath (context->path);
1175 context->path = NULL;
1176 }
1177 context->created.type = METAOBJECT_TYPE_EMPTY;
1178 context->created.ptr = NULL;
1179 if (context->objects) {
1180 int i;
1181 MetaObject *obj = context->objects;
1182 /* free each object */
1183 for (i = 0; i < context->objects_count; i++) {
1184 gdip_metafile_DeleteObject (context, i);
1185 obj++;
1186 }
1187 GdipFree (context->objects);
1188 context->objects = NULL;
1189 }
1190
1191 context->selected_pen = -1;
1192 context->selected_brush = -1;
1193 context->selected_font = -1;
1194 context->selected_font = -1;
1195 context->selected_palette = -1;
1196
1197 /* stock objects */
1198 if (context->stock_pen_white)
1199 GdipDeletePen (context->stock_pen_white);
1200 if (context->stock_pen_black)
1201 GdipDeletePen (context->stock_pen_black);
1202 if (context->stock_pen_null)
1203 GdipDeletePen (context->stock_pen_null);
1204 if (context->stock_brush_white)
1205 GdipDeleteBrush ((GpBrush*)context->stock_brush_white);
1206 if (context->stock_brush_ltgray)
1207 GdipDeleteBrush ((GpBrush*)context->stock_brush_ltgray);
1208 if (context->stock_brush_gray)
1209 GdipDeleteBrush ((GpBrush*)context->stock_brush_gray);
1210 if (context->stock_brush_dkgray)
1211 GdipDeleteBrush ((GpBrush*)context->stock_brush_dkgray);
1212 if (context->stock_brush_black)
1213 GdipDeleteBrush ((GpBrush*)context->stock_brush_black);
1214 if (context->stock_brush_null)
1215 GdipDeleteBrush ((GpBrush*)context->stock_brush_null);
1216
1217 GdipFree (context);
1218 return Ok;
1219 }
1220
1221 static void
WmfPlaceableFileHeaderLE(WmfPlaceableFileHeader * wmfPlaceableFileHeader)1222 WmfPlaceableFileHeaderLE (WmfPlaceableFileHeader *wmfPlaceableFileHeader)
1223 {
1224 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1225 /* header->Key is already adjusted */
1226 wmfPlaceableFileHeader->Hmf = GUINT16_FROM_LE (wmfPlaceableFileHeader->Hmf);
1227 wmfPlaceableFileHeader->BoundingBox.Left = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Left);
1228 wmfPlaceableFileHeader->BoundingBox.Top = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Top);
1229 wmfPlaceableFileHeader->BoundingBox.Right = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Right);
1230 wmfPlaceableFileHeader->BoundingBox.Bottom = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Bottom);
1231 wmfPlaceableFileHeader->Inch = GUINT16_FROM_LE (wmfPlaceableFileHeader->Inch);
1232 wmfPlaceableFileHeader->Reserved = GUINT32_FROM_LE (wmfPlaceableFileHeader->Reserved);
1233 wmfPlaceableFileHeader->Checksum = GUINT16_FROM_LE (wmfPlaceableFileHeader->Checksum);
1234 #endif
1235 }
1236
1237 static void
MetafileHeaderLE(MetafileHeader * header)1238 MetafileHeaderLE (MetafileHeader *header)
1239 {
1240 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1241 header->Header.Wmf.mtType = GUINT16_FROM_LE (header->Header.Wmf.mtType);
1242 header->Header.Wmf.mtHeaderSize = GUINT16_FROM_LE (header->Header.Wmf.mtHeaderSize);
1243 header->Header.Wmf.mtVersion = GUINT16_FROM_LE (header->Header.Wmf.mtVersion);
1244 header->Header.Wmf.mtSize = GUINT32_FROM_LE (header->Header.Wmf.mtSize);
1245 header->Header.Wmf.mtNoObjects = GUINT16_FROM_LE (header->Header.Wmf.mtNoObjects);
1246 header->Header.Wmf.mtMaxRecord = GUINT32_FROM_LE (header->Header.Wmf.mtMaxRecord);
1247 header->Header.Wmf.mtNoParameters = GUINT16_FROM_LE (header->Header.Wmf.mtNoParameters);
1248 #endif
1249 }
1250
1251 static void
EnhMetaHeaderLE(ENHMETAHEADER3 * emf)1252 EnhMetaHeaderLE (ENHMETAHEADER3 *emf)
1253 {
1254 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1255 /* emf->iType is already adjusted */
1256 emf->nSize = GUINT32_FROM_LE (emf->nSize);
1257 emf->rclBounds.left = GUINT32_FROM_LE (emf->rclBounds.left);
1258 emf->rclBounds.top = GUINT32_FROM_LE (emf->rclBounds.top);
1259 emf->rclBounds.right = GUINT32_FROM_LE (emf->rclBounds.right);
1260 emf->rclBounds.bottom = GUINT32_FROM_LE (emf->rclBounds.bottom);
1261 emf->rclFrame.left = GUINT32_FROM_LE (emf->rclFrame.left);
1262 emf->rclFrame.top = GUINT32_FROM_LE (emf->rclFrame.top);
1263 emf->rclFrame.right = GUINT32_FROM_LE (emf->rclFrame.right);
1264 emf->rclFrame.bottom = GUINT32_FROM_LE (emf->rclFrame.bottom);
1265 emf->dSignature = GUINT32_FROM_LE (emf->dSignature);
1266 emf->nVersion = GUINT32_FROM_LE (emf->nVersion);
1267 emf->nBytes = GUINT32_FROM_LE (emf->nBytes);
1268 emf->nRecords = GUINT32_FROM_LE (emf->nRecords);
1269 emf->nHandles = GUINT32_FROM_LE (emf->nHandles);
1270
1271 emf->sReserved = GUINT32_FROM_LE (emf->sReserved);
1272 emf->nDescription = GUINT32_FROM_LE (emf->nDescription);
1273 emf->offDescription = GUINT32_FROM_LE (emf->offDescription);
1274 emf->nPalEntries = GUINT32_FROM_LE (emf->nPalEntries);
1275 emf->szlDevice.cx = GUINT32_FROM_LE (emf->szlDevice.cx);
1276 emf->szlDevice.cy = GUINT32_FROM_LE (emf->szlDevice.cy);
1277 emf->szlMillimeters.cx = GUINT32_FROM_LE (emf->szlMillimeters.cx);
1278 emf->szlMillimeters.cy = GUINT32_FROM_LE (emf->szlMillimeters.cy);
1279 #endif
1280 }
1281
1282 static GpStatus
combine_headers(GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,MetafileHeader * header)1283 combine_headers (GDIPCONST WmfPlaceableFileHeader *wmfPlaceableFileHeader, MetafileHeader *header)
1284 {
1285 if (wmfPlaceableFileHeader) {
1286 header->Type = MetafileTypeWmfPlaceable;
1287
1288 header->X = min (wmfPlaceableFileHeader->BoundingBox.Right, wmfPlaceableFileHeader->BoundingBox.Left);
1289 header->Y = min (wmfPlaceableFileHeader->BoundingBox.Top, wmfPlaceableFileHeader->BoundingBox.Bottom);
1290 header->Width = abs (wmfPlaceableFileHeader->BoundingBox.Right - wmfPlaceableFileHeader->BoundingBox.Left);
1291 header->Height= abs (wmfPlaceableFileHeader->BoundingBox.Bottom - wmfPlaceableFileHeader->BoundingBox.Top);
1292
1293 /* The units of a metafile are twips (1/20 of a point). The Inch field contains the number
1294 * of twips per inch used to represent the image. There are 1440 twips per inch by default.
1295 * Use the default if the placeable header has no twips per inch specified. */
1296 if (wmfPlaceableFileHeader->Inch == 0)
1297 header->DpiX = 1440;
1298 else
1299 header->DpiX = wmfPlaceableFileHeader->Inch;
1300 } else {
1301 header->Type = MetafileTypeWmf;
1302 header->X = 0;
1303 header->Y = 0;
1304 /* TODO: GDI+ uses screen resolution for non-placeable metafiles */
1305 header->Width = 1280;
1306 header->Height= 1024;
1307 header->DpiX = gdip_get_display_dpi ();
1308 }
1309
1310 header->DpiY = header->DpiX;
1311 header->Size = header->Header.Wmf.mtSize * 2;
1312 header->Version = header->Header.Wmf.mtVersion;
1313 header->EmfPlusFlags = 0;
1314 header->EmfPlusHeaderSize = 0;
1315 header->LogicalDpiX = 0;
1316 header->LogicalDpiY = 0;
1317 return Ok;
1318 }
1319
1320 /* Handle the record based headers that can exists in EMF files */
1321 /* Note: GDI+ doesn't report correctly all stuff within the headers :| */
1322 static GpStatus
update_emf_header(MetafileHeader * header,BYTE * data,int length)1323 update_emf_header (MetafileHeader *header, BYTE* data, int length)
1324 {
1325 GpStatus status = Ok;
1326 MetafilePlayContext context;
1327 GpMetafile mf;
1328 if (length < sizeof (DWORD))
1329 return Ok;
1330
1331 DWORD *func = (DWORD*)data;
1332
1333 switch (*func) {
1334 case EMR_HEADER:
1335 g_warning ("TODO - EMR_HEADER. Not common, need test case :)");
1336 break;
1337 case EMR_GDICOMMENT:
1338 /* this could be an embedded EmfPlusRecordTypeHeader */
1339 context.metafile = &mf;
1340 context.graphics = NULL; /* special case where we're not playing the metafile */
1341 status = GdiComment (&context, data, length);
1342 if (status == Ok) {
1343 header->Type = mf.metafile_header.Type;
1344 header->Version = mf.metafile_header.Version; /* GDI+ seems to report the object header */
1345 /* Horizontal and Vertical Logical DPI are available but not reported */
1346 }
1347 break;
1348 }
1349 return status;
1350 }
1351
1352 static GpStatus
gdip_read_emf_header_optionals(ENHMETAHEADER3 * header,void * pointer,ImageSource source)1353 gdip_read_emf_header_optionals (ENHMETAHEADER3 *header, void *pointer, ImageSource source)
1354 {
1355 /* This algorithm is taken from the specification: https://msdn.microsoft.com/en-us/library/cc230635.aspx. */
1356 const int HeaderRecordSize = sizeof (ENHMETAHEADER3);
1357 const int HeaderExtension1Size = sizeof (ENHMETAHEADER3) + sizeof (HeaderExtension1);
1358
1359 /* Initialize header size to minimum. */
1360 int headerSize = HeaderRecordSize;
1361
1362 /* Valid header record size? */
1363 if (header->nSize >= HeaderRecordSize)
1364 {
1365 /* Set HeaderSize to header record size. */
1366 headerSize = header->nSize;
1367
1368 /* Valid description values? If so, set HeaderSize to description offset. */
1369 if (header->offDescription >= HeaderRecordSize && (header->offDescription + header->nDescription * 2) <= header->nSize)
1370 headerSize = header->offDescription;
1371
1372 /* Header big enough to contain an extension? */
1373 if (headerSize >= HeaderExtension1Size)
1374 {
1375 /* Match GDI+ behaviour where missing header data is set to 0. */
1376 HeaderExtension1 extension;
1377 memset (&extension, 0, sizeof (HeaderExtension1));
1378 gdip_read_emf_data (pointer, (BYTE *) &extension, sizeof (HeaderExtension1), source);
1379
1380 /* Valid pixel format values? */
1381 if (extension.offPixelFormat >= HeaderExtension1Size && (extension.offPixelFormat + extension.cbPixelFormat) <= header->nSize)
1382 {
1383 /* Pixel format before description? If so, set HeaderSize to pixel format offset. */
1384 if (extension.offPixelFormat > header->offDescription)
1385 headerSize = extension.offPixelFormat;
1386 }
1387 }
1388 }
1389
1390 header->nSize = headerSize;
1391 return Ok;
1392 }
1393
1394 static GpStatus
gdip_get_metafileheader_from(void * pointer,MetafileHeader * header,ImageSource source)1395 gdip_get_metafileheader_from (void *pointer, MetafileHeader *header, ImageSource source)
1396 {
1397 int size;
1398 DWORD key;
1399 GpStatus status = NotImplemented;
1400 WmfPlaceableFileHeader aldus_header;
1401 ENHMETAHEADER3 *emf;
1402
1403 /* peek at first DWORD to select the right format */
1404 size = sizeof (DWORD);
1405 if (gdip_read_wmf_data (pointer, (void*)&key, size, source) != size)
1406 return OutOfMemory;
1407
1408 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1409 key = GUINT32_FROM_LE (key);
1410 #endif
1411 switch (key) {
1412 case ALDUS_PLACEABLE_METAFILE_KEY:
1413 aldus_header.Key = key;
1414 size = sizeof (WmfPlaceableFileHeader) - size;
1415 if (gdip_read_wmf_data(pointer, (BYTE*)(&aldus_header) + sizeof(DWORD), size, source) != size)
1416 return OutOfMemory;
1417 #ifdef DEBUG_METAFILE
1418 g_warning ("ALDUS_PLACEABLE_METAFILE key %d, hmf %d, L %d, T %d, R %d, B %d, inch %d, reserved %d, checksum %d",
1419 aldus_header.Key, aldus_header.Hmf, aldus_header.BoundingBox.Left, aldus_header.BoundingBox.Top,
1420 aldus_header.BoundingBox.Right, aldus_header.BoundingBox.Bottom, aldus_header.Inch, aldus_header.Reserved,
1421 aldus_header.Checksum);
1422 #endif
1423 size = sizeof (METAHEADER);
1424 if (gdip_read_wmf_data (pointer, (BYTE*)&header->Header.Wmf, size, source) != size)
1425 return OutOfMemory;
1426
1427 WmfPlaceableFileHeaderLE (&aldus_header);
1428 MetafileHeaderLE (header);
1429
1430 if (header->Header.Wmf.mtType != 1 && header->Header.Wmf.mtType != 2)
1431 return OutOfMemory;
1432 if (header->Header.Wmf.mtHeaderSize != 9)
1433 return OutOfMemory;
1434 if (header->Header.Wmf.mtVersion != 0x0100 && header->Header.Wmf.mtVersion != 0x0300)
1435 return OutOfMemory;
1436
1437 status = combine_headers (&aldus_header, header);
1438 break;
1439 case WMF_TYPE_AND_HEADERSIZE_KEY:
1440 memcpy (&header->Header.Wmf, &key, size);
1441
1442 size = sizeof (METAHEADER) - size;
1443 if (gdip_read_wmf_data (pointer, (BYTE*)(&header->Header.Wmf) + sizeof(DWORD), size, source) != size)
1444 return InvalidParameter;
1445
1446 MetafileHeaderLE (header);
1447 status = combine_headers (NULL, header);
1448 break;
1449 case EMF_EMR_HEADER_KEY:
1450 emf = &header->Header.Emf;
1451 emf->iType = key;
1452
1453 /* Match GDI+ behaviour where missing header data is set to 0. */
1454 size = sizeof (ENHMETAHEADER3) - size;
1455 memset ((BYTE *) emf + sizeof (DWORD), 0, size);
1456 gdip_read_emf_data (pointer, (BYTE *) emf + sizeof (DWORD), size, source);
1457
1458 EnhMetaHeaderLE (&header->Header.Emf);
1459
1460 #ifdef DEBUG_METAFILE
1461 g_warning ("EMF HEADER iType %d, nSize %d, Bounds L %d, T %d, R %d, B %d, Frame L %d, T %d, R %d, B %d, signature %X, version %d, bytes %d, records %d, handles %d, reserved %d, description %d, %d, palentries %d, device %d, %d, millimeters %d, %d",
1462 emf->iType, emf->nSize,
1463 emf->rclBounds.left, emf->rclBounds.top, emf->rclBounds.right, emf->rclBounds.bottom,
1464 emf->rclFrame.left, emf->rclFrame.top, emf->rclFrame.right, emf->rclFrame.bottom,
1465 emf->dSignature, emf->nVersion, emf->nBytes, emf->nRecords, emf->nHandles,
1466 emf->sReserved, emf->nDescription, emf->offDescription, emf->nPalEntries,
1467 emf->szlDevice.cx, emf->szlDevice.cy, emf->szlMillimeters.cx, emf->szlMillimeters.cy);
1468 #endif
1469 if ((emf->iType != 1) || (emf->dSignature != 0x464D4520))
1470 return OutOfMemory;
1471 if (emf->nRecords < 2)
1472 return OutOfMemory;
1473 if (emf->nHandles == 0)
1474 return OutOfMemory;
1475 if (emf->nBytes < emf->nSize || emf->nBytes & 3)
1476 return OutOfMemory;
1477 if (emf->szlDevice.cx == 0 || emf->szlDevice.cy == 0)
1478 return OutOfMemory;
1479 if (emf->szlMillimeters.cx == 0 || emf->szlMillimeters.cy == 0)
1480 return OutOfMemory;
1481
1482 header->Type = MetafileTypeEmf;
1483
1484 /* Convert millimetres to inches to get the DPI for each dimension. */
1485 header->DpiX = emf->szlDevice.cx / (emf->szlMillimeters.cx / MM_PER_INCH);
1486 header->DpiY = emf->szlDevice.cy / (emf->szlMillimeters.cy / MM_PER_INCH);
1487
1488 /* The bounding box of the metafile is derived from rclFrame, not rclBounds.
1489 * We need to perform some unit conversions from 100s of millimetres to pixels. */
1490 REAL inchLeft = emf->rclFrame.left / (MM_PER_INCH * 100);
1491 REAL inchTop = emf->rclFrame.top / (MM_PER_INCH * 100);
1492 REAL inchRight = emf->rclFrame.right / (MM_PER_INCH * 100);
1493 REAL inchBottom = emf->rclFrame.bottom / (MM_PER_INCH * 100);
1494
1495 header->X = iround (inchLeft * header->DpiX);
1496 header->Y = iround (inchTop * header->DpiY);
1497
1498 /* The frame is inclusive, so we need to add 1. */
1499 header->Width = iround ((inchRight - inchLeft) * header->DpiX) + 1;
1500 header->Height = iround ((inchBottom - inchTop) * header->DpiY) + 1;
1501
1502 header->Size = emf->nBytes;
1503 header->Version = emf->nVersion;
1504 /* We need to check for the EmfHeader record (can't be done at this stage) but some files still returns
1505 * invalid values. E.g. Files with "strange" bounds also have insanely large EmfPlusHeaderSize and logical
1506 * DPI values (with MS GDI+) */
1507 header->EmfPlusFlags = 0;
1508 header->EmfPlusHeaderSize = 0;
1509 header->LogicalDpiX = 0;
1510 header->LogicalDpiY = 0;
1511
1512 status = gdip_read_emf_header_optionals (emf, pointer, source);
1513 break;
1514 default:
1515 status = OutOfMemory;
1516 }
1517
1518 #ifdef DEBUG_METAFILE
1519 g_warning ("METAHEADER type %d, header %d, version %d, size %d, #obj %d, max rec %d, #params %d",
1520 header->Header.Wmf.mtType, header->Header.Wmf.mtHeaderSize, header->Header.Wmf.mtVersion,
1521 header->Header.Wmf.mtSize, header->Header.Wmf.mtNoObjects, header->Header.Wmf.mtMaxRecord,
1522 header->Header.Wmf.mtNoParameters);
1523 #endif
1524 return status;
1525 }
1526
1527 GpStatus
gdip_get_metafile_from(void * pointer,GpMetafile ** metafile,ImageSource source)1528 gdip_get_metafile_from (void *pointer, GpMetafile **metafile, ImageSource source)
1529 {
1530 BOOL adjust_emf_headers = FALSE;
1531 GpStatus status = OutOfMemory;
1532 GpMetafile *mf = gdip_metafile_create ();
1533 if (!mf)
1534 goto error;
1535
1536 /* decode metafile header */
1537 status = gdip_get_metafileheader_from (pointer, &mf->metafile_header, source);
1538 if (status != Ok)
1539 goto error;
1540
1541 switch (mf->metafile_header.Type) {
1542 case MetafileTypeWmfPlaceable:
1543 case MetafileTypeWmf:
1544 mf->base.image_format = WMF;
1545 /* note: mtSize is in WORD, mtSize contains the METAHEADER, mf->length is in bytes */
1546 mf->length = mf->metafile_header.Header.Wmf.mtSize * 2 - sizeof (METAHEADER);
1547
1548 /* The file size has to be enough for an EOF record. */
1549 if (mf->length == 0)
1550 {
1551 if (mf->metafile_header.Header.Wmf.mtMaxRecord != 3)
1552 {
1553 status = OutOfMemory;
1554 goto error;
1555 }
1556 }
1557 else if (mf->length <= 4)
1558 {
1559 status = OutOfMemory;
1560 goto error;
1561 }
1562
1563 break;
1564 case MetafileTypeEmf:
1565 case MetafileTypeEmfPlusOnly:
1566 case MetafileTypeEmfPlusDual:
1567 mf->base.image_format = EMF;
1568 mf->length = mf->metafile_header.Header.Emf.nBytes - mf->metafile_header.Header.Emf.nSize;
1569 adjust_emf_headers = TRUE;
1570 break;
1571 default:
1572 status = GenericError;
1573 goto error;
1574 }
1575
1576 mf->data = (BYTE*) GdipAlloc (mf->length);
1577 if (!mf->data)
1578 goto error;
1579
1580 /* Copy the data into memory for playback later. To match GDI+ behaviour, we don't validate that there is
1581 * as much data as the header says. Instead, if the data length is invalid and there is no EOF record before
1582 * we run out of space in the buffer playback will fail. */
1583 mf->length = gdip_read_wmf_data (pointer, (void *) mf->data, mf->length, source);
1584
1585 if (adjust_emf_headers) {
1586 /* if the first EMF record is an EmfHeader (or an Header inside a Comment) then we have extra data to extract */
1587 status = update_emf_header (&mf->metafile_header, mf->data, mf->length);
1588 if (status != Ok)
1589 goto error;
1590 }
1591
1592 *metafile = mf;
1593 return Ok;
1594 error:
1595 if (mf)
1596 gdip_metafile_dispose (mf);
1597 *metafile = NULL;
1598 return status;
1599 }
1600
1601 /* public (GDI+) functions */
1602
1603 GpStatus
GdipCreateMetafileFromFile(GDIPCONST WCHAR * file,GpMetafile ** metafile)1604 GdipCreateMetafileFromFile (GDIPCONST WCHAR *file, GpMetafile **metafile)
1605 {
1606 FILE *fp;
1607 char *file_name;
1608 GpStatus status;
1609
1610 if (!gdiplusInitialized)
1611 return GdiplusNotInitialized;
1612
1613 if (!file || !metafile)
1614 return InvalidParameter;
1615
1616 file_name = (char *) utf16_to_utf8 ((const gunichar2 *)file, -1);
1617 if (!file_name)
1618 return InvalidParameter;
1619
1620 fp = fopen (file_name, "rb");
1621 if (!fp) {
1622 GdipFree (file_name);
1623 return GenericError;
1624 }
1625
1626 /* Match GDI+ behaviour by either returning Ok or GenericError. */
1627 status = gdip_get_metafile_from (fp, metafile, File);
1628 if (status != Ok)
1629 status = GenericError;
1630
1631 fclose (fp);
1632 GdipFree (file_name);
1633
1634 return status;
1635 }
1636
1637 /*
1638 * GdipCreateMetafileFromStream will never be implemented, as 'stream' is a COM IStream ...
1639 */
1640 GpStatus
GdipCreateMetafileFromStream(void * stream,GpMetafile ** metafile)1641 GdipCreateMetafileFromStream (void *stream, GpMetafile **metafile)
1642 {
1643 if (!gdiplusInitialized)
1644 return GdiplusNotInitialized;
1645
1646 if (!stream || !metafile)
1647 return InvalidParameter;
1648
1649 return NotImplemented;
1650 }
1651 /*
1652 * instead we'll use delegates to load the metafile header with this function
1653 */
GdipCreateMetafileFromDelegate_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,GpMetafile ** metafile)1654 GpStatus GdipCreateMetafileFromDelegate_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
1655 PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
1656 GpMetafile **metafile)
1657 {
1658 GpStatus status = InvalidParameter;
1659 dstream_t *loader;
1660
1661 if (!metafile)
1662 return InvalidParameter;
1663
1664 loader = dstream_input_new (getBytesFunc, seekFunc);
1665 if (loader) {
1666 status = gdip_get_metafile_from (loader, metafile, DStream);
1667 dstream_free (loader);
1668 }
1669 return status;
1670 }
1671
1672 GpStatus
GdipCreateMetafileFromEmf(HENHMETAFILE hEmf,BOOL deleteEmf,GpMetafile ** metafile)1673 GdipCreateMetafileFromEmf (HENHMETAFILE hEmf, BOOL deleteEmf, GpMetafile **metafile)
1674 {
1675 GpStatus status;
1676
1677 if (!gdiplusInitialized)
1678 return GdiplusNotInitialized;
1679
1680 if (!hEmf || !metafile)
1681 return InvalidParameter;
1682
1683 switch (((GpMetafile *) hEmf)->metafile_header.Type) {
1684 case MetafileTypeEmf:
1685 case MetafileTypeEmfPlusOnly:
1686 case MetafileTypeEmfPlusDual:
1687 status = gdip_metafile_clone ((GpMetafile*)hEmf, metafile);
1688 if (status != Ok)
1689 return status;
1690
1691 (*metafile)->delete = deleteEmf;
1692 return Ok;
1693 case MetafileTypeWmfPlaceable:
1694 case MetafileTypeWmf:
1695 default:
1696 *metafile = NULL;
1697 return GenericError;
1698 }
1699 }
1700
1701 GpStatus
GdipCreateMetafileFromWmf(HMETAFILE hWmf,BOOL deleteWmf,GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,GpMetafile ** metafile)1702 GdipCreateMetafileFromWmf (HMETAFILE hWmf, BOOL deleteWmf, GDIPCONST WmfPlaceableFileHeader *wmfPlaceableFileHeader, GpMetafile **metafile)
1703 {
1704 GpStatus status;
1705
1706 if (!gdiplusInitialized)
1707 return GdiplusNotInitialized;
1708
1709 if (!hWmf || !metafile)
1710 return InvalidParameter;
1711
1712 status = gdip_metafile_clone ((GpMetafile*)hWmf, metafile);
1713 if (status != Ok)
1714 return status;
1715
1716 switch ((*metafile)->metafile_header.Type) {
1717 case MetafileTypeWmfPlaceable:
1718 case MetafileTypeWmf:
1719 if (wmfPlaceableFileHeader) {
1720 status = GdipGetMetafileHeaderFromWmf (hWmf, wmfPlaceableFileHeader, &(*metafile)->metafile_header);
1721 if (status != Ok) {
1722 GdipFree (*metafile);
1723 return status;
1724 }
1725 }
1726
1727 (*metafile)->delete = deleteWmf;
1728 return Ok;
1729 case MetafileTypeEmf:
1730 case MetafileTypeEmfPlusOnly:
1731 case MetafileTypeEmfPlusDual:
1732 default:
1733 GdipFree (*metafile);
1734 *metafile = NULL;
1735 return GenericError;
1736 }
1737 }
1738
1739 GpStatus
GdipGetMetafileHeaderFromWmf(HMETAFILE hWmf,GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,MetafileHeader * header)1740 GdipGetMetafileHeaderFromWmf (HMETAFILE hWmf, GDIPCONST WmfPlaceableFileHeader *wmfPlaceableFileHeader, MetafileHeader *header)
1741 {
1742 GpMetafile *mf;
1743
1744 if (!hWmf || !wmfPlaceableFileHeader || !header)
1745 return InvalidParameter;
1746
1747 mf = (GpMetafile*)hWmf;
1748 memcpy (header, &mf->metafile_header, sizeof (MetafileHeader));
1749 return combine_headers (wmfPlaceableFileHeader, header);
1750 }
1751
1752 GpStatus
GdipGetMetafileHeaderFromEmf(HENHMETAFILE hEmf,MetafileHeader * header)1753 GdipGetMetafileHeaderFromEmf (HENHMETAFILE hEmf, MetafileHeader *header)
1754 {
1755 GpMetafile* metafile = (GpMetafile*)hEmf;
1756
1757 if (!metafile || !header)
1758 return InvalidParameter;
1759
1760 switch (metafile->metafile_header.Type) {
1761 case MetafileTypeEmf:
1762 case MetafileTypeEmfPlusOnly:
1763 case MetafileTypeEmfPlusDual:
1764 return GdipGetMetafileHeaderFromMetafile (metafile, header);
1765 /* you can get a HENHMETAFILE from a WMF metafile but it seems (unit tests) that you can't use it as such */
1766 case MetafileTypeWmfPlaceable:
1767 case MetafileTypeWmf:
1768 default:
1769 return InvalidParameter;
1770 }
1771 }
1772
1773 GpStatus
GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR * filename,MetafileHeader * header)1774 GdipGetMetafileHeaderFromFile (GDIPCONST WCHAR *filename, MetafileHeader *header)
1775 {
1776 FILE *fp;
1777 char *file_name;
1778 GpStatus status = InvalidParameter;
1779
1780 if (!filename || !header)
1781 return InvalidParameter;
1782
1783 file_name = (char *) utf16_to_utf8 ((const gunichar2 *)filename, -1);
1784 if (!file_name)
1785 return InvalidParameter;
1786
1787 fp = fopen (file_name, "rb");
1788 if (fp) {
1789 status = gdip_get_metafileheader_from (fp, header, File);
1790 /* if EMF check for additional header record */
1791 if (header->Type == MetafileTypeEmf) {
1792 /* look for more details, possibly upgrading the metafile to MetafileTypeEmfPlusOnly or EMFPLUSDUAL */
1793 /* TODO */
1794 }
1795 fclose (fp);
1796 }
1797 GdipFree (file_name);
1798 return status;
1799 }
1800
1801 /*
1802 * GdipGetMetafileHeaderFromStream will never be implemented, as 'stream' is a COM IStream ...
1803 */
1804 GpStatus
GdipGetMetafileHeaderFromStream(void * stream,MetafileHeader * header)1805 GdipGetMetafileHeaderFromStream (void *stream, MetafileHeader *header)
1806 {
1807 if (!gdiplusInitialized)
1808 return GdiplusNotInitialized;
1809
1810 if (!stream || !header)
1811 return InvalidParameter;
1812
1813 return NotImplemented;
1814 }
1815 /*
1816 * instead we'll use delegates to load the metafile header with this function
1817 */
1818 GpStatus
GdipGetMetafileHeaderFromDelegate_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,MetafileHeader * header)1819 GdipGetMetafileHeaderFromDelegate_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
1820 PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
1821 MetafileHeader *header)
1822 {
1823 GpStatus status = InvalidParameter;
1824 dstream_t *loader;
1825
1826 if (!header)
1827 return status;
1828
1829 loader = dstream_input_new (getBytesFunc, seekFunc);
1830 if (loader) {
1831 status = gdip_get_metafileheader_from (loader, header, DStream);
1832 /* if EMF check for additional header record */
1833 if (header->Type == MetafileTypeEmf) {
1834 /* look for more details, possibly upgrading the metafile to MetafileTypeEmfPlusOnly or EMFPLUSDUAL */
1835 /* TODO */
1836 }
1837 dstream_free (loader);
1838 }
1839 return status;
1840 }
1841
1842 GpStatus
GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,MetafileHeader * header)1843 GdipGetMetafileHeaderFromMetafile (GpMetafile *metafile, MetafileHeader *header)
1844 {
1845 if (!metafile || !header)
1846 return InvalidParameter;
1847
1848 memcpy (header, &metafile->metafile_header, sizeof (MetafileHeader));
1849 return Ok;
1850 }
1851
1852 GpStatus
GdipGetHemfFromMetafile(GpMetafile * metafile,HENHMETAFILE * hEmf)1853 GdipGetHemfFromMetafile (GpMetafile *metafile, HENHMETAFILE *hEmf)
1854 {
1855 if (!metafile || !hEmf)
1856 return InvalidParameter;
1857
1858 *hEmf = (HENHMETAFILE)metafile;
1859 return Ok;
1860 }
1861
1862 GpStatus
GdipGetMetafileDownLevelRasterizationLimit(GpMetafile * metafile,UINT * metafileRasterizationLimitDpi)1863 GdipGetMetafileDownLevelRasterizationLimit (GpMetafile *metafile, UINT *metafileRasterizationLimitDpi)
1864 {
1865 if (!metafile || !metafileRasterizationLimitDpi)
1866 return InvalidParameter;
1867
1868 switch (metafile->metafile_header.Type) {
1869 case MetafileTypeEmf:
1870 case MetafileTypeEmfPlusDual:
1871 /* TODO */
1872 *metafileRasterizationLimitDpi = 0;
1873 return Ok;
1874 case MetafileTypeWmfPlaceable:
1875 case MetafileTypeWmf:
1876 case MetafileTypeEmfPlusOnly:
1877 default:
1878 return WrongState;
1879 }
1880 }
1881
1882 GpStatus
GdipSetMetafileDownLevelRasterizationLimit(GpMetafile * metafile,UINT metafileRasterizationLimitDpi)1883 GdipSetMetafileDownLevelRasterizationLimit (GpMetafile *metafile, UINT metafileRasterizationLimitDpi)
1884 {
1885 if (!metafile)
1886 return InvalidParameter;
1887
1888 switch (metafile->metafile_header.Type) {
1889 case MetafileTypeEmf:
1890 case MetafileTypeEmfPlusDual:
1891 // values less than 10 left resolution unchanged (from GDI+ documentation)
1892 if (metafileRasterizationLimitDpi >= 10) {
1893 /* TODO */
1894 }
1895 return Ok;
1896 case MetafileTypeWmfPlaceable:
1897 case MetafileTypeWmf:
1898 case MetafileTypeEmfPlusOnly:
1899 default:
1900 return WrongState;
1901 }
1902 }
1903
1904 GpStatus
GdipPlayMetafileRecord(GDIPCONST GpMetafile * metafile,EmfPlusRecordType recordType,UINT flags,UINT dataSize,GDIPCONST BYTE * data)1905 GdipPlayMetafileRecord (GDIPCONST GpMetafile *metafile, EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE* data)
1906 {
1907 if (!metafile || (dataSize && !data))
1908 return InvalidParameter;
1909
1910 /* TODO */
1911 return NotImplemented;
1912 }
1913
1914 GpStatus
GdipRecordMetafile(HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)1915 GdipRecordMetafile (HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect, MetafileFrameUnit frameUnit,
1916 GDIPCONST WCHAR *description, GpMetafile **metafile)
1917 {
1918 GpMetafile *mf;
1919
1920 if (!gdiplusInitialized)
1921 return GdiplusNotInitialized;
1922
1923 if (!referenceHdc || !frameRect || !metafile)
1924 return InvalidParameter;
1925
1926 if ((type < EmfTypeEmfOnly) || (type > EmfTypeEmfPlusDual))
1927 return InvalidParameter;
1928
1929 if ((frameUnit < MetafileFrameUnitPixel) || (frameUnit > MetafileFrameUnitGdi))
1930 return InvalidParameter;
1931
1932 if (((frameRect->Width == 0) || (frameRect->Height == 0)) && (frameUnit != MetafileFrameUnitGdi))
1933 return GenericError;
1934
1935 mf = gdip_metafile_create ();
1936 if (!mf)
1937 return OutOfMemory;
1938
1939 mf->metafile_header.X = frameRect->X;
1940 mf->metafile_header.Y = frameRect->Y;
1941 mf->metafile_header.Width = frameRect->Width;
1942 mf->metafile_header.Height = frameRect->Height;
1943 mf->metafile_header.Size = 0;
1944 mf->metafile_header.Type = (MetafileType)type;
1945 mf->recording = TRUE;
1946
1947 /* TODO - more stuff here! */
1948
1949 *metafile = mf;
1950 return Ok;
1951 }
1952
1953 GpStatus
GdipRecordMetafileI(HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)1954 GdipRecordMetafileI (HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect, MetafileFrameUnit frameUnit,
1955 GDIPCONST WCHAR *description, GpMetafile **metafile)
1956 {
1957 GpRectF rect;
1958
1959 if (!frameRect)
1960 return InvalidParameter;
1961
1962 rect.X = frameRect->X;
1963 rect.Y = frameRect->Y;
1964 rect.Width = frameRect->Width;
1965 rect.Height = frameRect->Height;
1966 return GdipRecordMetafile (referenceHdc, type, (GDIPCONST GpRectF*) &rect, frameUnit, description, metafile);
1967 }
1968
1969 GpStatus
GdipRecordMetafileFileName(GDIPCONST WCHAR * fileName,HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)1970 GdipRecordMetafileFileName (GDIPCONST WCHAR *fileName, HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect,
1971 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
1972 {
1973 GpStatus status;
1974 GpMetafile *mf = NULL;
1975 char *file_name;
1976
1977 if (!gdiplusInitialized)
1978 return GdiplusNotInitialized;
1979
1980 if (!fileName)
1981 return InvalidParameter;
1982
1983 file_name = (char *) utf16_to_utf8 ((const gunichar2 *)fileName, -1);
1984 if (!file_name) {
1985 *metafile = NULL;
1986 return InvalidParameter;
1987 }
1988
1989 status = GdipRecordMetafile (referenceHdc, type, frameRect, frameUnit, description, &mf);
1990 if (status != Ok) {
1991 GdipFree (file_name);
1992 return status;
1993 }
1994
1995 /* yep, an existing file is overwritten */
1996 mf->fp = fopen (file_name, "wb");
1997
1998 GdipFree (file_name);
1999 *metafile = mf;
2000 return Ok;
2001 }
2002
2003 GpStatus
GdipRecordMetafileFileNameI(GDIPCONST WCHAR * fileName,HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2004 GdipRecordMetafileFileNameI (GDIPCONST WCHAR *fileName, HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect,
2005 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2006 {
2007 GpRectF rect;
2008
2009 if (!frameRect)
2010 return InvalidParameter;
2011
2012 rect.X = frameRect->X;
2013 rect.Y = frameRect->Y;
2014 rect.Width = frameRect->Width;
2015 rect.Height = frameRect->Height;
2016 return GdipRecordMetafileFileName (fileName, referenceHdc, type, (GDIPCONST GpRectF*) &rect, frameUnit, description, metafile);
2017 }
2018
2019 /*
2020 * GdipRecordMetafileStream and GdipRecordMetafileStreamI will never be implemented, as 'stream' is a COM IStream ...
2021 */
2022 GpStatus
GdipRecordMetafileStream(void * stream,HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2023 GdipRecordMetafileStream (void /* IStream */ *stream, HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect,
2024 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2025 {
2026 if (!gdiplusInitialized)
2027 return GdiplusNotInitialized;
2028
2029 return NotImplemented;
2030 }
2031
2032 GpStatus
GdipRecordMetafileStreamI(void * stream,HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2033 GdipRecordMetafileStreamI (void /* IStream */ *stream, HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect,
2034 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2035 {
2036 return NotImplemented;
2037 }
2038 /*
2039 * instead we'll use delegates to create the metafile header with these functions
2040 */
2041 GpStatus
GdipRecordMetafileFromDelegate_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2042 GdipRecordMetafileFromDelegate_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
2043 PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
2044 HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect,
2045 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2046 {
2047 GpStatus status;
2048
2049 if (!putBytesFunc)
2050 return InvalidParameter;
2051
2052 status = GdipRecordMetafile (referenceHdc, type, frameRect, frameUnit, description, metafile);
2053 if (status != Ok)
2054 return status;
2055
2056 /* TODO - keep delegates around to write stuff */
2057
2058 return Ok;
2059 }
2060
2061 GpStatus
GdipRecordMetafileFromDelegateI_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2062 GdipRecordMetafileFromDelegateI_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
2063 PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
2064 HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect,
2065 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2066 {
2067 GpRectF rect;
2068
2069 if (!frameRect)
2070 return InvalidParameter;
2071
2072 rect.X = frameRect->X;
2073 rect.Y = frameRect->Y;
2074 rect.Width = frameRect->Width;
2075 rect.Height = frameRect->Height;
2076 return GdipRecordMetafileFromDelegate_linux (getHeaderFunc, getBytesFunc, putBytesFunc, seekFunc, closeFunc, sizeFunc,
2077 referenceHdc, type, (GDIPCONST GpRectF*) &rect, frameUnit, description, metafile);
2078 }
2079