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