Visual C++ 7.1Visual Studio 6Visual C++ 7.0Windows 2003Windows 2000Visual C++ 6.0Windows XPMFCIntermediateDevVisual StudioWindowsC++
增强图元文件与位图之间的转换以及反之






4.78/5 (40投票s)
一篇关于 EMF 文件与位图文件格式之间转换以及 BMP 与 EMF 之间转换的文章
引言
我遇到了很多可以将图像从一种格式转换为另一种格式的库,但大多数都局限于栅格格式(如 BMP、JPG、GIF 等)之间的转换。本文介绍了一个类,它封装了将矢量文件格式 (EMF) 转换为栅格文件格式 (BMP) 的功能。一旦 EMF 被转换为 BMP 栅格文件格式,就可以使用 CxImage
等免费库将其转换为任何其他格式。
背景
在我之前的项目中,我遇到了将 EMF 转换为 BMP 的问题。我搜了很多资料,但除了在讨论组中看到许多人问类似问题外,别无他法。我想我应该与他人分享这个解决方案,并写了我的第一篇文章到 Code project。我希望它对您有所帮助。
使用代码
使用此代码非常简单。只需实例化该类并像这样调用 convert 函数
CConvertEMFToBMP conv;
//call the convert function
BOOL bRet = conv.ConvertEMFToBMP(m_strEMF,m_strBMP,m_chkScale);
ConvertEMFToBMP
接受三个参数
// m_strEMF - path and file name of EMF file to be converted // m_strBMP - path and file name of BMP file // m_chkScale - boolean. If this is false, EMF is converted to // BMP in original size. // If this is true, the image is scaled to preset size // in the code (explained below)
示例
CConvertEMFToBMP conv; //call the convert function BOOL bRet = conv.ConvertEMFToBMP("D:\temp\Sample.emf", "D:\temp\sample.bmp",false);
将 BMP 转换为 EMF
CConvertEMFToBMP conv;
//call the convert function
BOOL bRet = conv.ConvertBMPToEMF(m_strBMP,m_strEMF);
ConvertBMPToEMF
接受两个参数
m_strBMP - path and file name of BMP file to be converted m_strEMF - path and file name of EMF file
示例
CConvertEMFToBMP conv; //call the convert function BOOL bRet = conv.ConvertBMPToEMF("D:\temp\Sample.bmp", "D:\temp\sample.emf",false);
它是如何工作的?
没有直接的技术可以将 EMF 转换为 BMP。ConvertEMFToBMP
通过在 MemoryDC 中播放图元文件,然后获取 DIB 位并将其存储为位图格式来将 EMF 转换为位图。为了获取内存 DC,会创建一个不可见的窗口,并在转换结束时销毁。或者,可以修改源代码以将 hwnd
传递给 ConvertEMFToBMP
函数。scale image 选项会将图像缩放到源代码中 #defined 的固定大小。这也可能被修改为根据用户传递的参数进行缩放。我只使用了 API 函数,以避免 MFC DLL,使代码轻量级。以下是转换核心逻辑的分步说明。- 创建一个大小任意的、不可见的窗口
//Create an invisible window HWND hWnd = CreateWindow("#32770","NULL",~WS_VISIBLE,0,0, 100,100,NULL,NULL,NULL,0);
- 获取窗口的 DC
//Get the DC of the Window HDC dc = ::GetDC(hWnd);
- 获取 EMF 文件的句柄并获取 EMF 头
//Get the Handle from the enhanced metafile hemf = ::GetEnhMetaFile(strFileName); // Get the header from the enhanced metafile. ZeroMemory( &emh, sizeof(ENHMETAHEADER) ); emh.nSize = sizeof(ENHMETAHEADER); if( GetEnhMetaFileHeader( hemf, sizeof( ENHMETAHEADER ), &emh ) == 0 ) { DeleteEnhMetaFile( hemf ); return FALSE; }
- 获取 EMF 的宽度和高度
//Declare variables for calculation of metafile rect RECT rect; float PixelsX, PixelsY, MMX, MMY; float fAspectRatio; long lWidth,lHeight; // Get the characteristics of the output device. PixelsX = (float)GetDeviceCaps( dc, HORZRES ); PixelsY = (float)GetDeviceCaps( dc, VERTRES ); MMX = (float)GetDeviceCaps( dc, HORZSIZE ); MMY = (float)GetDeviceCaps( dc, VERTSIZE ); // Calculate the rect in which to draw the metafile based on the // intended size and the current output device resolution. // Remember that the intended size is given in 0.01 mm units, so // convert those to device units on the target device. rect.top = (int)((float)(emh.rclFrame.top) * PixelsY / (MMY*100.0f)); rect.left = (int)((float)(emh.rclFrame.left) * PixelsX / (MMX*100.0f)); rect.right = (int)((float)(emh.rclFrame.right) * PixelsX / (MMX*100.0f)); rect.bottom = (int)((float)(emh.rclFrame.bottom) * PixelsY / (MMY*100.0f)); //Calculate the Width and Height of the metafile lWidth = (long)((float)(abs(rect.left - rect.right))); lHeight =(long)((float)(abs(rect.top-rect.bottom )));
- 如果图像需要缩放,请根据纵横比修改宽度和高度
fAspectRatio = (float)lWidth/(float)lHeight; if(bScaleImage) //If miniature option is ON, change //the width and height accordingly { if(fAspectRatio > 1 ) //width is more than height { //Make width as constant and calculate Height lWidth = X_MINIATUREFRAME; lHeight = (long)((float)Y_MINIATUREFRAME / fAspectRatio); } else //width is less than height(or equal to height) { //Make Height as constant and calculate Width lHeight = Y_MINIATUREFRAME; lWidth = (long)((float)X_MINIATUREFRAME * fAspectRatio); } }
- 创建一个与 WindowDC 兼容的 Memory DC
memDC=::CreateCompatibleDC(dc);
- 创建一个与 Window DC 兼容的位图
bitmap = ::CreateCompatibleBitmap(dc,lWidth,lHeight);
- 将位图选入 Mem DC
::SelectObject(memDC,bitmap);
- 将 DC 的背景涂成白色
SetBackColorToWhite(memDC);
- 现在将增强型图元文件播放到内存 DC 中;忽略其返回值,即使成功也可能为 false
PlayEnhMetaFile(memDC,hemf,&rect); DWORD dwRet = GetLastError();
- 如果设备支持调色板,则创建逻辑调色板
HPALETTE pal; if( GetDeviceCaps(dc,RASTERCAPS) & RC_PALETTE ) { UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256); LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize]; pLP->palVersion = 0x300; pLP->palNumEntries = GetSystemPaletteEntries( dc, 0, 255, pLP->palPalEntry ); // Create the palette pal = ::CreatePalette(pLP ); delete[] pLP; }
- 将位图转换为 DIB(有关完整源代码,请参阅下载内容)
HANDLE hDIB = DDBToDIB( bitmap, BI_RGB, pal );
- 将 DIB 写入文件(有关完整源代码,请参阅下载内容)
WriteDIB((char*)strBMPFile, hDIB );
- 清理。销毁窗口并返回 true
DeleteEnhMetaFile( hemf ); ::DestroyWindow(hWnd);
位图到 EMF
我添加了将 BMP 转换为 EMF 的新功能。以下是将 BMP 文件转换为 EMF 文件的分步过程。此代码基于 Gernot Frisch 的评论- 从文件加载位图以获取位图句柄
hBmp = (HBITMAP*)::LoadImage(NULL,pszBMPFile,IMAGE_BITMAP,0,0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);
- 获取
BITMAP
结构以获取 bmp 的高度和宽度GetObject(hBmp, sizeof(BITMAP), &bm );
- 创建 MemDC
hdcMem =::CreateCompatibleDC(GetDC(NULL));
- 将 BMP 选入 DC
SelectObject(hdcMem, hBmp);
- 创建 EMF 并获取 EMF DC
HDC emfdc = CreateEnhMetaFile(hdcMem, pszEMFFile, NULL, "Created by Pravarakhya");
- 从 BMF DC 块传输到 EMF DC
BitBlt(emfdc,0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
- 关闭 EMF 文件
::CloseEnhMetaFile(emfdc);
关注点
将矢量格式转换为栅格格式有很多优点,其中之一是将图像转换为 JPG 或 GIF 后可以在 Web 上使用。另一个优点是矢量图像可以缩放而不会损失质量。因此,任何巨大的 EMF 都可以缩减为小巧的缩略图图像,可以在 Web 上使用。在我们之前的项目中,我们想要创建 Visio 图(约 4000 平方英寸!)的缩略图,当我们以 100x100 像素的位图保存图时,我们损失了质量,图像无法接受。然后我们找到了将图保存为 EMF,然后将其缩放到 100x100 像素位图的解决方案。这个缩略图与其大尺寸版本一样好,没有任何质量损失。历史
- 2003年10月18日 - 版本 1.0 初始发布
- 2003年10月24日 - 版本 2.0
- 删除了窗口创建代码(感谢 Paolo Messina)
- 添加了 BMP 到 EMF 的功能(感谢 Gernot Frisch)