]> Creatis software - clitk.git/blob - utilities/CxImage/ximawmf.cpp
97ebf123db5777780394c89c406d3136c6963b85
[clitk.git] / utilities / CxImage / ximawmf.cpp
1 /*\r
2 *********************************************************************\r
3  * File:        ximawmf.cpp\r
4  * Purpose:     Windows Metafile Class Loader and Writer\r
5  * Author:      Volker Horch - vhorch@gmx.de\r
6  * created:     13-Jun-2002\r
7  *\r
8  * Note:        If the code below works, i wrote it.\r
9  *                      If it doesn't work, i don't know who wrote it.\r
10 *********************************************************************\r
11  */\r
12 \r
13 /*\r
14 *********************************************************************\r
15         Note by Author:\r
16 *********************************************************************\r
17 \r
18         Metafile Formats:\r
19         =================\r
20 \r
21         There are 2 kinds of Windows Metafiles:\r
22         - Standard Windows Metafile\r
23         - Placeable Windows Metafile\r
24 \r
25         A StandardWindows Metafile looks like:\r
26         - Metafile Header (MEATAHEADER)\r
27         - Metafile Records \r
28 \r
29         A Placeable Metafile looks like:\r
30         - Aldus Header (METAFILEHEADER)\r
31         - Metafile Header (METAHEADER)\r
32         - Metafile Records\r
33 \r
34         The "Metafile Header" and the "Metafile Records" are the same\r
35         for both formats. However, the Standard Metafile does not contain any\r
36         information about the original dimensions or x/y ratio of the Metafile.\r
37 \r
38         I decided, to allow only placeable Metafiles here. If you also want to\r
39         enable Standard Metafiles, you will have to guess the dimensions of\r
40         the image.\r
41 \r
42 *********************************************************************\r
43         Limitations:    see ximawmf.h\r
44                                         you may configure some stuff there\r
45 *********************************************************************\r
46 */\r
47 \r
48 #include "ximawmf.h"\r
49 \r
50 #if CXIMAGE_SUPPORT_WMF && CXIMAGE_SUPPORT_WINDOWS\r
51 \r
52 ////////////////////////////////////////////////////////////////////////////////\r
53 #if CXIMAGE_SUPPORT_DECODE\r
54 ////////////////////////////////////////////////////////////////////////////////\r
55 bool CxImageWMF::Decode(CxFile *hFile, long nForceWidth, long nForceHeight)\r
56 {\r
57         if (hFile == NULL) return false;\r
58 \r
59         HENHMETAFILE    hMeta;\r
60         HDC                             hDC;\r
61         int                             cx,cy;\r
62 \r
63         //save the current position of the file\r
64         long pos = hFile->Tell();\r
65 \r
66         // Read the Metafile and convert to an Enhanced Metafile\r
67         METAFILEHEADER  mfh;\r
68         hMeta = ConvertWmfFiletoEmf(hFile, &mfh);\r
69         if (hMeta) {    // ok, it's a WMF\r
70 \r
71 /////////////////////////////////////////////////////////////////////\r
72 //      We use the original WMF size information, because conversion to\r
73 //      EMF adjusts the Metafile to Full Screen or does not set rclBounds at all\r
74 //      ENHMETAHEADER   emh;\r
75 //      UINT                    uRet;\r
76 //      uRet = GetEnhMetaFileHeader(hMeta,                                      // handle of enhanced metafile \r
77 //                                                              sizeof(ENHMETAHEADER),  // size of buffer, in bytes \r
78 //                                                              &emh);                                  // address of buffer to receive data  \r
79 //      if (!uRet){\r
80 //              DeleteEnhMetaFile(hMeta);\r
81 //              return false;\r
82 //      }\r
83 //      // calculate size\r
84 //      cx = emh.rclBounds.right - emh.rclBounds.left;\r
85 //      cy = emh.rclBounds.bottom - emh.rclBounds.top;\r
86 /////////////////////////////////////////////////////////////////////\r
87 \r
88                 // calculate size\r
89                 // scale the metafile (pixels/inch of metafile => pixels/inch of display)\r
90                 // mfh.inch already checked to be <> 0\r
91 \r
92                 hDC = ::GetDC(0);\r
93                 int cx1 = ::GetDeviceCaps(hDC, LOGPIXELSX);\r
94                 int cy1 = ::GetDeviceCaps(hDC, LOGPIXELSY);\r
95                 ::ReleaseDC(0, hDC);\r
96 \r
97                 cx = (mfh.inch/2 + (mfh.bbox.right - mfh.bbox.left) * cx1) / mfh.inch;\r
98                 cy = (mfh.inch/2 + (mfh.bbox.bottom - mfh.bbox.top) * cy1) / mfh.inch;\r
99 \r
100         } else {                // maybe it's an EMF...\r
101 \r
102                 hFile->Seek(pos,SEEK_SET);\r
103 \r
104                 ENHMETAHEADER   emh;\r
105                 hMeta = ConvertEmfFiletoEmf(hFile, &emh);\r
106 \r
107                 if (!hMeta){\r
108                         strcpy(info.szLastError,"corrupted WMF");\r
109                         return false; // definitively give up\r
110                 }\r
111 \r
112                 // ok, it's an EMF; calculate canvas size\r
113                 cx = emh.rclBounds.right - emh.rclBounds.left;\r
114                 cy = emh.rclBounds.bottom - emh.rclBounds.top;\r
115 \r
116                 // alternative methods, sometime not so reliable... [DP]\r
117                 //cx = emh.szlDevice.cx;\r
118                 //cy = emh.szlDevice.cy;\r
119                 //\r
120                 //hDC = ::GetDC(0);\r
121                 //float hscale = (float)GetDeviceCaps(hDC, HORZRES)/(100.0f * GetDeviceCaps(hDC, HORZSIZE));\r
122                 //float vscale  =  (float)GetDeviceCaps(hDC, VERTRES)/(100.0f * GetDeviceCaps(hDC, VERTSIZE));\r
123                 //::ReleaseDC(0, hDC);\r
124                 //cx = (long)((emh.rclFrame.right - emh.rclFrame.left) * hscale);\r
125                 //cy = (long)((emh.rclFrame.bottom - emh.rclFrame.top) * vscale);\r
126         }\r
127 \r
128         if (info.nEscape == -1) {       // Check if cancelled\r
129                 head.biWidth = cx;\r
130                 head.biHeight= cy;\r
131                 info.dwType = CXIMAGE_FORMAT_WMF;\r
132                 DeleteEnhMetaFile(hMeta);\r
133                 strcpy(info.szLastError,"output dimensions returned");\r
134                 return true;\r
135         }\r
136 \r
137         if (!cx || !cy) {\r
138                 DeleteEnhMetaFile(hMeta);\r
139                 strcpy(info.szLastError,"empty WMF");\r
140                 return false;\r
141         }\r
142 \r
143         if (nForceWidth) cx=nForceWidth;\r
144         if (nForceHeight) cy=nForceHeight;\r
145         ShrinkMetafile(cx, cy);         // !! Otherwise Bitmap may have bombastic size\r
146 \r
147         HDC hDC0 = ::GetDC(0);  // DC of screen\r
148         HBITMAP hBitmap = CreateCompatibleBitmap(hDC0, cx, cy); // has # colors of display\r
149         hDC = CreateCompatibleDC(hDC0); // memory dc compatible with screen\r
150         ::ReleaseDC(0, hDC0);   // don't need anymore. get rid of it.\r
151 \r
152         if (hDC){\r
153                 if (hBitmap){\r
154                         RECT rc = {0,0,cx,cy};\r
155                         int bpp = ::GetDeviceCaps(hDC, BITSPIXEL);\r
156 \r
157                         HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);\r
158 \r
159                         // clear out the entire bitmap with windows background\r
160                         // because the MetaFile may not contain background information\r
161                         DWORD   dwBack = XMF_COLOR_BACK;\r
162 #if XMF_SUPPORT_TRANSPARENCY\r
163                         if (bpp == 24) dwBack = XMF_COLOR_TRANSPARENT;\r
164 #endif\r
165                     DWORD OldColor = SetBkColor(hDC, dwBack);\r
166                     ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);\r
167                         SetBkColor(hDC, OldColor);\r
168 \r
169                         //retrieves optional palette entries from the specified enhanced metafile\r
170                         PLOGPALETTE plogPal;\r
171                         PBYTE pjTmp; \r
172                         HPALETTE hPal; \r
173                         int iEntries = GetEnhMetaFilePaletteEntries(hMeta, 0, NULL);\r
174                         if (iEntries) { \r
175                                 if ((plogPal = (PLOGPALETTE)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, \r
176                                         sizeof(DWORD) + sizeof(PALETTEENTRY)*iEntries )) == NULL) { \r
177                                         DeleteObject(hBitmap);\r
178                                         DeleteDC(hDC);\r
179                                         DeleteEnhMetaFile(hMeta);\r
180                                         strcpy(info.szLastError,"Cancelled");\r
181                                         return false;\r
182                                 } \r
183 \r
184                                 plogPal->palVersion = 0x300; \r
185                                 plogPal->palNumEntries = (WORD) iEntries; \r
186                                 pjTmp = (PBYTE) plogPal; \r
187                                 pjTmp += 4; \r
188 \r
189                                 GetEnhMetaFilePaletteEntries(hMeta, iEntries, (PPALETTEENTRY)pjTmp); \r
190                                 hPal = CreatePalette(plogPal); \r
191                                 GlobalFree(plogPal); \r
192 \r
193                                 SelectPalette(hDC, hPal, FALSE); \r
194                                 RealizePalette(hDC); \r
195                         } \r
196                         \r
197                         // Play the Metafile into Memory DC\r
198                         BOOL bRet = PlayEnhMetaFile(hDC,        // handle to a device context \r
199                                                                         hMeta,  // handle to an enhanced metafile  \r
200                                                                         &rc);   // pointer to bounding rectangle\r
201 \r
202                         SelectObject(hDC, hBitmapOld);\r
203                         DeleteEnhMetaFile(hMeta);       // we are done with this one\r
204 \r
205                         if (info.nEscape) {     // Check if cancelled\r
206                                 DeleteObject(hBitmap);\r
207                                 DeleteDC(hDC);\r
208                                 strcpy(info.szLastError,"Cancelled");\r
209                                 return false;\r
210                         }\r
211 \r
212                         // the Bitmap now has the image.\r
213                         // Create our DIB and convert the DDB into DIB\r
214                         if (!Create(cx, cy, bpp, CXIMAGE_FORMAT_WMF)) {\r
215                                 DeleteObject(hBitmap);\r
216                                 DeleteDC(hDC);\r
217                                 return false;\r
218                         }\r
219 \r
220 #if XMF_SUPPORT_TRANSPARENCY\r
221                         if (bpp == 24) {\r
222                                 RGBQUAD rgbTrans = { XMF_RGBQUAD_TRANSPARENT };\r
223                                 SetTransColor(rgbTrans);\r
224                         }\r
225 #endif\r
226                     // We're finally ready to get the DIB. Call the driver and let\r
227                     // it party on our bitmap. It will fill in the color table,\r
228                     // and bitmap bits of our global memory block.\r
229                         bRet = GetDIBits(hDC, hBitmap, 0,\r
230                                 (UINT)cy, GetBits(), (LPBITMAPINFO)pDib, DIB_RGB_COLORS);\r
231 \r
232                         DeleteObject(hBitmap);\r
233                         DeleteDC(hDC);\r
234 \r
235                         return (bRet!=0);\r
236                 } else {\r
237                         DeleteDC(hDC);\r
238                 }\r
239         } else {\r
240                 if (hBitmap) DeleteObject(hBitmap);\r
241         }\r
242 \r
243         DeleteEnhMetaFile(hMeta);\r
244 \r
245         return false;\r
246 }\r
247 \r
248 /**********************************************************************\r
249  Function:      CheckMetafileHeader\r
250  Purpose:       Check if the Metafileheader of a file is valid\r
251 **********************************************************************/\r
252 BOOL CxImageWMF::CheckMetafileHeader(METAFILEHEADER *metafileheader)\r
253 {\r
254         WORD    *pw;\r
255         WORD    cs;\r
256         int             i;\r
257 \r
258         // check magic #\r
259         if (metafileheader->key != 0x9ac6cdd7L) return false;\r
260 \r
261         // test checksum of header\r
262         pw = (WORD *)metafileheader;\r
263         cs = *pw;\r
264         pw++;\r
265         for (i = 0; i < 9; i++) {\r
266                 cs ^= *pw;\r
267                 pw++;\r
268         }\r
269 \r
270         if (cs != metafileheader->checksum)     return false;\r
271 \r
272         // check resolution\r
273         if ((metafileheader->inch <= 0) || (metafileheader->inch > 2540)) return false;\r
274 \r
275         return true;\r
276 }\r
277 \r
278 /**********************************************************************\r
279  Function:      ConvertWmfFiletoEmf\r
280  Purpose:       Converts a Windows Metafile into an Enhanced Metafile\r
281 **********************************************************************/\r
282 HENHMETAFILE CxImageWMF::ConvertWmfFiletoEmf(CxFile *fp, METAFILEHEADER *metafileheader)\r
283 {\r
284         HENHMETAFILE    hMeta;\r
285         long                    lenFile;\r
286         long                    len;\r
287         BYTE                    *p;\r
288         METAHEADER              mfHeader;\r
289         DWORD                   seekpos;\r
290 \r
291         hMeta = 0;\r
292 \r
293         // get length of the file\r
294         lenFile = fp->Size();\r
295 \r
296         // a placeable metafile starts with a METAFILEHEADER\r
297         // read it and check metafileheader\r
298         len = fp->Read(metafileheader, 1, sizeof(METAFILEHEADER));\r
299         if (len < sizeof(METAFILEHEADER)) return (hMeta);\r
300 \r
301         if (CheckMetafileHeader(metafileheader)) {\r
302                 // This is a placeable metafile \r
303                 // Convert the placeable format into something that can\r
304                 // be used with GDI metafile functions \r
305                 seekpos = sizeof(METAFILEHEADER);\r
306         } else {\r
307                 // Not a placeable wmf. A windows metafile?\r
308                 // at least not scaleable.\r
309                 // we could try to convert, but would loose ratio. don't allow this\r
310                 return (hMeta);\r
311 \r
312                 //metafileheader->bbox.right = ?;\r
313                 //metafileheader->bbox.left = ?;\r
314                 //metafileheader->bbox.bottom = ?;\r
315                 //metafileheader->bbox.top = ?;\r
316                 //metafileheader->inch = ?;\r
317                 //\r
318                 //seekpos = 0;\r
319                 // fp->Seek(0, SEEK_SET);       // rewind\r
320         }\r
321 \r
322         // At this point we have a metaheader regardless of whether\r
323         // the metafile was a windows metafile or a placeable metafile\r
324         // so check to see if it is valid. There is really no good\r
325         // way to do this so just make sure that the mtType is either\r
326         // 1 or 2 (memory or disk file) \r
327         // in addition we compare the length of the METAHEADER against\r
328         // the length of the file. if filelength < len => no Metafile\r
329 \r
330         len = fp->Read(&mfHeader, 1, sizeof(METAHEADER));\r
331         if (len < sizeof(METAHEADER)) return (hMeta);\r
332 \r
333         if ((mfHeader.mtType != 1) && (mfHeader.mtType != 2)) return (hMeta);\r
334 \r
335         // Length in Bytes from METAHEADER\r
336         len = mfHeader.mtSize * 2;\r
337         if (len > lenFile) return (hMeta);\r
338 \r
339         // Allocate memory for the metafile bits \r
340         p = (BYTE *)malloc(len);\r
341         if (!p) return (hMeta);\r
342 \r
343         // seek back to METAHEADER and read all the stuff at once\r
344         fp->Seek(seekpos, SEEK_SET);\r
345         lenFile = fp->Read(p, 1, len);\r
346         if (lenFile != len)     {\r
347                 free(p);\r
348                 return (hMeta);\r
349         }\r
350 \r
351         // the following (commented code)  works, but adjusts rclBound of the\r
352         // Enhanced Metafile to full screen.\r
353         // the METAFILEHEADER from above is needed to scale the image\r
354 \r
355 //      hMeta = SetWinMetaFileBits(len, p, NULL, NULL);\r
356 \r
357         // scale the metafile (pixels/inch of metafile => pixels/inch of display)\r
358 \r
359         METAFILEPICT    mfp;\r
360         int cx1, cy1;\r
361         HDC hDC;\r
362 \r
363         hDC = ::GetDC(0);\r
364         cx1 = ::GetDeviceCaps(hDC, LOGPIXELSX);\r
365         cy1 = ::GetDeviceCaps(hDC, LOGPIXELSY);\r
366 \r
367         memset(&mfp, 0, sizeof(mfp));\r
368 \r
369         mfp.mm = MM_ANISOTROPIC;\r
370         mfp.xExt = 10000; //(metafileheader->bbox.right - metafileheader->bbox.left) * cx1 / metafileheader->inch;\r
371         mfp.yExt = 10000; //(metafileheader->bbox.bottom - metafileheader->bbox.top) * cy1 / metafileheader->inch;\r
372         mfp.hMF = 0;\r
373 \r
374         // in MM_ANISOTROPIC mode xExt and yExt are in MM_HIENGLISH\r
375         // MM_HIENGLISH means: Each logical unit is converted to 0.001 inch\r
376         //mfp.xExt *= 1000;\r
377         //mfp.yExt *= 1000;\r
378         // ????\r
379         //int k = 332800 / ::GetSystemMetrics(SM_CXSCREEN);\r
380         //mfp.xExt *= k;        mfp.yExt *= k;\r
381 \r
382         // fix for Win9x\r
383         while ((mfp.xExt < 6554) && (mfp.yExt < 6554))\r
384         {\r
385                 mfp.xExt *= 10;\r
386                 mfp.yExt *= 10;\r
387         }\r
388 \r
389         hMeta = SetWinMetaFileBits(len, p, hDC, &mfp);\r
390 \r
391         if (!hMeta){ //try 2nd conversion using a different mapping\r
392                 mfp.mm = MM_TEXT;\r
393                 hMeta = SetWinMetaFileBits(len, p, hDC, &mfp);\r
394         }\r
395 \r
396         ::ReleaseDC(0, hDC);\r
397 \r
398         // Free Memory\r
399         free(p);\r
400 \r
401         return (hMeta);\r
402 }\r
403 /////////////////////////////////////////////////////////////////////\r
404 HENHMETAFILE CxImageWMF::ConvertEmfFiletoEmf(CxFile *pFile, ENHMETAHEADER *pemfh)\r
405 {\r
406         HENHMETAFILE    hMeta;\r
407         long iLen = pFile->Size();\r
408 \r
409         // Check the header first: <km>\r
410         long pos = pFile->Tell();\r
411         long iLenRead = pFile->Read(pemfh, 1, sizeof(ENHMETAHEADER));\r
412         if (iLenRead < sizeof(ENHMETAHEADER))         return NULL;\r
413         if (pemfh->iType != EMR_HEADER)               return NULL;\r
414         if (pemfh->dSignature != ENHMETA_SIGNATURE)   return NULL;\r
415         //if (pemfh->nBytes != (DWORD)iLen)             return NULL;\r
416         pFile->Seek(pos,SEEK_SET);\r
417 \r
418         BYTE* pBuff = (BYTE *)malloc(iLen);\r
419         if (!pBuff)     return (FALSE);\r
420 \r
421         // Read the Enhanced Metafile\r
422         iLenRead = pFile->Read(pBuff, 1, iLen);\r
423         if (iLenRead != iLen) {\r
424                 free(pBuff);\r
425                 return NULL;\r
426         }\r
427 \r
428         // Make it a Memory Metafile\r
429         hMeta = SetEnhMetaFileBits(iLen, pBuff);\r
430 \r
431         free(pBuff);    // finished with this one\r
432 \r
433         if (!hMeta)     return NULL;    // oops.\r
434 \r
435         // Get the Enhanced Metafile Header\r
436         UINT uRet = GetEnhMetaFileHeader(hMeta,                         // handle of enhanced metafile \r
437                                                                 sizeof(ENHMETAHEADER),  // size of buffer, in bytes \r
438                                                                 pemfh);                                 // address of buffer to receive data  \r
439   \r
440         if (!uRet) {\r
441                 DeleteEnhMetaFile(hMeta);\r
442                 return NULL;\r
443         }\r
444 \r
445         return (hMeta);\r
446 }\r
447 ////////////////////////////////////////////////////////////////////////////////\r
448 #endif //CXIMAGE_SUPPORT_DECODE\r
449 ////////////////////////////////////////////////////////////////////////////////\r
450 #if CXIMAGE_SUPPORT_ENCODE\r
451 /////////////////////////////////////////////////////////////////////\r
452 bool CxImageWMF::Encode(CxFile * hFile)\r
453 {\r
454         if (hFile == NULL) return false;\r
455         strcpy(info.szLastError, "Save WMF not supported");\r
456         return false;\r
457 }\r
458 #endif  // CXIMAGE_SUPPORT_ENCODE\r
459 /////////////////////////////////////////////////////////////////////\r
460 \r
461 /**********************************************************************\r
462 Function:       ShrinkMetafile\r
463 Purpose:        Shrink the size of a metafile to be not larger than\r
464                         the definition\r
465 **********************************************************************/\r
466 void CxImageWMF::ShrinkMetafile(int &cx, int &cy)\r
467 {\r
468         int     xScreen = XMF_MAXSIZE_CX;\r
469         int     yScreen = XMF_MAXSIZE_CY;\r
470 \r
471         if (cx > xScreen){\r
472                 cy = cy * xScreen / cx;\r
473                 cx = xScreen;\r
474         }\r
475 \r
476         if (cy > yScreen){\r
477                 cx = cx * yScreen / cy;\r
478                 cy = yScreen;\r
479         }\r
480 }\r
481 \r
482 #endif  // CIMAGE_SUPPORT_WMF\r
483 \r