在EnumEnhMetaFile解析期间从Windows EMF中提取图像?

时间:2012-04-13 17:18:46

标签: gdi+ gdi metafile

我正在修改的程序将EMF文件转换为其他格式(SVG)。它是这样做的 EnumEnhMetaFile(),它为EMF的每个记录调用程序的解析例程。这适用于矢量的东西。但是,当它到达EMR_STRETCHDIBITS时,它还需要能够拉出位图。 (最终目标实际上是一个base64编码的PNG,但是关键点在于抓住Bitmap。)Microsoft是否在此处提供了一个用于拉出图像的功能?向EMF添加位图的逆操作是abitmap.Draw,我需要的是某种可以在EnumEnhMetaFile数据处理中操作的abitmap.Read。

是否有一个函数可以将EMR_STRETCHDIBITS提供的数据偏移字段转换为Windows位图?注意,我不想将EMF渲染成位图,我想要存储在EMF中的原始位图。

感谢。

3 个答案:

答案 0 :(得分:0)

没有

但是这里是Wine的PlayEnhMetaFile用于绘制记录的代码的链接:http://source.winehq.org/source/dlls/gdi32/enhmetafile.c?v=wine-1.5.2#L1111

它非常简单,并且StretchDIBits调用具有位,BITMAPINFO和iUsage - 您需要插入CreateDIBSection和SetDIBits以获取HBITMAP所需的所有信息。 (如果你使用CreateDIBitmap,你可能会在进程中丢失信息,具体取决于当前的显示模式。呃。你不能直接将这些位复制到DIB中,因为这些位可能是RLE压缩的.SetDIBits会正确处理它。 )

好的,所以你需要获得传递给SetDIBits的高度。将它从BITMAPINFO中拉出是有问题的,因为它实际上可能是具有不同结构的BITMAPCOREINFO。可能最容易创建HBITMAP然后检查它的高度。

我认为如果使用DIB_PAL_COLORS,调色板将不会在BITMAPINFO中,而是通过之前的记录选择到HDC中,因此您最好播放那些操纵DC并使用您给出的HDC的记录。

所以,把它们放在一起,这样的事情(未经测试,缺乏错误检查)应该有效:

HBITMAP bitmap_from_stretchdibits(HDC hdc, const ENHMETARECORD *lpEMFR)
{
    const EMRSTRETCHDIBITS *pStretchDIBits = (const EMRSTRETCHDIBITS *)lpEMFR;
    BITMAP bm;
    HBITMAP hbm;

    hbm = CreateDIBitmap(
        hdc,
        (const BITMAPINFO *)((const BYTE *)lpEMFR + pStretchDIBits->offBmiSrc),
        pStretchDIBits->iUsageSrc,
        NULL,
        NULL,
        0);

    if (hbm)
    {
        GetObjectA(hbm, sizeof(bm), &bm);

        SetDIBits(
            hdc,
            hbm,
            1,
            abs(bm.bmHeight),
            (const BYTE *)lpEMFR + pStretchDIBits->offBitsSrc,
            (const BITMAPINFO *)((const BYTE *)lpEMFR + pStretchDIBits->offBmiSrc),
            pStretchDIBits->iUsageSrc);
    }

    return hbm;
}

答案 1 :(得分:0)

这样做 - 但它保存的PNG是压缩的。

        BITMAPINFO *pbitmapinfo = (BITMAPINFO *)((char *)lpEMFR + pEmr->offBmiSrc);
        void *pBitsInMem = (char *)lpEMFR + pEmr->offBitsSrc;
        HBITMAP hbm;
        HDC tmpDC = CreateDC("DISPLAY", "", NULL, NULL);
        hbm = CreateDIBitmap(
            tmpDC,
            &(pbitmapinfo->bmiHeader),
            CBM_INIT,
            pBitsInMem,
            pbitmapinfo,
            DIB_RGB_COLORS);
        if(hbm){
          Gdiplus::Bitmap *pbmp = NULL;
          pbmp = Gdiplus::Bitmap::FromHBITMAP(hbm,NULL);
          CLSID pngClsid;
          GetEncoderClsid(L"image/png", &pngClsid);

          pbmp->Save(L"C:\\Temp\\scratch.png",&pngClsid, NULL);
          delete pbmp;
       }
       (void) DeleteObject(hbm);
       (void) DeleteDC(tmpDC);

答案 2 :(得分:0)

我刚发现的一件事是,如果使用CreateDIBitmap创建HBM,GetObjectA将返回NULL bmbits指针。为了使bm.bmbits指向数据,必须使用CreateDIBSection。问题是第二个函数显然无法从EMR_STRETCHDIBITS记录中读取数据!所以你的选择是:

  1. 数据从文件读入HBM,但没有通过此机制指向该数据的指针
  2. HBM创建了正确的大小,但没有从文件中读取数据。机制返回一个指针,但它指向一个充满零值的缓冲区!
  3. 我正在重新审视这一点,因为GDI +保存到文件函数并不保留JPEG质量= 100模式或PNG中的所有可用数据,这是我唯一的两种文件类型选项。试图将数据转换为GDK :: Pixbuf。据推测,Bitmap或HBitmap对象中的某个地方有一个指向它的指针。