0

我正在修改的程序将 EMF 文件转换为不同的格式 (SVG)。它使用 EnumEnhMetaFile() 执行此操作,该函数为 EMF 的每条记录调用程序的解析例程。这对矢量素材非常有用。但是,它还需要能够在遇到 EMR_STRETCHDIBITS 时拉出位图。(最终目标实际上是一个 base64 编码的 PNG,但症结在于抓取位图。)微软是否在某个地方提供了一个函数来提取图像?向 EMF 添加位图的逆操作是 abitmap.Draw,我需要的是某种 abitmap.Read,它可以在 EnumEnhMetaFile 数据处理中进行操作。

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

谢谢。

4

3 回答 3

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);
于 2012-04-16T18:08:12.713 回答
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;
}
于 2012-04-14T03:07:53.057 回答
0

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

  1. 数据从文件读取到 HBM,但没有通过此机制指向该数据的指针
  2. HBM 创建了正确的大小,但没有从文件中读取数据。机制返回一个指针,但它指向一个充满零值的缓冲区!

我正在重新检查这一点,因为 GDI+ 保存到文件功能没有保留 JPEG 质量 = 100 模式或 PNG 的所有可用数据,这是我仅有的两个文件类型选项。试图将数据放入 GDK::Pixbuf。大概在 Bitmap 或 HBitmap 对象的某个地方有一个指向它的指针。

于 2012-04-17T18:24:47.780 回答