1 /*
2 * Enhanced metafile functions
3 * Copyright 1998 Douglas Ridgway
4 * 1999 Huw D M Davies
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 * NOTES:
21 *
22 * The enhanced format consists of the following elements:
23 *
24 * A header
25 * A table of handles to GDI objects
26 * An array of metafile records
27 * A private palette
28 *
29 *
30 * The standard format consists of a header and an array of metafile records.
31 *
32 */
33
34 #include "config.h"
35 #include "wine/port.h"
36
37 #include <stdarg.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <assert.h>
41 #include "windef.h"
42 #include "winbase.h"
43 #include "wingdi.h"
44 #include "winnls.h"
45 #include "winerror.h"
46 #include "gdi_private.h"
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(enhmetafile);
50
51 typedef struct
52 {
53 GDIOBJHDR header;
54 ENHMETAHEADER *emh;
55 BOOL on_disk; /* true if metafile is on disk */
56 } ENHMETAFILEOBJ;
57
58 static const struct emr_name {
59 DWORD type;
60 const char *name;
61 } emr_names[] = {
62 #define X(p) {p, #p}
63 X(EMR_HEADER),
64 X(EMR_POLYBEZIER),
65 X(EMR_POLYGON),
66 X(EMR_POLYLINE),
67 X(EMR_POLYBEZIERTO),
68 X(EMR_POLYLINETO),
69 X(EMR_POLYPOLYLINE),
70 X(EMR_POLYPOLYGON),
71 X(EMR_SETWINDOWEXTEX),
72 X(EMR_SETWINDOWORGEX),
73 X(EMR_SETVIEWPORTEXTEX),
74 X(EMR_SETVIEWPORTORGEX),
75 X(EMR_SETBRUSHORGEX),
76 X(EMR_EOF),
77 X(EMR_SETPIXELV),
78 X(EMR_SETMAPPERFLAGS),
79 X(EMR_SETMAPMODE),
80 X(EMR_SETBKMODE),
81 X(EMR_SETPOLYFILLMODE),
82 X(EMR_SETROP2),
83 X(EMR_SETSTRETCHBLTMODE),
84 X(EMR_SETTEXTALIGN),
85 X(EMR_SETCOLORADJUSTMENT),
86 X(EMR_SETTEXTCOLOR),
87 X(EMR_SETBKCOLOR),
88 X(EMR_OFFSETCLIPRGN),
89 X(EMR_MOVETOEX),
90 X(EMR_SETMETARGN),
91 X(EMR_EXCLUDECLIPRECT),
92 X(EMR_INTERSECTCLIPRECT),
93 X(EMR_SCALEVIEWPORTEXTEX),
94 X(EMR_SCALEWINDOWEXTEX),
95 X(EMR_SAVEDC),
96 X(EMR_RESTOREDC),
97 X(EMR_SETWORLDTRANSFORM),
98 X(EMR_MODIFYWORLDTRANSFORM),
99 X(EMR_SELECTOBJECT),
100 X(EMR_CREATEPEN),
101 X(EMR_CREATEBRUSHINDIRECT),
102 X(EMR_DELETEOBJECT),
103 X(EMR_ANGLEARC),
104 X(EMR_ELLIPSE),
105 X(EMR_RECTANGLE),
106 X(EMR_ROUNDRECT),
107 X(EMR_ARC),
108 X(EMR_CHORD),
109 X(EMR_PIE),
110 X(EMR_SELECTPALETTE),
111 X(EMR_CREATEPALETTE),
112 X(EMR_SETPALETTEENTRIES),
113 X(EMR_RESIZEPALETTE),
114 X(EMR_REALIZEPALETTE),
115 X(EMR_EXTFLOODFILL),
116 X(EMR_LINETO),
117 X(EMR_ARCTO),
118 X(EMR_POLYDRAW),
119 X(EMR_SETARCDIRECTION),
120 X(EMR_SETMITERLIMIT),
121 X(EMR_BEGINPATH),
122 X(EMR_ENDPATH),
123 X(EMR_CLOSEFIGURE),
124 X(EMR_FILLPATH),
125 X(EMR_STROKEANDFILLPATH),
126 X(EMR_STROKEPATH),
127 X(EMR_FLATTENPATH),
128 X(EMR_WIDENPATH),
129 X(EMR_SELECTCLIPPATH),
130 X(EMR_ABORTPATH),
131 X(EMR_GDICOMMENT),
132 X(EMR_FILLRGN),
133 X(EMR_FRAMERGN),
134 X(EMR_INVERTRGN),
135 X(EMR_PAINTRGN),
136 X(EMR_EXTSELECTCLIPRGN),
137 X(EMR_BITBLT),
138 X(EMR_STRETCHBLT),
139 X(EMR_MASKBLT),
140 X(EMR_PLGBLT),
141 X(EMR_SETDIBITSTODEVICE),
142 X(EMR_STRETCHDIBITS),
143 X(EMR_EXTCREATEFONTINDIRECTW),
144 X(EMR_EXTTEXTOUTA),
145 X(EMR_EXTTEXTOUTW),
146 X(EMR_POLYBEZIER16),
147 X(EMR_POLYGON16),
148 X(EMR_POLYLINE16),
149 X(EMR_POLYBEZIERTO16),
150 X(EMR_POLYLINETO16),
151 X(EMR_POLYPOLYLINE16),
152 X(EMR_POLYPOLYGON16),
153 X(EMR_POLYDRAW16),
154 X(EMR_CREATEMONOBRUSH),
155 X(EMR_CREATEDIBPATTERNBRUSHPT),
156 X(EMR_EXTCREATEPEN),
157 X(EMR_POLYTEXTOUTA),
158 X(EMR_POLYTEXTOUTW),
159 X(EMR_SETICMMODE),
160 X(EMR_CREATECOLORSPACE),
161 X(EMR_SETCOLORSPACE),
162 X(EMR_DELETECOLORSPACE),
163 X(EMR_GLSRECORD),
164 X(EMR_GLSBOUNDEDRECORD),
165 X(EMR_PIXELFORMAT),
166 X(EMR_DRAWESCAPE),
167 X(EMR_EXTESCAPE),
168 X(EMR_STARTDOC),
169 X(EMR_SMALLTEXTOUT),
170 X(EMR_FORCEUFIMAPPING),
171 X(EMR_NAMEDESCAPE),
172 X(EMR_COLORCORRECTPALETTE),
173 X(EMR_SETICMPROFILEA),
174 X(EMR_SETICMPROFILEW),
175 X(EMR_ALPHABLEND),
176 X(EMR_SETLAYOUT),
177 X(EMR_TRANSPARENTBLT),
178 X(EMR_RESERVED_117),
179 X(EMR_GRADIENTFILL),
180 X(EMR_SETLINKEDUFI),
181 X(EMR_SETTEXTJUSTIFICATION),
182 X(EMR_COLORMATCHTOTARGETW),
183 X(EMR_CREATECOLORSPACEW)
184 #undef X
185 };
186
187 /****************************************************************************
188 * get_emr_name
189 */
190 static const char *get_emr_name(DWORD type)
191 {
192 unsigned int i;
193 for(i = 0; i < sizeof(emr_names) / sizeof(emr_names[0]); i++)
194 if(type == emr_names[i].type) return emr_names[i].name;
195 TRACE("Unknown record type %d\n", type);
196 return NULL;
197 }
198
199 /***********************************************************************
200 * is_dib_monochrome
201 *
202 * Returns whether a DIB can be converted to a monochrome DDB.
203 *
204 * A DIB can be converted if its color table contains only black and
205 * white. Black must be the first color in the color table.
206 *
207 * Note : If the first color in the color table is white followed by
208 * black, we can't convert it to a monochrome DDB with
209 * SetDIBits, because black and white would be inverted.
210 */
211 static inline BOOL is_dib_monochrome( const BITMAPINFO* info )
212 {
213 if (info->bmiHeader.biBitCount != 1) return FALSE;
214
215 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
216 {
217 const RGBTRIPLE *rgb = ((const BITMAPCOREINFO *) info)->bmciColors;
218
219 /* Check if the first color is black */
220 if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
221 {
222 rgb++;
223 /* Check if the second color is white */
224 return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
225 && (rgb->rgbtBlue == 0xff));
226 }
227 else return FALSE;
228 }
229 else /* assume BITMAPINFOHEADER */
230 {
231 const RGBQUAD *rgb = info->bmiColors;
232
233 /* Check if the first color is black */
234 if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
235 (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
236 {
237 rgb++;
238
239 /* Check if the second color is white */
240 return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
241 && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
242 }
243 else return FALSE;
244 }
245 }
246
247 /****************************************************************************
248 * EMF_Create_HENHMETAFILE
249 */
250 HENHMETAFILE EMF_Create_HENHMETAFILE(ENHMETAHEADER *emh, BOOL on_disk )
251 {
252 HENHMETAFILE hmf = 0;
253 ENHMETAFILEOBJ *metaObj;
254
255 if (emh->iType != EMR_HEADER || emh->dSignature != ENHMETA_SIGNATURE ||
256 (emh->nBytes & 3)) /* refuse to load unaligned EMF as Windows does */
257 {
258 WARN("Invalid emf header type 0x%08x sig 0x%08x.\n",
259 emh->iType, emh->dSignature);
260 SetLastError(ERROR_INVALID_DATA);
261 return 0;
262 }
263
264 metaObj = GDI_AllocObject( sizeof(ENHMETAFILEOBJ),
265 ENHMETAFILE_MAGIC,
266 (HGDIOBJ *)&hmf, NULL );
267 if (metaObj)
268 {
269 metaObj->emh = emh;
270 metaObj->on_disk = on_disk;
271 GDI_ReleaseObj( hmf );
272 }
273 return hmf;
274 }
275
276 /****************************************************************************
277 * EMF_Delete_HENHMETAFILE
278 */
279 static BOOL EMF_Delete_HENHMETAFILE( HENHMETAFILE hmf )
280 {
281 ENHMETAFILEOBJ *metaObj = (ENHMETAFILEOBJ *)GDI_GetObjPtr( hmf,
282 ENHMETAFILE_MAGIC );
283 if(!metaObj) return FALSE;
284
285 if(metaObj->on_disk)
286 UnmapViewOfFile( metaObj->emh );
287 else
288 HeapFree( GetProcessHeap(), 0, metaObj->emh );
289 return GDI_FreeObject( hmf, metaObj );
290 }
291
292 /******************************************************************
293 * EMF_GetEnhMetaHeader
294 *
295 * Returns ptr to ENHMETAHEADER associated with HENHMETAFILE
296 */
297 static ENHMETAHEADER *EMF_GetEnhMetaHeader( HENHMETAFILE hmf )
298 {
299 ENHMETAHEADER *ret = NULL;
300 ENHMETAFILEOBJ *metaObj = (ENHMETAFILEOBJ *)GDI_GetObjPtr( hmf, ENHMETAFILE_MAGIC );
301 TRACE("hmf %p -> enhmetaObj %p\n", hmf, metaObj);
302 if (metaObj)
303 {
304 ret = metaObj->emh;
305 GDI_ReleaseObj( hmf );
306 }
307 return ret;
308 }
309
310 /*****************************************************************************
311 * EMF_GetEnhMetaFile
312 *
313 */
314 static HENHMETAFILE EMF_GetEnhMetaFile( HANDLE hFile )
315 {
316 ENHMETAHEADER *emh;
317 HANDLE hMapping;
318 HENHMETAFILE hemf;
319
320 hMapping = CreateFileMappingA( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
321 emh = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
322 CloseHandle( hMapping );
323
324 if (!emh) return 0;
325
326 hemf = EMF_Create_HENHMETAFILE( emh, TRUE );
327 if (!hemf)
328 UnmapViewOfFile( emh );
329 return hemf;
330 }
331
332
333 /*****************************************************************************
334 * GetEnhMetaFileA (GDI32.@)
335 *
336 *
337 */
338 HENHMETAFILE WINAPI GetEnhMetaFileA(
339 LPCSTR lpszMetaFile /* [in] filename of enhanced metafile */
340 )
341 {
342 HENHMETAFILE hmf;
343 HANDLE hFile;
344
345 hFile = CreateFileA(lpszMetaFile, GENERIC_READ, FILE_SHARE_READ, 0,
346 OPEN_EXISTING, 0, 0);
347 if (hFile == INVALID_HANDLE_VALUE) {
348 WARN("could not open %s\n", lpszMetaFile);
349 return 0;
350 }
351 hmf = EMF_GetEnhMetaFile( hFile );
352 CloseHandle( hFile );
353 return hmf;
354 }
355
356 /*****************************************************************************
357 * GetEnhMetaFileW (GDI32.@)
358 */
359 HENHMETAFILE WINAPI GetEnhMetaFileW(
360 LPCWSTR lpszMetaFile) /* [in] filename of enhanced metafile */
361 {
362 HENHMETAFILE hmf;
363 HANDLE hFile;
364
365 hFile = CreateFileW(lpszMetaFile, GENERIC_READ, FILE_SHARE_READ, 0,
366 OPEN_EXISTING, 0, 0);
367 if (hFile == INVALID_HANDLE_VALUE) {
368 WARN("could not open %s\n", debugstr_w(lpszMetaFile));
369 return 0;
370 }
371 hmf = EMF_GetEnhMetaFile( hFile );
372 CloseHandle( hFile );
373 return hmf;
374 }
375
376 /*****************************************************************************
377 * GetEnhMetaFileHeader (GDI32.@)
378 *
379 * Retrieves the record containing the header for the specified
380 * enhanced-format metafile.
381 *
382 * RETURNS
383 * If buf is NULL, returns the size of buffer required.
384 * Otherwise, copy up to bufsize bytes of enhanced metafile header into
385 * buf.
386 */
387 UINT WINAPI GetEnhMetaFileHeader(
388 HENHMETAFILE hmf, /* [in] enhanced metafile */
389 UINT bufsize, /* [in] size of buffer */
390 LPENHMETAHEADER buf /* [out] buffer */
391 )
392 {
393 LPENHMETAHEADER emh;
394 UINT size;
395
396 emh = EMF_GetEnhMetaHeader(hmf);
397 if(!emh) return FALSE;
398 size = emh->nSize;
399 if (!buf) return size;
400 size = min(size, bufsize);
401 memmove(buf, emh, size);
402 return size;
403 }
404
405
406 /*****************************************************************************
407 * GetEnhMetaFileDescriptionA (GDI32.@)
408 *
409 * See GetEnhMetaFileDescriptionW.
410 */
411 UINT WINAPI GetEnhMetaFileDescriptionA(
412 HENHMETAFILE hmf, /* [in] enhanced metafile */
413 UINT size, /* [in] size of buf */
414 LPSTR buf /* [out] buffer to receive description */
415 )
416 {
417 LPENHMETAHEADER emh = EMF_GetEnhMetaHeader(hmf);
418 DWORD len;
419 WCHAR *descrW;
420
421 if(!emh) return FALSE;
422 if(emh->nDescription == 0 || emh->offDescription == 0) return 0;
423 descrW = (WCHAR *) ((char *) emh + emh->offDescription);
424 len = WideCharToMultiByte( CP_ACP, 0, descrW, emh->nDescription, NULL, 0, NULL, NULL );
425
426 if (!buf || !size ) return len;
427
428 len = min( size, len );
429 WideCharToMultiByte( CP_ACP, 0, descrW, emh->nDescription, buf, len, NULL, NULL );
430 return len;
431 }
432
433 /*****************************************************************************
434 * GetEnhMetaFileDescriptionW (GDI32.@)
435 *
436 * Copies the description string of an enhanced metafile into a buffer
437 * _buf_.
438 *
439 * RETURNS
440 * If _buf_ is NULL, returns size of _buf_ required. Otherwise, returns
441 * number of characters copied.
442 */
443 UINT WINAPI GetEnhMetaFileDescriptionW(
444 HENHMETAFILE hmf, /* [in] enhanced metafile */
445 UINT size, /* [in] size of buf */
446 LPWSTR buf /* [out] buffer to receive description */
447 )
448 {
449 LPENHMETAHEADER emh = EMF_GetEnhMetaHeader(hmf);
450
451 if(!emh) return FALSE;
452 if(emh->nDescription == 0 || emh->offDescription == 0) return 0;
453 if (!buf || !size ) return emh->nDescription;
454
455 memmove(buf, (char *) emh + emh->offDescription, min(size,emh->nDescription)*sizeof(WCHAR));
456 return min(size, emh->nDescription);
457 }
458
459 /****************************************************************************
460 * SetEnhMetaFileBits (GDI32.@)
461 *
462 * Creates an enhanced metafile by copying _bufsize_ bytes from _buf_.
463 */
464 HENHMETAFILE WINAPI SetEnhMetaFileBits(UINT bufsize, const BYTE *buf)
465 {
466 ENHMETAHEADER *emh = HeapAlloc( GetProcessHeap(), 0, bufsize );
467 memmove(emh, buf, bufsize);
468 return EMF_Create_HENHMETAFILE( emh, FALSE );
469 }
470
471 /*****************************************************************************
472 * GetEnhMetaFileBits (GDI32.@)
473 *
474 */
475 UINT WINAPI GetEnhMetaFileBits(
476 HENHMETAFILE hmf,
477 UINT bufsize,
478 LPBYTE buf
479 )
480 {
481 LPENHMETAHEADER emh = EMF_GetEnhMetaHeader( hmf );
482 UINT size;
483
484 if(!emh) return 0;
485
486 size = emh->nBytes;
487 if( buf == NULL ) return size;
488
489 size = min( size, bufsize );
490 memmove(buf, emh, size);
491 return size;
492 }
493
494 typedef struct EMF_dc_state
495 {
496 INT mode;
497 XFORM world_transform;
498 INT wndOrgX;
499 INT wndOrgY;
500 INT wndExtX;
501 INT wndExtY;
502 INT vportOrgX;
503 INT vportOrgY;
504 INT vportExtX;
505 INT vportExtY;
506 struct EMF_dc_state *next;
507 } EMF_dc_state;
508
509 typedef struct enum_emh_data
510 {
511 XFORM init_transform;
512 EMF_dc_state state;
513 INT save_level;
514 EMF_dc_state *saved_state;
515 } enum_emh_data;
516
517 #define ENUM_GET_PRIVATE_DATA(ht) \
518 ((enum_emh_data*)(((unsigned char*)(ht))-sizeof (enum_emh_data)))
519
520 #define WIDTH(rect) ( (rect).right - (rect).left )
521 #define HEIGHT(rect) ( (rect).bottom - (rect).top )
522
523 #define IS_WIN9X() (GetVersion()&0x80000000)
524
525 static void EMF_Update_MF_Xform(HDC hdc, const enum_emh_data *info)
526 {
527 XFORM mapping_mode_trans, final_trans;
528 double scaleX, scaleY;
529
530 scaleX = (double)info->state.vportExtX / (double)info->state.wndExtX;
531 scaleY = (double)info->state.vportExtY / (double)info->state.wndExtY;
532 mapping_mode_trans.eM11 = scaleX;
533 mapping_mode_trans.eM12 = 0.0;
534 mapping_mode_trans.eM21 = 0.0;
535 mapping_mode_trans.eM22 = scaleY;
536 mapping_mode_trans.eDx = (double)info->state.vportOrgX - scaleX * (double)info->state.wndOrgX;
537 mapping_mode_trans.eDy = (double)info->state.vportOrgY - scaleY * (double)info->state.wndOrgY;
538
539 CombineTransform(&final_trans, &info->state.world_transform, &mapping_mode_trans);
540 CombineTransform(&final_trans, &final_trans, &info->init_transform);
541
542 if (!SetWorldTransform(hdc, &final_trans))
543 {
544 ERR("World transform failed!\n");
545 }
546 }
547
548 static void EMF_RestoreDC( enum_emh_data *info, INT level )
549 {
550 if (abs(level) > info->save_level || level == 0) return;
551
552 if (level < 0) level = info->save_level + level + 1;
553
554 while (info->save_level >= level)
555 {
556 EMF_dc_state *state = info->saved_state;
557 info->saved_state = state->next;
558 state->next = NULL;
559 if (--info->save_level < level)
560 info->state = *state;
561 HeapFree( GetProcessHeap(), 0, state );
562 }
563 }
564
565 static void EMF_SaveDC( enum_emh_data *info )
566 {
567 EMF_dc_state *state = HeapAlloc( GetProcessHeap(), 0, sizeof(*state));
568 if (state)
569 {
570 *state = info->state;
571 state->next = info->saved_state;
572 info->saved_state = state;
573 info->save_level++;
574 TRACE("save_level %d\n", info->save_level);
575 }
576 }
577
578 static void EMF_SetMapMode(HDC hdc, enum_emh_data *info)
579 {
580 INT horzSize = GetDeviceCaps( hdc, HORZSIZE );
581 INT vertSize = GetDeviceCaps( hdc, VERTSIZE );
582 INT horzRes = GetDeviceCaps( hdc, HORZRES );
583 INT vertRes = GetDeviceCaps( hdc, VERTRES );
584
585 TRACE("%d\n", info->state.mode);
586
587 switch(info->state.mode)
588 {
589 case MM_TEXT:
590 info->state.wndExtX = 1;
591 info->state.wndExtY = 1;
592 info->state.vportExtX = 1;
593 info->state.vportExtY = 1;
594 break;
595 case MM_LOMETRIC:
596 case MM_ISOTROPIC:
597 info->state.wndExtX = horzSize * 10;
598 info->state.wndExtY = vertSize * 10;
599 info->state.vportExtX = horzRes;
600 info->state.vportExtY = -vertRes;
601 break;
602 case MM_HIMETRIC:
603 info->state.wndExtX = horzSize * 100;
604 info->state.wndExtY = vertSize * 100;
605 info->state.vportExtX = horzRes;
606 info->state.vportExtY = -vertRes;
607 break;
608 case MM_LOENGLISH:
609 info->state.wndExtX = MulDiv(1000, horzSize, 254);
610 info->state.wndExtY = MulDiv(1000, vertSize, 254);
611 info->state.vportExtX = horzRes;
612 info->state.vportExtY = -vertRes;
613 break;
614 case MM_HIENGLISH:
615 info->state.wndExtX = MulDiv(10000, horzSize, 254);
616 info->state.wndExtY = MulDiv(10000, vertSize, 254);
617 info->state.vportExtX = horzRes;
618 info->state.vportExtY = -vertRes;
619 break;
620 case MM_TWIPS:
621 info->state.wndExtX = MulDiv(14400, horzSize, 254);
622 info->state.wndExtY = MulDiv(14400, vertSize, 254);
623 info->state.vportExtX = horzRes;
624 info->state.vportExtY = -vertRes;
625 break;
626 case MM_ANISOTROPIC:
627 break;
628 default:
629 return;
630 }
631 }
632
633 /***********************************************************************
634 * EMF_FixIsotropic
635 *
636 * Fix viewport extensions for isotropic mode.
637 */
638
639 static void EMF_FixIsotropic(HDC hdc, enum_emh_data *info)
640 {
641 double xdim = fabs((double)info->state.vportExtX * GetDeviceCaps( hdc, HORZSIZE ) /
642 (GetDeviceCaps( hdc, HORZRES ) * info->state.wndExtX));
643 double ydim = fabs((double)info->state.vportExtY * GetDeviceCaps( hdc, VERTSIZE ) /
644 (GetDeviceCaps( hdc, VERTRES ) * info->state.wndExtY));
645
646 if (xdim > ydim)
647 {
648 INT mincx = (info->state.vportExtX >= 0) ? 1 : -1;
649 info->state.vportExtX = floor(info->state.vportExtX * ydim / xdim + 0.5);
650 if (!info->state.vportExtX) info->state.vportExtX = mincx;
651 }
652 else
653 {
654 INT mincy = (info->state.vportExtY >= 0) ? 1 : -1;
655 info->state.vportExtY = floor(info->state.vportExtY * xdim / ydim + 0.5);
656 if (!info->state.vportExtY) info->state.vportExtY = mincy;
657 }
658 }
659
660 /*****************************************************************************
661 * emr_produces_output
662 *
663 * Returns TRUE if the record type writes something to the dc. Used by
664 * PlayEnhMetaFileRecord to determine whether it needs to update the
665 * dc's xform when in win9x mode.
666 *
667 * FIXME: need to test which records should be here.
668 */
669 static BOOL emr_produces_output(int type)
670 {
671 switch(type) {
672 case EMR_POLYBEZIER:
673 case EMR_POLYGON:
674 case EMR_POLYLINE:
675 case EMR_POLYBEZIERTO:
676 case EMR_POLYLINETO:
677 case EMR_POLYPOLYLINE:
678 case EMR_POLYPOLYGON:
679 case EMR_SETPIXELV:
680 case EMR_MOVETOEX:
681 case EMR_EXCLUDECLIPRECT:
682 case EMR_INTERSECTCLIPRECT:
683 case EMR_SELECTOBJECT:
684 case EMR_ANGLEARC:
685 case EMR_ELLIPSE:
686 case EMR_RECTANGLE:
687 case EMR_ROUNDRECT:
688 case EMR_ARC:
689 case EMR_CHORD:
690 case EMR_PIE:
691 case EMR_EXTFLOODFILL:
692 case EMR_LINETO:
693 case EMR_ARCTO:
694 case EMR_POLYDRAW:
695 case EMR_FILLRGN:
696 case EMR_FRAMERGN:
697 case EMR_INVERTRGN:
698 case EMR_PAINTRGN:
699 case EMR_BITBLT:
700 case EMR_STRETCHBLT:
701 case EMR_MASKBLT:
702 case EMR_PLGBLT:
703 case EMR_SETDIBITSTODEVICE:
704 case EMR_STRETCHDIBITS:
705 case EMR_EXTTEXTOUTA:
706 case EMR_EXTTEXTOUTW:
707 case EMR_POLYBEZIER16:
708 case EMR_POLYGON16:
709 case EMR_POLYLINE16:
710 case EMR_POLYBEZIERTO16:
711 case EMR_POLYLINETO16:
712 case EMR_POLYPOLYLINE16:
713 case EMR_POLYPOLYGON16:
714 case EMR_POLYDRAW16:
715 case EMR_POLYTEXTOUTA:
716 case EMR_POLYTEXTOUTW:
717 case EMR_SMALLTEXTOUT:
718 case EMR_ALPHABLEND:
719 case EMR_TRANSPARENTBLT:
720 return TRUE;
721 default:
722 return FALSE;
723 }
724 }
725
726
727 /*****************************************************************************
728 * PlayEnhMetaFileRecord (GDI32.@)
729 *
730 * Render a single enhanced metafile record in the device context hdc.
731 *
732 * RETURNS
733 * TRUE (non zero) on success, FALSE on error.
734 * BUGS
735 * Many unimplemented records.
736 * No error handling on record play failures (ie checking return codes)
737 *
738 * NOTES
739 * WinNT actually updates the current world transform in this function
740 * whereas Win9x does not.
741 */
742 BOOL WINAPI PlayEnhMetaFileRecord(
743 HDC hdc, /* [in] device context in which to render EMF record */
744 LPHANDLETABLE handletable, /* [in] array of handles to be used in rendering record */
745 const ENHMETARECORD *mr, /* [in] EMF record to render */
746 UINT handles /* [in] size of handle array */
747 )
748 {
749 int type;
750 RECT tmprc;
751 enum_emh_data *info = ENUM_GET_PRIVATE_DATA(handletable);
752
753 TRACE("hdc = %p, handletable = %p, record = %p, numHandles = %d\n",
754 hdc, handletable, mr, handles);
755 if (!mr) return FALSE;
756
757 type = mr->iType;
758
759 /* In Win9x mode we update the xform if the record will produce output */
760 if ( IS_WIN9X() && emr_produces_output(type) )
761 EMF_Update_MF_Xform(hdc, info);
762
763 TRACE("record %s\n", get_emr_name(type));
764 switch(type)
765 {
766 case EMR_HEADER:
767 break;
768 case EMR_EOF:
769 break;
770 case EMR_GDICOMMENT:
771 {
772 const EMRGDICOMMENT *lpGdiComment = (const EMRGDICOMMENT *)mr;
773 /* In an enhanced metafile, there can be both public and private GDI comments */
774 GdiComment( hdc, lpGdiComment->cbData, lpGdiComment->Data );
775 break;
776 }
777 case EMR_SETMAPMODE:
778 {
779 const EMRSETMAPMODE *pSetMapMode = (const EMRSETMAPMODE *)mr;
780
781 if (info->state.mode == pSetMapMode->iMode &&
782 (info->state.mode == MM_ISOTROPIC || info->state.mode == MM_ANISOTROPIC))
783 break;
784 info->state.mode = pSetMapMode->iMode;
785 EMF_SetMapMode(hdc, info);
786 break;
787 }
788 case EMR_SETBKMODE:
789 {
790 const EMRSETBKMODE *pSetBkMode = (const EMRSETBKMODE *)mr;
791 SetBkMode(hdc, pSetBkMode->iMode);
792 break;
793 }
794 case EMR_SETBKCOLOR:
795 {
796 const EMRSETBKCOLOR *pSetBkColor = (const EMRSETBKCOLOR *)mr;
797 SetBkColor(hdc, pSetBkColor->crColor);
798 break;
799 }
800 case EMR_SETPOLYFILLMODE:
801 {
802 const EMRSETPOLYFILLMODE *pSetPolyFillMode = (const EMRSETPOLYFILLMODE *)mr;
803 SetPolyFillMode(hdc, pSetPolyFillMode->iMode);
804 break;
805 }
806 case EMR_SETROP2:
807 {
808 const EMRSETROP2 *pSetROP2 = (const EMRSETROP2 *)mr;
809 SetROP2(hdc, pSetROP2->iMode);
810 break;
811 }
812 case EMR_SETSTRETCHBLTMODE:
813 {
814 const EMRSETSTRETCHBLTMODE *pSetStretchBltMode = (const EMRSETSTRETCHBLTMODE *)mr;
815 SetStretchBltMode(hdc, pSetStretchBltMode->iMode);
816 break;
817 }
818 case EMR_SETTEXTALIGN:
819 {
820 const EMRSETTEXTALIGN *pSetTextAlign = (const EMRSETTEXTALIGN *)mr;
821 SetTextAlign(hdc, pSetTextAlign->iMode);
822 break;
823 }
824 case EMR_SETTEXTCOLOR:
825 {
826 const EMRSETTEXTCOLOR *pSetTextColor = (const EMRSETTEXTCOLOR *)mr;
827 SetTextColor(hdc, pSetTextColor->crColor);
828 break;
829 }
830 case EMR_SAVEDC:
831 {
832 if (SaveDC( hdc ))
833 EMF_SaveDC( info );
834 break;
835 }
836 case EMR_RESTOREDC:
837 {
838 const EMRRESTOREDC *pRestoreDC = (const EMRRESTOREDC *)mr;
839 TRACE("EMR_RESTORE: %d\n", pRestoreDC->iRelative);
840 if (RestoreDC( hdc, pRestoreDC->iRelative ))
841 EMF_RestoreDC( info, pRestoreDC->iRelative );
842 break;
843 }
844 case EMR_INTERSECTCLIPRECT:
845 {
846 const EMRINTERSECTCLIPRECT *pClipRect = (const EMRINTERSECTCLIPRECT *)mr;
847 TRACE("EMR_INTERSECTCLIPRECT: rect %d,%d - %d, %d\n",
848 pClipRect->rclClip.left, pClipRect->rclClip.top,
849 pClipRect->rclClip.right, pClipRect->rclClip.bottom);
850 IntersectClipRect(hdc, pClipRect->rclClip.left, pClipRect->rclClip.top,
851 pClipRect->rclClip.right, pClipRect->rclClip.bottom);
852 break;
853 }
854 case EMR_SELECTOBJECT:
855 {
856 const EMRSELECTOBJECT *pSelectObject = (const EMRSELECTOBJECT *)mr;
857 if( pSelectObject->ihObject & 0x80000000 ) {
858 /* High order bit is set - it's a stock object
859 * Strip the high bit to get the index.
860 * See MSDN article Q142319
861 */
862 SelectObject( hdc, GetStockObject( pSelectObject->ihObject &
863 0x7fffffff ) );
864 } else {
865 /* High order bit wasn't set - not a stock object
866 */
867 SelectObject( hdc,
868 (handletable->objectHandle)[pSelectObject->ihObject] );
869 }
870 break;
871 }
872 case EMR_DELETEOBJECT:
873 {
874 const EMRDELETEOBJECT *pDeleteObject = (const EMRDELETEOBJECT *)mr;
875 DeleteObject( (handletable->objectHandle)[pDeleteObject->ihObject]);
876 (handletable->objectHandle)[pDeleteObject->ihObject] = 0;
877 break;
878 }
879 case EMR_SETWINDOWORGEX:
880 {
881 const EMRSETWINDOWORGEX *pSetWindowOrgEx = (const EMRSETWINDOWORGEX *)mr;
882
883 info->state.wndOrgX = pSetWindowOrgEx->ptlOrigin.x;
884 info->state.wndOrgY = pSetWindowOrgEx->ptlOrigin.y;
885
886 TRACE("SetWindowOrgEx: %d,%d\n", info->state.wndOrgX, info->state.wndOrgY);
887 break;
888 }
889 case EMR_SETWINDOWEXTEX:
890 {
891 const EMRSETWINDOWEXTEX *pSetWindowExtEx = (const EMRSETWINDOWEXTEX *)mr;
892
893 if (info->state.mode != MM_ISOTROPIC && info->state.mode != MM_ANISOTROPIC)
894 break;
895 info->state.wndExtX = pSetWindowExtEx->szlExtent.cx;
896 info->state.wndExtY = pSetWindowExtEx->szlExtent.cy;
897 if (info->state.mode == MM_ISOTROPIC)
898 EMF_FixIsotropic(hdc, info);
899
900 TRACE("SetWindowExtEx: %d,%d\n",info->state.wndExtX, info->state.wndExtY);
901 break;
902 }
903 case EMR_SETVIEWPORTORGEX:
904 {
905 const EMRSETVIEWPORTORGEX *pSetViewportOrgEx = (const EMRSETVIEWPORTORGEX *)mr;
906
907 info->state.vportOrgX = pSetViewportOrgEx->ptlOrigin.