Introduction
I have come across loads of libraries which convert images from one format to other, but most of them are confined to conversion between raster formats like BMP, JPG, GIF etc., This article explains a class which encapsulates the functionality of converting a vector file format (EMF) to raster file format (BMP). Once an EMF is converted to BMP raster file format, it can then be converted to any other format using the freely available libraries like CxImage
.
Background
I had this problem of converting an EMF to BMP when I was doing my earlier project. I have searched a lot for help but couldn't find any except that many people asking similar question in discussion groups. I thought I should share this solution with others and wrote my first article to Code project. I hope it will be useful to you.
Using the code
Using this code is very simple. Just instantiate the class and call the convert function like this:
CConvertEMFToBMP conv;
BOOL bRet = conv.ConvertEMFToBMP(m_strEMF,m_strBMP,m_chkScale);
ConvertEMFToBMP
takes three parameters:
Example:
CConvertEMFToBMP conv;
BOOL bRet = conv.ConvertEMFToBMP("D:\temp\Sample.emf",
"D:\temp\sample.bmp",false);
To Convert a BMP to EMF:
CConvertEMFToBMP conv;
BOOL bRet = conv.ConvertBMPToEMF(m_strBMP,m_strEMF);
ConvertBMPToEMF
takes two paramters:
m_strBMP - path and file name of BMP file to be converted
m_strEMF - path and file name of EMF file
Example:
CConvertEMFToBMP conv;
BOOL bRet = conv.ConvertBMPToEMF("D:\temp\Sample.bmp",
"D:\temp\sample.emf",false);
How Does it Work?
There is no straight forward technique to convert an EMF to BMP.
ConvertEMFToBMP
converts an EMF to Bitmap by playing the metafile in the MemoryDC and then getting the DIB bits and storing them in the bitmap format. To get the memory DC, an invisible window is created and destroyed at the end of conversion. alternatively, the source code can be modified to take the
hwnd
to the
ConvertEMFToBMP
function. The scale image option scales the image to a fixed size #defined in the source code. This can also be modified so as to scale as per the user passed parameter. I have used only API functions so as to make the code light avoiding the MFC DLLs. The following is the step by step explanation of core logic of conversion.
- Create a window which is invisible, of any size
HWND hWnd = CreateWindow("#32770","NULL",~WS_VISIBLE,0,0,
100,100,NULL,NULL,NULL,0);
- Get the DC of the window
HDC dc = ::GetDC(hWnd);
- Get the handle to the emf file and get the EMF header
hemf = ::GetEnhMetaFile(strFileName);
ZeroMemory( &emh, sizeof(ENHMETAHEADER) );
emh.nSize = sizeof(ENHMETAHEADER);
if( GetEnhMetaFileHeader( hemf,
sizeof( ENHMETAHEADER ), &emh ) == 0 )
{
DeleteEnhMetaFile( hemf );
return FALSE;
}
- Get the width and height of the EMF
RECT rect;
float PixelsX, PixelsY, MMX, MMY;
float fAspectRatio;
long lWidth,lHeight;
PixelsX = (float)GetDeviceCaps( dc, HORZRES );
PixelsY = (float)GetDeviceCaps( dc, VERTRES );
MMX = (float)GetDeviceCaps( dc, HORZSIZE );
MMY = (float)GetDeviceCaps( dc, VERTSIZE );
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));
lWidth = (long)((float)(abs(rect.left - rect.right)));
lHeight =(long)((float)(abs(rect.top-rect.bottom )));
- If the image needs to be scaled, modify the width and height as per the aspect ratio
fAspectRatio = (float)lWidth/(float)lHeight;
if(bScaleImage)
{
if(fAspectRatio > 1 )
{
lWidth = X_MINIATUREFRAME;
lHeight = (long)((float)Y_MINIATUREFRAME / fAspectRatio);
}
else
{
lHeight = Y_MINIATUREFRAME;
lWidth = (long)((float)X_MINIATUREFRAME * fAspectRatio);
}
}
- Create a Memory DC compatible to WindowDC
memDC=::CreateCompatibleDC(dc);
- Create a bitmap compatible to Window DC
bitmap = ::CreateCompatibleBitmap(dc,lWidth,lHeight);
- Select the bitmap into the Mem DC
::SelectObject(memDC,bitmap);
- Paint the background of the DC to White
SetBackColorToWhite(memDC);
- Now play the enhanced metafile into the memory DC; ignore its return value it may be false even if successful
PlayEnhMetaFile(memDC,hemf,&rect);
DWORD dwRet = GetLastError();
- Create logical palette if device support a palette
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 );
pal = ::CreatePalette(pLP );
delete[] pLP;
}
- Convert the bitmap to a DIB (refer to the download for full source code)
HANDLE hDIB = DDBToDIB( bitmap, BI_RGB, pal );
- Write DIB to file (refer to the download for full source code)
WriteDIB((char*)strBMPFile, hDIB );
- Clean up. Destroy the window and return true
DeleteEnhMetaFile( hemf );
::DestroyWindow(hWnd);
Bitmap to EMF
I have added a new functionality of converting the BMP to EMF. Following is the step by step process for converting a BMP file to EMF File. This code is based on the comment submitted by Gernot Frisch
- Load the bitmap from file to get the bitmap handle
hBmp = (HBITMAP*)::LoadImage(NULL,pszBMPFile,IMAGE_BITMAP,0,0,
LR_CREATEDIBSECTION|LR_LOADFROMFILE);
- Get the
BITMAP
structure to get the bmp's height and width GetObject(hBmp, sizeof(BITMAP), &bm );
- Create MemDC
hdcMem =::CreateCompatibleDC(GetDC(NULL));
- Select the BMP into the DC
SelectObject(hdcMem, hBmp);
- Create EMF and get the emf DC
HDC emfdc = CreateEnhMetaFile(hdcMem, pszEMFFile, NULL,
"Created by Pravarakhya");
- Blit from BMF DC to EMF DC
BitBlt(emfdc,0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
- Close the EMF file
::CloseEnhMetaFile(emfdc);
Points of Interest
Converting a vector format to raster format has a lot of advantages, one of them is that the image can be used over the web when it is converted to JPG or GIF. One more advantage is that the vector images can be scaled down without losing the quality. So any huge EMF can be brought down to a miniature thumbnail image which can be used over web. In our earlier project, we wanted to create a thumbnail of huge Visio Diagrams (of around 4000 sq inches!), when we saved the diagram as 100X100 pixels bitmap, we were losing quality and image was not acceptable. We then found this solution of saving the diagram as EMF and then scaling it down to 100X100 pixels bitmap. This miniature is as good as its bigger one without any loss of quality.
History
- 18 Oct 2003 - Version 1.0 Initial Release
- 24 Oct 2003 - Version 2.0
- Removed the Window Creation code (Thanks to Paolo Messina)
- Added the BMP to EMF functionality (Thanks to Gernot Frisch )