aboutsummaryrefslogtreecommitdiff
path: root/archive/hge/CxImage/ximawmf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'archive/hge/CxImage/ximawmf.cpp')
-rw-r--r--archive/hge/CxImage/ximawmf.cpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/archive/hge/CxImage/ximawmf.cpp b/archive/hge/CxImage/ximawmf.cpp
new file mode 100644
index 0000000..a1057f5
--- /dev/null
+++ b/archive/hge/CxImage/ximawmf.cpp
@@ -0,0 +1,483 @@
+/*
+*********************************************************************
+ * File: ximawmf.cpp
+ * Purpose: Windows Metafile Class Loader and Writer
+ * Author: Volker Horch - vhorch@gmx.de
+ * created: 13-Jun-2002
+ *
+ * Note: If the code below works, i wrote it.
+ * If it doesn't work, i don't know who wrote it.
+*********************************************************************
+ */
+
+/*
+*********************************************************************
+ Note by Author:
+*********************************************************************
+
+ Metafile Formats:
+ =================
+
+ There are 2 kinds of Windows Metafiles:
+ - Standard Windows Metafile
+ - Placeable Windows Metafile
+
+ A StandardWindows Metafile looks like:
+ - Metafile Header (MEATAHEADER)
+ - Metafile Records
+
+ A Placeable Metafile looks like:
+ - Aldus Header (METAFILEHEADER)
+ - Metafile Header (METAHEADER)
+ - Metafile Records
+
+ The "Metafile Header" and the "Metafile Records" are the same
+ for both formats. However, the Standard Metafile does not contain any
+ information about the original dimensions or x/y ratio of the Metafile.
+
+ I decided, to allow only placeable Metafiles here. If you also want to
+ enable Standard Metafiles, you will have to guess the dimensions of
+ the image.
+
+*********************************************************************
+ Limitations: see ximawmf.h
+ you may configure some stuff there
+*********************************************************************
+*/
+
+#include "ximawmf.h"
+
+#if CXIMAGE_SUPPORT_WMF && CXIMAGE_SUPPORT_WINDOWS
+
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+bool CxImageWMF::Decode(CxFile *hFile, int32_t nForceWidth, int32_t nForceHeight)
+{
+ if (hFile == NULL) return false;
+
+ HENHMETAFILE hMeta;
+ HDC hDC;
+ int32_t cx,cy;
+
+ //save the current position of the file
+ int32_t pos = hFile->Tell();
+
+ // Read the Metafile and convert to an Enhanced Metafile
+ METAFILEHEADER mfh;
+ hMeta = ConvertWmfFiletoEmf(hFile, &mfh);
+ if (hMeta) { // ok, it's a WMF
+
+/////////////////////////////////////////////////////////////////////
+// We use the original WMF size information, because conversion to
+// EMF adjusts the Metafile to Full Screen or does not set rclBounds at all
+// ENHMETAHEADER emh;
+// uint32_t uRet;
+// uRet = GetEnhMetaFileHeader(hMeta, // handle of enhanced metafile
+// sizeof(ENHMETAHEADER), // size of buffer, in bytes
+// &emh); // address of buffer to receive data
+// if (!uRet){
+// DeleteEnhMetaFile(hMeta);
+// return false;
+// }
+// // calculate size
+// cx = emh.rclBounds.right - emh.rclBounds.left;
+// cy = emh.rclBounds.bottom - emh.rclBounds.top;
+/////////////////////////////////////////////////////////////////////
+
+ // calculate size
+ // scale the metafile (pixels/inch of metafile => pixels/inch of display)
+ // mfh.inch already checked to be <> 0
+
+ hDC = ::GetDC(0);
+ int32_t cx1 = ::GetDeviceCaps(hDC, LOGPIXELSX);
+ int32_t cy1 = ::GetDeviceCaps(hDC, LOGPIXELSY);
+ ::ReleaseDC(0, hDC);
+
+ cx = (mfh.inch/2 + (mfh.bbox.right - mfh.bbox.left) * cx1) / mfh.inch;
+ cy = (mfh.inch/2 + (mfh.bbox.bottom - mfh.bbox.top) * cy1) / mfh.inch;
+
+ } else { // maybe it's an EMF...
+
+ hFile->Seek(pos,SEEK_SET);
+
+ ENHMETAHEADER emh;
+ hMeta = ConvertEmfFiletoEmf(hFile, &emh);
+
+ if (!hMeta){
+ strcpy(info.szLastError,"corrupted WMF");
+ return false; // definitively give up
+ }
+
+ // ok, it's an EMF; calculate canvas size
+ cx = emh.rclBounds.right - emh.rclBounds.left;
+ cy = emh.rclBounds.bottom - emh.rclBounds.top;
+
+ // alternative methods, sometime not so reliable... [DP]
+ //cx = emh.szlDevice.cx;
+ //cy = emh.szlDevice.cy;
+ //
+ //hDC = ::GetDC(0);
+ //float hscale = (float)GetDeviceCaps(hDC, HORZRES)/(100.0f * GetDeviceCaps(hDC, HORZSIZE));
+ //float vscale = (float)GetDeviceCaps(hDC, VERTRES)/(100.0f * GetDeviceCaps(hDC, VERTSIZE));
+ //::ReleaseDC(0, hDC);
+ //cx = (int32_t)((emh.rclFrame.right - emh.rclFrame.left) * hscale);
+ //cy = (int32_t)((emh.rclFrame.bottom - emh.rclFrame.top) * vscale);
+ }
+
+ if (info.nEscape == -1) { // Check if cancelled
+ head.biWidth = cx;
+ head.biHeight= cy;
+ info.dwType = CXIMAGE_FORMAT_WMF;
+ DeleteEnhMetaFile(hMeta);
+ strcpy(info.szLastError,"output dimensions returned");
+ return true;
+ }
+
+ if (!cx || !cy) {
+ DeleteEnhMetaFile(hMeta);
+ strcpy(info.szLastError,"empty WMF");
+ return false;
+ }
+
+ if (nForceWidth) cx=nForceWidth;
+ if (nForceHeight) cy=nForceHeight;
+ ShrinkMetafile(cx, cy); // !! Otherwise Bitmap may have bombastic size
+
+ HDC hDC0 = ::GetDC(0); // DC of screen
+ HBITMAP hBitmap = CreateCompatibleBitmap(hDC0, cx, cy); // has # colors of display
+ hDC = CreateCompatibleDC(hDC0); // memory dc compatible with screen
+ ::ReleaseDC(0, hDC0); // don't need anymore. get rid of it.
+
+ if (hDC){
+ if (hBitmap){
+ RECT rc = {0,0,cx,cy};
+ int32_t bpp = ::GetDeviceCaps(hDC, BITSPIXEL);
+
+ HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);
+
+ // clear out the entire bitmap with windows background
+ // because the MetaFile may not contain background information
+ uint32_t dwBack = XMF_COLOR_BACK;
+#if XMF_SUPPORT_TRANSPARENCY
+ if (bpp == 24) dwBack = XMF_COLOR_TRANSPARENT;
+#endif
+ uint32_t OldColor = SetBkColor(hDC, dwBack);
+ ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ SetBkColor(hDC, OldColor);
+
+ //retrieves optional palette entries from the specified enhanced metafile
+ PLOGPALETTE plogPal;
+ PBYTE pjTmp;
+ HPALETTE hPal;
+ int32_t iEntries = GetEnhMetaFilePaletteEntries(hMeta, 0, NULL);
+ if (iEntries) {
+ if ((plogPal = (PLOGPALETTE)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
+ sizeof(uint32_t) + sizeof(PALETTEENTRY)*iEntries )) == NULL) {
+ DeleteObject(hBitmap);
+ DeleteDC(hDC);
+ DeleteEnhMetaFile(hMeta);
+ strcpy(info.szLastError,"Cancelled");
+ return false;
+ }
+
+ plogPal->palVersion = 0x300;
+ plogPal->palNumEntries = (uint16_t) iEntries;
+ pjTmp = (PBYTE) plogPal;
+ pjTmp += 4;
+
+ GetEnhMetaFilePaletteEntries(hMeta, iEntries, (PPALETTEENTRY)pjTmp);
+ hPal = CreatePalette(plogPal);
+ GlobalFree(plogPal);
+
+ SelectPalette(hDC, hPal, FALSE);
+ RealizePalette(hDC);
+ }
+
+ // Play the Metafile into Memory DC
+ BOOL bRet = PlayEnhMetaFile(hDC, // handle to a device context
+ hMeta, // handle to an enhanced metafile
+ &rc); // pointer to bounding rectangle
+
+ SelectObject(hDC, hBitmapOld);
+ DeleteEnhMetaFile(hMeta); // we are done with this one
+
+ if (info.nEscape) { // Check if cancelled
+ DeleteObject(hBitmap);
+ DeleteDC(hDC);
+ strcpy(info.szLastError,"Cancelled");
+ return false;
+ }
+
+ // the Bitmap now has the image.
+ // Create our DIB and convert the DDB into DIB
+ if (!Create(cx, cy, bpp, CXIMAGE_FORMAT_WMF)) {
+ DeleteObject(hBitmap);
+ DeleteDC(hDC);
+ return false;
+ }
+
+#if XMF_SUPPORT_TRANSPARENCY
+ if (bpp == 24) {
+ RGBQUAD rgbTrans = { XMF_RGBQUAD_TRANSPARENT };
+ SetTransColor(rgbTrans);
+ }
+#endif
+ // We're finally ready to get the DIB. Call the driver and let
+ // it party on our bitmap. It will fill in the color table,
+ // and bitmap bits of our global memory block.
+ bRet = GetDIBits(hDC, hBitmap, 0,
+ (uint32_t)cy, GetBits(), (LPBITMAPINFO)pDib, DIB_RGB_COLORS);
+
+ DeleteObject(hBitmap);
+ DeleteDC(hDC);
+
+ return (bRet!=0);
+ } else {
+ DeleteDC(hDC);
+ }
+ } else {
+ if (hBitmap) DeleteObject(hBitmap);
+ }
+
+ DeleteEnhMetaFile(hMeta);
+
+ return false;
+}
+
+/**********************************************************************
+ Function: CheckMetafileHeader
+ Purpose: Check if the Metafileheader of a file is valid
+**********************************************************************/
+BOOL CxImageWMF::CheckMetafileHeader(METAFILEHEADER *metafileheader)
+{
+ uint16_t *pw;
+ uint16_t cs;
+ int32_t i;
+
+ // check magic #
+ if (metafileheader->key != 0x9ac6cdd7L) return false;
+
+ // test checksum of header
+ pw = (uint16_t *)metafileheader;
+ cs = *pw;
+ pw++;
+ for (i = 0; i < 9; i++) {
+ cs ^= *pw;
+ pw++;
+ }
+
+ if (cs != metafileheader->checksum) return false;
+
+ // check resolution
+ if ((metafileheader->inch <= 0) || (metafileheader->inch > 2540)) return false;
+
+ return true;
+}
+
+/**********************************************************************
+ Function: ConvertWmfFiletoEmf
+ Purpose: Converts a Windows Metafile into an Enhanced Metafile
+**********************************************************************/
+HENHMETAFILE CxImageWMF::ConvertWmfFiletoEmf(CxFile *fp, METAFILEHEADER *metafileheader)
+{
+ HENHMETAFILE hMeta;
+ uint32_t lenFile;
+ uint32_t len;
+ uint8_t *p;
+ METAHEADER mfHeader;
+ uint32_t seekpos;
+
+ hMeta = 0;
+
+ // get length of the file
+ lenFile = fp->Size();
+
+ // a placeable metafile starts with a METAFILEHEADER
+ // read it and check metafileheader
+ len = fp->Read(metafileheader, 1, sizeof(METAFILEHEADER));
+ if (len < sizeof(METAFILEHEADER)) return (hMeta);
+
+ if (CheckMetafileHeader(metafileheader)) {
+ // This is a placeable metafile
+ // Convert the placeable format into something that can
+ // be used with GDI metafile functions
+ seekpos = sizeof(METAFILEHEADER);
+ } else {
+ // Not a placeable wmf. A windows metafile?
+ // at least not scaleable.
+ // we could try to convert, but would loose ratio. don't allow this
+ return (hMeta);
+
+ //metafileheader->bbox.right = ?;
+ //metafileheader->bbox.left = ?;
+ //metafileheader->bbox.bottom = ?;
+ //metafileheader->bbox.top = ?;
+ //metafileheader->inch = ?;
+ //
+ //seekpos = 0;
+ // fp->Seek(0, SEEK_SET); // rewind
+ }
+
+ // At this point we have a metaheader regardless of whether
+ // the metafile was a windows metafile or a placeable metafile
+ // so check to see if it is valid. There is really no good
+ // way to do this so just make sure that the mtType is either
+ // 1 or 2 (memory or disk file)
+ // in addition we compare the length of the METAHEADER against
+ // the length of the file. if filelength < len => no Metafile
+
+ len = fp->Read(&mfHeader, 1, sizeof(METAHEADER));
+ if (len < sizeof(METAHEADER)) return (hMeta);
+
+ if ((mfHeader.mtType != 1) && (mfHeader.mtType != 2)) return (hMeta);
+
+ // Length in Bytes from METAHEADER
+ len = mfHeader.mtSize * 2;
+ if (len > lenFile) return (hMeta);
+
+ // Allocate memory for the metafile bits
+ p = (uint8_t *)malloc(len);
+ if (!p) return (hMeta);
+
+ // seek back to METAHEADER and read all the stuff at once
+ fp->Seek(seekpos, SEEK_SET);
+ lenFile = fp->Read(p, 1, len);
+ if (lenFile != len) {
+ free(p);
+ return (hMeta);
+ }
+
+ // the following (commented code) works, but adjusts rclBound of the
+ // Enhanced Metafile to full screen.
+ // the METAFILEHEADER from above is needed to scale the image
+
+// hMeta = SetWinMetaFileBits(len, p, NULL, NULL);
+
+ // scale the metafile (pixels/inch of metafile => pixels/inch of display)
+
+ METAFILEPICT mfp;
+ int32_t cx1, cy1;
+ HDC hDC;
+
+ hDC = ::GetDC(0);
+ cx1 = ::GetDeviceCaps(hDC, LOGPIXELSX);
+ cy1 = ::GetDeviceCaps(hDC, LOGPIXELSY);
+
+ memset(&mfp, 0, sizeof(mfp));
+
+ mfp.mm = MM_ANISOTROPIC;
+ mfp.xExt = 10000; //(metafileheader->bbox.right - metafileheader->bbox.left) * cx1 / metafileheader->inch;
+ mfp.yExt = 10000; //(metafileheader->bbox.bottom - metafileheader->bbox.top) * cy1 / metafileheader->inch;
+ mfp.hMF = 0;
+
+ // in MM_ANISOTROPIC mode xExt and yExt are in MM_HIENGLISH
+ // MM_HIENGLISH means: Each logical unit is converted to 0.001 inch
+ //mfp.xExt *= 1000;
+ //mfp.yExt *= 1000;
+ // ????
+ //int32_t k = 332800 / ::GetSystemMetrics(SM_CXSCREEN);
+ //mfp.xExt *= k; mfp.yExt *= k;
+
+ // fix for Win9x
+ while ((mfp.xExt < 6554) && (mfp.yExt < 6554))
+ {
+ mfp.xExt *= 10;
+ mfp.yExt *= 10;
+ }
+
+ hMeta = SetWinMetaFileBits(len, p, hDC, &mfp);
+
+ if (!hMeta){ //try 2nd conversion using a different mapping
+ mfp.mm = MM_TEXT;
+ hMeta = SetWinMetaFileBits(len, p, hDC, &mfp);
+ }
+
+ ::ReleaseDC(0, hDC);
+
+ // Free Memory
+ free(p);
+
+ return (hMeta);
+}
+/////////////////////////////////////////////////////////////////////
+HENHMETAFILE CxImageWMF::ConvertEmfFiletoEmf(CxFile *pFile, ENHMETAHEADER *pemfh)
+{
+ HENHMETAFILE hMeta;
+ int32_t iLen = pFile->Size();
+
+ // Check the header first: <km>
+ int32_t pos = pFile->Tell();
+ int32_t iLenRead = pFile->Read(pemfh, 1, sizeof(ENHMETAHEADER));
+ if (iLenRead < sizeof(ENHMETAHEADER)) return NULL;
+ if (pemfh->iType != EMR_HEADER) return NULL;
+ if (pemfh->dSignature != ENHMETA_SIGNATURE) return NULL;
+ //if (pemfh->nBytes != (uint32_t)iLen) return NULL;
+ pFile->Seek(pos,SEEK_SET);
+
+ uint8_t* pBuff = (uint8_t *)malloc(iLen);
+ if (!pBuff) return (FALSE);
+
+ // Read the Enhanced Metafile
+ iLenRead = pFile->Read(pBuff, 1, iLen);
+ if (iLenRead != iLen) {
+ free(pBuff);
+ return NULL;
+ }
+
+ // Make it a Memory Metafile
+ hMeta = SetEnhMetaFileBits(iLen, pBuff);
+
+ free(pBuff); // finished with this one
+
+ if (!hMeta) return NULL; // oops.
+
+ // Get the Enhanced Metafile Header
+ uint32_t uRet = GetEnhMetaFileHeader(hMeta, // handle of enhanced metafile
+ sizeof(ENHMETAHEADER), // size of buffer, in bytes
+ pemfh); // address of buffer to receive data
+
+ if (!uRet) {
+ DeleteEnhMetaFile(hMeta);
+ return NULL;
+ }
+
+ return (hMeta);
+}
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_ENCODE
+/////////////////////////////////////////////////////////////////////
+bool CxImageWMF::Encode(CxFile * hFile)
+{
+ if (hFile == NULL) return false;
+ strcpy(info.szLastError, "Save WMF not supported");
+ return false;
+}
+#endif // CXIMAGE_SUPPORT_ENCODE
+/////////////////////////////////////////////////////////////////////
+
+/**********************************************************************
+Function: ShrinkMetafile
+Purpose: Shrink the size of a metafile to be not larger than
+ the definition
+**********************************************************************/
+void CxImageWMF::ShrinkMetafile(int32_t &cx, int32_t &cy)
+{
+ int32_t xScreen = XMF_MAXSIZE_CX;
+ int32_t yScreen = XMF_MAXSIZE_CY;
+
+ if (cx > xScreen){
+ cy = cy * xScreen / cx;
+ cx = xScreen;
+ }
+
+ if (cy > yScreen){
+ cx = cx * yScreen / cy;
+ cy = yScreen;
+ }
+}
+
+#endif // CIMAGE_SUPPORT_WMF
+