65.9K
CodeProject 正在变化。 阅读更多。
Home

增强图元文件与位图之间的转换以及反之

2003年10月18日

CPOL

4分钟阅读

viewsIcon

228333

downloadIcon

9149

一篇关于 EMF 文件与位图文件格式之间转换以及 BMP 与 EMF 之间转换的文章

Sample Image - ConvertEMFToBMP.jpg

引言

我遇到了很多可以将图像从一种格式转换为另一种格式的库,但大多数都局限于栅格格式(如 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,使代码轻量级。以下是转换核心逻辑的分步说明。
  1. 创建一个大小任意的、不可见的窗口
        //Create an invisible window
        HWND hWnd = CreateWindow("#32770","NULL",~WS_VISIBLE,0,0,
                100,100,NULL,NULL,NULL,0);
    
  2. 获取窗口的 DC
        //Get the DC of the Window
        HDC dc = ::GetDC(hWnd);
    
  3. 获取 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;
        }
        
    
  4. 获取 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 )));
    
  5. 如果图像需要缩放,请根据纵横比修改宽度和高度
        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);
            }
        }
    
  6. 创建一个与 WindowDC 兼容的 Memory DC
        memDC=::CreateCompatibleDC(dc); 
    
  7. 创建一个与 Window DC 兼容的位图
        bitmap = ::CreateCompatibleBitmap(dc,lWidth,lHeight);
    
  8. 将位图选入 Mem DC
        ::SelectObject(memDC,bitmap);
    
  9. 将 DC 的背景涂成白色
        SetBackColorToWhite(memDC);
    
  10. 现在将增强型图元文件播放到内存 DC 中;忽略其返回值,即使成功也可能为 false
        PlayEnhMetaFile(memDC,hemf,&rect);    
        DWORD dwRet = GetLastError();
    
  11. 如果设备支持调色板,则创建逻辑调色板
        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;
        }
    
  12. 将位图转换为 DIB(有关完整源代码,请参阅下载内容)
        HANDLE hDIB = DDBToDIB( bitmap, BI_RGB, pal );
    
  13. 将 DIB 写入文件(有关完整源代码,请参阅下载内容)
        WriteDIB((char*)strBMPFile, hDIB );
    
  14. 清理。销毁窗口并返回 true
        DeleteEnhMetaFile( hemf );
        ::DestroyWindow(hWnd);
    

位图到 EMF

我添加了将 BMP 转换为 EMF 的新功能。以下是将 BMP 文件转换为 EMF 文件的分步过程。此代码基于 Gernot Frisch 的评论
  1. 从文件加载位图以获取位图句柄
        hBmp = (HBITMAP*)::LoadImage(NULL,pszBMPFile,IMAGE_BITMAP,0,0, 
                LR_CREATEDIBSECTION|LR_LOADFROMFILE);
  2. 获取 BITMAP 结构以获取 bmp 的高度和宽度
        GetObject(hBmp, sizeof(BITMAP), &bm );
  3. 创建 MemDC
        hdcMem =::CreateCompatibleDC(GetDC(NULL));
  4. 将 BMP 选入 DC
        SelectObject(hdcMem, hBmp);
  5. 创建 EMF 并获取 EMF DC
        HDC emfdc = CreateEnhMetaFile(hdcMem, pszEMFFile, NULL, 
              "Created by Pravarakhya");
    
  6. 从 BMF DC 块传输到 EMF DC
        BitBlt(emfdc,0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
    
  7. 关闭 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)
增强型图元文件到位图的转换以及反之亦然 - CodeProject - 代码之家
© . All rights reserved.